mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
fix: prefix tool_choice.name when type is tool
Addresses review feedback: when tool_choice specifies a specific tool (type: 'tool'), the name must also be prefixed to match the prefixed tool names in the tools array.
This commit is contained in:
parent
6173606fe3
commit
f578dfb4ef
2 changed files with 111 additions and 2 deletions
|
|
@ -719,6 +719,92 @@ describe("Claude Code Streaming Client", () => {
|
|||
expect(userMessage.content[0].tool_use_id).toBe("tool_123")
|
||||
})
|
||||
|
||||
test("should prefix tool name in tool_choice when type is tool", async () => {
|
||||
const mockFetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
body: {
|
||||
getReader: () => ({
|
||||
read: vi.fn().mockResolvedValue({ done: true, value: undefined }),
|
||||
releaseLock: vi.fn(),
|
||||
}),
|
||||
},
|
||||
})
|
||||
global.fetch = mockFetch
|
||||
|
||||
const { createStreamingMessage } = await import("../streaming-client")
|
||||
|
||||
const tools = [
|
||||
{
|
||||
name: "read_file",
|
||||
description: "Read a file",
|
||||
input_schema: { type: "object" as const, properties: {} },
|
||||
},
|
||||
]
|
||||
|
||||
const stream = createStreamingMessage({
|
||||
accessToken: "test-token",
|
||||
model: "claude-3-5-sonnet-20241022",
|
||||
systemPrompt: "You are helpful",
|
||||
messages: [{ role: "user", content: "Read a file" }],
|
||||
tools,
|
||||
toolChoice: { type: "tool", name: "read_file" },
|
||||
})
|
||||
|
||||
// Consume the stream
|
||||
for await (const _ of stream) {
|
||||
// Just consume
|
||||
}
|
||||
|
||||
const call = mockFetch.mock.calls[0]
|
||||
const body = JSON.parse(call[1].body)
|
||||
|
||||
// tool_choice.name should be prefixed to match the prefixed tool names
|
||||
expect(body.tool_choice).toEqual({ type: "tool", name: "oc_read_file" })
|
||||
})
|
||||
|
||||
test("should not modify tool_choice when type is auto or any", async () => {
|
||||
const mockFetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
body: {
|
||||
getReader: () => ({
|
||||
read: vi.fn().mockResolvedValue({ done: true, value: undefined }),
|
||||
releaseLock: vi.fn(),
|
||||
}),
|
||||
},
|
||||
})
|
||||
global.fetch = mockFetch
|
||||
|
||||
const { createStreamingMessage } = await import("../streaming-client")
|
||||
|
||||
const tools = [
|
||||
{
|
||||
name: "read_file",
|
||||
description: "Read a file",
|
||||
input_schema: { type: "object" as const, properties: {} },
|
||||
},
|
||||
]
|
||||
|
||||
const stream = createStreamingMessage({
|
||||
accessToken: "test-token",
|
||||
model: "claude-3-5-sonnet-20241022",
|
||||
systemPrompt: "You are helpful",
|
||||
messages: [{ role: "user", content: "Hello" }],
|
||||
tools,
|
||||
toolChoice: { type: "any" },
|
||||
})
|
||||
|
||||
// Consume the stream
|
||||
for await (const _ of stream) {
|
||||
// Just consume
|
||||
}
|
||||
|
||||
const call = mockFetch.mock.calls[0]
|
||||
const body = JSON.parse(call[1].body)
|
||||
|
||||
// tool_choice with type "any" should be unchanged
|
||||
expect(body.tool_choice).toEqual({ type: "any" })
|
||||
})
|
||||
|
||||
test("should strip prefix from tool names in streaming responses", async () => {
|
||||
// Simulate a tool_use response from the API with prefixed name
|
||||
const sseData = [
|
||||
|
|
|
|||
|
|
@ -85,6 +85,28 @@ function prefixToolNamesInMessages(messages: Anthropic.Messages.MessageParam[]):
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefixes tool name in tool_choice when type is "tool".
|
||||
* This ensures consistency with the prefixed tool names in the tools array.
|
||||
*/
|
||||
function prefixToolChoice(
|
||||
toolChoice: Anthropic.Messages.ToolChoice | undefined,
|
||||
): Anthropic.Messages.ToolChoice | undefined {
|
||||
if (!toolChoice) {
|
||||
return toolChoice
|
||||
}
|
||||
|
||||
// Only prefix when tool_choice specifies a specific tool by name
|
||||
if (toolChoice.type === "tool" && "name" in toolChoice) {
|
||||
return {
|
||||
...toolChoice,
|
||||
name: prefixToolName(toolChoice.name),
|
||||
}
|
||||
}
|
||||
|
||||
return toolChoice
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out non-Anthropic content blocks from messages before sending to the API.
|
||||
*
|
||||
|
|
@ -487,9 +509,10 @@ export async function* createStreamingMessage(options: StreamMessageOptions): As
|
|||
// when using Claude Code OAuth tokens
|
||||
body.tools = prefixToolNames(tools)
|
||||
// Default tool_choice to "auto" when tools are provided (as per spec example)
|
||||
body.tool_choice = toolChoice || { type: "auto" }
|
||||
// Prefix tool name in tool_choice if it specifies a specific tool
|
||||
body.tool_choice = prefixToolChoice(toolChoice) || { type: "auto" }
|
||||
} else if (toolChoice) {
|
||||
body.tool_choice = toolChoice
|
||||
body.tool_choice = prefixToolChoice(toolChoice)
|
||||
}
|
||||
|
||||
// Build minimal headers
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue