fix(parser): handle nested <parameter> tags in parameter values correctly

This commit is contained in:
Hannes Rudolph 2025-10-21 17:39:26 -06:00
parent 0a63a54352
commit e7e29a8b4d
2 changed files with 35 additions and 2 deletions

View file

@ -12,6 +12,7 @@ export function parseAssistantMessage(assistantMessage: string): AssistantMessag
let currentToolUseStartIndex = 0
let currentParamName: ToolParamName | undefined = undefined
let currentParamValueStartIndex = 0
let parameterNestingDepth = 0
let accumulator = ""
let inFunctionCalls = false
@ -22,15 +23,28 @@ export function parseAssistantMessage(assistantMessage: string): AssistantMessag
// Inside function_calls block, handle parameters (check this FIRST to avoid nested tag issues)
if (currentToolUse && currentParamName) {
const currentParamValue = accumulator.slice(currentParamValueStartIndex)
// Check for nested <parameter> opening tags within param value
if (currentParamValue.endsWith('<parameter name="')) {
parameterNestingDepth++
}
// Check for </parameter> closing tag
const paramClosingTag = `</parameter>`
if (currentParamValue.endsWith(paramClosingTag)) {
// End of param value
if (parameterNestingDepth > 0) {
// This is a nested closing tag, decrement depth and continue
parameterNestingDepth--
continue
}
// This is the actual closing tag for our parameter
const paramValue = currentParamValue.slice(0, -paramClosingTag.length)
currentToolUse.params[currentParamName] =
currentParamName === "content"
? paramValue.replace(/^\n/, "").replace(/\n$/, "")
: paramValue.trim()
currentParamName = undefined
parameterNestingDepth = 0
continue
} else {
// Partial param value is accumulating
@ -104,6 +118,7 @@ export function parseAssistantMessage(assistantMessage: string): AssistantMessag
if (toolParamNames.includes(paramName as ToolParamName)) {
currentParamName = paramName as ToolParamName
currentParamValueStartIndex = accumulator.length
parameterNestingDepth = 0
}
continue
}

View file

@ -47,6 +47,7 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
let currentToolUse: ToolUse | undefined = undefined
let currentParamValueStart = 0
let currentParamName: ToolParamName | undefined = undefined
let parameterNestingDepth = 0
let inFunctionCalls = false
const len = assistantMessage.length
@ -56,12 +57,27 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
// Inside function_calls block, handle parameters (check FIRST to avoid nested tag issues)
if (currentToolUse && currentParamName) {
// Check for nested <parameter name=" opening tags
const paramOpenPattern = '<parameter name="'
if (
currentCharIndex >= paramOpenPattern.length - 1 &&
assistantMessage.startsWith(paramOpenPattern, currentCharIndex - paramOpenPattern.length + 1)
) {
parameterNestingDepth++
}
// Check for </parameter> closing tag
const paramCloseTag = "</parameter>"
if (
currentCharIndex >= paramCloseTag.length - 1 &&
assistantMessage.startsWith(paramCloseTag, currentCharIndex - paramCloseTag.length + 1)
) {
// Found the closing tag for the parameter
if (parameterNestingDepth > 0) {
// This is a nested closing tag, decrement depth and continue
parameterNestingDepth--
continue
}
// This is the actual closing tag for our parameter
const value = assistantMessage.slice(
currentParamValueStart,
currentCharIndex - paramCloseTag.length + 1,
@ -69,6 +85,7 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
currentToolUse.params[currentParamName] =
currentParamName === "content" ? value.replace(/^\n/, "").replace(/\n$/, "") : value.trim()
currentParamName = undefined
parameterNestingDepth = 0
} else {
continue // Still inside param value
}
@ -159,6 +176,7 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
if (toolParamNames.includes(paramName as ToolParamName)) {
currentParamName = paramName as ToolParamName
currentParamValueStart = currentCharIndex + 1
parameterNestingDepth = 0
}
continue
}