Skip to content
Closed
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
10 changes: 10 additions & 0 deletions .changeset/fix-max-tokens-slider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"roo-cline": patch
---

Fix: Decouple Max Output Tokens from Max Thinking Tokens controls

- Separated Max Output Tokens control into its own component that displays for all models with configurable maxTokens
- Updated ThinkingBudget component to only handle thinking/reasoning tokens
- Fixed issue where non-thinking models with configurable maxTokens didn't show the output tokens slider
- Updated to use new model properties (supportsReasoningBudget) from latest main branch
14 changes: 14 additions & 0 deletions webview-ui/src/components/__mocks__/Slider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react"

// This is a manual mock for the Slider component.
// It will be automatically used by Jest when jest.mock('../Slider') is called.

// Create a Jest mock function for the component itself
const MockSlider = jest.fn((props: any) => {
// You can add basic rendering if needed for other tests,
// or just keep it simple for prop checking.
// Include data-testid for potential queries if the component rendered something.
return <div data-testid="mock-slider" {...props} />
})

export const Slider = MockSlider
12 changes: 12 additions & 0 deletions webview-ui/src/components/settings/ApiOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { TemperatureControl } from "./TemperatureControl"
import { RateLimitSecondsControl } from "./RateLimitSecondsControl"
import { BedrockCustomArn } from "./providers/BedrockCustomArn"
import { buildDocLink } from "@src/utils/docLinks"
import { MaxOutputTokensControl } from "./MaxOutputTokensControl"

export interface ApiOptionsProps {
uriScheme: string | undefined
Expand Down Expand Up @@ -462,9 +463,20 @@ const ApiOptions = ({
isDescriptionExpanded={isDescriptionExpanded}
setIsDescriptionExpanded={setIsDescriptionExpanded}
/>

</>
)}

{selectedModelInfo &&
typeof selectedModelInfo.maxTokens === "number" &&
selectedModelInfo.maxTokens > 0 && (
<MaxOutputTokensControl
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}
modelInfo={selectedModelInfo}
/>
)}

<ThinkingBudget
key={`${selectedProvider}-${selectedModelId}`}
apiConfiguration={apiConfiguration}
Expand Down
46 changes: 46 additions & 0 deletions webview-ui/src/components/settings/MaxOutputTokensControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react"

import { type ProviderSettings, type ModelInfo } from "@roo-code/types"

import { useAppTranslation } from "@src/i18n/TranslationContext"
import { Slider } from "@src/components/ui"

interface MaxOutputTokensControlProps {
apiConfiguration: ProviderSettings
setApiConfigurationField: <K extends keyof ProviderSettings>(field: K, value: ProviderSettings[K]) => void
modelInfo?: ModelInfo
}

const MIN_OUTPUT_TOKENS = 2048
const STEP_OUTPUT_TOKENS = 1024

export const MaxOutputTokensControl: React.FC<MaxOutputTokensControlProps> = ({
apiConfiguration,
setApiConfigurationField,
modelInfo,
}) => {
const { t } = useAppTranslation()
const shouldRender = modelInfo && typeof modelInfo.maxTokens === "number" && modelInfo.maxTokens > 0

if (!shouldRender) {
return null
}

const currentMaxOutputTokens = apiConfiguration.modelMaxTokens ?? modelInfo.maxTokens!

return (
<div className="flex flex-col gap-1" data-testid="max-output-tokens-control">
<div className="font-medium">{t("settings:thinkingBudget.maxTokens")}</div>
<div className="flex items-center gap-1">
<Slider
min={MIN_OUTPUT_TOKENS}
max={modelInfo.maxTokens!}
step={STEP_OUTPUT_TOKENS}
value={[currentMaxOutputTokens]}
onValueChange={([value]) => setApiConfigurationField("modelMaxTokens", value)}
/>
<div className="w-12 text-sm text-center">{currentMaxOutputTokens}</div>
</div>
</div>
)
}
41 changes: 13 additions & 28 deletions webview-ui/src/components/settings/ThinkingBudget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,34 +59,19 @@ export const ThinkingBudget = ({ apiConfiguration, setApiConfigurationField, mod
</div>
)}
{(isReasoningBudgetRequired || enableReasoningEffort) && (
<>
<div className="flex flex-col gap-1">
<div className="font-medium">{t("settings:thinkingBudget.maxTokens")}</div>
<div className="flex items-center gap-1">
<Slider
min={8192}
max={modelInfo.maxTokens}
step={1024}
value={[customMaxOutputTokens]}
onValueChange={([value]) => setApiConfigurationField("modelMaxTokens", value)}
/>
<div className="w-12 text-sm text-center">{customMaxOutputTokens}</div>
</div>
</div>
<div className="flex flex-col gap-1">
<div className="font-medium">{t("settings:thinkingBudget.maxThinkingTokens")}</div>
<div className="flex items-center gap-1" data-testid="reasoning-budget">
<Slider
min={1024}
max={modelMaxThinkingTokens}
step={1024}
value={[customMaxThinkingTokens]}
onValueChange={([value]) => setApiConfigurationField("modelMaxThinkingTokens", value)}
/>
<div className="w-12 text-sm text-center">{customMaxThinkingTokens}</div>
</div>
<div className="flex flex-col gap-1">
<div className="font-medium">{t("settings:thinkingBudget.maxThinkingTokens")}</div>
<div className="flex items-center gap-1" data-testid="reasoning-budget">
<Slider
min={1024}
max={modelMaxThinkingTokens}
step={1024}
value={[customMaxThinkingTokens]}
onValueChange={([value]) => setApiConfigurationField("modelMaxThinkingTokens", value)}
/>
<div className="w-12 text-sm text-center">{customMaxThinkingTokens}</div>
</div>
</>
</div>
)}
</>
) : isReasoningEffortSupported ? (
Expand All @@ -110,4 +95,4 @@ export const ThinkingBudget = ({ apiConfiguration, setApiConfigurationField, mod
</Select>
</div>
) : null
}
}
Loading