136136 ENABLE_FORWARD_USER_INFO_HEADERS ,
137137 FORWARD_SESSION_INFO_HEADER_CHAT_ID ,
138138 FORWARD_SESSION_INFO_HEADER_MESSAGE_ID ,
139+ ENABLE_RESPONSES_API_STATEFUL ,
139140)
140141from open_webui .utils .headers import include_user_info_headers
141142from open_webui .constants import TASKS
@@ -850,7 +851,11 @@ def handle_responses_streaming_event(
850851 if item .get ('type' ) == 'reasoning' and item .get ('status' ) != 'completed' :
851852 item ['status' ] = 'completed'
852853
853- return new_output , {'usage' : response_data .get ('usage' ), 'done' : True }
854+ return new_output , {
855+ 'usage' : response_data .get ('usage' ),
856+ 'done' : True ,
857+ 'response_id' : response_data .get ('id' ),
858+ }
854859
855860 elif event_type == 'response.in_progress' :
856861 # State Machine Event: In Progress
@@ -3393,6 +3398,7 @@ def set_last_text(out, text):
33933398
33943399 usage = None
33953400 prior_output = []
3401+ last_response_id = None
33963402
33973403 def full_output ():
33983404 return prior_output + output if prior_output else output
@@ -3431,6 +3437,7 @@ async def stream_body_handler(response, form_data):
34313437 nonlocal usage
34323438 nonlocal output
34333439 nonlocal prior_output
3440+ nonlocal last_response_id
34343441
34353442 response_tool_calls = []
34363443
@@ -3519,6 +3526,10 @@ async def flush_pending_delta_data(threshold: int = 0):
35193526 # calls. The outer middleware manages the
35203527 # actual completion signal.
35213528 if response_metadata :
3529+ if ENABLE_RESPONSES_API_STATEFUL :
3530+ response_id = response_metadata .pop ('response_id' , None )
3531+ if response_id :
3532+ last_response_id = response_id
35223533 processed_data .update (response_metadata )
35233534 processed_data .pop ('done' , None )
35243535
@@ -3927,10 +3938,12 @@ async def flush_pending_delta_data(threshold: int = 0):
39273938
39283939 # Responses API path: extract function_call items from output
39293940 if not response_tool_calls and output :
3930- # Collect call_ids that already have results
3941+ # Collect call_ids that already have results,
3942+ # including those from prior_output so we don't
3943+ # re-process tool calls from a previous turn.
39313944 handled_call_ids = {
39323945 item .get ('call_id' )
3933- for item in output
3946+ for item in ( prior_output + output )
39343947 if item .get ('type' ) == 'function_call_output'
39353948 }
39363949 responses_api_tool_calls = []
@@ -4249,11 +4262,20 @@ async def flush_pending_delta_data(threshold: int = 0):
42494262 ** form_data ,
42504263 'model' : model_id ,
42514264 'stream' : True ,
4252- 'messages' : [
4265+ }
4266+
4267+ if ENABLE_RESPONSES_API_STATEFUL and last_response_id :
4268+ system_message = get_system_message (form_data ['messages' ])
4269+ new_form_data ['messages' ] = (
4270+ ([system_message ] if system_message else [])
4271+ + convert_output_to_messages (output , raw = True )
4272+ )
4273+ new_form_data ['previous_response_id' ] = last_response_id
4274+ else :
4275+ new_form_data ['messages' ] = [
42534276 * form_data ['messages' ],
42544277 * convert_output_to_messages (output , raw = True ),
4255- ],
4256- }
4278+ ]
42574279
42584280 res = await generate_chat_completion (
42594281 request ,
@@ -4270,6 +4292,20 @@ async def flush_pending_delta_data(threshold: int = 0):
42704292 # ensures the UI shows tool history during
42714293 # streaming.
42724294 prior_output = list (output )
4295+ # Trim the trailing empty placeholder message
4296+ # so it doesn't persist as a ghost item once
4297+ # the new stream produces real content.
4298+ if (
4299+ prior_output
4300+ and prior_output [- 1 ].get ('type' ) == 'message'
4301+ and prior_output [- 1 ].get ('status' ) == 'in_progress'
4302+ ):
4303+ msg_parts = prior_output [- 1 ].get ('content' , [])
4304+ if (
4305+ not msg_parts
4306+ or (len (msg_parts ) == 1 and not msg_parts [0 ].get ('text' , '' ).strip ())
4307+ ):
4308+ prior_output .pop ()
42734309 output = []
42744310 await stream_body_handler (res , new_form_data )
42754311 output [:0 ] = prior_output
0 commit comments