From bcb427579c7c25d57f304b01ddc40d26b563e537 Mon Sep 17 00:00:00 2001 From: Ruakij Date: Fri, 30 May 2025 08:44:45 +0200 Subject: [PATCH] Refactor reasoning budget settings and update localization for reasoning effort --- evals/packages/types/src/roo-code.ts | 6 ++-- packages/types/src/provider-settings.ts | 4 +-- src/api/providers/__tests__/openai.spec.ts | 4 +-- src/api/providers/gemini.ts | 7 ++-- src/api/providers/vertex.ts | 7 ++-- .../transform/__tests__/model-params.test.ts | 14 ++++---- src/api/transform/__tests__/reasoning.test.ts | 18 +++++----- src/api/transform/model-params.ts | 4 +-- src/api/transform/reasoning.ts | 6 ++-- src/core/config/ProviderSettingsManager.ts | 26 ++++++++++++++ src/shared/__tests__/api.test.ts | 36 +++++++++---------- src/shared/api.ts | 18 +++++----- src/utils/__tests__/enhance-prompt.test.ts | 4 +-- .../components/settings/ThinkingBudget.tsx | 19 ++++++---- .../settings/__tests__/ApiOptions.test.tsx | 16 ++++----- .../settings/providers/OpenAICompatible.tsx | 6 ++-- webview-ui/src/i18n/locales/ca/settings.json | 4 ++- webview-ui/src/i18n/locales/de/settings.json | 4 ++- webview-ui/src/i18n/locales/en/settings.json | 4 ++- webview-ui/src/i18n/locales/es/settings.json | 4 ++- webview-ui/src/i18n/locales/fr/settings.json | 4 ++- webview-ui/src/i18n/locales/hi/settings.json | 4 ++- webview-ui/src/i18n/locales/it/settings.json | 4 ++- webview-ui/src/i18n/locales/ja/settings.json | 4 ++- webview-ui/src/i18n/locales/ko/settings.json | 4 ++- webview-ui/src/i18n/locales/nl/settings.json | 4 ++- webview-ui/src/i18n/locales/pl/settings.json | 4 ++- .../src/i18n/locales/pt-BR/settings.json | 4 ++- webview-ui/src/i18n/locales/ru/settings.json | 4 ++- webview-ui/src/i18n/locales/tr/settings.json | 4 ++- webview-ui/src/i18n/locales/vi/settings.json | 4 ++- .../src/i18n/locales/zh-CN/settings.json | 4 ++- .../src/i18n/locales/zh-TW/settings.json | 4 ++- 33 files changed, 165 insertions(+), 98 deletions(-) diff --git a/evals/packages/types/src/roo-code.ts b/evals/packages/types/src/roo-code.ts index 0363c888b6..2edeb652b7 100644 --- a/evals/packages/types/src/roo-code.ts +++ b/evals/packages/types/src/roo-code.ts @@ -340,7 +340,7 @@ const genericProviderSettingsSchema = z.object({ rateLimitSeconds: z.number().optional(), // Model reasoning. - enableReasoningEffort: z.boolean().optional(), + setReasoningEffort: z.boolean().optional(), reasoningEffort: reasoningEffortsSchema.optional(), modelMaxTokens: z.number().optional(), modelMaxThinkingTokens: z.number().optional(), @@ -395,7 +395,7 @@ const openAiSchema = z.object({ openAiUseAzure: z.boolean().optional(), azureApiVersion: z.string().optional(), openAiStreamingEnabled: z.boolean().optional(), - enableReasoningEffort: z.boolean().optional(), + setReasoningEffort: z.boolean().optional(), openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration. openAiHeaders: z.record(z.string(), z.string()).optional(), }) @@ -663,7 +663,7 @@ const providerSettingsRecord: ProviderSettingsRecord = { openAiUseAzure: undefined, azureApiVersion: undefined, openAiStreamingEnabled: undefined, - enableReasoningEffort: undefined, + setReasoningEffort: undefined, openAiHostHeader: undefined, // Keep temporarily for backward compatibility during migration openAiHeaders: undefined, // Ollama diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 794f437a3a..12b9f72704 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -60,7 +60,7 @@ const baseProviderSettingsSchema = z.object({ rateLimitSeconds: z.number().optional(), // Model reasoning. - enableReasoningEffort: z.boolean().optional(), + setReasoningEffort: z.boolean().optional(), reasoningEffort: reasoningEffortsSchema.optional(), modelMaxTokens: z.number().optional(), modelMaxThinkingTokens: z.number().optional(), @@ -331,7 +331,7 @@ export const PROVIDER_SETTINGS_KEYS = keysOf()([ "codeIndexOpenAiKey", "codeIndexQdrantApiKey", // Reasoning - "enableReasoningEffort", + "setReasoningEffort", "reasoningEffort", "modelMaxTokens", "modelMaxThinkingTokens", diff --git a/src/api/providers/__tests__/openai.spec.ts b/src/api/providers/__tests__/openai.spec.ts index 81c0b45e41..32ceea8b0d 100644 --- a/src/api/providers/__tests__/openai.spec.ts +++ b/src/api/providers/__tests__/openai.spec.ts @@ -162,7 +162,7 @@ describe("OpenAiHandler", () => { it("should include reasoning_effort when reasoning effort is enabled", async () => { const reasoningOptions: ApiHandlerOptions = { ...mockOptions, - enableReasoningEffort: true, + setReasoningEffort: true, openAiCustomModelInfo: { contextWindow: 128_000, supportsPromptCache: false, @@ -184,7 +184,7 @@ describe("OpenAiHandler", () => { it("should not include reasoning_effort when reasoning effort is disabled", async () => { const noReasoningOptions: ApiHandlerOptions = { ...mockOptions, - enableReasoningEffort: false, + setReasoningEffort: false, openAiCustomModelInfo: { contextWindow: 128_000, supportsPromptCache: false }, } const noReasoningHandler = new OpenAiHandler(noReasoningOptions) diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index e5ceffbf43..0dfea47b5e 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -120,9 +120,10 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl return { id, info, - thinkingConfig: this.options.modelMaxThinkingTokens - ? { thinkingBudget: this.options.modelMaxThinkingTokens } - : undefined, + thinkingConfig: + this.options.setReasoningEffort && this.options.modelMaxThinkingTokens !== undefined + ? { thinkingBudget: this.options.modelMaxThinkingTokens } + : undefined, maxOutputTokens: this.options.modelMaxTokens ?? info.maxTokens ?? undefined, } } diff --git a/src/api/providers/vertex.ts b/src/api/providers/vertex.ts index 2bc940de7a..36fe29c86d 100644 --- a/src/api/providers/vertex.ts +++ b/src/api/providers/vertex.ts @@ -23,9 +23,10 @@ export class VertexHandler extends GeminiHandler implements SingleCompletionHand return { id, info, - thinkingConfig: this.options.modelMaxThinkingTokens - ? { thinkingBudget: this.options.modelMaxThinkingTokens } - : undefined, + thinkingConfig: + this.options.setReasoningEffort && this.options.modelMaxThinkingTokens !== undefined + ? { thinkingBudget: this.options.modelMaxThinkingTokens } + : undefined, maxOutputTokens: this.options.modelMaxTokens ?? info.maxTokens ?? undefined, } } diff --git a/src/api/transform/__tests__/model-params.test.ts b/src/api/transform/__tests__/model-params.test.ts index 2eabe1c7fa..d74fb41068 100644 --- a/src/api/transform/__tests__/model-params.test.ts +++ b/src/api/transform/__tests__/model-params.test.ts @@ -207,7 +207,7 @@ describe("getModelParams", () => { }) }) - it("should handle supportsReasoningBudget with enableReasoningEffort setting", () => { + it("should handle supportsReasoningBudget with setReasoningEffort setting", () => { const model: ModelInfo = { ...baseModel, maxTokens: 2000, @@ -216,7 +216,7 @@ describe("getModelParams", () => { const result = getModelParams({ ...anthropicParams, - settings: { enableReasoningEffort: true }, + settings: { setReasoningEffort: true }, model, }) @@ -228,7 +228,7 @@ describe("getModelParams", () => { }) }) - it("should not use reasoning budget when supportsReasoningBudget is true but enableReasoningEffort is false", () => { + it("should not use reasoning budget when supportsReasoningBudget is true but setReasoningEffort is false", () => { const model: ModelInfo = { ...baseModel, maxTokens: 2000, @@ -237,7 +237,7 @@ describe("getModelParams", () => { const result = getModelParams({ ...anthropicParams, - settings: { enableReasoningEffort: false }, + settings: { setReasoningEffort: false }, model, }) @@ -537,7 +537,7 @@ describe("getModelParams", () => { it("should keep model maxTokens for hybrid models when using reasoning budget", () => { const result = getModelParams({ ...anthropicParams, - settings: { enableReasoningEffort: true }, + settings: { setReasoningEffort: true }, model, }) @@ -560,7 +560,7 @@ describe("getModelParams", () => { // Only reasoning budget should be used (takes precedence) const result = getModelParams({ ...anthropicParams, - settings: { enableReasoningEffort: true }, + settings: { setReasoningEffort: true }, model, }) @@ -645,7 +645,7 @@ describe("getModelParams", () => { const result = getModelParams({ ...anthropicParams, settings: { - enableReasoningEffort: true, + setReasoningEffort: true, modelMaxTokens: 20000, modelMaxThinkingTokens: 10000, modelTemperature: 0.8, diff --git a/src/api/transform/__tests__/reasoning.test.ts b/src/api/transform/__tests__/reasoning.test.ts index 47a0317a50..cb0e0b9811 100644 --- a/src/api/transform/__tests__/reasoning.test.ts +++ b/src/api/transform/__tests__/reasoning.test.ts @@ -47,7 +47,7 @@ describe("reasoning.ts", () => { } const settingsWithEnabled: ProviderSettings = { - enableReasoningEffort: true, + setReasoningEffort: true, } const options = { @@ -108,7 +108,7 @@ describe("reasoning.ts", () => { } const settingsWithBoth: ProviderSettings = { - enableReasoningEffort: true, + setReasoningEffort: true, reasoningEffort: "low", } @@ -189,14 +189,14 @@ describe("reasoning.ts", () => { expect(result).toEqual({ max_tokens: 0 }) }) - it("should not use reasoning budget when supportsReasoningBudget is true but enableReasoningEffort is false", () => { + it("should not use reasoning budget when supportsReasoningBudget is true but setReasoningEffort is false", () => { const modelWithSupported: ModelInfo = { ...baseModel, supportsReasoningBudget: true, } const settingsWithDisabled: ProviderSettings = { - enableReasoningEffort: false, + setReasoningEffort: false, } const options = { @@ -252,7 +252,7 @@ describe("reasoning.ts", () => { } const settingsWithEnabled: ProviderSettings = { - enableReasoningEffort: true, + setReasoningEffort: true, } const options = { @@ -274,14 +274,14 @@ describe("reasoning.ts", () => { expect(result).toBeUndefined() }) - it("should return undefined when supportsReasoningBudget is true but enableReasoningEffort is false", () => { + it("should return undefined when supportsReasoningBudget is true but setReasoningEffort is false", () => { const modelWithSupported: ModelInfo = { ...baseModel, supportsReasoningBudget: true, } const settingsWithDisabled: ProviderSettings = { - enableReasoningEffort: false, + setReasoningEffort: false, } const options = { @@ -513,7 +513,7 @@ describe("reasoning.ts", () => { } const settingsWithEnabled: ProviderSettings = { - enableReasoningEffort: true, + setReasoningEffort: true, } const options = { @@ -583,7 +583,7 @@ describe("reasoning.ts", () => { } const settingsWithBoth: ProviderSettings = { - enableReasoningEffort: true, + setReasoningEffort: true, reasoningEffort: "high", } diff --git a/src/api/transform/model-params.ts b/src/api/transform/model-params.ts index 2fb5012655..4a358bb2ca 100644 --- a/src/api/transform/model-params.ts +++ b/src/api/transform/model-params.ts @@ -1,7 +1,7 @@ import type { ModelInfo, ProviderSettings } from "@roo-code/types" import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "../providers/constants" -import { shouldUseReasoningBudget, shouldUseReasoningEffort } from "../../shared/api" +import { shouldSetReasoningBudget, shouldUseReasoningEffort } from "../../shared/api" import { type AnthropicReasoningParams, @@ -67,7 +67,7 @@ export function getModelParams({ let reasoningBudget: ModelParams["reasoningBudget"] = undefined let reasoningEffort: ModelParams["reasoningEffort"] = undefined - if (shouldUseReasoningBudget({ model, settings })) { + if (shouldSetReasoningBudget({ model, settings })) { // "Hybrid" reasoning models use the `reasoningBudget` parameter. maxTokens = customMaxTokens ?? maxTokens diff --git a/src/api/transform/reasoning.ts b/src/api/transform/reasoning.ts index 9887f1137a..e313f6f9da 100644 --- a/src/api/transform/reasoning.ts +++ b/src/api/transform/reasoning.ts @@ -3,7 +3,7 @@ import OpenAI from "openai" import type { ModelInfo, ProviderSettings } from "@roo-code/types" -import { shouldUseReasoningBudget, shouldUseReasoningEffort } from "../../shared/api" +import { shouldSetReasoningBudget, shouldUseReasoningEffort } from "../../shared/api" type ReasoningEffort = "low" | "medium" | "high" @@ -30,7 +30,7 @@ export const getOpenRouterReasoning = ({ reasoningEffort, settings, }: GetModelReasoningOptions): OpenRouterReasoningParams | undefined => - shouldUseReasoningBudget({ model, settings }) + shouldSetReasoningBudget({ model, settings }) ? { max_tokens: reasoningBudget } : shouldUseReasoningEffort({ model, settings }) ? { effort: reasoningEffort } @@ -41,7 +41,7 @@ export const getAnthropicReasoning = ({ reasoningBudget, settings, }: GetModelReasoningOptions): AnthropicReasoningParams | undefined => - shouldUseReasoningBudget({ model, settings }) ? { type: "enabled", budget_tokens: reasoningBudget! } : undefined + shouldSetReasoningBudget({ model, settings }) ? { type: "enabled", budget_tokens: reasoningBudget! } : undefined export const getOpenAiReasoning = ({ model, diff --git a/src/core/config/ProviderSettingsManager.ts b/src/core/config/ProviderSettingsManager.ts index 32c0135d3b..fd0efab751 100644 --- a/src/core/config/ProviderSettingsManager.ts +++ b/src/core/config/ProviderSettingsManager.ts @@ -26,6 +26,7 @@ export const providerProfilesSchema = z.object({ rateLimitSecondsMigrated: z.boolean().optional(), diffSettingsMigrated: z.boolean().optional(), openAiHeadersMigrated: z.boolean().optional(), + manualThinkingBudgetMigrated: z.boolean().optional(), }) .optional(), }) @@ -48,6 +49,7 @@ export class ProviderSettingsManager { rateLimitSecondsMigrated: true, // Mark as migrated on fresh installs diffSettingsMigrated: true, // Mark as migrated on fresh installs openAiHeadersMigrated: true, // Mark as migrated on fresh installs + manualThinkingBudgetMigrated: true, // Mark as migrated on fresh installs }, } @@ -113,6 +115,7 @@ export class ProviderSettingsManager { rateLimitSecondsMigrated: false, diffSettingsMigrated: false, openAiHeadersMigrated: false, + manualThinkingBudgetMigrated: false, } // Initialize with default values isDirty = true } @@ -135,6 +138,12 @@ export class ProviderSettingsManager { isDirty = true } + if (!providerProfiles.migrations.manualThinkingBudgetMigrated) { + await this.migrateManualThinkingBudget(providerProfiles) + providerProfiles.migrations.manualThinkingBudgetMigrated = true + isDirty = true + } + if (isDirty) { await this.store(providerProfiles) } @@ -228,6 +237,23 @@ export class ProviderSettingsManager { } } + private async migrateManualThinkingBudget(providerProfiles: ProviderProfiles) { + try { + for (const [_name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) { + // For existing users who have modelMaxThinkingTokens set, enable manual control + // This maintains backward compatibility - if they were manually setting thinking tokens, + // they should continue to have manual control enabled + if (apiConfig.modelMaxThinkingTokens !== undefined && apiConfig.setReasoningEffort === undefined) { + apiConfig.setReasoningEffort = true + } + // For new users or existing users without thinking tokens set, + // default to false (automatic mode) - this is handled by the UI component's default logic + } + } catch (error) { + console.error(`[MigrateManualThinkingBudget] Failed to migrate manual thinking budget settings:`, error) + } + } + /** * List all available configs with metadata. */ diff --git a/src/shared/__tests__/api.test.ts b/src/shared/__tests__/api.test.ts index b71b68095f..8c9d6bb612 100644 --- a/src/shared/__tests__/api.test.ts +++ b/src/shared/__tests__/api.test.ts @@ -4,7 +4,7 @@ import type { ModelInfo, ProviderSettings } from "@roo-code/types" import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "../../api/providers/constants" -import { getModelMaxOutputTokens, shouldUseReasoningBudget, shouldUseReasoningEffort } from "../api" +import { getModelMaxOutputTokens, shouldSetReasoningBudget, shouldUseReasoningEffort } from "../api" describe("getMaxTokensForModel", () => { const modelId = "test" @@ -159,7 +159,7 @@ describe("getMaxTokensForModel", () => { }) test("should return ANTHROPIC_DEFAULT_MAX_TOKENS for Anthropic models that support reasoning budget but aren't using it", () => { - // Test case for models that support reasoning budget but enableReasoningEffort is false + // Test case for models that support reasoning budget but setReasoningEffort is false const anthropicModelId = "claude-sonnet-4-20250514" const model: ModelInfo = { contextWindow: 200_000, @@ -169,7 +169,7 @@ describe("getMaxTokensForModel", () => { } const settings: ProviderSettings = { - enableReasoningEffort: false, // Not using reasoning + setReasoningEffort: false, // Not using reasoning } const result = getModelMaxOutputTokens({ modelId: anthropicModelId, model, settings }) @@ -187,7 +187,7 @@ describe("getMaxTokensForModel", () => { } const settings: ProviderSettings = { - enableReasoningEffort: false, // Not using reasoning + setReasoningEffort: false, // Not using reasoning } const result = getModelMaxOutputTokens({ modelId: geminiModelId, model, settings }) @@ -204,9 +204,9 @@ describe("shouldUseReasoningBudget", () => { } // Should return true regardless of settings - expect(shouldUseReasoningBudget({ model })).toBe(true) - expect(shouldUseReasoningBudget({ model, settings: {} })).toBe(true) - expect(shouldUseReasoningBudget({ model, settings: { enableReasoningEffort: false } })).toBe(true) + expect(shouldSetReasoningBudget({ model })).toBe(true) + expect(shouldSetReasoningBudget({ model, settings: {} })).toBe(true) + expect(shouldSetReasoningBudget({ model, settings: { setReasoningEffort: false } })).toBe(true) }) it("should return true when model supports reasoning budget and settings enable reasoning effort", () => { @@ -217,10 +217,10 @@ describe("shouldUseReasoningBudget", () => { } const settings: ProviderSettings = { - enableReasoningEffort: true, + setReasoningEffort: true, } - expect(shouldUseReasoningBudget({ model, settings })).toBe(true) + expect(shouldSetReasoningBudget({ model, settings })).toBe(true) }) it("should return false when model supports reasoning budget but settings don't enable reasoning effort", () => { @@ -231,12 +231,12 @@ describe("shouldUseReasoningBudget", () => { } const settings: ProviderSettings = { - enableReasoningEffort: false, + setReasoningEffort: false, } - expect(shouldUseReasoningBudget({ model, settings })).toBe(false) - expect(shouldUseReasoningBudget({ model, settings: {} })).toBe(false) - expect(shouldUseReasoningBudget({ model })).toBe(false) + expect(shouldSetReasoningBudget({ model, settings })).toBe(false) + expect(shouldSetReasoningBudget({ model, settings: {} })).toBe(false) + expect(shouldSetReasoningBudget({ model })).toBe(false) }) it("should return false when model doesn't support reasoning budget", () => { @@ -246,11 +246,11 @@ describe("shouldUseReasoningBudget", () => { } const settings: ProviderSettings = { - enableReasoningEffort: true, + setReasoningEffort: true, } - expect(shouldUseReasoningBudget({ model, settings })).toBe(false) - expect(shouldUseReasoningBudget({ model })).toBe(false) + expect(shouldSetReasoningBudget({ model, settings })).toBe(false) + expect(shouldSetReasoningBudget({ model })).toBe(false) }) it("should handle undefined settings gracefully", () => { @@ -266,8 +266,8 @@ describe("shouldUseReasoningBudget", () => { supportsReasoningBudget: true, } - expect(shouldUseReasoningBudget({ model: modelWithRequired, settings: undefined })).toBe(true) - expect(shouldUseReasoningBudget({ model: modelWithSupported, settings: undefined })).toBe(false) + expect(shouldSetReasoningBudget({ model: modelWithRequired, settings: undefined })).toBe(true) + expect(shouldSetReasoningBudget({ model: modelWithSupported, settings: undefined })).toBe(false) }) }) diff --git a/src/shared/api.ts b/src/shared/api.ts index 8e26523a07..368d843a5a 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -10,7 +10,7 @@ export type AnthropicModelId = keyof typeof anthropicModels export const anthropicDefaultModelId: AnthropicModelId = "claude-sonnet-4-20250514" export const anthropicModels = { "claude-sonnet-4-20250514": { - maxTokens: 64_000, // Overridden to 8k if `enableReasoningEffort` is false. + maxTokens: 64_000, // Overridden to 8k if `setReasoningEffort` is false. contextWindow: 200_000, supportsImages: true, supportsComputerUse: true, @@ -22,7 +22,7 @@ export const anthropicModels = { supportsReasoningBudget: true, }, "claude-opus-4-20250514": { - maxTokens: 32_000, // Overridden to 8k if `enableReasoningEffort` is false. + maxTokens: 32_000, // Overridden to 8k if `setReasoningEffort` is false. contextWindow: 200_000, supportsImages: true, supportsComputerUse: true, @@ -533,7 +533,7 @@ export const vertexModels = { outputPrice: 3.5, maxThinkingTokens: 24_576, supportsReasoningBudget: true, - requiredReasoningBudget: true, + requiredReasoningBudget: false, }, "gemini-2.5-flash-preview-05-20": { maxTokens: 65_535, @@ -552,7 +552,7 @@ export const vertexModels = { outputPrice: 3.5, maxThinkingTokens: 24_576, supportsReasoningBudget: true, - requiredReasoningBudget: true, + requiredReasoningBudget: false, }, "gemini-2.5-flash-preview-04-17": { maxTokens: 65_535, @@ -757,7 +757,7 @@ export const geminiModels = { outputPrice: 3.5, maxThinkingTokens: 24_576, supportsReasoningBudget: true, - requiredReasoningBudget: true, + requiredReasoningBudget: false, }, "gemini-2.5-flash-preview-04-17": { maxTokens: 65_535, @@ -778,7 +778,7 @@ export const geminiModels = { cacheWritesPrice: 1.0, maxThinkingTokens: 24_576, supportsReasoningBudget: true, - requiredReasoningBudget: true, + requiredReasoningBudget: false, }, "gemini-2.5-flash-preview-05-20": { maxTokens: 65_535, @@ -1990,13 +1990,13 @@ export type ModelRecord = Record export type RouterModels = Record -export const shouldUseReasoningBudget = ({ +export const shouldSetReasoningBudget = ({ model, settings, }: { model: ModelInfo settings?: ProviderSettings -}): boolean => !!model.requiredReasoningBudget || (!!model.supportsReasoningBudget && !!settings?.enableReasoningEffort) +}): boolean => !!model.requiredReasoningBudget || (!!model.supportsReasoningBudget && !!settings?.setReasoningEffort) export const shouldUseReasoningEffort = ({ model, @@ -2018,7 +2018,7 @@ export const getModelMaxOutputTokens = ({ model: ModelInfo settings?: ProviderSettings }): number | undefined => { - if (shouldUseReasoningBudget({ model, settings })) { + if (shouldSetReasoningBudget({ model, settings })) { return settings?.modelMaxTokens || DEFAULT_HYBRID_REASONING_MODEL_MAX_TOKENS } diff --git a/src/utils/__tests__/enhance-prompt.test.ts b/src/utils/__tests__/enhance-prompt.test.ts index 6f4ea2be62..c29dd8cd7a 100644 --- a/src/utils/__tests__/enhance-prompt.test.ts +++ b/src/utils/__tests__/enhance-prompt.test.ts @@ -16,7 +16,7 @@ describe("enhancePrompt", () => { apiProvider: "openai", openAiApiKey: "test-key", openAiBaseUrl: "https://api.openai.com/v1", - enableReasoningEffort: false, + setReasoningEffort: false, } beforeEach(() => { @@ -101,7 +101,7 @@ describe("enhancePrompt", () => { apiProvider: "openrouter", openRouterApiKey: "test-key", openRouterModelId: "test-model", - enableReasoningEffort: false, + setReasoningEffort: false, } // Mock successful enhancement diff --git a/webview-ui/src/components/settings/ThinkingBudget.tsx b/webview-ui/src/components/settings/ThinkingBudget.tsx index 456e0be17a..289fdec13d 100644 --- a/webview-ui/src/components/settings/ThinkingBudget.tsx +++ b/webview-ui/src/components/settings/ThinkingBudget.tsx @@ -21,10 +21,10 @@ export const ThinkingBudget = ({ apiConfiguration, setApiConfigurationField, mod const isReasoningBudgetRequired = !!modelInfo && modelInfo.requiredReasoningBudget const isReasoningEffortSupported = !!modelInfo && modelInfo.supportsReasoningEffort - const enableReasoningEffort = apiConfiguration.enableReasoningEffort + const setReasoningEffort = apiConfiguration.setReasoningEffort const customMaxOutputTokens = apiConfiguration.modelMaxTokens || DEFAULT_HYBRID_REASONING_MODEL_MAX_TOKENS const customMaxThinkingTokens = - apiConfiguration.modelMaxThinkingTokens || DEFAULT_HYBRID_REASONING_MODEL_THINKING_TOKENS + apiConfiguration.modelMaxThinkingTokens ?? DEFAULT_HYBRID_REASONING_MODEL_THINKING_TOKENS // Dynamically expand or shrink the max thinking budget based on the custom // max output tokens so that there's always a 20% buffer. @@ -50,15 +50,20 @@ export const ThinkingBudget = ({ apiConfiguration, setApiConfigurationField, mod {!isReasoningBudgetRequired && (
- setApiConfigurationField("enableReasoningEffort", checked === true) + setApiConfigurationField("setReasoningEffort", checked === true) }> - {t("settings:providers.useReasoning")} + {t("settings:providers.setReasoningEffort")} +
+ {setReasoningEffort + ? t("settings:providers.explicitReasoning") + : t("settings:providers.implicitReasoning")} +
)} - {(isReasoningBudgetRequired || enableReasoningEffort) && ( + {(isReasoningBudgetRequired || setReasoningEffort) && ( <>
{t("settings:thinkingBudget.maxTokens")}
@@ -77,7 +82,7 @@ export const ThinkingBudget = ({ apiConfiguration, setApiConfigurationField, mod
{t("settings:thinkingBudget.maxThinkingTokens")}
{ const mockSetApiConfigurationField = jest.fn() const initialConfig = { apiProvider: "openai" as const, - enableReasoningEffort: true, + setReasoningEffort: true, openAiCustomModelInfo: { ...openAiModelInfoSaneDefaults, // Start with defaults reasoningEffort: "low" as const, // Set an initial value @@ -320,8 +320,8 @@ describe("ApiOptions", () => { // Simulate unchecking the checkbox fireEvent.click(checkbox) - // 1. Check if enableReasoningEffort was set to false - expect(mockSetApiConfigurationField).toHaveBeenCalledWith("enableReasoningEffort", false) + // 1. Check if setReasoningEffort was set to false + expect(mockSetApiConfigurationField).toHaveBeenCalledWith("setReasoningEffort", false) // 2. Check if openAiCustomModelInfo was updated const updateCall = mockSetApiConfigurationField.mock.calls.find( @@ -341,7 +341,7 @@ describe("ApiOptions", () => { const mockSetApiConfigurationField = jest.fn() const initialConfig = { apiProvider: "openai" as const, - enableReasoningEffort: false, // Initially disabled + setReasoningEffort: false, // Initially disabled openAiCustomModelInfo: { ...openAiModelInfoSaneDefaults, }, @@ -360,7 +360,7 @@ describe("ApiOptions", () => { const mockSetApiConfigurationField = jest.fn() const initialConfig = { apiProvider: "openai" as const, - enableReasoningEffort: false, // Initially disabled + setReasoningEffort: false, // Initially disabled openAiCustomModelInfo: { ...openAiModelInfoSaneDefaults, }, @@ -376,8 +376,8 @@ describe("ApiOptions", () => { // Simulate checking the checkbox fireEvent.click(checkbox) - // 1. Check if enableReasoningEffort was set to true - expect(mockSetApiConfigurationField).toHaveBeenCalledWith("enableReasoningEffort", true) + // 1. Check if setReasoningEffort was set to true + expect(mockSetApiConfigurationField).toHaveBeenCalledWith("setReasoningEffort", true) // We can't directly test the rendering of the ReasoningEffort component after the state change // without a more complex setup involving state management mocks or re-rendering. @@ -388,7 +388,7 @@ describe("ApiOptions", () => { const mockSetApiConfigurationField = jest.fn() const initialConfig = { apiProvider: "openai" as const, - enableReasoningEffort: true, // Initially enabled + setReasoningEffort: true, // Initially enabled openAiCustomModelInfo: { ...openAiModelInfoSaneDefaults, reasoningEffort: "low" as const, diff --git a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx index a68f78a051..a63fbc3306 100644 --- a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx +++ b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx @@ -225,9 +225,9 @@ export const OpenAICompatible = ({
{ - setApiConfigurationField("enableReasoningEffort", checked) + setApiConfigurationField("setReasoningEffort", checked) if (!checked) { const { reasoningEffort: _, ...openAiCustomModelInfo } = @@ -238,7 +238,7 @@ export const OpenAICompatible = ({ }}> {t("settings:providers.setReasoningLevel")} - {!!apiConfiguration.enableReasoningEffort && ( + {!!apiConfiguration.setReasoningEffort && (