Skip to content

Commit de90499

Browse files
authored
fix(agent): repair unterminated strings in truncated tool call JSON (#1836)
1 parent 239b654 commit de90499

1 file changed

Lines changed: 41 additions & 12 deletions

File tree

multimodal/tarko/agent/src/tool-call-engine/PromptEngineeringToolCallEngine.ts

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,45 @@ ${JSON.stringify(schema)}
559559
return trimmed;
560560
}
561561

562+
/**
563+
* Repair truncated JSON by closing unterminated strings and missing braces.
564+
* Handles cases where LLM output was cut off mid-string (e.g. long HTML content).
565+
*/
566+
private repairTruncatedJson(content: string): string {
567+
if (!content) return content;
568+
569+
let repaired = content;
570+
571+
// Count unescaped double quotes to detect unterminated strings
572+
let inString = false;
573+
for (let i = 0; i < repaired.length; i++) {
574+
if (repaired[i] === '\\' && inString) {
575+
i++; // skip escaped character
576+
continue;
577+
}
578+
if (repaired[i] === '"') {
579+
inString = !inString;
580+
}
581+
}
582+
583+
// Close unterminated string
584+
if (inString) {
585+
repaired += '"';
586+
this.logger.debug('Closed unterminated string in truncated JSON');
587+
}
588+
589+
// Close missing braces
590+
const openBraces = (repaired.match(/\{/g) || []).length;
591+
const closeBraces = (repaired.match(/\}/g) || []).length;
592+
const missingBraces = openBraces - closeBraces;
593+
if (missingBraces > 0) {
594+
repaired += '}'.repeat(missingBraces);
595+
this.logger.debug(`Added ${missingBraces} closing braces to complete JSON`);
596+
}
597+
598+
return repaired;
599+
}
600+
562601
/**
563602
* Complete a tool call when closing tag is found
564603
*/
@@ -618,18 +657,8 @@ ${JSON.stringify(schema)}
618657
// Add closing brace if it seems like valid JSON that was truncated
619658
let toolCallContent = this.extractCleanJsonContent(extendedState.currentToolCallBuffer);
620659

621-
// Attempt to repair incomplete JSON
622-
if (toolCallContent && !toolCallContent.endsWith('}')) {
623-
// Simple heuristic: if it looks like JSON and has opening braces, try to close them
624-
const openBraces = (toolCallContent.match(/\{/g) || []).length;
625-
const closeBraces = (toolCallContent.match(/\}/g) || []).length;
626-
const missingBraces = openBraces - closeBraces;
627-
628-
if (missingBraces > 0) {
629-
toolCallContent += '}'.repeat(missingBraces);
630-
this.logger.debug(`Added ${missingBraces} closing braces to complete JSON`);
631-
}
632-
}
660+
// Attempt to repair incomplete JSON (unterminated strings + missing braces)
661+
toolCallContent = this.repairTruncatedJson(toolCallContent);
633662

634663
const toolCallData = JSON.parse(toolCallContent);
635664

0 commit comments

Comments
 (0)