Skip to content

Commit 98e813d

Browse files
committed
feat: max value of maxOutputTokens is model's maxTokens + adding more tests
1 parent 24e8ed5 commit 98e813d

File tree

3 files changed

+114
-2
lines changed

3 files changed

+114
-2
lines changed

src/core/sliding-window/__tests__/sliding-window.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,30 @@ describe("Sliding Window", () => {
250250
{ role: "assistant", content: "Fourth message" },
251251
{ role: "user", content: "Fifth message" },
252252
]
253+
it("should use contextLimit as contextWindow when apiProvider is gemini", async () => {
254+
const contextLimit = 2
255+
const messages: ApiMessage[] = [
256+
{ role: "user", content: "First message" },
257+
{ role: "assistant", content: "Second message" },
258+
{ role: "user", content: "Third message" },
259+
{ role: "assistant", content: "Fourth message" },
260+
{ role: "user", content: "" },
261+
]
262+
const result = await truncateConversationIfNeeded({
263+
messages,
264+
totalTokens: 2,
265+
contextWindow: contextLimit,
266+
maxTokens: null,
267+
apiHandler: mockApiHandler,
268+
autoCondenseContext: false,
269+
autoCondenseContextPercent: 100,
270+
systemPrompt: "",
271+
taskId,
272+
profileThresholds: {},
273+
currentProfileId: "default",
274+
})
275+
expect(result.messages).toEqual([messages[0], messages[3], messages[4]])
276+
})
253277

254278
it("should not truncate if tokens are below max tokens threshold", async () => {
255279
const modelInfo = createModelInfo(100000, 30000)

webview-ui/src/components/settings/providers/Gemini.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ export const Gemini = ({
198198
<div className="flex items-center space-x-2">
199199
<Slider
200200
min={3000}
201-
max={8192}
201+
max={modelInfo.maxTokens}
202202
step={1}
203203
value={[apiConfiguration.maxOutputTokens ?? 0]}
204204
onValueChange={(values: number[]) => setApiConfigurationField("maxOutputTokens", values[0])}
@@ -208,7 +208,10 @@ export const Gemini = ({
208208
value={(apiConfiguration.maxOutputTokens ?? 0).toString()}
209209
type="text"
210210
inputMode="numeric"
211-
onInput={handleInputChange("maxOutputTokens", (e) => parseInt((e as any).target.value, 10))}
211+
onInput={handleInputChange("maxOutputTokens", (e) => {
212+
const val = parseInt((e as any).target.value, 10)
213+
return Number.isNaN(val) ? 0 : Math.min(val, modelInfo.maxTokens)
214+
})}
212215
className="w-16"
213216
/>
214217
</div>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import React from "react"
2+
import { render, screen, fireEvent } from "@testing-library/react"
3+
import { Gemini } from "../Gemini"
4+
import type { ProviderSettings } from "@roo-code/types"
5+
import { geminiModels, geminiDefaultModelId, type GeminiModelId } from "@roo-code/types"
6+
7+
vi.mock("@vscode/webview-ui-toolkit/react", () => ({
8+
VSCodeTextField: ({ children, value, onInput, type }: any) => (
9+
<div>
10+
{children}
11+
<input type={type} value={value} onChange={(e) => onInput(e)} />
12+
</div>
13+
),
14+
}))
15+
16+
vi.mock("vscrui", () => ({
17+
Checkbox: ({ children, checked, onChange }: any) => (
18+
<label data-testid="checkbox-custom-context-limit">
19+
<input type="checkbox" checked={checked} onChange={(e) => onChange(e.target.checked)} />
20+
{children}
21+
</label>
22+
),
23+
}))
24+
25+
vi.mock("@src/components/ui", () => ({
26+
Slider: ({ min, max, step, value, onValueChange }: any) => (
27+
<input
28+
data-testid="slider"
29+
type="range"
30+
min={min}
31+
max={max}
32+
step={step}
33+
value={value[0]}
34+
onChange={(e) => onValueChange([Number(e.target.value)])}
35+
/>
36+
),
37+
}))
38+
39+
vi.mock("@src/i18n/TranslationContext", () => ({
40+
useAppTranslation: () => ({ t: (key: string) => key }),
41+
}))
42+
43+
vi.mock("@src/components/common/VSCodeButtonLink", () => ({
44+
VSCodeButtonLink: ({ children, href }: any) => <a href={href}>{children}</a>,
45+
}))
46+
47+
const defaultModelId: GeminiModelId = geminiDefaultModelId
48+
const defaultContextWindow = geminiModels[defaultModelId].contextWindow
49+
50+
describe("Gemini provider settings", () => {
51+
it("does not render context limit slider when custom context limit is not enabled", () => {
52+
const setApiField = vi.fn()
53+
const config: ProviderSettings = {}
54+
render(
55+
<Gemini apiConfiguration={config} setApiConfigurationField={setApiField} currentModelId={defaultModelId} />,
56+
)
57+
expect(screen.queryByTestId("slider")).toBeNull()
58+
})
59+
60+
it("enables custom context limit on checkbox toggle and shows slider with default value", () => {
61+
const setApiField = vi.fn()
62+
const config: ProviderSettings = {}
63+
render(
64+
<Gemini apiConfiguration={config} setApiConfigurationField={setApiField} currentModelId={defaultModelId} />,
65+
)
66+
const checkbox = screen.getByTestId("checkbox-custom-context-limit")
67+
fireEvent.click(checkbox)
68+
expect(setApiField).toHaveBeenCalledWith("contextLimit", defaultContextWindow)
69+
const slider = screen.getByTestId("slider")
70+
expect(slider).toHaveValue(defaultContextWindow.toString())
71+
})
72+
73+
it("renders slider when contextLimit already set and updates on slider change", () => {
74+
const setApiField = vi.fn()
75+
const initialLimit = 100000
76+
const config: ProviderSettings = { contextLimit: initialLimit }
77+
render(
78+
<Gemini apiConfiguration={config} setApiConfigurationField={setApiField} currentModelId={defaultModelId} />,
79+
)
80+
const slider = screen.getByTestId("slider")
81+
expect(slider).toHaveValue(initialLimit.toString())
82+
fireEvent.change(slider, { target: { value: "50000" } })
83+
expect(setApiField).toHaveBeenCalledWith("contextLimit", 50000)
84+
})
85+
})

0 commit comments

Comments
 (0)