mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Add minimal reasoning support to OpenRouter (#6998)
Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: daniel-lxs <ricciodaniel98@gmail.com>
This commit is contained in:
parent
01e417e0e5
commit
cee7c9894c
3 changed files with 89 additions and 20 deletions
|
|
@ -1,6 +1,6 @@
|
|||
// npx vitest run src/api/transform/__tests__/reasoning.spec.ts
|
||||
|
||||
import type { ModelInfo, ProviderSettings } from "@roo-code/types"
|
||||
import type { ModelInfo, ProviderSettings, ReasoningEffortWithMinimal } from "@roo-code/types"
|
||||
|
||||
import {
|
||||
getOpenRouterReasoning,
|
||||
|
|
@ -154,24 +154,81 @@ describe("reasoning.ts", () => {
|
|||
|
||||
const result = getOpenRouterReasoning(optionsWithoutEffort)
|
||||
|
||||
expect(result).toEqual({ effort: undefined })
|
||||
// When reasoningEffort is undefined, the function should return undefined
|
||||
expect(result).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should handle all reasoning effort values", () => {
|
||||
const efforts: Array<"low" | "medium" | "high"> = ["low", "medium", "high"]
|
||||
it("should handle all reasoning effort values including minimal", () => {
|
||||
const efforts: Array<ReasoningEffortWithMinimal> = ["minimal", "low", "medium", "high"]
|
||||
|
||||
efforts.forEach((effort) => {
|
||||
const modelWithEffort: ModelInfo = {
|
||||
...baseModel,
|
||||
supportsReasoningEffort: true,
|
||||
}
|
||||
|
||||
const settingsWithEffort: ProviderSettings = {
|
||||
reasoningEffort: effort,
|
||||
}
|
||||
|
||||
const options = { ...baseOptions, model: modelWithEffort, reasoningEffort: effort }
|
||||
const options = {
|
||||
...baseOptions,
|
||||
model: modelWithEffort,
|
||||
settings: settingsWithEffort,
|
||||
reasoningEffort: effort,
|
||||
}
|
||||
const result = getOpenRouterReasoning(options)
|
||||
// All effort values including "minimal" should be passed through
|
||||
expect(result).toEqual({ effort })
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle minimal reasoning effort specifically", () => {
|
||||
const modelWithSupported: ModelInfo = {
|
||||
...baseModel,
|
||||
supportsReasoningEffort: true,
|
||||
}
|
||||
|
||||
const settingsWithEffort: ProviderSettings = {
|
||||
reasoningEffort: "minimal",
|
||||
}
|
||||
|
||||
const options = {
|
||||
...baseOptions,
|
||||
model: modelWithSupported,
|
||||
settings: settingsWithEffort,
|
||||
reasoningEffort: "minimal" as ReasoningEffortWithMinimal,
|
||||
}
|
||||
|
||||
const result = getOpenRouterReasoning(options)
|
||||
|
||||
// "minimal" should be passed through to OpenRouter
|
||||
expect(result).toEqual({ effort: "minimal" })
|
||||
})
|
||||
|
||||
it("should handle minimal reasoning effort from settings", () => {
|
||||
const modelWithSupported: ModelInfo = {
|
||||
...baseModel,
|
||||
supportsReasoningEffort: true,
|
||||
}
|
||||
|
||||
const settingsWithMinimal: ProviderSettings = {
|
||||
reasoningEffort: "minimal" as ReasoningEffortWithMinimal,
|
||||
}
|
||||
|
||||
const options = {
|
||||
...baseOptions,
|
||||
model: modelWithSupported,
|
||||
settings: settingsWithMinimal,
|
||||
reasoningEffort: "minimal" as ReasoningEffortWithMinimal,
|
||||
}
|
||||
|
||||
const result = getOpenRouterReasoning(options)
|
||||
|
||||
// "minimal" should be passed through to OpenRouter
|
||||
expect(result).toEqual({ effort: "minimal" })
|
||||
})
|
||||
|
||||
it("should handle zero reasoningBudget", () => {
|
||||
const modelWithRequired: ModelInfo = {
|
||||
...baseModel,
|
||||
|
|
|
|||
|
|
@ -6,10 +6,8 @@ import type { ModelInfo, ProviderSettings, ReasoningEffortWithMinimal } from "@r
|
|||
|
||||
import { shouldUseReasoningBudget, shouldUseReasoningEffort } from "../../shared/api"
|
||||
|
||||
type ReasoningEffort = "low" | "medium" | "high"
|
||||
|
||||
export type OpenRouterReasoningParams = {
|
||||
effort?: ReasoningEffort
|
||||
effort?: ReasoningEffortWithMinimal
|
||||
max_tokens?: number
|
||||
exclude?: boolean
|
||||
}
|
||||
|
|
@ -36,7 +34,7 @@ export const getOpenRouterReasoning = ({
|
|||
shouldUseReasoningBudget({ model, settings })
|
||||
? { max_tokens: reasoningBudget }
|
||||
: shouldUseReasoningEffort({ model, settings })
|
||||
? reasoningEffort !== "minimal"
|
||||
? reasoningEffort
|
||||
? { effort: reasoningEffort }
|
||||
: undefined
|
||||
: undefined
|
||||
|
|
|
|||
|
|
@ -24,6 +24,17 @@ interface ThinkingBudgetProps {
|
|||
modelInfo?: ModelInfo
|
||||
}
|
||||
|
||||
// Helper function to determine if minimal option should be shown
|
||||
const shouldShowMinimalOption = (
|
||||
provider: string | undefined,
|
||||
modelId: string | undefined,
|
||||
supportsEffort: boolean | undefined,
|
||||
): boolean => {
|
||||
const isGpt5Model = provider === "openai-native" && modelId?.startsWith("gpt-5")
|
||||
const isOpenRouterWithEffort = provider === "openrouter" && supportsEffort === true
|
||||
return !!(isGpt5Model || isOpenRouterWithEffort)
|
||||
}
|
||||
|
||||
export const ThinkingBudget = ({ apiConfiguration, setApiConfigurationField, modelInfo }: ThinkingBudgetProps) => {
|
||||
const { t } = useAppTranslation()
|
||||
const { id: selectedModelId } = useSelectedModel(apiConfiguration)
|
||||
|
|
@ -32,14 +43,21 @@ export const ThinkingBudget = ({ apiConfiguration, setApiConfigurationField, mod
|
|||
const isGemini25Pro = selectedModelId && selectedModelId.includes("gemini-2.5-pro")
|
||||
const minThinkingTokens = isGemini25Pro ? GEMINI_25_PRO_MIN_THINKING_TOKENS : 1024
|
||||
|
||||
// Check if this is a GPT-5 model to show "minimal" option
|
||||
// Only show minimal for OpenAI Native provider GPT-5 models
|
||||
const isOpenAiNativeProvider = apiConfiguration.apiProvider === "openai-native"
|
||||
const isGpt5Model = isOpenAiNativeProvider && selectedModelId && selectedModelId.startsWith("gpt-5")
|
||||
// Add "minimal" option for GPT-5 models
|
||||
// Spread to convert readonly tuple into a mutable array, then expose as readonly for safety
|
||||
// Check model capabilities
|
||||
const isReasoningBudgetSupported = !!modelInfo && modelInfo.supportsReasoningBudget
|
||||
const isReasoningBudgetRequired = !!modelInfo && modelInfo.requiredReasoningBudget
|
||||
const isReasoningEffortSupported = !!modelInfo && modelInfo.supportsReasoningEffort
|
||||
|
||||
// Determine if minimal option should be shown
|
||||
const showMinimalOption = shouldShowMinimalOption(
|
||||
apiConfiguration.apiProvider,
|
||||
selectedModelId,
|
||||
isReasoningEffortSupported,
|
||||
)
|
||||
|
||||
// Build available reasoning efforts list
|
||||
const baseEfforts = [...reasoningEfforts] as ReasoningEffortWithMinimal[]
|
||||
const availableReasoningEfforts: ReadonlyArray<ReasoningEffortWithMinimal> = isGpt5Model
|
||||
const availableReasoningEfforts: ReadonlyArray<ReasoningEffortWithMinimal> = showMinimalOption
|
||||
? (["minimal", ...baseEfforts] as ReasoningEffortWithMinimal[])
|
||||
: baseEfforts
|
||||
|
||||
|
|
@ -50,10 +68,6 @@ export const ThinkingBudget = ({ apiConfiguration, setApiConfigurationField, mod
|
|||
const currentReasoningEffort: ReasoningEffortWithMinimal =
|
||||
(apiConfiguration.reasoningEffort as ReasoningEffortWithMinimal | undefined) || defaultReasoningEffort
|
||||
|
||||
const isReasoningBudgetSupported = !!modelInfo && modelInfo.supportsReasoningBudget
|
||||
const isReasoningBudgetRequired = !!modelInfo && modelInfo.requiredReasoningBudget
|
||||
const isReasoningEffortSupported = !!modelInfo && modelInfo.supportsReasoningEffort
|
||||
|
||||
// Set default reasoning effort when model supports it and no value is set
|
||||
useEffect(() => {
|
||||
if (isReasoningEffortSupported && !apiConfiguration.reasoningEffort && defaultReasoningEffort) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue