mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-07 02:58:15 +00:00
refactor(task): restore appendEnvironmentDetails helper and tests
Re-applies the appendEnvironmentDetails module and comprehensive test coverage that was removed in the revert (#11256). The Task.ts integration changes will follow in a subsequent commit.
This commit is contained in:
parent
78e64115e8
commit
6e8f77270e
2 changed files with 652 additions and 0 deletions
414
src/core/task/__tests__/appendEnvironmentDetails.spec.ts
Normal file
414
src/core/task/__tests__/appendEnvironmentDetails.spec.ts
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import {
|
||||
appendEnvironmentDetails,
|
||||
removeEnvironmentDetailsBlocks,
|
||||
stripAppendedEnvironmentDetails,
|
||||
UserContentBlock,
|
||||
} from "../appendEnvironmentDetails"
|
||||
|
||||
describe("appendEnvironmentDetails", () => {
|
||||
const envDetails = "<environment_details>\n# Test\nSome details\n</environment_details>"
|
||||
|
||||
describe("empty content", () => {
|
||||
it("should return a text block when content is empty", () => {
|
||||
const result = appendEnvironmentDetails([], envDetails)
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0].type).toBe("text")
|
||||
expect((result[0] as Anthropic.Messages.TextBlockParam).text).toBe(envDetails)
|
||||
})
|
||||
})
|
||||
|
||||
describe("text block handling", () => {
|
||||
it("should append to the last text block", () => {
|
||||
const content: UserContentBlock[] = [{ type: "text", text: "User message" }]
|
||||
|
||||
const result = appendEnvironmentDetails(content, envDetails)
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0].type).toBe("text")
|
||||
expect((result[0] as Anthropic.Messages.TextBlockParam).text).toBe("User message\n\n" + envDetails)
|
||||
})
|
||||
|
||||
it("should append to the last text block when multiple text blocks exist", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{ type: "text", text: "First message" },
|
||||
{ type: "text", text: "Second message" },
|
||||
]
|
||||
|
||||
const result = appendEnvironmentDetails(content, envDetails)
|
||||
|
||||
expect(result).toHaveLength(2)
|
||||
expect((result[0] as Anthropic.Messages.TextBlockParam).text).toBe("First message")
|
||||
expect((result[1] as Anthropic.Messages.TextBlockParam).text).toBe("Second message\n\n" + envDetails)
|
||||
})
|
||||
|
||||
it("should not mutate the original content array", () => {
|
||||
const content: UserContentBlock[] = [{ type: "text", text: "Original" }]
|
||||
|
||||
appendEnvironmentDetails(content, envDetails)
|
||||
|
||||
expect((content[0] as Anthropic.Messages.TextBlockParam).text).toBe("Original")
|
||||
})
|
||||
})
|
||||
|
||||
describe("tool_result block handling", () => {
|
||||
it("should append to tool_result with string content", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-123",
|
||||
content: "Tool result text",
|
||||
},
|
||||
]
|
||||
|
||||
const result = appendEnvironmentDetails(content, envDetails)
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0].type).toBe("tool_result")
|
||||
const toolResult = result[0] as Anthropic.Messages.ToolResultBlockParam
|
||||
expect(toolResult.content).toBe("Tool result text\n\n" + envDetails)
|
||||
expect(toolResult.tool_use_id).toBe("tool-123")
|
||||
})
|
||||
|
||||
it("should append to tool_result with undefined content", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-123",
|
||||
} as Anthropic.Messages.ToolResultBlockParam,
|
||||
]
|
||||
|
||||
const result = appendEnvironmentDetails(content, envDetails)
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
const toolResult = result[0] as Anthropic.Messages.ToolResultBlockParam
|
||||
expect(toolResult.content).toBe(envDetails)
|
||||
})
|
||||
|
||||
it("should append to tool_result with array content containing text", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-123",
|
||||
content: [{ type: "text", text: "Result line 1" }],
|
||||
},
|
||||
]
|
||||
|
||||
const result = appendEnvironmentDetails(content, envDetails)
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
const toolResult = result[0] as Anthropic.Messages.ToolResultBlockParam
|
||||
expect(Array.isArray(toolResult.content)).toBe(true)
|
||||
const contentArray = toolResult.content as Anthropic.Messages.TextBlockParam[]
|
||||
expect(contentArray).toHaveLength(1)
|
||||
expect(contentArray[0].text).toBe("Result line 1\n\n" + envDetails)
|
||||
})
|
||||
|
||||
it("should add text block to tool_result with array content containing only images", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-123",
|
||||
content: [
|
||||
{
|
||||
type: "image",
|
||||
source: { type: "base64", media_type: "image/png", data: "abc123" },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const result = appendEnvironmentDetails(content, envDetails)
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
const toolResult = result[0] as Anthropic.Messages.ToolResultBlockParam
|
||||
expect(Array.isArray(toolResult.content)).toBe(true)
|
||||
const contentArray = toolResult.content as Array<
|
||||
Anthropic.Messages.TextBlockParam | Anthropic.Messages.ImageBlockParam
|
||||
>
|
||||
expect(contentArray).toHaveLength(2)
|
||||
expect(contentArray[0].type).toBe("image")
|
||||
expect(contentArray[1].type).toBe("text")
|
||||
expect((contentArray[1] as Anthropic.Messages.TextBlockParam).text).toBe(envDetails)
|
||||
})
|
||||
|
||||
it("should append to the last text block in tool_result array content", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-123",
|
||||
content: [
|
||||
{ type: "text", text: "First text" },
|
||||
{
|
||||
type: "image",
|
||||
source: { type: "base64", media_type: "image/png", data: "abc123" },
|
||||
},
|
||||
{ type: "text", text: "Last text" },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const result = appendEnvironmentDetails(content, envDetails)
|
||||
|
||||
const toolResult = result[0] as Anthropic.Messages.ToolResultBlockParam
|
||||
const contentArray = toolResult.content as Array<
|
||||
Anthropic.Messages.TextBlockParam | Anthropic.Messages.ImageBlockParam
|
||||
>
|
||||
expect(contentArray).toHaveLength(3)
|
||||
expect((contentArray[0] as Anthropic.Messages.TextBlockParam).text).toBe("First text")
|
||||
expect((contentArray[2] as Anthropic.Messages.TextBlockParam).text).toBe("Last text\n\n" + envDetails)
|
||||
})
|
||||
|
||||
it("should preserve is_error flag on tool_result", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-123",
|
||||
content: "Error message",
|
||||
is_error: true,
|
||||
},
|
||||
]
|
||||
|
||||
const result = appendEnvironmentDetails(content, envDetails)
|
||||
|
||||
const toolResult = result[0] as Anthropic.Messages.ToolResultBlockParam
|
||||
expect(toolResult.is_error).toBe(true)
|
||||
expect(toolResult.content).toBe("Error message\n\n" + envDetails)
|
||||
})
|
||||
})
|
||||
|
||||
describe("mixed content handling", () => {
|
||||
it("should append to last text block when text comes after tool_result", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-123",
|
||||
content: "Tool result",
|
||||
},
|
||||
{ type: "text", text: "User comment" },
|
||||
]
|
||||
|
||||
const result = appendEnvironmentDetails(content, envDetails)
|
||||
|
||||
expect(result).toHaveLength(2)
|
||||
expect((result[0] as Anthropic.Messages.ToolResultBlockParam).content).toBe("Tool result")
|
||||
expect((result[1] as Anthropic.Messages.TextBlockParam).text).toBe("User comment\n\n" + envDetails)
|
||||
})
|
||||
|
||||
it("should append to last tool_result when no text block follows", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{ type: "text", text: "User message" },
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-123",
|
||||
content: "Tool result",
|
||||
},
|
||||
]
|
||||
|
||||
const result = appendEnvironmentDetails(content, envDetails)
|
||||
|
||||
expect(result).toHaveLength(2)
|
||||
expect((result[0] as Anthropic.Messages.TextBlockParam).text).toBe("User message")
|
||||
expect((result[1] as Anthropic.Messages.ToolResultBlockParam).content).toBe("Tool result\n\n" + envDetails)
|
||||
})
|
||||
|
||||
it("should add new text block when content only has images", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "image",
|
||||
source: { type: "base64", media_type: "image/png", data: "abc123" },
|
||||
},
|
||||
]
|
||||
|
||||
const result = appendEnvironmentDetails(content, envDetails)
|
||||
|
||||
expect(result).toHaveLength(2)
|
||||
expect(result[0].type).toBe("image")
|
||||
expect(result[1].type).toBe("text")
|
||||
expect((result[1] as Anthropic.Messages.TextBlockParam).text).toBe(envDetails)
|
||||
})
|
||||
|
||||
it("should handle multiple tool_results and append to the last one", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-1",
|
||||
content: "First result",
|
||||
},
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-2",
|
||||
content: "Second result",
|
||||
},
|
||||
]
|
||||
|
||||
const result = appendEnvironmentDetails(content, envDetails)
|
||||
|
||||
expect(result).toHaveLength(2)
|
||||
expect((result[0] as Anthropic.Messages.ToolResultBlockParam).content).toBe("First result")
|
||||
expect((result[1] as Anthropic.Messages.ToolResultBlockParam).content).toBe(
|
||||
"Second result\n\n" + envDetails,
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("removeEnvironmentDetailsBlocks", () => {
|
||||
const envDetailsBlock: UserContentBlock = {
|
||||
type: "text",
|
||||
text: "<environment_details>\n# Test\nSome details\n</environment_details>",
|
||||
}
|
||||
|
||||
it("should remove standalone environment_details text blocks", () => {
|
||||
const content: UserContentBlock[] = [{ type: "text", text: "User message" }, envDetailsBlock]
|
||||
|
||||
const result = removeEnvironmentDetailsBlocks(content)
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect((result[0] as Anthropic.Messages.TextBlockParam).text).toBe("User message")
|
||||
})
|
||||
|
||||
it("should not remove text blocks that mention environment_details but aren't complete blocks", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{ type: "text", text: "Let me explain <environment_details> tags" },
|
||||
{ type: "text", text: "The closing tag is </environment_details>" },
|
||||
]
|
||||
|
||||
const result = removeEnvironmentDetailsBlocks(content)
|
||||
|
||||
expect(result).toHaveLength(2)
|
||||
})
|
||||
|
||||
it("should preserve non-text blocks", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-123",
|
||||
content: "Result",
|
||||
},
|
||||
envDetailsBlock,
|
||||
{
|
||||
type: "image",
|
||||
source: { type: "base64", media_type: "image/png", data: "abc123" },
|
||||
},
|
||||
]
|
||||
|
||||
const result = removeEnvironmentDetailsBlocks(content)
|
||||
|
||||
expect(result).toHaveLength(2)
|
||||
expect(result[0].type).toBe("tool_result")
|
||||
expect(result[1].type).toBe("image")
|
||||
})
|
||||
|
||||
it("should handle empty content", () => {
|
||||
const result = removeEnvironmentDetailsBlocks([])
|
||||
expect(result).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should handle whitespace around environment_details tags", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "text",
|
||||
text: " <environment_details>\n# Test\nSome details\n</environment_details> ",
|
||||
},
|
||||
]
|
||||
|
||||
const result = removeEnvironmentDetailsBlocks(content)
|
||||
|
||||
expect(result).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("stripAppendedEnvironmentDetails", () => {
|
||||
const envDetails = "<environment_details>\n# Test\nSome details\n</environment_details>"
|
||||
|
||||
it("should strip environment details from the end of a text block", () => {
|
||||
const content: UserContentBlock[] = [{ type: "text", text: "User message\n\n" + envDetails }]
|
||||
|
||||
const result = stripAppendedEnvironmentDetails(content)
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect((result[0] as Anthropic.Messages.TextBlockParam).text).toBe("User message")
|
||||
})
|
||||
|
||||
it("should strip environment details from tool_result string content", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-123",
|
||||
content: "Tool result\n\n" + envDetails,
|
||||
},
|
||||
]
|
||||
|
||||
const result = stripAppendedEnvironmentDetails(content)
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect((result[0] as Anthropic.Messages.ToolResultBlockParam).content).toBe("Tool result")
|
||||
})
|
||||
|
||||
it("should strip environment details from tool_result array content", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-123",
|
||||
content: [{ type: "text", text: "Result text\n\n" + envDetails }],
|
||||
},
|
||||
]
|
||||
|
||||
const result = stripAppendedEnvironmentDetails(content)
|
||||
|
||||
const toolResult = result[0] as Anthropic.Messages.ToolResultBlockParam
|
||||
const contentArray = toolResult.content as Anthropic.Messages.TextBlockParam[]
|
||||
expect(contentArray[0].text).toBe("Result text")
|
||||
})
|
||||
|
||||
it("should also remove standalone environment_details blocks", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{ type: "text", text: "User message" },
|
||||
{ type: "text", text: envDetails },
|
||||
]
|
||||
|
||||
const result = stripAppendedEnvironmentDetails(content)
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect((result[0] as Anthropic.Messages.TextBlockParam).text).toBe("User message")
|
||||
})
|
||||
|
||||
it("should handle content without environment details", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{ type: "text", text: "User message" },
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-123",
|
||||
content: "Tool result",
|
||||
},
|
||||
]
|
||||
|
||||
const result = stripAppendedEnvironmentDetails(content)
|
||||
|
||||
expect(result).toEqual(content)
|
||||
})
|
||||
|
||||
it("should handle empty content", () => {
|
||||
const result = stripAppendedEnvironmentDetails([])
|
||||
expect(result).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should preserve is_error flag when stripping from tool_result", () => {
|
||||
const content: UserContentBlock[] = [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "tool-123",
|
||||
content: "Error\n\n" + envDetails,
|
||||
is_error: true,
|
||||
},
|
||||
]
|
||||
|
||||
const result = stripAppendedEnvironmentDetails(content)
|
||||
|
||||
const toolResult = result[0] as Anthropic.Messages.ToolResultBlockParam
|
||||
expect(toolResult.is_error).toBe(true)
|
||||
expect(toolResult.content).toBe("Error")
|
||||
})
|
||||
})
|
||||
238
src/core/task/appendEnvironmentDetails.ts
Normal file
238
src/core/task/appendEnvironmentDetails.ts
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
|
||||
/**
|
||||
* Type for content block params that can appear in messages.
|
||||
* Using the broader ContentBlockParam type to handle all possible block types.
|
||||
*/
|
||||
export type UserContentBlock = Anthropic.Messages.ContentBlockParam
|
||||
|
||||
/**
|
||||
* Appends environment details to the last text block or tool_result block in user content.
|
||||
* This avoids creating a standalone trailing text block, which can break interleaved-thinking
|
||||
* models like DeepSeek reasoner that expect specific message shapes.
|
||||
*
|
||||
* Priority:
|
||||
* 1. If the last block is a text block, append to it
|
||||
* 2. If the last block is a tool_result, append to its content
|
||||
* 3. If no suitable block found, add as a new text block (fallback)
|
||||
*
|
||||
* @param content - Array of content blocks from a user message
|
||||
* @param environmentDetails - The environment details string to append
|
||||
* @returns New array with environment details appended to the appropriate block
|
||||
*/
|
||||
export function appendEnvironmentDetails(content: UserContentBlock[], environmentDetails: string): UserContentBlock[] {
|
||||
if (content.length === 0) {
|
||||
// No existing content, just return the environment details as a text block
|
||||
return [{ type: "text" as const, text: environmentDetails }]
|
||||
}
|
||||
|
||||
// Create a shallow copy so we don't mutate the original array
|
||||
const result = [...content]
|
||||
|
||||
// Find the last suitable block (text or tool_result)
|
||||
let lastSuitableIndex = -1
|
||||
for (let i = result.length - 1; i >= 0; i--) {
|
||||
const block = result[i]
|
||||
if (block.type === "text" || block.type === "tool_result") {
|
||||
lastSuitableIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (lastSuitableIndex === -1) {
|
||||
// No text or tool_result block found (content only has images?), add new text block
|
||||
result.push({ type: "text" as const, text: environmentDetails })
|
||||
return result
|
||||
}
|
||||
|
||||
const lastBlock = result[lastSuitableIndex]
|
||||
|
||||
if (lastBlock.type === "text") {
|
||||
// Append to existing text block
|
||||
result[lastSuitableIndex] = {
|
||||
type: "text" as const,
|
||||
text: lastBlock.text + "\n\n" + environmentDetails,
|
||||
}
|
||||
} else if (lastBlock.type === "tool_result") {
|
||||
// Append to tool_result content
|
||||
result[lastSuitableIndex] = appendToToolResult(lastBlock, environmentDetails)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends text to a tool_result block's content.
|
||||
* Tool result content can be a string or an array of content blocks.
|
||||
*/
|
||||
function appendToToolResult(
|
||||
toolResult: Anthropic.Messages.ToolResultBlockParam,
|
||||
textToAppend: string,
|
||||
): Anthropic.Messages.ToolResultBlockParam {
|
||||
const { content, ...rest } = toolResult
|
||||
|
||||
if (content === undefined || content === null) {
|
||||
// No existing content, just set the text
|
||||
return {
|
||||
...rest,
|
||||
content: textToAppend,
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof content === "string") {
|
||||
// String content, just concatenate
|
||||
return {
|
||||
...rest,
|
||||
content: content + "\n\n" + textToAppend,
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(content)) {
|
||||
// Array content - find the last text block and append, or add new text block
|
||||
const contentCopy = [...content]
|
||||
let lastTextIndex = -1
|
||||
|
||||
for (let i = contentCopy.length - 1; i >= 0; i--) {
|
||||
if (contentCopy[i].type === "text") {
|
||||
lastTextIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (lastTextIndex >= 0) {
|
||||
// Append to last text block in array
|
||||
const lastTextBlock = contentCopy[lastTextIndex] as Anthropic.Messages.TextBlockParam
|
||||
contentCopy[lastTextIndex] = {
|
||||
type: "text" as const,
|
||||
text: lastTextBlock.text + "\n\n" + textToAppend,
|
||||
}
|
||||
} else {
|
||||
// No text block in array, add new one
|
||||
contentCopy.push({ type: "text" as const, text: textToAppend })
|
||||
}
|
||||
|
||||
return {
|
||||
...rest,
|
||||
content: contentCopy,
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown content type, return with text appended as new content
|
||||
return {
|
||||
...rest,
|
||||
content: textToAppend,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any existing environment_details blocks from the content array.
|
||||
* A block is considered an environment_details block if it's a text block
|
||||
* that starts with <environment_details> and ends with </environment_details>.
|
||||
*
|
||||
* @param content - Array of content blocks to filter
|
||||
* @returns New array with environment_details blocks removed
|
||||
*/
|
||||
export function removeEnvironmentDetailsBlocks(content: UserContentBlock[]): UserContentBlock[] {
|
||||
return content.filter((block) => {
|
||||
if (block.type === "text" && typeof block.text === "string") {
|
||||
const trimmed = block.text.trim()
|
||||
const isEnvironmentDetailsBlock =
|
||||
trimmed.startsWith("<environment_details>") && trimmed.endsWith("</environment_details>")
|
||||
return !isEnvironmentDetailsBlock
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips environment details from the last text block or tool_result in the content.
|
||||
* This handles the case where environment details were appended to an existing block
|
||||
* rather than added as a standalone block.
|
||||
*
|
||||
* @param content - Array of content blocks
|
||||
* @returns New array with environment details stripped from the last suitable block
|
||||
*/
|
||||
export function stripAppendedEnvironmentDetails(content: UserContentBlock[]): UserContentBlock[] {
|
||||
if (content.length === 0) {
|
||||
return content
|
||||
}
|
||||
|
||||
// First, remove any standalone environment_details blocks
|
||||
let result = removeEnvironmentDetailsBlocks(content)
|
||||
|
||||
if (result.length === 0) {
|
||||
return result
|
||||
}
|
||||
|
||||
// Then, strip appended environment details from the last block
|
||||
const lastIndex = result.length - 1
|
||||
const lastBlock = result[lastIndex]
|
||||
|
||||
if (lastBlock.type === "text") {
|
||||
const strippedText = stripEnvDetailsFromText(lastBlock.text)
|
||||
if (strippedText !== lastBlock.text) {
|
||||
result = [...result]
|
||||
result[lastIndex] = { type: "text" as const, text: strippedText }
|
||||
}
|
||||
} else if (lastBlock.type === "tool_result") {
|
||||
const strippedToolResult = stripEnvDetailsFromToolResult(lastBlock)
|
||||
if (strippedToolResult !== lastBlock) {
|
||||
result = [...result]
|
||||
result[lastIndex] = strippedToolResult
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips environment details from the end of a text string.
|
||||
*/
|
||||
function stripEnvDetailsFromText(text: string): string {
|
||||
// Match environment details at the end of the string, with optional preceding newlines
|
||||
const envDetailsPattern = /\n*<environment_details>[\s\S]*<\/environment_details>\s*$/
|
||||
return text.replace(envDetailsPattern, "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips environment details from a tool_result block's content.
|
||||
*/
|
||||
function stripEnvDetailsFromToolResult(
|
||||
toolResult: Anthropic.Messages.ToolResultBlockParam,
|
||||
): Anthropic.Messages.ToolResultBlockParam {
|
||||
const { content, ...rest } = toolResult
|
||||
|
||||
if (content === undefined || content === null) {
|
||||
return toolResult
|
||||
}
|
||||
|
||||
if (typeof content === "string") {
|
||||
const strippedContent = stripEnvDetailsFromText(content)
|
||||
if (strippedContent === content) {
|
||||
return toolResult
|
||||
}
|
||||
return { ...rest, content: strippedContent }
|
||||
}
|
||||
|
||||
if (Array.isArray(content)) {
|
||||
let changed = false
|
||||
const newContent = content.map((block) => {
|
||||
if (block.type === "text") {
|
||||
const strippedText = stripEnvDetailsFromText(block.text)
|
||||
if (strippedText !== block.text) {
|
||||
changed = true
|
||||
return { type: "text" as const, text: strippedText }
|
||||
}
|
||||
}
|
||||
return block
|
||||
})
|
||||
|
||||
if (!changed) {
|
||||
return toolResult
|
||||
}
|
||||
|
||||
return { ...rest, content: newContent }
|
||||
}
|
||||
|
||||
return toolResult
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue