Skip to content

Commit fda09c0

Browse files
committed
fix: update parseAssistantMessage and parseAssistantMessageV2 to preserve newlines in content parameters while stripping leading and trailing newlines
1 parent d24d3b1 commit fda09c0

File tree

2 files changed

+23
-12
lines changed

2 files changed

+23
-12
lines changed

src/core/assistant-message/parseAssistantMessage.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ export function parseAssistantMessage(assistantMessage: string): AssistantMessag
2424
const paramClosingTag = `</${currentParamName}>`
2525
if (currentParamValue.endsWith(paramClosingTag)) {
2626
// End of param value.
27-
// Don't trim content parameters to preserve newlines
27+
// Don't trim content parameters to preserve newlines, but strip first and last newline only
2828
const paramValue = currentParamValue.slice(0, -paramClosingTag.length)
2929
currentToolUse.params[currentParamName] =
30-
currentParamName === "content" ? paramValue : paramValue.trim()
30+
currentParamName === "content"
31+
? paramValue.replace(/^\n/, "").replace(/\n$/, "")
32+
: paramValue.trim()
3133
currentParamName = undefined
3234
continue
3335
} else {
@@ -75,8 +77,11 @@ export function parseAssistantMessage(assistantMessage: string): AssistantMessag
7577
const contentEndIndex = toolContent.lastIndexOf(contentEndTag)
7678

7779
if (contentStartIndex !== -1 && contentEndIndex !== -1 && contentEndIndex > contentStartIndex) {
78-
// Don't trim content to preserve newlines
79-
currentToolUse.params[contentParamName] = toolContent.slice(contentStartIndex, contentEndIndex)
80+
// Don't trim content to preserve newlines, but strip first and last newline only
81+
currentToolUse.params[contentParamName] = toolContent
82+
.slice(contentStartIndex, contentEndIndex)
83+
.replace(/^\n/, "")
84+
.replace(/\n$/, "")
8085
}
8186
}
8287

@@ -140,9 +145,10 @@ export function parseAssistantMessage(assistantMessage: string): AssistantMessag
140145
// Stream did not complete tool call, add it as partial.
141146
if (currentParamName) {
142147
// Tool call has a parameter that was not completed.
143-
// Don't trim content parameters to preserve newlines
148+
// Don't trim content parameters to preserve newlines, but strip first and last newline only
144149
const paramValue = accumulator.slice(currentParamValueStartIndex)
145-
currentToolUse.params[currentParamName] = currentParamName === "content" ? paramValue : paramValue.trim()
150+
currentToolUse.params[currentParamName] =
151+
currentParamName === "content" ? paramValue.replace(/^\n/, "").replace(/\n$/, "") : paramValue.trim()
146152
}
147153

148154
contentBlocks.push(currentToolUse)

src/core/assistant-message/parseAssistantMessageV2.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,9 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
8080
currentParamValueStart, // Start after the opening tag.
8181
currentCharIndex - closeTag.length + 1, // End before the closing tag.
8282
)
83-
// Don't trim content parameters to preserve newlines
84-
currentToolUse.params[currentParamName] = currentParamName === "content" ? value : value.trim()
83+
// Don't trim content parameters to preserve newlines, but strip first and last newline only
84+
currentToolUse.params[currentParamName] =
85+
currentParamName === "content" ? value.replace(/^\n/, "").replace(/\n$/, "") : value.trim()
8586
currentParamName = undefined // Go back to parsing tool content.
8687
// We don't continue loop here, need to check for tool close or other params at index i.
8788
} else {
@@ -145,8 +146,11 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
145146
const contentEnd = toolContentSlice.lastIndexOf(contentEndTag)
146147

147148
if (contentStart !== -1 && contentEnd !== -1 && contentEnd > contentStart) {
148-
// Don't trim content to preserve newlines
149-
const contentValue = toolContentSlice.slice(contentStart + contentStartTag.length, contentEnd)
149+
// Don't trim content to preserve newlines, but strip first and last newline only
150+
const contentValue = toolContentSlice
151+
.slice(contentStart + contentStartTag.length, contentEnd)
152+
.replace(/^\n/, "")
153+
.replace(/\n$/, "")
150154
currentToolUse.params[contentParamName] = contentValue
151155
}
152156
}
@@ -249,8 +253,9 @@ export function parseAssistantMessageV2(assistantMessage: string): AssistantMess
249253
// Finalize any open parameter within an open tool use.
250254
if (currentToolUse && currentParamName) {
251255
const value = assistantMessage.slice(currentParamValueStart) // From param start to end of string.
252-
// Don't trim content parameters to preserve newlines
253-
currentToolUse.params[currentParamName] = currentParamName === "content" ? value : value.trim()
256+
// Don't trim content parameters to preserve newlines, but strip first and last newline only
257+
currentToolUse.params[currentParamName] =
258+
currentParamName === "content" ? value.replace(/^\n/, "").replace(/\n$/, "") : value.trim()
254259
// Tool use remains partial.
255260
}
256261

0 commit comments

Comments
 (0)