diff --git a/packages/types/src/__tests__/vscode-llm.spec.ts b/packages/types/src/__tests__/vscode-llm.spec.ts index 6ce55a592f..e099c098f4 100644 --- a/packages/types/src/__tests__/vscode-llm.spec.ts +++ b/packages/types/src/__tests__/vscode-llm.spec.ts @@ -16,15 +16,15 @@ describe("vscodeLlmModels", () => { expect(vscodeLlmModels["gemini-2.5-pro"].maxInputTokens).toBe(108594) }) it.each([ - ["claude-opus-5", "Claude Opus 5", 935793, "advertised, unverified"], - ["claude-sonnet-5", "Claude Sonnet 5", 925449, "measured"], - ["gpt-5.6-luna", "GPT-5.6 Luna", 199753, "measured"], - ["gpt-5.6-sol", "GPT-5.6 Sol", 271785, "measured"], - ["gpt-5.6-terra", "GPT-5.6 Terra", 271785, "measured"], - ["gemini-3.6-flash", "Gemini 3.6 Flash", 935793, "advertised, unverified"], - ] as const)("preserves PR #13 row %s (%s), limit %i (%s)", (...[family, name, limit]) => { + ["claude-opus-5", "Claude Opus 5", 935793, 197897], + ["claude-sonnet-5", "Claude Sonnet 5", 925449, 925449], + ["gpt-5.6-luna", "GPT-5.6 Luna", 199753, 199753], + ["gpt-5.6-sol", "GPT-5.6 Sol", 271785, 271785], + ["gpt-5.6-terra", "GPT-5.6 Terra", 271785, 271785], + ["gemini-3.6-flash", "Gemini 3.6 Flash", 935793, 197897], + ] as const)("preserves PR #13 row %s (%s) with PR #14 ceilings", (family, name, contextWindow, maxInputTokens) => { expect(vscodeLlmModels[family]).toEqual({ - contextWindow: limit, + contextWindow, supportsImages: true, supportsPromptCache: false, inputPrice: 0, @@ -33,7 +33,7 @@ describe("vscodeLlmModels", () => { version: family, name, supportsToolCalling: true, - maxInputTokens: limit, + maxInputTokens, }) }) diff --git a/packages/types/src/providers/vscode-llm.ts b/packages/types/src/providers/vscode-llm.ts index b0c0072412..cfbacde1d7 100644 --- a/packages/types/src/providers/vscode-llm.ts +++ b/packages/types/src/providers/vscode-llm.ts @@ -14,12 +14,11 @@ export type VscodeLlmModelId = keyof typeof vscodeLlmModels export const vscodeLlmDefaultModelId: VscodeLlmModelId = "claude-sonnet-4.5" -// Curated VS Code LM (GitHub Copilot) model catalog. -// The API exposes only `maxInputTokens`; the UI and condense gate read that. `contextWindow` is -// the advertised window, kept for rows where it diverges from the ceiling (e.g. claude-opus-4.8). -// https://github.com/simurg79/Roo-Code/pull/13: sonnet-5 and gpt-5.6 limits were measured -// by single-message binary search on VS Code 1.126.0 (2026-07-14). Opus-5 and gemini-3.6-flash -// retain advertised, unverified limits from the Copilot model-picker cache (2026-07-26). +// The UI and condense gate use maxInputTokens; contextWindow preserves the advertised window. +// https://github.com/simurg79/Roo-Code/pull/13: sonnet-5 and gpt-5.6 were binary-searched. +// https://github.com/simurg79/Roo-Code/pull/14: opus-5 and gemini-3.6-flash retain advertised +// 935793 windows but conservatively reuse the 197897 ceiling measured on other models to avoid +// request overflow; neither model's actual input ceiling has been measured. export const vscodeLlmModels = { "claude-opus-5": { contextWindow: 935793, @@ -31,7 +30,7 @@ export const vscodeLlmModels = { version: "claude-opus-5", name: "Claude Opus 5", supportsToolCalling: true, - maxInputTokens: 935793, + maxInputTokens: 197897, }, "claude-opus-4.8": { contextWindow: 679560, @@ -247,7 +246,7 @@ export const vscodeLlmModels = { version: "gemini-3.6-flash", name: "Gemini 3.6 Flash", supportsToolCalling: true, - maxInputTokens: 935793, + maxInputTokens: 197897, }, "gemini-3.1-pro-preview": { contextWindow: 197897, diff --git a/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts b/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts index 78810f0dd5..0ae95e8c73 100644 --- a/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts +++ b/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts @@ -1422,24 +1422,25 @@ describe("useSelectedModel", () => { expect(result.current.info?.supportsImages).toBe(false) }) - it("pins a divergent family's contextWindow to maxInputTokens, not its advertised window", () => { - // claude-opus-4.8 is the row where contextWindow and maxInputTokens differ; a field swap to - // the advertised window would be caught here. - const family = "claude-opus-4.8" - const apiConfiguration: ProviderSettings = { - apiProvider: providerIdentifiers.vscodeLm, - vsCodeLmModelSelector: { vendor: "copilot", family }, - } + it.each(["claude-opus-4.8", "claude-opus-5", "gemini-3.6-flash"] as const)( + "pins %s to the 197897 input ceiling, not its advertised window", + (family) => { + const apiConfiguration: ProviderSettings = { + apiProvider: providerIdentifiers.vscodeLm, + vsCodeLmModelSelector: { vendor: "copilot", family }, + } - const wrapper = createWrapper() - const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper }) + const wrapper = createWrapper() + const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper }) - expect(result.current.provider).toBe(providerIdentifiers.vscodeLm) - expect(result.current.id).toBe(`copilot/${family}`) - expect(result.current.info?.contextWindow).toBe(vscodeLlmModels[family].maxInputTokens) // 197897 - expect(result.current.info?.contextWindow).not.toBe(vscodeLlmModels[family].contextWindow) // NOT 679560 - expect(result.current.info?.supportsImages).toBe(false) - }) + expect(result.current.provider).toBe(providerIdentifiers.vscodeLm) + expect(result.current.id).toBe(`copilot/${family}`) + expect(result.current.info?.contextWindow).toBe(197897) + expect(result.current.info?.contextWindow).toBe(vscodeLlmModels[family].maxInputTokens) + expect(result.current.info?.contextWindow).not.toBe(vscodeLlmModels[family].contextWindow) + expect(result.current.info?.supportsImages).toBe(false) + }, + ) it("falls back to the default model's window for an unlisted family (NOT 128000)", () => { const apiConfiguration: ProviderSettings = {