Skip to content

Commit e6e8b8e

Browse files
sjhuclaude
andcommitted
fix(tool-parser): auto-repair truncated JSON from web models
Web models (especially DeepSeek) sometimes return tool_call JSON with missing closing braces due to SSE stream truncation. Added fuzzy repair: - Count open/close braces, append missing "}" automatically - Regex fallback for partial matches - All 5 truncation patterns now parse correctly Verified: DeepSeek "帮我看看桌面上有什么文件" → exec ls ~/Desktop → file list Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a269cd6 commit e6e8b8e

1 file changed

Lines changed: 21 additions & 2 deletions

File tree

src/zero-token/tool-calling/web-tool-parser.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export interface ParsedToolCall {
1313
}
1414

1515
// Fenced code block format (most reliable)
16-
const FENCED_REGEX = /```tool_json\s*\n?\s*(\{[\s\S]*?\})\s*\n?\s*```/;
16+
// Match both complete {"tool":...}} and truncated {"tool":...} (missing outer brace)
17+
const FENCED_REGEX = /```tool_json\s*\n?\s*(\{[\s\S]*?\})\}?\s*\n?\s*```/;
1718

1819
// Bare JSON format
1920
const BARE_JSON_REGEX = /\{\s*"tool"\s*:\s*"([^"]+)"\s*,\s*"parameters"\s*:\s*(\{[\s\S]*?\})\s*\}/;
@@ -45,12 +46,30 @@ export function extractToolCall(text: string): ParsedToolCall | null {
4546
return parseToolJson(xml[1]);
4647
}
4748

49+
// 4. Fuzzy repair: if text looks like a truncated tool_call JSON, try to fix it.
50+
// Common issue: SSE stream drops the final "}" → {"tool":"exec","parameters":{"command":"ls"}
51+
const fuzzyMatch = text.match(/\{\s*"tool"\s*:\s*"([^"]+)"\s*,\s*"parameters"\s*:\s*\{([^}]*)\}/);
52+
if (fuzzyMatch) {
53+
// We matched inner params but might be missing outer }
54+
const repaired = `{"tool":"${fuzzyMatch[1]}","parameters":{${fuzzyMatch[2]}}}`;
55+
const result = parseToolJson(repaired);
56+
if (result) {
57+
return result;
58+
}
59+
}
60+
4861
return null;
4962
}
5063

5164
function parseToolJson(raw: string): ParsedToolCall | null {
5265
try {
53-
const cleaned = raw.trim();
66+
let cleaned = raw.trim();
67+
// Auto-repair: if JSON has unbalanced braces, try appending }
68+
const opens = (cleaned.match(/\{/g) || []).length;
69+
const closes = (cleaned.match(/\}/g) || []).length;
70+
if (opens > closes) {
71+
cleaned += "}".repeat(opens - closes);
72+
}
5473
const obj = JSON.parse(cleaned);
5574

5675
// ComfyUI LLM Party format: {"tool":"name","parameters":{...}}

0 commit comments

Comments
 (0)