@@ -127,11 +127,18 @@ describe('MessageConversionStrategy', () => {
127127 } ) ;
128128
129129 // Tool result messages (function responses from user)
130+ // NOTE: Each test includes matching tool call + tool result pairs because
131+ // orphaned tool results (without matching tool_use) are filtered out to
132+ // prevent "unexpected tool_use_id found in tool_result blocks" errors
130133
131134 t (
132135 'tests that function response converts to tool role not user role' ,
133136 ( ) => {
134137 const contents : Content [ ] = [
138+ {
139+ role : 'model' ,
140+ parts : [ { functionCall : { id : 'call_123' , name : 'get_weather' , args : { } } } ] ,
141+ } ,
135142 {
136143 role : 'user' ,
137144 parts : [
@@ -149,14 +156,18 @@ describe('MessageConversionStrategy', () => {
149156 const result = strategy . geminiToVercel ( contents ) ;
150157
151158 // CRITICAL: Must be 'tool' role, not 'user'
152- expect ( result [ 0 ] . role ) . toBe ( 'tool' ) ;
159+ expect ( result [ 1 ] . role ) . toBe ( 'tool' ) ;
153160 } ,
154161 ) ;
155162
156163 t (
157164 'tests that function response content is array of tool-result parts' ,
158165 ( ) => {
159166 const contents : Content [ ] = [
167+ {
168+ role : 'model' ,
169+ parts : [ { functionCall : { id : 'call_456' , name : 'search' , args : { } } } ] ,
170+ } ,
160171 {
161172 role : 'user' ,
162173 parts : [
@@ -173,8 +184,8 @@ describe('MessageConversionStrategy', () => {
173184
174185 const result = strategy . geminiToVercel ( contents ) ;
175186
176- expect ( Array . isArray ( result [ 0 ] . content ) ) . toBe ( true ) ;
177- const content = result [ 0 ] . content as VercelContentPart [ ] ;
187+ expect ( Array . isArray ( result [ 1 ] . content ) ) . toBe ( true ) ;
188+ const content = result [ 1 ] . content as VercelContentPart [ ] ;
178189 const toolResult = content [ 0 ] as VercelToolResultPart ;
179190 expect ( toolResult . type ) . toBe ( 'tool-result' ) ;
180191 expect ( toolResult . toolCallId ) . toBe ( 'call_456' ) ;
@@ -184,6 +195,10 @@ describe('MessageConversionStrategy', () => {
184195
185196 t ( 'tests that function response output contains structured response per v5' , ( ) => {
186197 const contents : Content [ ] = [
198+ {
199+ role : 'model' ,
200+ parts : [ { functionCall : { id : 'call_789' , name : 'get_data' , args : { } } } ] ,
201+ } ,
187202 {
188203 role : 'user' ,
189204 parts : [
@@ -200,7 +215,7 @@ describe('MessageConversionStrategy', () => {
200215
201216 const result = strategy . geminiToVercel ( contents ) ;
202217
203- const content = result [ 0 ] . content as VercelContentPart [ ] ;
218+ const content = result [ 1 ] . content as VercelContentPart [ ] ;
204219 const toolResult = content [ 0 ] as VercelToolResultPart ;
205220 // AI SDK v5 uses structured output format
206221 expect ( toolResult . output ) . toEqual ( { type : 'json' , value : { data : 'test' , success : true } } ) ;
@@ -210,6 +225,10 @@ describe('MessageConversionStrategy', () => {
210225 'tests that function response with error field uses error output type' ,
211226 ( ) => {
212227 const contents : Content [ ] = [
228+ {
229+ role : 'model' ,
230+ parts : [ { functionCall : { id : 'call_error' , name : 'broken_tool' , args : { } } } ] ,
231+ } ,
213232 {
214233 role : 'user' ,
215234 parts : [
@@ -226,7 +245,7 @@ describe('MessageConversionStrategy', () => {
226245
227246 const result = strategy . geminiToVercel ( contents ) ;
228247
229- const content = result [ 0 ] . content as VercelContentPart [ ] ;
248+ const content = result [ 1 ] . content as VercelContentPart [ ] ;
230249 const toolResult = content [ 0 ] as VercelToolResultPart ;
231250 // AI SDK v5 uses error-text or error-json for error responses
232251 expect ( toolResult . output ) . toEqual ( {
@@ -240,6 +259,10 @@ describe('MessageConversionStrategy', () => {
240259 'tests that function response without response field uses empty json output' ,
241260 ( ) => {
242261 const contents : Content [ ] = [
262+ {
263+ role : 'model' ,
264+ parts : [ { functionCall : { id : 'call_no_response' , name : 'simple_tool' , args : { } } } ] ,
265+ } ,
243266 {
244267 role : 'user' ,
245268 parts : [
@@ -255,7 +278,7 @@ describe('MessageConversionStrategy', () => {
255278
256279 const result = strategy . geminiToVercel ( contents ) ;
257280
258- const content = result [ 0 ] . content as VercelContentPart [ ] ;
281+ const content = result [ 1 ] . content as VercelContentPart [ ] ;
259282 const toolResult = content [ 0 ] as VercelToolResultPart ;
260283 // AI SDK v5 uses structured output format
261284 expect ( toolResult . output ) . toEqual ( { type : 'json' , value : { } } ) ;
@@ -287,6 +310,10 @@ describe('MessageConversionStrategy', () => {
287310
288311 t ( 'tests that function response without name uses unknown' , ( ) => {
289312 const contents : Content [ ] = [
313+ {
314+ role : 'model' ,
315+ parts : [ { functionCall : { id : 'call_no_name' , name : 'some_tool' , args : { } } } ] ,
316+ } ,
290317 {
291318 role : 'user' ,
292319 parts : [
@@ -302,7 +329,7 @@ describe('MessageConversionStrategy', () => {
302329
303330 const result = strategy . geminiToVercel ( contents ) ;
304331
305- const content = result [ 0 ] . content as VercelContentPart [ ] ;
332+ const content = result [ 1 ] . content as VercelContentPart [ ] ;
306333 const toolResult = content [ 0 ] as VercelToolResultPart ;
307334 expect ( toolResult . toolName ) . toBe ( 'unknown' ) ;
308335 } ) ;
@@ -311,6 +338,13 @@ describe('MessageConversionStrategy', () => {
311338 'tests that multiple function responses in one message all convert' ,
312339 ( ) => {
313340 const contents : Content [ ] = [
341+ {
342+ role : 'model' ,
343+ parts : [
344+ { functionCall : { id : 'call_1' , name : 'tool1' , args : { } } } ,
345+ { functionCall : { id : 'call_2' , name : 'tool2' , args : { } } } ,
346+ ] ,
347+ } ,
314348 {
315349 role : 'user' ,
316350 parts : [
@@ -334,7 +368,7 @@ describe('MessageConversionStrategy', () => {
334368
335369 const result = strategy . geminiToVercel ( contents ) ;
336370
337- const content = result [ 0 ] . content as VercelContentPart [ ] ;
371+ const content = result [ 1 ] . content as VercelContentPart [ ] ;
338372 expect ( content ) . toHaveLength ( 2 ) ;
339373 const toolResult0 = content [ 0 ] as VercelToolResultPart ;
340374 const toolResult1 = content [ 1 ] as VercelToolResultPart ;
@@ -344,6 +378,9 @@ describe('MessageConversionStrategy', () => {
344378 ) ;
345379
346380 // Assistant messages with tool calls
381+ // NOTE: Each test includes matching tool call + tool result pairs because
382+ // orphaned tool calls (without matching tool_result) are filtered out to
383+ // prevent "tool_use ids were found without tool_result blocks" errors
347384
348385 t (
349386 'tests that function call converts to assistant message with tool-call part' ,
@@ -361,6 +398,10 @@ describe('MessageConversionStrategy', () => {
361398 } ,
362399 ] ,
363400 } ,
401+ {
402+ role : 'user' ,
403+ parts : [ { functionResponse : { id : 'call_abc' , name : 'search' , response : { } } } ] ,
404+ } ,
364405 ] ;
365406
366407 const result = strategy . geminiToVercel ( contents ) ;
@@ -391,6 +432,10 @@ describe('MessageConversionStrategy', () => {
391432 } ,
392433 ] ,
393434 } ,
435+ {
436+ role : 'user' ,
437+ parts : [ { functionResponse : { id : 'call_def' , name : 'get_weather' , response : { } } } ] ,
438+ } ,
394439 ] ;
395440
396441 const result = strategy . geminiToVercel ( contents ) ;
@@ -423,6 +468,10 @@ describe('MessageConversionStrategy', () => {
423468 } ,
424469 ] ,
425470 } ,
471+ {
472+ role : 'user' ,
473+ parts : [ { functionResponse : { id : 'call_search' , name : 'search' , response : { } } } ] ,
474+ } ,
426475 ] ;
427476
428477 const result = strategy . geminiToVercel ( contents ) ;
@@ -473,6 +522,10 @@ describe('MessageConversionStrategy', () => {
473522 } ,
474523 ] ,
475524 } ,
525+ {
526+ role : 'user' ,
527+ parts : [ { functionResponse : { id : 'call_xyz' , name : 'unknown' , response : { } } } ] ,
528+ } ,
476529 ] ;
477530
478531 const result = strategy . geminiToVercel ( contents ) ;
@@ -495,6 +548,10 @@ describe('MessageConversionStrategy', () => {
495548 } ,
496549 ] ,
497550 } ,
551+ {
552+ role : 'user' ,
553+ parts : [ { functionResponse : { id : 'call_no_args' , name : 'simple_tool' , response : { } } } ] ,
554+ } ,
498555 ] ;
499556
500557 const result = strategy . geminiToVercel ( contents ) ;
@@ -525,6 +582,13 @@ describe('MessageConversionStrategy', () => {
525582 } ,
526583 ] ,
527584 } ,
585+ {
586+ role : 'user' ,
587+ parts : [
588+ { functionResponse : { id : 'call_1' , name : 'tool1' , response : { } } } ,
589+ { functionResponse : { id : 'call_2' , name : 'tool2' , response : { } } } ,
590+ ] ,
591+ } ,
528592 ] ;
529593
530594 const result = strategy . geminiToVercel ( contents ) ;
0 commit comments