Skip to content

Commit e7e29a8

Browse files
committed
fix(parser): handle nested <parameter> tags in parameter values correctly
1 parent 0a63a54 commit e7e29a8

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

src/core/assistant-message/parseAssistantMessage.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export function parseAssistantMessage(assistantMessage: string): AssistantMessag
1212
let currentToolUseStartIndex = 0
1313
let currentParamName: ToolParamName | undefined = undefined
1414
let currentParamValueStartIndex = 0
15+
let parameterNestingDepth = 0
1516
let accumulator = ""
1617
let inFunctionCalls = false
1718

@@ -22,15 +23,28 @@ export function parseAssistantMessage(assistantMessage: string): AssistantMessag
2223
// Inside function_calls block, handle parameters (check this FIRST to avoid nested tag issues)
2324
if (currentToolUse && currentParamName) {
2425
const currentParamValue = accumulator.slice(currentParamValueStartIndex)
26+
27+
// Check for nested <parameter> opening tags within param value
28+
if (currentParamValue.endsWith('<parameter name="')) {
29+
parameterNestingDepth++
30+
}
31+
32+
// Check for </parameter> closing tag
2533
const paramClosingTag = `</parameter>`
2634
if (currentParamValue.endsWith(paramClosingTag)) {
27-
// End of param value
35+
if (parameterNestingDepth > 0) {
36+
// This is a nested closing tag, decrement depth and continue
37+
parameterNestingDepth--
38+
continue
39+
}
40+
// This is the actual closing tag for our parameter
2841
const paramValue = currentParamValue.slice(0, -paramClosingTag.length)
2942
currentToolUse.params[currentParamName] =
3043
currentParamName === "content"
3144
? paramValue.replace(/^\n/, "").replace(/\n$/, "")
3245
: paramValue.trim()
3346
currentParamName = undefined
47+
parameterNestingDepth = 0
3448
continue
3549
} else {
3650
// Partial param value is accumulating
@@ -104,6 +118,7 @@ export function parseAssistantMessage(assistantMessage: string): AssistantMessag
104118
if (toolParamNames.includes(paramName as ToolParamName)) {
105119
currentParamName = paramName as ToolParamName
106120
currentParamValueStartIndex = accumulator.length
121+
parameterNestingDepth = 0
107122
}
108123
continue
109124
}

src/core/assistant-message/parseAssistantMessageV2.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
4747
let currentToolUse: ToolUse | undefined = undefined
4848
let currentParamValueStart = 0
4949
let currentParamName: ToolParamName | undefined = undefined
50+
let parameterNestingDepth = 0
5051
let inFunctionCalls = false
5152

5253
const len = assistantMessage.length
@@ -56,19 +57,35 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
5657

5758
// Inside function_calls block, handle parameters (check FIRST to avoid nested tag issues)
5859
if (currentToolUse && currentParamName) {
60+
// Check for nested <parameter name=" opening tags
61+
const paramOpenPattern = '<parameter name="'
62+
if (
63+
currentCharIndex >= paramOpenPattern.length - 1 &&
64+
assistantMessage.startsWith(paramOpenPattern, currentCharIndex - paramOpenPattern.length + 1)
65+
) {
66+
parameterNestingDepth++
67+
}
68+
69+
// Check for </parameter> closing tag
5970
const paramCloseTag = "</parameter>"
6071
if (
6172
currentCharIndex >= paramCloseTag.length - 1 &&
6273
assistantMessage.startsWith(paramCloseTag, currentCharIndex - paramCloseTag.length + 1)
6374
) {
64-
// Found the closing tag for the parameter
75+
if (parameterNestingDepth > 0) {
76+
// This is a nested closing tag, decrement depth and continue
77+
parameterNestingDepth--
78+
continue
79+
}
80+
// This is the actual closing tag for our parameter
6581
const value = assistantMessage.slice(
6682
currentParamValueStart,
6783
currentCharIndex - paramCloseTag.length + 1,
6884
)
6985
currentToolUse.params[currentParamName] =
7086
currentParamName === "content" ? value.replace(/^\n/, "").replace(/\n$/, "") : value.trim()
7187
currentParamName = undefined
88+
parameterNestingDepth = 0
7289
} else {
7390
continue // Still inside param value
7491
}
@@ -159,6 +176,7 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
159176
if (toolParamNames.includes(paramName as ToolParamName)) {
160177
currentParamName = paramName as ToolParamName
161178
currentParamValueStart = currentCharIndex + 1
179+
parameterNestingDepth = 0
162180
}
163181
continue
164182
}

0 commit comments

Comments
 (0)