diff --git a/.changeset/swift-kings-attack.md b/.changeset/swift-kings-attack.md new file mode 100644 index 0000000000..8a8a425611 --- /dev/null +++ b/.changeset/swift-kings-attack.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Pass "thinking" params to OpenRouter diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index 2d1f07f833..ad58a1cf6b 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -14,8 +14,6 @@ import { ApiStream } from "../transform/stream" const ANTHROPIC_DEFAULT_TEMPERATURE = 0 -const THINKING_MODELS = ["claude-3-7-sonnet-20250219"] - export class AnthropicHandler implements ApiHandler, SingleCompletionHandler { private options: ApiHandlerOptions private client: Anthropic @@ -32,16 +30,19 @@ export class AnthropicHandler implements ApiHandler, SingleCompletionHandler { async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { let stream: AnthropicStream const cacheControl: CacheControlEphemeral = { type: "ephemeral" } - const modelId = this.getModel().id - const maxTokens = this.getModel().info.maxTokens || 8192 + let { id: modelId, info: modelInfo } = this.getModel() + const maxTokens = modelInfo.maxTokens || 8192 let temperature = this.options.modelTemperature ?? ANTHROPIC_DEFAULT_TEMPERATURE let thinking: BetaThinkingConfigParam | undefined = undefined - if (THINKING_MODELS.includes(modelId)) { - thinking = this.options.anthropicThinking - ? { type: "enabled", budget_tokens: this.options.anthropicThinking } - : { type: "disabled" } - + // Anthropic "Thinking" models require a temperature of 1.0. + if (modelId === "claude-3-7-sonnet-20250219:thinking") { + // The `:thinking` variant is a virtual identifier for the + // `claude-3-7-sonnet-20250219` model with a thinking budget. + // We can handle this more elegantly in the future. + modelId = "claude-3-7-sonnet-20250219" + const budgetTokens = this.options.anthropicThinking ?? Math.max(maxTokens * 0.8, 1024) + thinking = { type: "enabled", budget_tokens: budgetTokens } temperature = 1.0 } @@ -114,8 +115,8 @@ export class AnthropicHandler implements ApiHandler, SingleCompletionHandler { default: { stream = (await this.client.messages.create({ model: modelId, - max_tokens: this.getModel().info.maxTokens || 8192, - temperature: this.options.modelTemperature ?? ANTHROPIC_DEFAULT_TEMPERATURE, + max_tokens: maxTokens, + temperature, system: [{ text: systemPrompt, type: "text" }], messages, // tools, diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 69c55b8e71..0a9488e816 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -1,4 +1,5 @@ import { Anthropic } from "@anthropic-ai/sdk" +import { BetaThinkingConfigParam } from "@anthropic-ai/sdk/resources/beta" import axios from "axios" import OpenAI from "openai" import delay from "delay" @@ -17,6 +18,7 @@ const OPENROUTER_DEFAULT_TEMPERATURE = 0 type OpenRouterChatCompletionParams = OpenAI.Chat.ChatCompletionCreateParams & { transforms?: string[] include_reasoning?: boolean + thinking?: BetaThinkingConfigParam } // Add custom interface for OpenRouter usage chunk. @@ -57,7 +59,7 @@ export class OpenRouterHandler implements ApiHandler, SingleCompletionHandler { // prompt caching: https://openrouter.ai/docs/prompt-caching // this is specifically for claude models (some models may 'support prompt caching' automatically without this) switch (true) { - case this.getModel().id.startsWith("anthropic/"): + case modelId.startsWith("anthropic/"): openAiMessages[0] = { role: "system", content: [ @@ -107,9 +109,12 @@ export class OpenRouterHandler implements ApiHandler, SingleCompletionHandler { } let temperature = this.options.modelTemperature ?? defaultTemperature + let thinking: BetaThinkingConfigParam | undefined = undefined - // Anthropic "Thinking" models require a temperature of 1.0. if (modelInfo.thinking) { + const maxTokens = modelInfo.maxTokens || 8192 + const budgetTokens = this.options.anthropicThinking ?? Math.max(maxTokens * 0.8, 1024) + thinking = { type: "enabled", budget_tokens: budgetTokens } temperature = 1.0 } @@ -120,6 +125,7 @@ export class OpenRouterHandler implements ApiHandler, SingleCompletionHandler { model: modelId, max_tokens: modelInfo.maxTokens, temperature, + thinking, // OpenRouter is temporarily supporting this. top_p: topP, messages: openAiMessages, stream: true, diff --git a/src/shared/api.ts b/src/shared/api.ts index 63707e52b4..5d4b8b120d 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -103,7 +103,7 @@ export const THINKING_BUDGET = { export type AnthropicModelId = keyof typeof anthropicModels export const anthropicDefaultModelId: AnthropicModelId = "claude-3-7-sonnet-20250219" export const anthropicModels = { - "claude-3-7-sonnet-20250219": { + "claude-3-7-sonnet-20250219:thinking": { maxTokens: 16384, contextWindow: 200_000, supportsImages: true, @@ -115,6 +115,18 @@ export const anthropicModels = { cacheReadsPrice: 0.3, // $0.30 per million tokens thinking: true, }, + "claude-3-7-sonnet-20250219": { + maxTokens: 16384, + contextWindow: 200_000, + supportsImages: true, + supportsComputerUse: true, + supportsPromptCache: true, + inputPrice: 3.0, // $3 per million input tokens + outputPrice: 15.0, // $15 per million output tokens + cacheWritesPrice: 3.75, // $3.75 per million tokens + cacheReadsPrice: 0.3, // $0.30 per million tokens + thinking: false, + }, "claude-3-5-sonnet-20241022": { maxTokens: 8192, contextWindow: 200_000, diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 9d17cae4fa..73dc4fd41f 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -73,7 +73,7 @@ const ApiOptions = ({ const [openRouterBaseUrlSelected, setOpenRouterBaseUrlSelected] = useState(!!apiConfiguration?.openRouterBaseUrl) const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false) - const anthropicThinkingBudget = apiConfiguration?.anthropicThinking + const anthropicThinkingBudget = apiConfiguration?.anthropicThinking ?? THINKING_BUDGET.default const noTransform = (value: T) => value const inputEventTransform = (event: E) => (event as { target: HTMLInputElement })?.target?.value as any @@ -1272,39 +1272,21 @@ const ApiOptions = ({ )} {selectedModelInfo && selectedModelInfo.thinking && ( -
- - setApiConfigurationField( - "anthropicThinking", - checked - ? Math.min( - THINKING_BUDGET.default, - selectedModelInfo.maxTokens ?? THINKING_BUDGET.default, - ) - : undefined, - ) - }> - Thinking? - - {anthropicThinkingBudget && ( - <> -
- Number of tokens Claude is allowed to use for its internal reasoning process. -
-
- setApiConfigurationField("anthropicThinking", value[0])} - /> -
{anthropicThinkingBudget}
-
- - )} +
+
Thinking Budget
+
+ setApiConfigurationField("anthropicThinking", value[0])} + /> +
{anthropicThinkingBudget}
+
+
+ Number of tokens Claude is allowed to use for its internal reasoning process. +
)}