@@ -297,6 +297,82 @@ describe("OpenAI Chat -> Responses converters", () => {
297297 expect ( tool . content ) . toBe ( "4" ) ;
298298 } ) ;
299299
300+ it ( "groups parallel function_call items into a single assistant message with multiple tool_calls" , ( ) => {
301+ const req : ResponsesRequestBody = {
302+ model : "gpt-4o-mini" ,
303+ input : [
304+ { type : "message" , role : "user" , content : "Search for healthcare and finance projects" } ,
305+ { type : "function_call" , call_id : "fc_call1" , name : "search_projects" , arguments : "{\"queries\": [\"healthcare\"]}" } ,
306+ { type : "function_call" , call_id : "fc_call2" , name : "search_projects" , arguments : "{\"queries\": [\"finance\"]}" } ,
307+ { type : "function_call_output" , call_id : "fc_call1" , output : "{\"projects\": [{\"name\": \"Healthcare Strategy\"}]}" } ,
308+ { type : "function_call_output" , call_id : "fc_call2" , output : "{\"projects\": [{\"name\": \"Finance Overview\"}]}" } ,
309+ ] ,
310+ } ;
311+ const oai = toChatCompletions ( req ) ;
312+
313+ // Should have: user message, ONE assistant message with 2 tool_calls, 2 tool messages
314+ expect ( oai . messages ?. length ) . toBe ( 4 ) ;
315+
316+ // First message: user
317+ expect ( oai . messages ?. [ 0 ] ) . toMatchObject ( { role : "user" , content : "Search for healthcare and finance projects" } ) ;
318+
319+ // Second message: assistant with BOTH tool_calls in a single message
320+ const assistant = oai . messages ?. [ 1 ] as any ;
321+ expect ( assistant . role ) . toBe ( "assistant" ) ;
322+ expect ( assistant . tool_calls ?. length ) . toBe ( 2 ) ;
323+ expect ( assistant . tool_calls ?. [ 0 ] ) . toMatchObject ( {
324+ id : "fc_call1" ,
325+ type : "function" ,
326+ function : { name : "search_projects" , arguments : "{\"queries\": [\"healthcare\"]}" }
327+ } ) ;
328+ expect ( assistant . tool_calls ?. [ 1 ] ) . toMatchObject ( {
329+ id : "fc_call2" ,
330+ type : "function" ,
331+ function : { name : "search_projects" , arguments : "{\"queries\": [\"finance\"]}" }
332+ } ) ;
333+
334+ // Third and fourth messages: tool responses
335+ const tool1 = oai . messages ?. [ 2 ] as any ;
336+ expect ( tool1 . role ) . toBe ( "tool" ) ;
337+ expect ( tool1 . tool_call_id ) . toBe ( "fc_call1" ) ;
338+
339+ const tool2 = oai . messages ?. [ 3 ] as any ;
340+ expect ( tool2 . role ) . toBe ( "tool" ) ;
341+ expect ( tool2 . tool_call_id ) . toBe ( "fc_call2" ) ;
342+ } ) ;
343+
344+ it ( "handles multiple separate tool call rounds correctly" , ( ) => {
345+ const req : ResponsesRequestBody = {
346+ model : "gpt-4o-mini" ,
347+ input : [
348+ { type : "message" , role : "user" , content : "Do two things" } ,
349+ // First round: 2 parallel calls
350+ { type : "function_call" , call_id : "call_a" , name : "func_a" , arguments : "{}" } ,
351+ { type : "function_call" , call_id : "call_b" , name : "func_b" , arguments : "{}" } ,
352+ { type : "function_call_output" , call_id : "call_a" , output : "result_a" } ,
353+ { type : "function_call_output" , call_id : "call_b" , output : "result_b" } ,
354+ // Second round: 1 call
355+ { type : "function_call" , call_id : "call_c" , name : "func_c" , arguments : "{}" } ,
356+ { type : "function_call_output" , call_id : "call_c" , output : "result_c" } ,
357+ ] ,
358+ } ;
359+ const oai = toChatCompletions ( req ) ;
360+
361+ // Should have: user, assistant (2 calls), tool, tool, assistant (1 call), tool
362+ expect ( oai . messages ?. length ) . toBe ( 6 ) ;
363+
364+ // First assistant message should have 2 tool_calls
365+ const assistant1 = oai . messages ?. [ 1 ] as any ;
366+ expect ( assistant1 . role ) . toBe ( "assistant" ) ;
367+ expect ( assistant1 . tool_calls ?. length ) . toBe ( 2 ) ;
368+
369+ // Second assistant message should have 1 tool_call
370+ const assistant2 = oai . messages ?. [ 4 ] as any ;
371+ expect ( assistant2 . role ) . toBe ( "assistant" ) ;
372+ expect ( assistant2 . tool_calls ?. length ) . toBe ( 1 ) ;
373+ expect ( assistant2 . tool_calls ?. [ 0 ] . id ) . toBe ( "call_c" ) ;
374+ } ) ;
375+
300376 it ( "maps Responses tools (flattened) to Chat tools (nested)" , ( ) => {
301377 const req = {
302378 model : "gpt-4o-mini" ,
0 commit comments