fix(proxy): add safe_json_loads to prevent 500 errors on streaming tool call decoding
This commit is contained in:
+35
-19
@@ -277,6 +277,30 @@ def _ollama_tools_to_openai(tools):
|
||||
})
|
||||
return out
|
||||
|
||||
def safe_json_loads(val, default=None):
|
||||
"""Safely decode JSON string, handling extra data or syntax errors gracefully."""
|
||||
if default is None:
|
||||
default = {}
|
||||
if not val:
|
||||
return default
|
||||
if isinstance(val, dict):
|
||||
return val
|
||||
s = str(val).strip()
|
||||
if not s:
|
||||
return default
|
||||
try:
|
||||
return json.loads(s)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
decoder = json.JSONDecoder()
|
||||
obj, _ = decoder.raw_decode(s)
|
||||
if isinstance(obj, dict):
|
||||
return obj
|
||||
except Exception:
|
||||
pass
|
||||
return {"raw": s}
|
||||
|
||||
def _openai_tool_calls_to_ollama(tool_calls):
|
||||
"""OpenAI tool_calls -> ollama message.tool_calls."""
|
||||
if not tool_calls:
|
||||
@@ -284,10 +308,7 @@ def _openai_tool_calls_to_ollama(tool_calls):
|
||||
out = []
|
||||
for tc in tool_calls:
|
||||
fn = tc.get("function", {})
|
||||
try:
|
||||
args = json.loads(fn.get("arguments", "{}"))
|
||||
except json.JSONDecodeError:
|
||||
args = {}
|
||||
args = safe_json_loads(fn.get("arguments", "{}"))
|
||||
out.append({
|
||||
"function": {
|
||||
"name": fn.get("name", ""),
|
||||
@@ -674,15 +695,16 @@ def ollama_chat(payload):
|
||||
|
||||
om = {"role": "assistant", "content": ""}
|
||||
if tc_parts:
|
||||
om["tool_calls"] = [
|
||||
{
|
||||
tc_list = []
|
||||
for e in tc_parts.values():
|
||||
args = safe_json_loads(e.get("args"))
|
||||
tc_list.append({
|
||||
"function": {
|
||||
"name": e["name"],
|
||||
"arguments": json.loads(e["args"]) if e["args"] else {},
|
||||
"name": e.get("name", ""),
|
||||
"arguments": args,
|
||||
}
|
||||
}
|
||||
for e in tc_parts.values()
|
||||
]
|
||||
})
|
||||
om["tool_calls"] = tc_list
|
||||
|
||||
yield json.dumps({
|
||||
"model": MODEL_NAME,
|
||||
@@ -953,10 +975,7 @@ def _openai_tool_calls_to_anthropic(tool_calls):
|
||||
blocks = []
|
||||
for tc in tool_calls:
|
||||
fn = tc.get("function", {})
|
||||
try:
|
||||
args = json.loads(fn.get("arguments", "{}"))
|
||||
except json.JSONDecodeError:
|
||||
args = {}
|
||||
args = safe_json_loads(fn.get("arguments", "{}"))
|
||||
blocks.append({
|
||||
"type": "tool_use",
|
||||
"id": tc.get("id") or "toolu_" + uuid.uuid4().hex[:24],
|
||||
@@ -1115,10 +1134,7 @@ def anthropic_messages(payload):
|
||||
if finish:
|
||||
# close any open tool_use block(s) with accumulated input
|
||||
for tid, entry in tc_parts.items():
|
||||
try:
|
||||
args = json.loads(entry["args"]) if entry["args"] else {}
|
||||
except json.JSONDecodeError:
|
||||
args = {}
|
||||
args = safe_json_loads(entry.get("args"))
|
||||
yield sse("content_block_delta", {
|
||||
"type": "content_block_delta",
|
||||
"index": entry["block_index"],
|
||||
|
||||
Reference in New Issue
Block a user