diff --git a/cli/src/index.ts b/cli/src/index.ts index aabafcd279..c083fd196a 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -86,9 +86,9 @@ function normalizeReasoningEffort(value?: string): OpenaiReasoningEffort | undef } printWarning( - `Invalid --reasoning-effort '${value}'. Using 'low'. Valid values: ${OPENAI_REASONING_EFFORT_OPTIONS.join(", ")}.`, + `Invalid --reasoning-effort '${value}'. Using 'medium'. Valid values: ${OPENAI_REASONING_EFFORT_OPTIONS.join(", ")}.`, ) - return "low" + return "medium" } function normalizeMaxConsecutiveMistakes(value?: string): number | undefined { diff --git a/proto/cline/state.proto b/proto/cline/state.proto index 160d1b15a1..5d135a2eae 100644 --- a/proto/cline/state.proto +++ b/proto/cline/state.proto @@ -324,7 +324,7 @@ enum OpenaiReasoningEffort { LOW = 0; MEDIUM = 1; HIGH = 2; - MINIMAL = 3; + reserved 3; // was MINIMAL } enum McpDisplayMode { diff --git a/src/core/api/providers/oca.ts b/src/core/api/providers/oca.ts index 72014c8674..80da8d0b0a 100644 --- a/src/core/api/providers/oca.ts +++ b/src/core/api/providers/oca.ts @@ -238,7 +238,7 @@ export class OcaHandler implements ApiHandler { } if (this.options.ocaModelInfo?.supportsReasoningEffort) { - chatCompletionsParams["reasoning_effort"] = this.options.ocaReasoningEffort || ("low" as any) + chatCompletionsParams["reasoning_effort"] = this.options.ocaReasoningEffort || ("medium" as any) } const stream = await client.chat.completions.create(chatCompletionsParams) diff --git a/src/core/api/providers/openai.ts b/src/core/api/providers/openai.ts index 676e46b62c..2ae66f0759 100644 --- a/src/core/api/providers/openai.ts +++ b/src/core/api/providers/openai.ts @@ -129,10 +129,8 @@ export class OpenAiHandler implements ApiHandler { if (isReasoningModelFamily) { openAiMessages = [{ role: "developer", content: systemPrompt }, ...convertToOpenAiMessages(messages)] temperature = undefined // does not support temperature - if (this.options.reasoningEffort !== undefined) { - const requestedEffort = normalizeOpenaiReasoningEffort(this.options.reasoningEffort) - reasoningEffort = requestedEffort === "none" ? undefined : (requestedEffort as ChatCompletionReasoningEffort) - } + const requestedEffort = normalizeOpenaiReasoningEffort(this.options.reasoningEffort) + reasoningEffort = requestedEffort === "none" ? undefined : (requestedEffort as ChatCompletionReasoningEffort) } const stream = await client.chat.completions.create({ diff --git a/src/core/api/providers/requesty.ts b/src/core/api/providers/requesty.ts index e7fcad848b..8b2d4e96d3 100644 --- a/src/core/api/providers/requesty.ts +++ b/src/core/api/providers/requesty.ts @@ -67,7 +67,7 @@ export class RequestyHandler implements ApiHandler { ...convertToOpenAiMessages(messages), ] - const reasoningEffort = this.options.reasoningEffort || "low" + const reasoningEffort = this.options.reasoningEffort || "medium" const reasoning = { reasoning_effort: reasoningEffort } const reasoningArgs = model.id.startsWith("openai/o") ? reasoning : {} diff --git a/src/core/api/transform/openrouter-stream.ts b/src/core/api/transform/openrouter-stream.ts index 513c8a0cdd..28be2816f4 100644 --- a/src/core/api/transform/openrouter-stream.ts +++ b/src/core/api/transform/openrouter-stream.ts @@ -7,7 +7,7 @@ import { openRouterClaudeSonnet41mModelId, openRouterClaudeSonnet451mModelId, } from "@shared/api" -import { isOpenaiReasoningEffort } from "@shared/storage/types" +import { normalizeOpenaiReasoningEffort } from "@shared/storage/types" import { shouldSkipReasoningForModel, supportsReasoningEffortForModel } from "@utils/model-utils" import OpenAI from "openai" import { ChatCompletionTool } from "openai/resources/chat/completions" @@ -197,7 +197,7 @@ export async function createOpenRouterStream( openRouterProviderSorting = undefined } - const normalizedReasoningEffort = isOpenaiReasoningEffort(reasoningEffort) ? reasoningEffort : "low" + const normalizedReasoningEffort = reasoningEffort !== undefined ? normalizeOpenaiReasoningEffort(reasoningEffort) : undefined const reasoningEffortValue = supportsReasoningEffort ? normalizedReasoningEffort : undefined // Skip reasoning for models that don't support it (e.g., devstral, grok-4), or when effort explicitly disables it. const includeReasoning = !shouldSkipReasoningForModel(model.id) && reasoningEffortValue !== "none" diff --git a/src/core/api/transform/vercel-ai-gateway-stream.ts b/src/core/api/transform/vercel-ai-gateway-stream.ts index 1a3378150f..cfc5da02a4 100644 --- a/src/core/api/transform/vercel-ai-gateway-stream.ts +++ b/src/core/api/transform/vercel-ai-gateway-stream.ts @@ -6,7 +6,7 @@ import { openRouterClaudeSonnet41mModelId, openRouterClaudeSonnet451mModelId, } from "@shared/api" -import { isOpenaiReasoningEffort } from "@shared/storage/types" +import { normalizeOpenaiReasoningEffort } from "@shared/storage/types" import { shouldSkipReasoningForModel, supportsReasoningEffortForModel } from "@utils/model-utils" import OpenAI from "openai" import type { ChatCompletionTool as OpenAITool } from "openai/resources/chat/completions" @@ -124,7 +124,7 @@ export async function createVercelAIGatewayStream( reasoning = { max_tokens: thinkingBudgetTokens } } - const normalizedReasoningEffort = isOpenaiReasoningEffort(reasoningEffort) ? reasoningEffort : "low" + const normalizedReasoningEffort = reasoningEffort !== undefined ? normalizeOpenaiReasoningEffort(reasoningEffort) : undefined const reasoningEffortValue = supportsReasoningEffort ? normalizedReasoningEffort : undefined // Skip reasoning for models that don't support it (e.g., devstral, grok-4), or when effort explicitly disables it. const includeReasoning = !shouldSkipReasoningForModel(model.id) && reasoningEffortValue !== "none" diff --git a/src/shared/storage/types.ts b/src/shared/storage/types.ts index c9fd935fe6..0c2ac8c215 100644 --- a/src/shared/storage/types.ts +++ b/src/shared/storage/types.ts @@ -7,8 +7,8 @@ export function isOpenaiReasoningEffort(value: unknown): value is OpenaiReasonin } export function normalizeOpenaiReasoningEffort(effort?: string): OpenaiReasoningEffort { - const value = (effort || "low").toLowerCase() - return isOpenaiReasoningEffort(value) ? value : "low" + const value = (effort || "medium").toLowerCase() + return isOpenaiReasoningEffort(value) ? value : "medium" } export type Mode = "plan" | "act" diff --git a/src/shared/utils/reasoning-support.ts b/src/shared/utils/reasoning-support.ts new file mode 100644 index 0000000000..af0752c0be --- /dev/null +++ b/src/shared/utils/reasoning-support.ts @@ -0,0 +1,15 @@ +export function supportsReasoningEffortForModel(modelId?: string): boolean { + if (!modelId) { + return false + } + + const id = modelId.toLowerCase() + return ( + id.includes("gemini") || + id.includes("gpt") || + id.startsWith("openai/o") || + id.includes("/o") || + id.startsWith("o") || + id.includes("grok") + ) +} diff --git a/src/utils/model-utils.ts b/src/utils/model-utils.ts index 35c9da105e..58bdb86b6e 100644 --- a/src/utils/model-utils.ts +++ b/src/utils/model-utils.ts @@ -1,6 +1,8 @@ import { ApiHandlerModel, ApiProviderInfo } from "@core/api" import { AnthropicModelId, anthropicModels } from "@/shared/api" +export { supportsReasoningEffortForModel } from "@shared/utils/reasoning-support" + const CLAUDE_VERSION_MATCH_REGEX = /[-_ ]([\d](?:\.[05])?)[-_ ]?/ export function isNextGenModelProvider(providerInfo: ApiProviderInfo): boolean { @@ -142,14 +144,6 @@ export function isGemini3ModelFamily(id: string): boolean { return modelId.includes("gemini3") || modelId.includes("gemini-3") } -export function supportsReasoningEffortForModel(modelId?: string): boolean { - if (!modelId) { - return false - } - const id = normalize(modelId) - return id.includes("gemini") || id.includes("gpt") || id.startsWith("openai/o") || id.includes("grok") -} - function isDeepSeek32ModelFamily(id: string): boolean { const modelId = normalize(id) return modelId.includes("deepseek") && modelId.includes("3.2") && !modelId.includes("speciale") diff --git a/webview-ui/src/components/settings/ReasoningEffortSelector.tsx b/webview-ui/src/components/settings/ReasoningEffortSelector.tsx index 5e18326f2e..41f136a4b8 100644 --- a/webview-ui/src/components/settings/ReasoningEffortSelector.tsx +++ b/webview-ui/src/components/settings/ReasoningEffortSelector.tsx @@ -25,7 +25,7 @@ const ReasoningEffortSelector = ({ const selectedEffort = isOpenaiReasoningEffort(modeFields.reasoningEffort) && allowedEfforts.includes(modeFields.reasoningEffort) ? modeFields.reasoningEffort - : "low" + : "medium" return (