@@ -95,6 +95,15 @@ function bedrockStopReason(
9595 return overrideFinishReason ;
9696}
9797
98+ /**
99+ * Build a Bedrock-style usage object from optional overrides.
100+ *
101+ * When no overrides are provided (the common case for mock fixtures),
102+ * returns all-zero token counts. This is intentional — aimock does not
103+ * attempt to estimate token usage from fixture content. Callers that
104+ * need realistic usage numbers should set `usage` in their fixture's
105+ * response overrides.
106+ */
98107function bedrockUsage ( overrides ?: ResponseOverrides ) : {
99108 input_tokens : number ;
100109 output_tokens : number ;
@@ -119,6 +128,7 @@ function extractTextContent(content: string | BedrockContentBlock[]): string {
119128export function bedrockToCompletionRequest (
120129 req : BedrockRequest ,
121130 modelId : string ,
131+ logger ?: Logger ,
122132) : ChatCompletionRequest {
123133 const messages : ChatMessage [ ] = [ ] ;
124134
@@ -140,6 +150,17 @@ export function bedrockToCompletionRequest(
140150 if ( msg . role === "user" ) {
141151 // Check for tool_result blocks
142152 if ( typeof msg . content !== "string" && Array . isArray ( msg . content ) ) {
153+ // Warn about non-text content blocks that will be dropped (image, document, etc.)
154+ const unsupportedBlocks = msg . content . filter (
155+ ( b ) => b . type !== "text" && b . type !== "tool_result" ,
156+ ) ;
157+ if ( unsupportedBlocks . length > 0 && logger ) {
158+ const types = [ ...new Set ( unsupportedBlocks . map ( ( b ) => b . type ) ) ] . join ( ", " ) ;
159+ logger . warn (
160+ `Bedrock user message contains unsupported content block types [${ types } ] — these will be dropped during conversion` ,
161+ ) ;
162+ }
163+
143164 const toolResults = msg . content . filter ( ( b ) => b . type === "tool_result" ) ;
144165 const textBlocks = msg . content . filter ( ( b ) => b . type === "text" ) ;
145166
@@ -183,22 +204,35 @@ export function bedrockToCompletionRequest(
183204 if ( toolUseBlocks . length > 0 ) {
184205 messages . push ( {
185206 role : "assistant" ,
186- content : textContent || null ,
187- tool_calls : toolUseBlocks . map ( ( b ) => ( {
188- id : b . id ?? generateToolUseId ( ) ,
189- type : "function" as const ,
190- function : {
191- name : b . name ?? "" ,
192- arguments : typeof b . input === "string" ? b . input : JSON . stringify ( b . input ?? { } ) ,
193- } ,
194- } ) ) ,
207+ content : textContent ?? null ,
208+ tool_calls : toolUseBlocks . map ( ( b , index ) => {
209+ if ( ! b . id && logger ) {
210+ logger . warn (
211+ `Bedrock assistant tool_use block at index ${ index } is missing an id — using deterministic fallback "tool_use_${ index } "` ,
212+ ) ;
213+ }
214+ return {
215+ id : b . id ?? `tool_use_${ index } ` ,
216+ type : "function" as const ,
217+ function : {
218+ name : b . name ?? "" ,
219+ arguments : typeof b . input === "string" ? b . input : JSON . stringify ( b . input ?? { } ) ,
220+ } ,
221+ } ;
222+ } ) ,
195223 } ) ;
196224 } else {
197- messages . push ( { role : "assistant" , content : textContent || null } ) ;
225+ messages . push ( { role : "assistant" , content : textContent ?? null } ) ;
198226 }
199227 } else {
200228 messages . push ( { role : "assistant" , content : null } ) ;
201229 }
230+ } else {
231+ if ( logger ) {
232+ logger . warn (
233+ `Bedrock message has unexpected role "${ ( msg as { role : string } ) . role } " — skipping` ,
234+ ) ;
235+ }
202236 }
203237 }
204238
@@ -347,7 +381,7 @@ export async function handleBedrock(
347381 }
348382
349383 // Convert to ChatCompletionRequest for fixture matching
350- const completionReq = bedrockToCompletionRequest ( bedrockReq , modelId ) ;
384+ const completionReq = bedrockToCompletionRequest ( bedrockReq , modelId , logger ) ;
351385 completionReq . _endpointType = "chat" ;
352386
353387 const testId = getTestId ( req ) ;
@@ -449,7 +483,8 @@ export async function handleBedrock(
449483 body : completionReq ,
450484 response : { status, fixture } ,
451485 } ) ;
452- // Anthropic-style error format (Bedrock uses Claude): { type: "error", error: { type, message } }
486+ // Bedrock Claude error format: { type: "error", error: { type, message } }
487+ // Uses ?? (nullish coalescing) intentionally — preserves explicit empty-string types from fixtures.
453488 const anthropicError = {
454489 type : "error" ,
455490 error : {
@@ -526,6 +561,9 @@ export async function handleBedrock(
526561
527562 // Tool call response
528563 if ( isToolCallResponse ( response ) ) {
564+ if ( "webSearches" in response ) {
565+ logger . warn ( "webSearches in fixture response are not supported for Bedrock API — ignoring" ) ;
566+ }
529567 const overrides = extractOverrides ( response ) ;
530568 journal . add ( {
531569 method : req . method ?? "POST" ,
@@ -938,7 +976,8 @@ export async function handleBedrockStream(
938976 return ;
939977 }
940978
941- const completionReq = bedrockToCompletionRequest ( bedrockReq , modelId ) ;
979+ const completionReq = bedrockToCompletionRequest ( bedrockReq , modelId , logger ) ;
980+ completionReq . stream = true ;
942981 completionReq . _endpointType = "chat" ;
943982
944983 const testId = getTestId ( req ) ;
@@ -1042,7 +1081,8 @@ export async function handleBedrockStream(
10421081 body : completionReq ,
10431082 response : { status, fixture } ,
10441083 } ) ;
1045- // Anthropic-style error format (Bedrock uses Claude): { type: "error", error: { type, message } }
1084+ // Bedrock Claude error format: { type: "error", error: { type, message } }
1085+ // Uses ?? (nullish coalescing) intentionally — preserves explicit empty-string types from fixtures.
10461086 const anthropicError = {
10471087 type : "error" ,
10481088 error : {
@@ -1130,6 +1170,9 @@ export async function handleBedrockStream(
11301170
11311171 // Tool call response — stream as Event Stream
11321172 if ( isToolCallResponse ( response ) ) {
1173+ if ( "webSearches" in response ) {
1174+ logger . warn ( "webSearches in fixture response are not supported for Bedrock API — ignoring" ) ;
1175+ }
11331176 const overrides = extractOverrides ( response ) ;
11341177 const journalEntry = journal . add ( {
11351178 method : req . method ?? "POST" ,
0 commit comments