Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
This commit is contained in:
Timothy Jaeryang Baek
2026-09-21 11:21:42 -04:00
co-authored by Classic298
parent 30881dbcc9
commit 478d1785fd
2 changed files with 50 additions and 28 deletions
+2 -1
View File
@@ -1083,7 +1083,8 @@ async def _make_channel_emitter(request_info):
state['output'] = copy.deepcopy(output)
now = time.time()
if done or (now - state['last_emit_at']) >= THROTTLE_INTERVAL:
# Tool boundaries must publish all results before waiting on the next model response.
if done or data.get('flush') or (now - state['last_emit_at']) >= THROTTLE_INTERVAL:
state['last_emit_at'] = now
await _emit_channel_update(content, done, output if isinstance(output, list) else None)
+48 -27
View File
@@ -4974,6 +4974,14 @@ async def streaming_chat_response_handler(response, ctx):
**response_data,
'output_index': response_data['output_index'] + len(prior_output),
}
if prior_output and isinstance(response_data.get('response'), dict):
# response.output is this round only; the response.completed reducer drops earlier rounds
return {
**response_data,
'response': {
key: value for key, value in response_data['response'].items() if key != 'output'
},
}
return response_data
async def flush_pending_delta_data(threshold: int = 0):
@@ -5808,6 +5816,7 @@ async def streaming_chat_response_handler(response, ctx):
if responses_api_tool_calls:
tool_calls.append(_split_tool_calls(responses_api_tool_calls))
output_start = len(prior_output)
try:
await stream_body_handler(response, form_data)
finally:
@@ -5839,6 +5848,42 @@ async def streaming_chat_response_handler(response, ctx):
get_content_from_message(original_system_message) if original_system_message else None
)
async def emit_output():
# Channels publish whole messages; Continue can merge into the preceding item.
snapshot = continuing or (metadata.get('chat_id') or '').startswith('channel:')
frontend_output = []
for item in full_output() if snapshot else full_output()[output_start:]:
if item.get('type') == 'function_call_output':
# input_image parts are base64 data URIs only for the LLM, via convert_output_to_messages
item = {
**item,
'output': [
part for part in item.get('output', []) if part.get('type') != 'input_image'
],
}
frontend_output.append(item)
if snapshot:
await event_emitter(
{
'type': 'chat:completion',
'data': {'output': frontend_output, 'flush': True},
}
)
return
for output_index, item in enumerate(frontend_output, start=output_start):
await event_emitter(
{
'type': 'response:completion',
'data': {
'type': 'response.output_item.done',
'output_index': output_index,
'item': item,
},
}
)
while tool_calls and (
max_tool_call_iterations is None or tool_call_iterations < max_tool_call_iterations
):
@@ -5906,14 +5951,7 @@ async def streaming_chat_response_handler(response, ctx):
)
return
await event_emitter(
{
'type': 'chat:completion',
'data': {
'output': full_output(),
},
}
)
await emit_output()
tools = metadata.get('tools', {})
@@ -6180,25 +6218,7 @@ async def streaming_chat_response_handler(response, ctx):
)
tool_call_sources.clear()
# Strip input_image parts (large base64 data URIs) from the
# output sent to the frontend — they're only for LLM consumption
# via convert_output_to_messages.
frontend_output = []
for item in full_output():
if item.get('type') == 'function_call_output':
parts = item.get('output', [])
if any(p.get('type') == 'input_image' for p in parts):
item = {**item, 'output': [p for p in parts if p.get('type') != 'input_image']}
frontend_output.append(item)
await event_emitter(
{
'type': 'chat:completion',
'data': {
'output': frontend_output,
},
}
)
await emit_output()
try:
new_form_data = {
@@ -6297,6 +6317,7 @@ async def streaming_chat_response_handler(response, ctx):
if not msg_parts or (len(msg_parts) == 1 and not msg_parts[0].get('text', '').strip()):
prior_output.pop()
output = []
output_start = len(prior_output)
await stream_body_handler(res, new_form_data)
output = full_output()
prior_output = []