From d1e5842f0783053849c3685175200c72bbfe13c0 Mon Sep 17 00:00:00 2001 From: bigboateng Date: Mon, 15 Dec 2025 18:55:31 +0100 Subject: [PATCH] fix: ensure assistant messages have thinking blocks when extended thinking enabled This fixes the 400 error "Expected thinking or redacted_thinking, but found text" that occurs when thinkingBudget is enabled. The API requires that every assistant message must start with a thinking or redacted_thinking block when extended thinking is enabled. Changes: - Add ensureThinkingBlocksForExtendedThinking() function to filter out assistant messages without thinking blocks when thinking is enabled - Call this function in samplingLoop before API requests - Add comprehensive documentation for the new function Tested with examples/test-extended-thinking.ts - test passes successfully. --- .changeset/fix-extended-thinking-blocks.md | 5 +++ loop.ts | 5 +++ utils/message-processing.ts | 47 ++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 .changeset/fix-extended-thinking-blocks.md diff --git a/.changeset/fix-extended-thinking-blocks.md b/.changeset/fix-extended-thinking-blocks.md new file mode 100644 index 0000000..52aa94c --- /dev/null +++ b/.changeset/fix-extended-thinking-blocks.md @@ -0,0 +1,5 @@ +--- +"@centralinc/browseragent": patch +--- + +Fix extended thinking 400 error by ensuring all assistant messages start with thinking blocks when thinkingBudget is enabled diff --git a/loop.ts b/loop.ts index d5c9240..6d6e088 100644 --- a/loop.ts +++ b/loop.ts @@ -17,6 +17,7 @@ import { injectPromptCaching, truncateMessageHistory, cleanMessageHistory, + ensureThinkingBlocksForExtendedThinking, PROMPT_CACHING_BETA_FLAG, } from "./utils/message-processing"; import { makeApiToolResult } from "./utils/tool-results"; @@ -219,6 +220,10 @@ ${capabilityDocs}`, // Clean message history to ensure tool_use and tool_result blocks are properly paired cleanMessageHistory(messages); + // Ensure all assistant messages have thinking blocks when extended thinking is enabled + // This prevents 400 errors from the API + ensureThinkingBlocksForExtendedThinking(messages, !!thinkingBudget); + if (onlyNMostRecentImages) { maybeFilterToNMostRecentImages( messages, diff --git a/utils/message-processing.ts b/utils/message-processing.ts index 7bee476..86b7c85 100644 --- a/utils/message-processing.ts +++ b/utils/message-processing.ts @@ -244,3 +244,50 @@ export function cleanMessageHistory(messages: BetaMessageParam[]): void { } } } + +/** + * Ensure all assistant messages start with thinking blocks when extended thinking is enabled + * This prevents the 400 error: "Expected `thinking` or `redacted_thinking`, but found `text`" + * + * When thinking is enabled, the API requires that every assistant message must start with + * a thinking or redacted_thinking block. This function filters out any assistant messages + * that don't meet this requirement. + * + * @param messages - Array of conversation messages + * @param thinkingEnabled - Whether extended thinking is enabled + */ +export function ensureThinkingBlocksForExtendedThinking( + messages: BetaMessageParam[], + thinkingEnabled: boolean, +): void { + if (!thinkingEnabled) { + return; + } + + // Filter out assistant messages that don't start with a thinking block + // Keep user messages as they don't need thinking blocks + const indicesToRemove: number[] = []; + + for (let i = 0; i < messages.length; i++) { + const message = messages[i]; + if (message?.role === "assistant" && Array.isArray(message.content)) { + const firstBlock = message.content[0]; + const hasThinkingBlock = + firstBlock && + typeof firstBlock === "object" && + (firstBlock.type === "thinking" || firstBlock.type === "redacted_thinking"); + + if (!hasThinkingBlock) { + indicesToRemove.push(i); + } + } + } + + // Remove messages in reverse order to maintain correct indices + for (let i = indicesToRemove.length - 1; i >= 0; i--) { + const index = indicesToRemove[i]; + if (index !== undefined) { + messages.splice(index, 1); + } + } +}