mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix(bedrock): convert tool_result to XML text when native tools disabled (#10155)
This commit is contained in:
parent
752a95087e
commit
4693b9eb06
2 changed files with 167 additions and 5 deletions
|
|
@ -141,10 +141,10 @@ describe("convertToBedrockConverseMessages", () => {
|
|||
}
|
||||
})
|
||||
|
||||
it("converts tool result messages correctly", () => {
|
||||
it("converts tool result messages to XML text format (default, useNativeTools: false)", () => {
|
||||
const messages: Anthropic.Messages.MessageParam[] = [
|
||||
{
|
||||
role: "assistant",
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "tool_result",
|
||||
|
|
@ -155,6 +155,8 @@ describe("convertToBedrockConverseMessages", () => {
|
|||
},
|
||||
]
|
||||
|
||||
// Default behavior (useNativeTools: false) converts tool_result to XML text format
|
||||
// This fixes the Bedrock error "toolConfig field must be defined when using toolUse and toolResult content blocks"
|
||||
const result = convertToBedrockConverseMessages(messages)
|
||||
|
||||
if (!result[0] || !result[0].content) {
|
||||
|
|
@ -162,7 +164,41 @@ describe("convertToBedrockConverseMessages", () => {
|
|||
return
|
||||
}
|
||||
|
||||
expect(result[0].role).toBe("assistant")
|
||||
expect(result[0].role).toBe("user")
|
||||
const textBlock = result[0].content[0] as ContentBlock
|
||||
if ("text" in textBlock) {
|
||||
expect(textBlock.text).toContain("<tool_result>")
|
||||
expect(textBlock.text).toContain("<tool_use_id>test-id</tool_use_id>")
|
||||
expect(textBlock.text).toContain("File contents here")
|
||||
expect(textBlock.text).toContain("</tool_result>")
|
||||
} else {
|
||||
expect.fail("Expected text block with XML content not found")
|
||||
}
|
||||
})
|
||||
|
||||
it("converts tool result messages to native format (useNativeTools: true)", () => {
|
||||
const messages: Anthropic.Messages.MessageParam[] = [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "test-id",
|
||||
content: [{ type: "text", text: "File contents here" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
// With useNativeTools: true, keeps tool_result as native format
|
||||
const result = convertToBedrockConverseMessages(messages, { useNativeTools: true })
|
||||
|
||||
if (!result[0] || !result[0].content) {
|
||||
expect.fail("Expected result to have content")
|
||||
return
|
||||
}
|
||||
|
||||
expect(result[0].role).toBe("user")
|
||||
const resultBlock = result[0].content[0] as ContentBlock
|
||||
if ("toolResult" in resultBlock && resultBlock.toolResult) {
|
||||
const expectedContent: ToolResultContentBlock[] = [{ text: "File contents here" }]
|
||||
|
|
@ -176,7 +212,7 @@ describe("convertToBedrockConverseMessages", () => {
|
|||
}
|
||||
})
|
||||
|
||||
it("converts tool result messages with string content correctly", () => {
|
||||
it("converts tool result messages with string content to XML text format (default)", () => {
|
||||
const messages: Anthropic.Messages.MessageParam[] = [
|
||||
{
|
||||
role: "user",
|
||||
|
|
@ -197,6 +233,39 @@ describe("convertToBedrockConverseMessages", () => {
|
|||
return
|
||||
}
|
||||
|
||||
expect(result[0].role).toBe("user")
|
||||
const textBlock = result[0].content[0] as ContentBlock
|
||||
if ("text" in textBlock) {
|
||||
expect(textBlock.text).toContain("<tool_result>")
|
||||
expect(textBlock.text).toContain("<tool_use_id>test-id</tool_use_id>")
|
||||
expect(textBlock.text).toContain("File: test.txt")
|
||||
expect(textBlock.text).toContain("Hello World")
|
||||
} else {
|
||||
expect.fail("Expected text block with XML content not found")
|
||||
}
|
||||
})
|
||||
|
||||
it("converts tool result messages with string content to native format (useNativeTools: true)", () => {
|
||||
const messages: Anthropic.Messages.MessageParam[] = [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "test-id",
|
||||
content: "File: test.txt\nLines 1-5:\nHello World",
|
||||
} as any, // Anthropic types don't allow string content but runtime can have it
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const result = convertToBedrockConverseMessages(messages, { useNativeTools: true })
|
||||
|
||||
if (!result[0] || !result[0].content) {
|
||||
expect.fail("Expected result to have content")
|
||||
return
|
||||
}
|
||||
|
||||
expect(result[0].role).toBe("user")
|
||||
const resultBlock = result[0].content[0] as ContentBlock
|
||||
if ("toolResult" in resultBlock && resultBlock.toolResult) {
|
||||
|
|
@ -210,6 +279,56 @@ describe("convertToBedrockConverseMessages", () => {
|
|||
}
|
||||
})
|
||||
|
||||
it("converts both tool_use and tool_result consistently when native tools disabled", () => {
|
||||
// This test ensures tool_use AND tool_result are both converted to XML text
|
||||
// when useNativeTools is false, preventing Bedrock toolConfig errors
|
||||
const messages: Anthropic.Messages.MessageParam[] = [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "tool_use",
|
||||
id: "call-123",
|
||||
name: "read_file",
|
||||
input: { path: "test.txt" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "tool_result",
|
||||
tool_use_id: "call-123",
|
||||
content: "File contents here",
|
||||
} as any,
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const result = convertToBedrockConverseMessages(messages) // default useNativeTools: false
|
||||
|
||||
// Both should be text blocks, not native toolUse/toolResult
|
||||
const assistantContent = result[0]?.content?.[0] as ContentBlock
|
||||
const userContent = result[1]?.content?.[0] as ContentBlock
|
||||
|
||||
// tool_use should be XML text
|
||||
expect("text" in assistantContent).toBe(true)
|
||||
if ("text" in assistantContent) {
|
||||
expect(assistantContent.text).toContain("<tool_use>")
|
||||
}
|
||||
|
||||
// tool_result should also be XML text (this is what the fix addresses)
|
||||
expect("text" in userContent).toBe(true)
|
||||
if ("text" in userContent) {
|
||||
expect(userContent.text).toContain("<tool_result>")
|
||||
}
|
||||
|
||||
// Neither should have native format
|
||||
expect("toolUse" in assistantContent).toBe(false)
|
||||
expect("toolResult" in userContent).toBe(false)
|
||||
})
|
||||
|
||||
it("handles text content correctly", () => {
|
||||
const messages: Anthropic.Messages.MessageParam[] = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -111,7 +111,50 @@ export function convertToBedrockConverseMessages(
|
|||
}
|
||||
|
||||
if (messageBlock.type === "tool_result") {
|
||||
// Handle content field - can be string or array
|
||||
// When NOT using native tools, convert tool_result to text format
|
||||
// This matches how tool_use is converted to XML text when native tools are disabled.
|
||||
// Without this, Bedrock will error with "toolConfig field must be defined when using
|
||||
// toolUse and toolResult content blocks" because toolResult blocks require toolConfig.
|
||||
if (!useNativeTools) {
|
||||
let toolResultContent: string
|
||||
if (messageBlock.content) {
|
||||
if (typeof messageBlock.content === "string") {
|
||||
toolResultContent = messageBlock.content
|
||||
} else if (Array.isArray(messageBlock.content)) {
|
||||
toolResultContent = messageBlock.content
|
||||
.map((item) => (typeof item === "string" ? item : item.text || String(item)))
|
||||
.join("\n")
|
||||
} else {
|
||||
toolResultContent = String(messageBlock.output || "")
|
||||
}
|
||||
} else if (messageBlock.output) {
|
||||
if (typeof messageBlock.output === "string") {
|
||||
toolResultContent = messageBlock.output
|
||||
} else if (Array.isArray(messageBlock.output)) {
|
||||
toolResultContent = messageBlock.output
|
||||
.map((part) => {
|
||||
if (typeof part === "object" && "text" in part) {
|
||||
return part.text
|
||||
}
|
||||
if (typeof part === "object" && "type" in part && part.type === "image") {
|
||||
return "(see following message for image)"
|
||||
}
|
||||
return String(part)
|
||||
})
|
||||
.join("\n")
|
||||
} else {
|
||||
toolResultContent = String(messageBlock.output)
|
||||
}
|
||||
} else {
|
||||
toolResultContent = ""
|
||||
}
|
||||
|
||||
return {
|
||||
text: `<tool_result>\n<tool_use_id>${messageBlock.tool_use_id || ""}</tool_use_id>\n<output>${toolResultContent}</output>\n</tool_result>`,
|
||||
} as ContentBlock
|
||||
}
|
||||
|
||||
// Handle content field - can be string or array (native tool format)
|
||||
if (messageBlock.content) {
|
||||
// Content is a string
|
||||
if (typeof messageBlock.content === "string") {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue