diff --git a/packages/ai/src/protocols/anthropic-messages.ts b/packages/ai/src/protocols/anthropic-messages.ts index e312d0f2224..6388d4184f4 100644 --- a/packages/ai/src/protocols/anthropic-messages.ts +++ b/packages/ai/src/protocols/anthropic-messages.ts @@ -831,6 +831,7 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* ( const content: AnthropicUserBlock[] = [] for (const part of message.content) { if (part.type === "text") { + if (part.text.trim().length === 0) continue content.push({ type: "text", text: part.text, cache_control: cacheControl(breakpoints, part.cache) }) continue } @@ -840,7 +841,7 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* ( } return yield* ProviderShared.unsupportedContent("Anthropic Messages", "user", ["text", "media"]) } - messages.push({ role: "user", content }) + if (content.length > 0) messages.push({ role: "user", content }) continue } @@ -848,6 +849,7 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* ( const content: AnthropicAssistantBlock[] = [] for (const part of message.content) { if (part.type === "text") { + if (part.text.trim().length === 0) continue content.push({ type: "text", text: part.text, cache_control: cacheControl(breakpoints, part.cache) }) continue } @@ -891,7 +893,7 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* ( `Anthropic Messages assistant messages only support text, reasoning, and tool-call content for now`, ) } - messages.push({ role: "assistant", content }) + if (content.length > 0) messages.push({ role: "assistant", content }) continue } diff --git a/packages/ai/test/provider/anthropic-messages.test.ts b/packages/ai/test/provider/anthropic-messages.test.ts index f5fa21ca68d..b5696462f81 100644 --- a/packages/ai/test/provider/anthropic-messages.test.ts +++ b/packages/ai/test/provider/anthropic-messages.test.ts @@ -84,6 +84,38 @@ describe("Anthropic Messages route", () => { }), ) + it.effect("filters whitespace-only text and removes empty messages", () => + Effect.gen(function* () { + const prepared = yield* compileRequest( + LLM.request({ + model, + messages: [ + Message.user(" \n\t"), + Message.user([]), + Message.user([ + { type: "text", text: "" }, + { type: "text", text: " Keep this spacing. " }, + { type: "text", text: " \n\t" }, + ]), + Message.assistant(" \n\t"), + Message.assistant([]), + Message.assistant([{ type: "reasoning", text: "" }]), + Message.assistant([ + { type: "text", text: "" }, + { type: "reasoning", text: "", providerMetadata: { anthropic: { signature: "sig_1" } } }, + ]), + ], + cache: "none", + }), + ) + + expect(prepared.body.messages).toEqual([ + { role: "user", content: [{ type: "text", text: " Keep this spacing. " }] }, + { role: "assistant", content: [{ type: "thinking", thinking: "", signature: "sig_1" }] }, + ]) + }), + ) + it.effect("lowers adaptive thinking settings with effort", () => Effect.gen(function* () { const prepared = yield* compileRequest(