@@ -6,7 +6,7 @@ import type { OpenResponsesOptions } from '../types/open-responses-options'
66import type { StopCondition } from '../types/stop-when'
77
88import { objCamelToSnake , postJSON , trampoline } from '@xsai/shared'
9- import { computeTotalUsage , executeTool , resolvePrepareStep } from '@xsai/shared-chat'
9+ import { computeTotalUsage , executeTool , resolvePrepareStep , toCompletionToolCall } from '@xsai/shared-chat'
1010import { closeControllers , createControlledStream , errorControllers , EventSourceParserStream , JsonMessageTransformStream } from '@xsai/shared-stream'
1111
1212import { normalizeInput } from './normalize-input'
@@ -230,11 +230,7 @@ export const responses = (options: ResponsesOptions): ResponsesResult => {
230230 . getReader ( )
231231 }
232232
233- const pushFunctionCallOutput = async ( functionCall : FunctionCall , step : {
234- events : Event [ ]
235- toolCalls : CompletionToolCall [ ]
236- toolResults : CompletionToolResult [ ]
237- } ) => {
233+ const executeFunctionCall = async ( functionCall : FunctionCall ) => {
238234 const { completionToolCall, completionToolResult, result } = await executeTool ( {
239235 abortSignal : options . abortSignal ,
240236 messages : [ ] ,
@@ -253,35 +249,31 @@ export const responses = (options: ResponsesOptions): ResponsesResult => {
253249 type : 'function_call_output' ,
254250 }
255251
256- step . toolCalls . push ( completionToolCall )
257- step . toolResults . push ( completionToolResult )
258- input . push ( normalizeOutput ( functionCallOutput ) )
259- step . events . push ( {
260- ...completionToolCall ,
261- type : 'tool-call.done' ,
262- } , {
263- ...completionToolResult ,
264- type : 'tool-result.done' ,
265- } )
252+ return { completionToolCall, completionToolResult, functionCallOutput }
266253 }
267254
268- const handleOutputItemDone = async ( event : Extract < FullEvent , { type : 'response.output_item.done' } > , step : {
255+ const handleOutputItemDone = ( event : Extract < FullEvent , { type : 'response.output_item.done' } > , step : {
269256 events : Event [ ]
257+ functionCalls : FunctionCall [ ]
270258 toolCalls : CompletionToolCall [ ]
271- toolResults : CompletionToolResult [ ]
272259 } ) => {
273260 if ( event . item == null )
274261 return
275262
276263 input . push ( normalizeOutput ( event . item ) )
277264
278265 if ( event . item . type === 'function_call' ) {
279- await pushFunctionCallOutput ( event . item , step )
266+ step . functionCalls . push ( event . item )
267+ const toolCall = toCompletionToolCall ( toToolCall ( event . item ) )
268+ step . toolCalls . push ( toolCall )
269+ step . events . push ( { ...toolCall , type : 'tool-call.done' } )
280270 }
281271 }
282272
273+ // eslint-disable-next-line sonarjs/cognitive-complexity
283274 const doStream = async ( ) => {
284275 const reader = await createReader ( )
276+ const functionCalls : FunctionCall [ ] = [ ]
285277 const toolCalls : CompletionToolCall [ ] = [ ]
286278 const toolResults : CompletionToolResult [ ] = [ ]
287279
@@ -293,7 +285,7 @@ export const responses = (options: ResponsesOptions): ResponsesResult => {
293285
294286 let shouldContinue = false
295287 const events = mapFullEvent ( event )
296- const step = { events, toolCalls, toolResults }
288+ const step = { events, functionCalls , toolCalls, toolResults }
297289
298290 // eslint-disable-next-line ts/switch-exhaustiveness-check
299291 switch ( event . type ) {
@@ -306,13 +298,31 @@ export const responses = (options: ResponsesOptions): ResponsesResult => {
306298 toolResults,
307299 } )
308300
309- shouldContinue = input . at ( - 1 ) ?. type === 'function_call_output'
310- && options . abortSignal ?. aborted !== true
311- && ! shouldStop ( stopWhen , {
312- input,
313- step : completionStep ,
314- steps : [ ...steps , completionStep ] ,
315- } )
301+ if ( options . abortSignal ?. aborted === true )
302+ throw options . abortSignal . reason ?? new Error ( 'This operation was aborted' )
303+
304+ const stop = shouldStop ( stopWhen , {
305+ input,
306+ step : completionStep ,
307+ steps : [ ...steps , completionStep ] ,
308+ } )
309+
310+ if ( ! stop && functionCalls . length > 0 ) {
311+ const stepDoneEvent = events . pop ( )
312+ const results = await Promise . all ( functionCalls . map ( executeFunctionCall ) )
313+
314+ toolCalls . length = 0
315+ for ( const { completionToolCall, completionToolResult, functionCallOutput } of results ) {
316+ toolCalls . push ( completionToolCall )
317+ toolResults . push ( completionToolResult )
318+ input . push ( normalizeOutput ( functionCallOutput ) )
319+ events . push ( { ...completionToolResult , type : 'tool-result.done' } )
320+ }
321+ if ( stepDoneEvent != null )
322+ events . push ( stepDoneEvent )
323+ }
324+
325+ shouldContinue = functionCalls . length > 0 && ! stop && ! options . abortSignal ?. aborted
316326
317327 pushStep ( completionStep )
318328
@@ -333,7 +343,7 @@ export const responses = (options: ResponsesOptions): ResponsesResult => {
333343 } )
334344 break
335345 case 'response.output_item.done' :
336- await handleOutputItemDone ( event , step )
346+ handleOutputItemDone ( event , step )
337347 break
338348 default :
339349 break
0 commit comments