mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-23 05:33:48 +00:00
fix: handle null tool_call arguments during stream processing
Some OpenAI-compatible providers (e.g. DeepSeek via DeepInfra) stream tool_calls with function.arguments set to null. split_json_objects then called len(None) and aborted process_chat mid-stream after partial text had already rendered. Coerce null/non-string arguments before splitting, normalize them onto the tool call, and harden stream delta accumulation when arguments is null. Closes #27195
This commit is contained in:
parent
2dbdba1f91
commit
4566540f7b
1 changed files with 30 additions and 4 deletions
|
|
@ -238,7 +238,15 @@ def _split_tool_calls(
|
|||
independently. Single-object arguments pass through unchanged.
|
||||
"""
|
||||
|
||||
def split_json_objects(raw: str) -> list[str]:
|
||||
def split_json_objects(raw: str | None) -> list[str]:
|
||||
# Some providers stream tool_calls with arguments: null. Treat as empty.
|
||||
if raw is None:
|
||||
return ['{}']
|
||||
if not isinstance(raw, str):
|
||||
try:
|
||||
raw = json.dumps(raw)
|
||||
except Exception:
|
||||
raw = str(raw)
|
||||
decoder = json.JSONDecoder()
|
||||
results = []
|
||||
position = 0
|
||||
|
|
@ -259,7 +267,17 @@ def _split_tool_calls(
|
|||
|
||||
expanded = []
|
||||
for tool_call in tool_calls:
|
||||
arguments = tool_call.get('function', {}).get('arguments', '')
|
||||
arguments = tool_call.get('function', {}).get('arguments')
|
||||
if arguments is None:
|
||||
arguments = '{}'
|
||||
elif not isinstance(arguments, str):
|
||||
try:
|
||||
arguments = json.dumps(arguments)
|
||||
except Exception:
|
||||
arguments = str(arguments)
|
||||
# Normalize so downstream never sees None
|
||||
tool_call.setdefault('function', {})
|
||||
tool_call['function']['arguments'] = arguments
|
||||
split_arguments = split_json_objects(arguments)
|
||||
|
||||
if len(split_arguments) <= 1:
|
||||
|
|
@ -4331,6 +4349,9 @@ async def streaming_chat_response_handler(response, ctx):
|
|||
delta_tool_call.setdefault('function', {})
|
||||
delta_tool_call['function'].setdefault('name', '')
|
||||
delta_tool_call['function'].setdefault('arguments', '')
|
||||
# Providers may send arguments: null; setdefault won't replace null.
|
||||
if delta_tool_call['function'].get('arguments') is None:
|
||||
delta_tool_call['function']['arguments'] = ''
|
||||
response_tool_calls.append(delta_tool_call)
|
||||
else:
|
||||
# Update the existing tool call
|
||||
|
|
@ -4343,8 +4364,13 @@ async def streaming_chat_response_handler(response, ctx):
|
|||
current_response_tool_call['function']['name'] = delta_name
|
||||
|
||||
if delta_arguments:
|
||||
current_response_tool_call['function']['arguments'] += (
|
||||
delta_arguments
|
||||
existing_args = current_response_tool_call['function'].get(
|
||||
'arguments'
|
||||
)
|
||||
if existing_args is None:
|
||||
existing_args = ''
|
||||
current_response_tool_call['function']['arguments'] = (
|
||||
existing_args + delta_arguments
|
||||
)
|
||||
|
||||
# Emit pending tool calls in real-time
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue