Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions webview-ui/src/components/settings/ThinkingBudget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,16 @@ export const ThinkingBudget = ({ apiConfiguration, setApiConfigurationField, mod

// "disable" turns off reasoning entirely; "none" is a valid reasoning level.
// Both display as "None" in the UI but behave differently.
// Add "disable" option if reasoning effort is not required.
// Add "disable" option only when:
// 1. requiredReasoningEffort is not true, AND
// 2. supportsReasoningEffort is boolean true (not an explicit array)
// When the model provides an explicit array, respect those exact values.
type ReasoningEffortOption = ReasoningEffortWithMinimal | "none" | "disable"
const availableOptions: ReadonlyArray<ReasoningEffortOption> = modelInfo?.requiredReasoningEffort
? (baseAvailableOptions as ReadonlyArray<ReasoningEffortOption>)
: (["disable", ...baseAvailableOptions] as ReasoningEffortOption[])
const shouldAutoAddDisable =
!modelInfo?.requiredReasoningEffort && supports === true && !baseAvailableOptions.includes("disable" as any)
const availableOptions: ReadonlyArray<ReasoningEffortOption> = shouldAutoAddDisable
? (["disable", ...baseAvailableOptions] as ReasoningEffortOption[])
: (baseAvailableOptions as ReadonlyArray<ReasoningEffortOption>)

// Default reasoning effort - use model's default if available
// GPT-5 models have "medium" as their default in the model configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ vi.mock("@/components/ui", () => ({
onChange={(e) => onValueChange([parseInt(e.target.value)])}
/>
),
Select: ({ children, value, onValueChange: _onValueChange }: any) => (
<div data-testid="select" data-value={value}>
{children}
</div>
),
SelectTrigger: ({ children }: any) => <button data-testid="select-trigger">{children}</button>,
SelectValue: ({ placeholder }: any) => <span data-testid="select-value">{placeholder}</span>,
SelectContent: ({ children }: any) => <div data-testid="select-content">{children}</div>,
SelectItem: ({ children, value }: any) => (
<div data-testid={`select-item-${value}`} data-value={value}>
{children}
</div>
),
}))

vi.mock("@/components/ui/hooks/useSelectedModel", () => ({
Expand Down Expand Up @@ -215,4 +228,81 @@ describe("ThinkingBudget", () => {

expect(setApiConfigurationField).toHaveBeenCalledWith("modelMaxTokens", 12000)
})

describe("reasoning effort dropdown", () => {
const reasoningEffortModelInfo: ModelInfo = {
supportsReasoningEffort: true,
contextWindow: 200000,
supportsPromptCache: true,
}

it("should show 'disable' option when supportsReasoningEffort is boolean true", () => {
render(<ThinkingBudget {...defaultProps} modelInfo={reasoningEffortModelInfo} />)

expect(screen.getByTestId("reasoning-effort")).toBeInTheDocument()
// "disable" should be shown when supportsReasoningEffort is true (boolean)
expect(screen.getByTestId("select-item-disable")).toBeInTheDocument()
expect(screen.getByTestId("select-item-low")).toBeInTheDocument()
expect(screen.getByTestId("select-item-medium")).toBeInTheDocument()
expect(screen.getByTestId("select-item-high")).toBeInTheDocument()
})

it("should NOT show 'disable' option when supportsReasoningEffort is an explicit array without disable", () => {
render(
<ThinkingBudget
{...defaultProps}
modelInfo={{
...reasoningEffortModelInfo,
supportsReasoningEffort: ["low", "high"],
}}
/>,
)

expect(screen.getByTestId("reasoning-effort")).toBeInTheDocument()
// "disable" should NOT be shown when model explicitly specifies only ["low", "high"]
expect(screen.queryByTestId("select-item-disable")).not.toBeInTheDocument()
expect(screen.getByTestId("select-item-low")).toBeInTheDocument()
expect(screen.queryByTestId("select-item-medium")).not.toBeInTheDocument()
expect(screen.getByTestId("select-item-high")).toBeInTheDocument()
})

it("should show 'disable' option when supportsReasoningEffort array explicitly includes disable", () => {
render(
<ThinkingBudget
{...defaultProps}
modelInfo={{
...reasoningEffortModelInfo,
supportsReasoningEffort: ["disable", "low", "high"],
}}
/>,
)

expect(screen.getByTestId("reasoning-effort")).toBeInTheDocument()
// "disable" should be shown when model explicitly includes it in the array
expect(screen.getByTestId("select-item-disable")).toBeInTheDocument()
expect(screen.getByTestId("select-item-low")).toBeInTheDocument()
expect(screen.queryByTestId("select-item-medium")).not.toBeInTheDocument()
expect(screen.getByTestId("select-item-high")).toBeInTheDocument()
})

it("should show 'none' option when supportsReasoningEffort array includes none", () => {
render(
<ThinkingBudget
{...defaultProps}
modelInfo={{
...reasoningEffortModelInfo,
supportsReasoningEffort: ["none", "low", "medium", "high"],
}}
/>,
)

expect(screen.getByTestId("reasoning-effort")).toBeInTheDocument()
// Only values from the explicit array should be shown
expect(screen.queryByTestId("select-item-disable")).not.toBeInTheDocument()
expect(screen.getByTestId("select-item-none")).toBeInTheDocument()
expect(screen.getByTestId("select-item-low")).toBeInTheDocument()
expect(screen.getByTestId("select-item-medium")).toBeInTheDocument()
expect(screen.getByTestId("select-item-high")).toBeInTheDocument()
})
})
})
Loading