Skip to content

Commit 9bea57e

Browse files
author
Brendan Gray
committed
Strip raw inline JSON tool calls from local LLM display path (parity with cloud path)
1 parent 71b9c79 commit 9bea57e

2 files changed

Lines changed: 13 additions & 7 deletions

File tree

main/agenticChat.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1107,10 +1107,11 @@ function register(ctx) {
11071107

11081108
fullResponseText += responseText;
11091109

1110-
// Strip tool fences from display copy
1110+
// Strip tool fences and raw inline JSON tool calls from display copy
11111111
let displayChunk = responseText
11121112
.replace(/\n?```(?:json|tool_call|tool)\b[\s\S]*?```\n?/g, '')
11131113
.replace(/\n?```(?:json|tool_call|tool)\b[\s\S]*$/g, '')
1114+
.replace(/\[?\s*\{\s*"(?:tool|name)"\s*:\s*"[^"]*"[\s\S]*?\}\s*\]?/g, '')
11141115
.replace(/\n{3,}/g, '\n\n');
11151116
if (continuationCount > 0) {
11161117
displayChunk = displayChunk.replace(/\[(?:Continue your response|You were generating a tool call)[\s\S]*?\]/gi, '');
@@ -1576,6 +1577,8 @@ function register(ctx) {
15761577
cleanResponse = cleanResponse.replace(/\n?```(?:json|tool_call|tool)\b[\s\S]*?```\n?/g, '');
15771578
cleanResponse = cleanResponse.replace(/\n?```(?:json|tool_call|tool)\b[\s\S]*/g, '');
15781579
cleanResponse = cleanResponse.replace(/^\s*```\s*$/gm, '');
1580+
// Strip raw inline JSON tool calls (same regex the cloud path uses)
1581+
cleanResponse = cleanResponse.replace(/\[?\s*\{\s*"(?:tool|name)"\s*:\s*"[^"]*"[\s\S]*?\}\s*\]?/g, '');
15791582
cleanResponse = cleanResponse.replace(/\n{3,}/g, '\n\n').trim();
15801583

15811584
if (!cleanResponse) {

src/utils/chatContentParser.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,16 @@ export function splitInlineToolCalls(text: string): ContentSegment[] {
193193
// Look for the next clearly non-JSON content boundary: a line starting with
194194
// a letter/header/markdown that isn't part of code content, or end of text
195195
const afterBlob = text.substring(endIdx);
196-
const nextBoundary = afterBlob.search(/\n(?:#{1,3}\s|[A-Z][a-z]+ [a-z]|$|\n\n)/m);
197-
if (nextBoundary > 0) {
198-
skipEnd = endIdx + nextBoundary;
199-
} else if (afterBlob.length < 5000) {
200-
// Small remainder — likely all part of the leaked blob, consume it entirely
201-
skipEnd = text.length;
196+
// Find the next double-newline paragraph break — content before it is likely
197+
// leaked code from the blob, content after it is likely the model's prose response.
198+
const paraBreak = afterBlob.indexOf('\n\n');
199+
if (paraBreak > 0) {
200+
// Only extend to the paragraph break — preserve everything after it
201+
skipEnd = endIdx + paraBreak;
202202
}
203+
// If no paragraph break found, DON'T consume to end — the brace counter's endIdx
204+
// is the best we have. Some content may leak, but that's better than swallowing
205+
// the model's actual follow-up prose.
203206
}
204207
lastIndex = skipEnd;
205208
jsonRegex.lastIndex = lastIndex;

0 commit comments

Comments
 (0)