Skip to content

Commit 8c5eeeb

Browse files
committed
Add minimal reasoning support to OpenRouter
1 parent 5f3c67f commit 8c5eeeb

File tree

3 files changed

+72
-15
lines changed

3 files changed

+72
-15
lines changed

src/api/transform/__tests__/reasoning.spec.ts

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// npx vitest run src/api/transform/__tests__/reasoning.spec.ts
22

3-
import type { ModelInfo, ProviderSettings } from "@roo-code/types"
3+
import type { ModelInfo, ProviderSettings, ReasoningEffortWithMinimal } from "@roo-code/types"
44

55
import {
66
getOpenRouterReasoning,
@@ -154,24 +154,79 @@ describe("reasoning.ts", () => {
154154

155155
const result = getOpenRouterReasoning(optionsWithoutEffort)
156156

157-
expect(result).toEqual({ effort: undefined })
157+
// When reasoningEffort is undefined, the function should return undefined
158+
expect(result).toBeUndefined()
158159
})
159160

160-
it("should handle all reasoning effort values", () => {
161-
const efforts: Array<"low" | "medium" | "high"> = ["low", "medium", "high"]
161+
it("should handle all reasoning effort values including minimal", () => {
162+
const efforts: Array<ReasoningEffortWithMinimal> = ["minimal", "low", "medium", "high"]
162163

163164
efforts.forEach((effort) => {
164165
const modelWithEffort: ModelInfo = {
165166
...baseModel,
167+
supportsReasoningEffort: true,
168+
reasoningEffort: effort === "minimal" ? undefined : effort,
169+
}
170+
171+
const settingsWithEffort: ProviderSettings = {
166172
reasoningEffort: effort,
167173
}
168174

169-
const options = { ...baseOptions, model: modelWithEffort, reasoningEffort: effort }
175+
const options = {
176+
...baseOptions,
177+
model: modelWithEffort,
178+
settings: settingsWithEffort,
179+
reasoningEffort: effort,
180+
}
170181
const result = getOpenRouterReasoning(options)
171182
expect(result).toEqual({ effort })
172183
})
173184
})
174185

186+
it("should handle minimal reasoning effort specifically", () => {
187+
const modelWithSupported: ModelInfo = {
188+
...baseModel,
189+
supportsReasoningEffort: true,
190+
}
191+
192+
const settingsWithEffort: ProviderSettings = {
193+
reasoningEffort: "minimal",
194+
}
195+
196+
const options = {
197+
...baseOptions,
198+
model: modelWithSupported,
199+
settings: settingsWithEffort,
200+
reasoningEffort: "minimal" as ReasoningEffortWithMinimal,
201+
}
202+
203+
const result = getOpenRouterReasoning(options)
204+
205+
expect(result).toEqual({ effort: "minimal" })
206+
})
207+
208+
it("should handle minimal reasoning effort from settings", () => {
209+
const modelWithSupported: ModelInfo = {
210+
...baseModel,
211+
supportsReasoningEffort: true,
212+
}
213+
214+
const settingsWithMinimal: ProviderSettings = {
215+
reasoningEffort: "minimal" as ReasoningEffortWithMinimal,
216+
}
217+
218+
const options = {
219+
...baseOptions,
220+
model: modelWithSupported,
221+
settings: settingsWithMinimal,
222+
reasoningEffort: "minimal" as ReasoningEffortWithMinimal,
223+
}
224+
225+
const result = getOpenRouterReasoning(options)
226+
227+
expect(result).toEqual({ effort: "minimal" })
228+
})
229+
175230
it("should handle zero reasoningBudget", () => {
176231
const modelWithRequired: ModelInfo = {
177232
...baseModel,

src/api/transform/reasoning.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { ModelInfo, ProviderSettings, ReasoningEffortWithMinimal } from "@r
66

77
import { shouldUseReasoningBudget, shouldUseReasoningEffort } from "../../shared/api"
88

9-
type ReasoningEffort = "low" | "medium" | "high"
9+
type ReasoningEffort = "minimal" | "low" | "medium" | "high"
1010

1111
export type OpenRouterReasoningParams = {
1212
effort?: ReasoningEffort
@@ -36,7 +36,7 @@ export const getOpenRouterReasoning = ({
3636
shouldUseReasoningBudget({ model, settings })
3737
? { max_tokens: reasoningBudget }
3838
: shouldUseReasoningEffort({ model, settings })
39-
? reasoningEffort !== "minimal"
39+
? reasoningEffort
4040
? { effort: reasoningEffort }
4141
: undefined
4242
: undefined

webview-ui/src/components/settings/ThinkingBudget.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,20 @@ export const ThinkingBudget = ({ apiConfiguration, setApiConfigurationField, mod
3232
const isGemini25Pro = selectedModelId && selectedModelId.includes("gemini-2.5-pro")
3333
const minThinkingTokens = isGemini25Pro ? GEMINI_25_PRO_MIN_THINKING_TOKENS : 1024
3434

35-
// Check if this is a GPT-5 model to show "minimal" option
36-
// Only show minimal for OpenAI Native provider GPT-5 models
35+
// Check model capabilities
36+
const isReasoningBudgetSupported = !!modelInfo && modelInfo.supportsReasoningBudget
37+
const isReasoningBudgetRequired = !!modelInfo && modelInfo.requiredReasoningBudget
38+
const isReasoningEffortSupported = !!modelInfo && modelInfo.supportsReasoningEffort
39+
40+
// Check if this is a GPT-5 model or OpenRouter to show "minimal" option
3741
const isOpenAiNativeProvider = apiConfiguration.apiProvider === "openai-native"
42+
const isOpenRouterProvider = apiConfiguration.apiProvider === "openrouter"
3843
const isGpt5Model = isOpenAiNativeProvider && selectedModelId && selectedModelId.startsWith("gpt-5")
39-
// Add "minimal" option for GPT-5 models
44+
// Add "minimal" option for GPT-5 models and OpenRouter models that support reasoning effort
4045
// Spread to convert readonly tuple into a mutable array, then expose as readonly for safety
4146
const baseEfforts = [...reasoningEfforts] as ReasoningEffortWithMinimal[]
42-
const availableReasoningEfforts: ReadonlyArray<ReasoningEffortWithMinimal> = isGpt5Model
47+
const showMinimalOption = isGpt5Model || (isOpenRouterProvider && isReasoningEffortSupported)
48+
const availableReasoningEfforts: ReadonlyArray<ReasoningEffortWithMinimal> = showMinimalOption
4349
? (["minimal", ...baseEfforts] as ReasoningEffortWithMinimal[])
4450
: baseEfforts
4551

@@ -50,10 +56,6 @@ export const ThinkingBudget = ({ apiConfiguration, setApiConfigurationField, mod
5056
const currentReasoningEffort: ReasoningEffortWithMinimal =
5157
(apiConfiguration.reasoningEffort as ReasoningEffortWithMinimal | undefined) || defaultReasoningEffort
5258

53-
const isReasoningBudgetSupported = !!modelInfo && modelInfo.supportsReasoningBudget
54-
const isReasoningBudgetRequired = !!modelInfo && modelInfo.requiredReasoningBudget
55-
const isReasoningEffortSupported = !!modelInfo && modelInfo.supportsReasoningEffort
56-
5759
// Set default reasoning effort when model supports it and no value is set
5860
useEffect(() => {
5961
if (isReasoningEffortSupported && !apiConfiguration.reasoningEffort && defaultReasoningEffort) {

0 commit comments

Comments
 (0)