From 30304e25bed86b07675757d79721099909144806 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Tue, 25 Nov 2025 17:12:01 +0000 Subject: [PATCH] fix: filter out reasoning type messages in AnthropicVertexHandler The AnthropicVertexHandler was not filtering out reasoning type messages before sending them to the Vertex AI API, causing an error when the context filled up. This fix adds filtering similar to what GeminiHandler does, removing messages with type: "reasoning" which are not recognized by the Vertex AI API. Fixes #9583 --- .../__tests__/anthropic-vertex.spec.ts | 92 +++++++++++++++++++ src/api/providers/anthropic-vertex.ts | 16 +++- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/api/providers/__tests__/anthropic-vertex.spec.ts b/src/api/providers/__tests__/anthropic-vertex.spec.ts index 9d83f265c7c..30e3144bd3e 100644 --- a/src/api/providers/__tests__/anthropic-vertex.spec.ts +++ b/src/api/providers/__tests__/anthropic-vertex.spec.ts @@ -809,4 +809,96 @@ describe("VertexHandler", () => { ) }) }) + + describe("reasoning message filtering", () => { + it("should filter out reasoning type messages before sending to API", async () => { + handler = new AnthropicVertexHandler({ + apiModelId: "claude-3-5-sonnet-v2@20241022", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + }) + + // Include a reasoning type message that should be filtered out + const messagesWithReasoning = [ + { + role: "user", + content: "Hello", + } as Anthropic.Messages.MessageParam, + { + type: "reasoning", + encrypted_content: "encrypted_payload", + summary: [], + } as any, // This should be filtered out + { + role: "assistant", + content: "Hi there!", + } as Anthropic.Messages.MessageParam, + ] + + const mockStream = [ + { + type: "message_start", + message: { + usage: { + input_tokens: 10, + output_tokens: 0, + }, + }, + }, + { + type: "content_block_start", + index: 0, + content_block: { + type: "text", + text: "Response", + }, + }, + ] + + const asyncIterator = { + async *[Symbol.asyncIterator]() { + for (const chunk of mockStream) { + yield chunk + } + }, + } + + const mockCreate = vitest.fn().mockResolvedValue(asyncIterator) + ;(handler["client"].messages as any).create = mockCreate + + const stream = handler.createMessage("System prompt", messagesWithReasoning) + const chunks: ApiStreamChunk[] = [] + + for await (const chunk of stream) { + chunks.push(chunk) + } + + // Verify that the reasoning message was filtered out + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + messages: [ + { + role: "user", + content: [ + { + type: "text", + text: "Hello", + cache_control: { type: "ephemeral" }, + }, + ], + }, + { + role: "assistant", + content: "Hi there!", + }, + ], + }), + ) + + // Verify the reasoning message was not included + const calledMessages = mockCreate.mock.calls[0][0].messages + expect(calledMessages).toHaveLength(2) + expect(calledMessages.every((msg: any) => msg.type !== "reasoning")).toBe(true) + }) + }) }) diff --git a/src/api/providers/anthropic-vertex.ts b/src/api/providers/anthropic-vertex.ts index c70a15926d3..6e607171707 100644 --- a/src/api/providers/anthropic-vertex.ts +++ b/src/api/providers/anthropic-vertex.ts @@ -70,6 +70,20 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple reasoning: thinking, } = this.getModel() + // Filter out reasoning type messages that are not valid for Vertex AI API + // The message list can include provider-specific meta entries such as + // `{ type: "reasoning", ... }` that are intended only for providers like + // openai-native. Vertex AI doesn't recognize these and will return an error. + type ReasoningMetaLike = { type?: string } + + const filteredMessages = messages.filter((message): message is Anthropic.Messages.MessageParam => { + const meta = message as ReasoningMetaLike + if (meta.type === "reasoning") { + return false + } + return true + }) + /** * Vertex API has specific limitations for prompt caching: * 1. Maximum of 4 blocks can have cache_control @@ -92,7 +106,7 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple system: supportsPromptCache ? [{ text: systemPrompt, type: "text" as const, cache_control: { type: "ephemeral" } }] : systemPrompt, - messages: supportsPromptCache ? addCacheBreakpoints(messages) : messages, + messages: supportsPromptCache ? addCacheBreakpoints(filteredMessages) : filteredMessages, stream: true, }