@@ -3402,6 +3402,11 @@ async def stream_body_handler(response, form_data):
34023402
34033403 response_tool_calls = []
34043404
3405+ # Responses API output_index values are relative to the
3406+ # current response (0, 1, ...). During re-invocations,
3407+ # output has accumulated history, so we offset indices.
3408+ responses_output_offset = len (output )
3409+
34053410 delta_count = 0
34063411 delta_chunk_size = max (
34073412 CHAT_RESPONSE_STREAM_DELTA_CHUNK_SIZE ,
@@ -3470,6 +3475,10 @@ async def flush_pending_delta_data(threshold: int = 0):
34703475 )
34713476 # Check for Responses API events (type field starts with "response.")
34723477 elif data .get ('type' , '' ).startswith ('response.' ):
3478+ # Offset output_index for re-invocations
3479+ if responses_output_offset and 'output_index' in data :
3480+ data = {** data , 'output_index' : data ['output_index' ] + responses_output_offset }
3481+
34733482 output , response_metadata = handle_responses_streaming_event (data , output )
34743483
34753484 processed_data = {
@@ -3480,9 +3489,14 @@ async def flush_pending_delta_data(threshold: int = 0):
34803489 # print(data)
34813490 # print(processed_data)
34823491
3483- # Merge any metadata (usage, done, etc.)
3492+ # Merge any metadata (usage, etc.)
3493+ # Strip 'done' — response.completed emits
3494+ # it but we may still need to execute tool
3495+ # calls. The outer middleware manages the
3496+ # actual completion signal.
34843497 if response_metadata :
34853498 processed_data .update (response_metadata )
3499+ processed_data .pop ('done' , None )
34863500
34873501 await event_emitter (
34883502 {
@@ -3889,9 +3903,18 @@ async def flush_pending_delta_data(threshold: int = 0):
38893903
38903904 # Responses API path: extract function_call items from output
38913905 if not response_tool_calls and output :
3906+ # Collect call_ids that already have results
3907+ handled_call_ids = {
3908+ item .get ('call_id' )
3909+ for item in output
3910+ if item .get ('type' ) == 'function_call_output'
3911+ }
38923912 responses_api_tool_calls = []
38933913 for item in output :
3894- if item .get ('type' ) == 'function_call' and item .get ('status' ) != 'completed' :
3914+ if (
3915+ item .get ('type' ) == 'function_call'
3916+ and item .get ('call_id' ) not in handled_call_ids
3917+ ):
38953918 arguments = item .get ('arguments' , '{}' )
38963919 responses_api_tool_calls .append ({
38973920 'id' : item .get ('call_id' , '' ),
@@ -4210,7 +4233,17 @@ async def flush_pending_delta_data(threshold: int = 0):
42104233 )
42114234
42124235 if isinstance (res , StreamingResponse ):
4236+ # Save output before re-invocation. Responses API
4237+ # response.completed replaces the entire output
4238+ # with only the new response's items, losing tool
4239+ # call history. Restore previous items afterward.
4240+ output_prefix = list (output )
42134241 await stream_body_handler (res , new_form_data )
4242+ if output_prefix and (
4243+ not output
4244+ or output [0 ].get ('id' ) != output_prefix [0 ].get ('id' )
4245+ ):
4246+ output [:0 ] = output_prefix
42144247 else :
42154248 break
42164249 except Exception as e :
0 commit comments