fix: apply reasoning stripping to anthropic-vertex, add cache control test

- Apply the same reasoning_details/reasoning_content stripping to
  anthropic-vertex.ts that was already applied to anthropic.ts and
  openrouter.ts (identical no-op cast bug)
- Pass aiSdkMessages (mapped copy) to applyCacheControlToAiSdkMessages
  in anthropic-vertex.ts so cache control mutations target the copies
  sent to streamText
- Add test verifying cache control providerOptions are applied to the
  mapped message copies (not the originals) when messages contain extra
  legacy fields
This commit is contained in:
Roo Code 2026-02-12 04:10:48 +00:00
parent 7375a59931
commit 8b9ae4bb58
2 changed files with 48 additions and 3 deletions

View file

@ -438,6 +438,47 @@ describe("AnthropicHandler", () => {
expect(callArgs.messages[1].content).toEqual([{ type: "text", text: "Hi" }])
})
it("should apply cache control providerOptions to mapped message copies (not originals)", async () => {
setupStreamTextMock([{ type: "text-delta", text: "test" }])
// Messages with extra legacy fields — .map() creates new objects to strip them
const messagesWithExtraFields = [
{
role: "user",
content: [{ type: "text" as const, text: "Hello" }],
},
{
role: "assistant",
content: [{ type: "text" as const, text: "Hi" }],
reasoning_details: [{ type: "thinking", thinking: "deep thoughts" }],
},
{
role: "user",
content: [{ type: "text" as const, text: "Follow up" }],
},
] as any
const stream = handler.createMessage(systemPrompt, messagesWithExtraFields)
for await (const _chunk of stream) {
// Consume stream
}
expect(mockStreamText).toHaveBeenCalledTimes(1)
const callArgs = mockStreamText.mock.calls[0]![0]
// The last user message (index 2) should have cache control applied
const lastUserMsg = callArgs.messages[2]
expect(lastUserMsg.role).toBe("user")
expect(lastUserMsg.providerOptions).toBeDefined()
expect(lastUserMsg.providerOptions.anthropic).toEqual({
cacheControl: { type: "ephemeral" },
})
// And it should still NOT have reasoning_details (stripped by .map())
expect(lastUserMsg).not.toHaveProperty("reasoning_details")
})
it("should pass system prompt via system param with systemProviderOptions for cache control", async () => {
setupStreamTextMock([{ type: "text-delta", text: "test" }])

View file

@ -90,8 +90,12 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple
): ApiStream {
const modelConfig = this.getModel()
// Convert messages to AI SDK format
const aiSdkMessages = messages as ModelMessage[]
// Convert messages to AI SDK format, stripping extra fields from legacy
// ApiMessage objects that survive JSON deserialization (e.g. reasoning_details
// causes Anthropic 400: "Extra inputs are not permitted").
const aiSdkMessages = messages.map(
({ reasoning_details, reasoning_content, ...rest }: any) => rest,
) as ModelMessage[]
// Convert tools to AI SDK format
const openAiTools = this.convertToolsForOpenAI(metadata?.tools)
@ -147,7 +151,7 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple
if (secondLastUserMsgIndex >= 0) targetIndices.add(secondLastUserMsgIndex)
if (targetIndices.size > 0) {
this.applyCacheControlToAiSdkMessages(messages as ModelMessage[], targetIndices, cacheProviderOption)
this.applyCacheControlToAiSdkMessages(aiSdkMessages, targetIndices, cacheProviderOption)
}
// Build streamText request