|
|
@@ -1,36 +1,3 @@
|
|
|
-/*
|
|
|
-Semantics for Reasoning Effort (SimpleThinkingBudget)
|
|
|
-
|
|
|
-Capability surface:
|
|
|
-- modelInfo.supportsReasoningEffort: boolean | Array<"disable" | "none" | "minimal" | "low" | "medium" | "high">
|
|
|
- - true → UI shows ["low","medium","high"]
|
|
|
- - array → UI shows exactly the provided values
|
|
|
-
|
|
|
-Selection behavior:
|
|
|
-- "disable":
|
|
|
- - Label: t("settings:providers.reasoningEffort.none")
|
|
|
- - set enableReasoningEffort = false
|
|
|
- - persist reasoningEffort = "disable"
|
|
|
- - request builders omit any reasoning parameter/body sections
|
|
|
-- "none":
|
|
|
- - Label: t("settings:providers.reasoningEffort.none")
|
|
|
- - set enableReasoningEffort = true
|
|
|
- - persist reasoningEffort = "none"
|
|
|
- - request builders include reasoning with value "none"
|
|
|
-- "minimal" | "low" | "medium" | "high":
|
|
|
- - set enableReasoningEffort = true
|
|
|
- - persist the selected value
|
|
|
- - request builders include reasoning with the selected effort
|
|
|
-
|
|
|
-Required:
|
|
|
-- If modelInfo.requiredReasoningEffort is true, do not synthesize a "None" choice. Only show values from the capability.
|
|
|
-- On mount, if unset and a default exists, set enableReasoningEffort = true and use modelInfo.reasoningEffort.
|
|
|
-
|
|
|
-Notes:
|
|
|
-- Current selection is normalized to the capability: unsupported persisted values are not shown.
|
|
|
-- Both "disable" and "none" display as the "None" label per UX, but are wired differently as above.
|
|
|
-- "minimal" uses t("settings:providers.reasoningEffort.minimal").
|
|
|
-*/
|
|
|
import { useEffect } from "react"
|
|
|
|
|
|
import { type ProviderSettings, type ModelInfo, type ReasoningEffort, reasoningEfforts } from "@roo-code/types"
|
|
|
@@ -48,8 +15,8 @@ interface SimpleThinkingBudgetProps {
|
|
|
modelInfo?: ModelInfo
|
|
|
}
|
|
|
|
|
|
-// Reasoning selection values including control values
|
|
|
-type ReasoningSelectValue = "disable" | "none" | "minimal" | ReasoningEffort
|
|
|
+// Extended type to include "none" option
|
|
|
+type ReasoningEffortWithNone = ReasoningEffort | "none"
|
|
|
|
|
|
export const SimpleThinkingBudget = ({
|
|
|
apiConfiguration,
|
|
|
@@ -58,46 +25,57 @@ export const SimpleThinkingBudget = ({
|
|
|
}: SimpleThinkingBudgetProps) => {
|
|
|
const { t } = useAppTranslation()
|
|
|
|
|
|
- const isSupported = !!modelInfo?.supportsReasoningEffort
|
|
|
+ // Check model capabilities
|
|
|
+ const isReasoningEffortSupported = !!modelInfo && modelInfo.supportsReasoningEffort
|
|
|
+ const isReasoningEffortRequired = !!modelInfo && modelInfo.requiredReasoningEffort
|
|
|
|
|
|
- const isReasoningEffortRequired = !!modelInfo?.requiredReasoningEffort
|
|
|
+ // Build available reasoning efforts list
|
|
|
+ // Include "none" option unless reasoning effort is required
|
|
|
+ const baseEfforts = [...reasoningEfforts] as ReasoningEffort[]
|
|
|
+ const availableReasoningEfforts: ReadonlyArray<ReasoningEffortWithNone> = isReasoningEffortRequired
|
|
|
+ ? baseEfforts
|
|
|
+ : (["none", ...baseEfforts] as ReasoningEffortWithNone[])
|
|
|
|
|
|
- // Compute available options from capability
|
|
|
- const supports = modelInfo?.supportsReasoningEffort
|
|
|
- const availableOptions: readonly ReasoningSelectValue[] =
|
|
|
- supports === true ? ([...reasoningEfforts] as const) : (supports as any)
|
|
|
+ // Default reasoning effort - use model's default if available, otherwise "medium"
|
|
|
+ const modelDefaultReasoningEffort = modelInfo?.reasoningEffort as ReasoningEffort | undefined
|
|
|
+ const defaultReasoningEffort: ReasoningEffortWithNone = isReasoningEffortRequired
|
|
|
+ ? modelDefaultReasoningEffort || "medium"
|
|
|
+ : "none"
|
|
|
|
|
|
- // Helper for labels
|
|
|
- const labelFor = (v: ReasoningSelectValue) => {
|
|
|
- if (v === "disable" || v === "none") return t("settings:providers.reasoningEffort.none")
|
|
|
- if (v === "minimal") return t("settings:providers.reasoningEffort.minimal")
|
|
|
- return t(`settings:providers.reasoningEffort.${v}`)
|
|
|
- }
|
|
|
-
|
|
|
- // Determine current selection (normalize to capability)
|
|
|
- let current: ReasoningSelectValue | undefined = apiConfiguration.reasoningEffort as ReasoningSelectValue | undefined
|
|
|
- if (!current && isReasoningEffortRequired && modelInfo.reasoningEffort) {
|
|
|
- current = modelInfo.reasoningEffort as ReasoningSelectValue
|
|
|
- }
|
|
|
- // If persisted value isn't supported by capability (e.g., "minimal" while supports=true), don't show it
|
|
|
- const normalizedCurrent: ReasoningSelectValue | undefined =
|
|
|
- current && (availableOptions as readonly any[]).includes(current) ? current : undefined
|
|
|
+ // Current reasoning effort - treat undefined/null as "none"
|
|
|
+ const currentReasoningEffort: ReasoningEffortWithNone =
|
|
|
+ (apiConfiguration.reasoningEffort as ReasoningEffort | undefined) || defaultReasoningEffort
|
|
|
|
|
|
- // Default when required: set to model default on mount (no synthetic "None")
|
|
|
+ // Set default reasoning effort when model supports it and no value is set
|
|
|
useEffect(() => {
|
|
|
- if (!isReasoningEffortRequired) return
|
|
|
- if (!apiConfiguration.reasoningEffort && modelInfo?.reasoningEffort) {
|
|
|
- setApiConfigurationField("enableReasoningEffort", true, false)
|
|
|
- setApiConfigurationField("reasoningEffort", modelInfo?.reasoningEffort as any, false)
|
|
|
+ if (isReasoningEffortSupported && !apiConfiguration.reasoningEffort) {
|
|
|
+ // Only set a default if reasoning is required, otherwise leave as undefined (which maps to "none")
|
|
|
+ if (isReasoningEffortRequired && defaultReasoningEffort !== "none") {
|
|
|
+ setApiConfigurationField("reasoningEffort", defaultReasoningEffort as ReasoningEffort, false)
|
|
|
+ }
|
|
|
}
|
|
|
}, [
|
|
|
+ isReasoningEffortSupported,
|
|
|
isReasoningEffortRequired,
|
|
|
apiConfiguration.reasoningEffort,
|
|
|
- modelInfo?.reasoningEffort,
|
|
|
+ defaultReasoningEffort,
|
|
|
setApiConfigurationField,
|
|
|
])
|
|
|
|
|
|
- if (!isSupported) {
|
|
|
+ useEffect(() => {
|
|
|
+ if (!isReasoningEffortSupported) return
|
|
|
+ const shouldEnable = isReasoningEffortRequired || currentReasoningEffort !== "none"
|
|
|
+ if (shouldEnable && apiConfiguration.enableReasoningEffort !== true) {
|
|
|
+ setApiConfigurationField("enableReasoningEffort", true, false)
|
|
|
+ }
|
|
|
+ }, [
|
|
|
+ isReasoningEffortSupported,
|
|
|
+ isReasoningEffortRequired,
|
|
|
+ currentReasoningEffort,
|
|
|
+ apiConfiguration.enableReasoningEffort,
|
|
|
+ setApiConfigurationField,
|
|
|
+ ])
|
|
|
+ if (!modelInfo || !isReasoningEffortSupported) {
|
|
|
return null
|
|
|
}
|
|
|
|
|
|
@@ -107,25 +85,32 @@ export const SimpleThinkingBudget = ({
|
|
|
<label className="block font-medium mb-1">{t("settings:providers.reasoningEffort.label")}</label>
|
|
|
</div>
|
|
|
<Select
|
|
|
- value={normalizedCurrent}
|
|
|
- onValueChange={(value: ReasoningSelectValue) => {
|
|
|
- if (value === "disable") {
|
|
|
- setApiConfigurationField("enableReasoningEffort", false, true)
|
|
|
- setApiConfigurationField("reasoningEffort", "disable" as any, true)
|
|
|
+ value={currentReasoningEffort}
|
|
|
+ onValueChange={(value: ReasoningEffortWithNone) => {
|
|
|
+ // If "none" is selected, clear the reasoningEffort field
|
|
|
+ if (value === "none") {
|
|
|
+ setApiConfigurationField("reasoningEffort", undefined)
|
|
|
} else {
|
|
|
- setApiConfigurationField("enableReasoningEffort", true, true)
|
|
|
- setApiConfigurationField("reasoningEffort", value as any, true)
|
|
|
+ setApiConfigurationField("reasoningEffort", value as ReasoningEffort)
|
|
|
}
|
|
|
}}>
|
|
|
<SelectTrigger className="w-full">
|
|
|
<SelectValue
|
|
|
- placeholder={normalizedCurrent ? labelFor(normalizedCurrent) : t("settings:common.select")}
|
|
|
+ placeholder={
|
|
|
+ currentReasoningEffort
|
|
|
+ ? currentReasoningEffort === "none"
|
|
|
+ ? t("settings:providers.reasoningEffort.none")
|
|
|
+ : t(`settings:providers.reasoningEffort.${currentReasoningEffort}`)
|
|
|
+ : t("settings:common.select")
|
|
|
+ }
|
|
|
/>
|
|
|
</SelectTrigger>
|
|
|
<SelectContent>
|
|
|
- {availableOptions.map((value) => (
|
|
|
+ {availableReasoningEfforts.map((value) => (
|
|
|
<SelectItem key={value} value={value}>
|
|
|
- {labelFor(value)}
|
|
|
+ {value === "none"
|
|
|
+ ? t("settings:providers.reasoningEffort.none")
|
|
|
+ : t(`settings:providers.reasoningEffort.${value}`)}
|
|
|
</SelectItem>
|
|
|
))}
|
|
|
</SelectContent>
|