diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts index 191bf8e27db..c3c4dfb2dbe 100644 --- a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts +++ b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts @@ -3042,7 +3042,7 @@ describe("ClaudeAdapterLive", () => { attachments: [], }); - assert.deepEqual(harness.query.setModelCalls, ["claude-opus-4-6"]); + assert.deepEqual(harness.query.setModelCalls, ["claude-opus-4-6[1m]"]); }).pipe( Effect.provideService(Random.Random, makeDeterministicRandomService()), Effect.provide(harness.layer), @@ -3140,10 +3140,11 @@ describe("ClaudeAdapterLive", () => { yield* adapter.sendTurn({ threadId: session.threadId, input: "hello again", - modelSelection: { - instanceId: ProviderInstanceId.make("claudeAgent"), - model: "claude-opus-4-6", - }, + modelSelection: createModelSelection( + ProviderInstanceId.make("claudeAgent"), + "claude-opus-4-6", + [{ id: "contextWindow", value: "200k" }], + ), attachments: [], }); diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.ts b/apps/server/src/provider/Layers/ClaudeAdapter.ts index 97a93f85829..a8e6cfda230 100644 --- a/apps/server/src/provider/Layers/ClaudeAdapter.ts +++ b/apps/server/src/provider/Layers/ClaudeAdapter.ts @@ -76,6 +76,7 @@ import { isClaudeUltracodeEffort, normalizeClaudeCliEffort, resolveClaudeApiModelId, + resolveClaudeContextWindow, resolveClaudeEffort, } from "./ClaudeProvider.ts"; import { @@ -340,24 +341,18 @@ function selectedClaudeContextWindow( switch (modelSelection?.model) { case "claude-opus-4-8": case "claude-opus-4-7": + // Always 1M at the API; these models expose no contextWindow option. return 1_000_000; } - const optionValue = getModelSelectionStringOptionValue(modelSelection, "contextWindow"); - if (optionValue === "1m") { - return 1_000_000; - } - if (optionValue === "200k") { - return 200_000; - } - const caps = getClaudeModelCapabilities(modelSelection?.model); - const hasContextWindowOption = getProviderOptionDescriptors({ caps }).some( - (descriptor) => descriptor.type === "select" && descriptor.id === "contextWindow", - ); - if (hasContextWindowOption) { - return 200_000; + switch (resolveClaudeContextWindow(modelSelection)) { + case "1m": + return 1_000_000; + case "200k": + return 200_000; + default: + return undefined; } - return undefined; } function finiteNonNegativeInteger(value: unknown): number | undefined { diff --git a/apps/server/src/provider/Layers/ClaudeProvider.ts b/apps/server/src/provider/Layers/ClaudeProvider.ts index 59b7d0464de..5471c710543 100644 --- a/apps/server/src/provider/Layers/ClaudeProvider.ts +++ b/apps/server/src/provider/Layers/ClaudeProvider.ts @@ -77,8 +77,8 @@ const BUILT_IN_MODELS: ReadonlyArray = [ id: "contextWindow", label: "Context Window", options: [ - { value: "200k", label: "200k", isDefault: true }, - { value: "1m", label: "1M" }, + { value: "200k", label: "200k" }, + { value: "1m", label: "1M", isDefault: true }, ], }), ], @@ -163,8 +163,8 @@ const BUILT_IN_MODELS: ReadonlyArray = [ id: "contextWindow", label: "Context Window", options: [ - { value: "200k", label: "200k", isDefault: true }, - { value: "1m", label: "1M" }, + { value: "200k", label: "200k" }, + { value: "1m", label: "1M", isDefault: true }, ], }), ], @@ -216,8 +216,8 @@ const BUILT_IN_MODELS: ReadonlyArray = [ id: "contextWindow", label: "Context Window", options: [ - { value: "200k", label: "200k", isDefault: true }, - { value: "1m", label: "1M" }, + { value: "200k", label: "200k" }, + { value: "1m", label: "1M", isDefault: true }, ], }), ], @@ -245,8 +245,8 @@ const BUILT_IN_MODELS: ReadonlyArray = [ id: "contextWindow", label: "Context Window", options: [ - { value: "200k", label: "200k", isDefault: true }, - { value: "1m", label: "1M" }, + { value: "200k", label: "200k" }, + { value: "1m", label: "1M", isDefault: true }, ], }), ], @@ -370,8 +370,22 @@ export function isClaudeUltracodeEffort(effort: string | null | undefined): bool return effort === "ultracode"; } +export function resolveClaudeContextWindow( + modelSelection: ModelSelection | undefined, +): string | undefined { + const caps = getClaudeModelCapabilities(modelSelection?.model); + const raw = getModelSelectionStringOptionValue(modelSelection, "contextWindow"); + const descriptors = getProviderOptionDescriptors({ + caps, + ...(raw ? { selections: [{ id: "contextWindow", value: raw }] } : {}), + }); + const descriptor = descriptors.find((candidate) => candidate.id === "contextWindow"); + const value = getProviderOptionCurrentValue(descriptor); + return typeof value === "string" ? value : undefined; +} + export function resolveClaudeApiModelId(modelSelection: ModelSelection): string { - switch (getModelSelectionStringOptionValue(modelSelection, "contextWindow")) { + switch (resolveClaudeContextWindow(modelSelection)) { case "1m": return `${modelSelection.model}[1m]`; default: