Skip to content

Commit 8640fd1

Browse files
fix: calculate header percentage based on available input space (#11054)
Co-authored-by: Roo Code <[email protected]>
1 parent fe722da commit 8640fd1

File tree

2 files changed

+75
-3
lines changed

2 files changed

+75
-3
lines changed

webview-ui/src/components/chat/TaskHeader.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -282,9 +282,13 @@ const TaskHeader = ({
282282
sideOffset={8}>
283283
<span className="flex items-center gap-1.5">
284284
{(() => {
285-
const percentage = Math.round(
286-
(((contextTokens || 0) + reservedForOutput) / contextWindow) * 100,
287-
)
285+
// Calculate percentage of available input space used
286+
// Available input space = context window - reserved for output
287+
const availableInputSpace = contextWindow - reservedForOutput
288+
const percentage =
289+
availableInputSpace > 0
290+
? Math.round(((contextTokens || 0) / availableInputSpace) * 100)
291+
: 0
288292
return (
289293
<>
290294
<CircularProgress percentage={percentage} />

webview-ui/src/components/chat/__tests__/TaskHeader.spec.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,26 @@ vi.mock("@roo/array", () => ({
9191
},
9292
}))
9393

94+
// Create a variable to hold the mock model info for useSelectedModel
95+
let mockModelInfo: { contextWindow: number; maxTokens: number } | undefined = undefined
96+
97+
// Mock useSelectedModel hook
98+
vi.mock("@/components/ui/hooks/useSelectedModel", () => ({
99+
useSelectedModel: () => ({
100+
provider: "anthropic",
101+
id: "test-model",
102+
info: mockModelInfo,
103+
isLoading: false,
104+
isError: false,
105+
}),
106+
}))
107+
108+
// Mock getModelMaxOutputTokens from @roo/api
109+
let mockMaxOutputTokens = 0
110+
vi.mock("@roo/api", () => ({
111+
getModelMaxOutputTokens: () => mockMaxOutputTokens,
112+
}))
113+
94114
describe("TaskHeader", () => {
95115
const defaultProps: TaskHeaderProps = {
96116
task: { type: "say", ts: Date.now(), text: "Test task", images: [] },
@@ -402,4 +422,52 @@ describe("TaskHeader", () => {
402422
expect(backButton?.querySelector("svg.lucide-arrow-left")).toBeInTheDocument()
403423
})
404424
})
425+
426+
describe("Context window percentage calculation", () => {
427+
// The percentage should be calculated as:
428+
// contextTokens / (contextWindow - reservedForOutput) * 100
429+
// This represents the percentage of AVAILABLE input space used,
430+
// not the percentage of the total context window.
431+
432+
beforeEach(() => {
433+
// Set up mock model with known contextWindow
434+
mockModelInfo = { contextWindow: 1000, maxTokens: 200 }
435+
// Set up mock for getModelMaxOutputTokens to return reservedForOutput
436+
mockMaxOutputTokens = 200
437+
})
438+
439+
afterEach(() => {
440+
// Reset mocks
441+
mockModelInfo = undefined
442+
mockMaxOutputTokens = 0
443+
})
444+
445+
it("should calculate percentage based on available input space, not total context window", () => {
446+
// With the formula: contextTokens / (contextWindow - reservedForOutput) * 100
447+
// If contextTokens = 200, contextWindow = 1000, reservedForOutput = 200
448+
// Then available input space = 1000 - 200 = 800
449+
// Percentage = 200 / 800 * 100 = 25%
450+
//
451+
// Old (incorrect) formula would have been: (200 + 200) / 1000 * 100 = 40%
452+
453+
renderTaskHeader({ contextTokens: 200 })
454+
455+
// The percentage should be rendered in the collapsed header state
456+
// Verify that 25% is displayed (correct formula) and NOT 40% (old incorrect formula)
457+
expect(screen.getByText("25%")).toBeInTheDocument()
458+
expect(screen.queryByText("40%")).not.toBeInTheDocument()
459+
})
460+
461+
it("should handle edge case when available input space is zero", () => {
462+
// When contextWindow equals reservedForOutput, available space is 0
463+
// The percentage should be 0 to avoid division by zero
464+
mockModelInfo = { contextWindow: 200, maxTokens: 200 }
465+
mockMaxOutputTokens = 200
466+
467+
renderTaskHeader({ contextTokens: 100 })
468+
469+
// Should show 0% when available input space is 0
470+
expect(screen.getByText("0%")).toBeInTheDocument()
471+
})
472+
})
405473
})

0 commit comments

Comments
 (0)