Skip to content
Open
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
60 changes: 59 additions & 1 deletion apps/server/src/provider/ClaudeModelCatalog.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { assert, describe, it } from "@effect/vitest";
import { ProviderInstanceId } from "@t3tools/contracts";
import { createModelSelection } from "@t3tools/shared/model";

import { hasValidClaudeManifestAdapters } from "./ClaudeModelManifest.ts";
import type { ModelManifestData } from "./ModelManifest.ts";
import {
formatClaudeVersionUpgradeMessage,
normalizeClaudeCatalogEffort,
resolveClaudeCatalogApiModelId,
resolveClaudeCatalogContextWindowEnv,
resolveClaudeModelCatalog,
resolveClaudeModelsForVersion,
resolveClaudeModelSlug,
Expand Down Expand Up @@ -38,14 +40,18 @@ const manifest = (): ModelManifestData => ({
id: "contextWindow",
label: "Context Window",
type: "select",
options: [{ id: "large", label: "Large", isDefault: true }],
options: [
{ id: "small", label: "Small" },
{ id: "large", label: "Large", isDefault: true },
],
},
],
},
adapter: {
claudeCode: {
effortMap: { extreme: "high" },
modelSuffixes: { contextWindow: { large: "[large]" } },
contextWindowTokens: { small: 200_000, large: 1_000_000 },
},
},
},
Expand Down Expand Up @@ -114,6 +120,58 @@ describe("Claude model catalog", () => {
);
});

it("states the Claude Code 1M context window opt-out per selection", () => {
const catalog = resolveClaudeModelCatalog(manifest());
const instanceId = ProviderInstanceId.make("claudeAgent");
assert.deepStrictEqual(
resolveClaudeCatalogContextWindowEnv(
catalog,
createModelSelection(instanceId, "synthetic", [{ id: "contextWindow", value: "small" }]),
),
{ CLAUDE_CODE_DISABLE_1M_CONTEXT: "1" },
);
assert.deepStrictEqual(
resolveClaudeCatalogContextWindowEnv(catalog, { instanceId, model: "synthetic" }),
{ CLAUDE_CODE_DISABLE_1M_CONTEXT: "0" },
);
assert.strictEqual(resolveClaudeCatalogContextWindowEnv(catalog, undefined), undefined);
});

it("states the 1M context window opt-out for models with a fixed window", () => {
const base = manifest();
const claudeAgent = base.providers!.claudeAgent!;
const input: ModelManifestData = {
...base,
providers: {
...base.providers,
claudeAgent: {
...claudeAgent,
profiles: {
...claudeAgent.profiles,
fixed: { adapter: { claudeCode: { fixedContextWindowTokens: 1_000_000 } } },
},
models: [
...claudeAgent.models,
{
slug: "claude-synthetic-fixed",
name: "Claude Synthetic Fixed",
status: "current",
profile: "fixed",
},
],
},
},
};
const catalog = resolveClaudeModelCatalog(input);
assert.deepStrictEqual(
resolveClaudeCatalogContextWindowEnv(catalog, {
instanceId: ProviderInstanceId.make("claudeAgent"),
model: "claude-synthetic-fixed",
}),
{ CLAUDE_CODE_DISABLE_1M_CONTEXT: "0" },
);
});

it("rejects malformed adapter mappings", () => {
const base = manifest();
const malformed: ModelManifestData = {
Expand Down
20 changes: 20 additions & 0 deletions apps/server/src/provider/ClaudeModelCatalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,23 @@ export function resolveClaudeCatalogContextWindowTokens(
const contextWindow = resolveClaudeCatalogContextWindow(catalog, modelSelection);
return contextWindow ? entry.runtime.contextWindowTokens?.[contextWindow] : undefined;
}

/**
* Claude Code auto-enables the 1M-token context window for every model with a
* native 1M window, so a bare model slug does not select 200k — the CLI only
* holds a session at 200k when `CLAUDE_CODE_DISABLE_1M_CONTEXT` is set, and a
* user or project settings file may already set it (live-test finding). State
* the window the catalog resolved in both directions, so the session runs the
* window T3 displays — for a fixed-window model as much as for a picked option.
* Managed policy settings still outrank this; the CLI's own usage report then
* corrects the meter. Models without catalog token data are left to the
* user's configuration.
*/
export function resolveClaudeCatalogContextWindowEnv(
catalog: ClaudeModelCatalog,
modelSelection: ModelSelection | undefined,
): Record<string, string> | undefined {
const tokens = resolveClaudeCatalogContextWindowTokens(catalog, modelSelection);
if (tokens === undefined) return undefined;
return { CLAUDE_CODE_DISABLE_1M_CONTEXT: tokens <= 200_000 ? "1" : "0" };
}
203 changes: 200 additions & 3 deletions apps/server/src/provider/Layers/ClaudeAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class FakeClaudeQuery implements AsyncIterable<SDKMessage> {
private failure: unknown | undefined;

public readonly setModelCalls: Array<string | undefined> = [];
public readonly applyFlagSettingsCalls: Array<Record<string, unknown>> = [];
public readonly setPermissionModeCalls: Array<string> = [];
public readonly setMaxThinkingTokensCalls: Array<number | null> = [];
public closeCalls = 0;
Expand Down Expand Up @@ -106,6 +107,10 @@ class FakeClaudeQuery implements AsyncIterable<SDKMessage> {
this.setModelCalls.push(model);
};

readonly applyFlagSettings = async (settings: Record<string, unknown>): Promise<void> => {
this.applyFlagSettingsCalls.push(settings);
};

readonly setPermissionMode = async (mode: PermissionMode): Promise<void> => {
this.setPermissionModeCalls.push(mode);
};
Expand Down Expand Up @@ -555,7 +560,9 @@ describe("ClaudeAdapterLive", () => {
});

const createInput = harness.getLastCreateQueryInput();
assert.equal(createInput?.options.settings, undefined);
assert.deepEqual(createInput?.options.settings, {
env: { CLAUDE_CODE_DISABLE_1M_CONTEXT: "0" },
});
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Effect.provide(harness.layer),
Expand All @@ -580,6 +587,7 @@ describe("ClaudeAdapterLive", () => {
const createInput = harness.getLastCreateQueryInput();
assert.deepEqual(createInput?.options.settings, {
fastMode: true,
env: { CLAUDE_CODE_DISABLE_1M_CONTEXT: "0" },
});
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Expand All @@ -603,7 +611,132 @@ describe("ClaudeAdapterLive", () => {
});

const createInput = harness.getLastCreateQueryInput();
assert.equal(createInput?.options.settings, undefined);
assert.deepEqual(createInput?.options.settings, {
env: { CLAUDE_CODE_DISABLE_1M_CONTEXT: "0" },
});
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Effect.provide(harness.layer),
);
});

it.effect("opts out of the 1M context window when the 200k window is selected", () => {
const harness = makeHarness();
return Effect.gen(function* () {
const adapter = yield* ClaudeAdapter;
yield* adapter.startSession({
threadId: THREAD_ID,
provider: ProviderDriverKind.make("claudeAgent"),
modelSelection: createModelSelection(
ProviderInstanceId.make("claudeAgent"),
SYNTHETIC_CLAUDE_CAPABLE_MODEL,
[{ id: "contextWindow", value: "standard" }],
),
runtimeMode: "full-access",
});

const createInput = harness.getLastCreateQueryInput();
assert.equal(createInput?.options.model, SYNTHETIC_CLAUDE_CAPABLE_MODEL);
assert.deepEqual(createInput?.options.settings, {
env: { CLAUDE_CODE_DISABLE_1M_CONTEXT: "1" },
});
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Effect.provide(harness.layer),
);
});

it.effect(
"updates cached usage after a mid-thread context-window switch so streaming usage is not clamped to the old window",
() => {
const harness = makeHarness();
return Effect.gen(function* () {
const adapter = yield* ClaudeAdapter;

const runtimeEventsFiber = yield* Stream.takeUntil(
adapter.streamEvents,
(event) => event.type === "thread.token-usage.updated",
).pipe(Stream.runCollect, Effect.forkChild);

yield* adapter.startSession({
threadId: THREAD_ID,
provider: ProviderDriverKind.make("claudeAgent"),
modelSelection: createModelSelection(
ProviderInstanceId.make("claudeAgent"),
SYNTHETIC_CLAUDE_CAPABLE_MODEL,
[{ id: "contextWindow", value: "standard" }],
),
runtimeMode: "full-access",
});
yield* adapter.sendTurn({
threadId: THREAD_ID,
input: "hello",
modelSelection: createModelSelection(
ProviderInstanceId.make("claudeAgent"),
SYNTHETIC_CLAUDE_CAPABLE_MODEL,
[{ id: "contextWindow", value: "expanded" }],
),
attachments: [],
});

harness.query.emit({
type: "stream_event",
session_id: "sdk-session-window-switch",
uuid: "stream-window-switch",
parent_tool_use_id: null,
event: {
type: "message_delta",
usage: {
input_tokens: 500_000,
output_tokens: 100,
},
},
} as unknown as SDKMessage);

const runtimeEvents = Array.from(yield* Fiber.join(runtimeEventsFiber));
const usageEvent = runtimeEvents.find(
(event) => event.type === "thread.token-usage.updated",
);
assert.equal(usageEvent?.type, "thread.token-usage.updated");
if (usageEvent?.type === "thread.token-usage.updated") {
// Before the fix this clamped to the session's starting 200k
// window (usedTokens: 200000, maxTokens: 200000) because the
// model switch never refreshed the cache streaming usage reads.
assert.deepEqual(usageEvent.payload.usage, {
usedTokens: 500_100,
lastUsedTokens: 500_100,
inputTokens: 500_000,
outputTokens: 100,
maxTokens: 1_000_000,
});
}
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Effect.provide(harness.layer),
);
},
);

it.effect("opts back into the 1M context window when the expanded window is selected", () => {
const harness = makeHarness();
return Effect.gen(function* () {
const adapter = yield* ClaudeAdapter;
yield* adapter.startSession({
threadId: THREAD_ID,
provider: ProviderDriverKind.make("claudeAgent"),
modelSelection: createModelSelection(
ProviderInstanceId.make("claudeAgent"),
SYNTHETIC_CLAUDE_CAPABLE_MODEL,
[{ id: "contextWindow", value: "expanded" }],
),
runtimeMode: "full-access",
});

const createInput = harness.getLastCreateQueryInput();
assert.equal(createInput?.options.model, `${SYNTHETIC_CLAUDE_CAPABLE_MODEL}[expanded]`);
assert.deepEqual(createInput?.options.settings, {
env: { CLAUDE_CODE_DISABLE_1M_CONTEXT: "0" },
});
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Effect.provide(harness.layer),
Expand Down Expand Up @@ -691,16 +824,75 @@ describe("ClaudeAdapterLive", () => {
`${SYNTHETIC_CLAUDE_CAPABLE_MODEL}[expanded]`,
SYNTHETIC_CLAUDE_COLLIDING_ALIAS,
]);
assert.deepEqual(customHarness.query.applyFlagSettingsCalls, [
{ env: { CLAUDE_CODE_DISABLE_1M_CONTEXT: "0" } },
{ env: null },
]);
assert.equal(customPrompt, "keep this prompt literal");

const builtInOptions = yield* start(builtInHarness, SYNTHETIC_CLAUDE_CAPABLE_MODEL);
assert.equal(builtInOptions.model, `${SYNTHETIC_CLAUDE_CAPABLE_MODEL}[expanded]`);
assert.equal(builtInOptions.effort, "max");
assert.deepEqual(builtInOptions.settings, { fastMode: true });
assert.deepEqual(builtInOptions.settings, {
fastMode: true,
env: { CLAUDE_CODE_DISABLE_1M_CONTEXT: "0" },
});
});
},
);

it.effect("leaves the 1M context window alone for models without the option", () => {
const harness = makeHarness();
return Effect.gen(function* () {
const adapter = yield* ClaudeAdapter;
yield* adapter.startSession({
threadId: THREAD_ID,
provider: ProviderDriverKind.make("claudeAgent"),
modelSelection: createModelSelection(
ProviderInstanceId.make("claudeAgent"),
SYNTHETIC_CLAUDE_THINKING_MODEL,
[],
),
runtimeMode: "full-access",
});

const createInput = harness.getLastCreateQueryInput();
assert.equal(createInput?.options.settings, undefined);
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Effect.provide(harness.layer),
);
});

it.effect("merges the 1M opt-out with other SDK settings", () => {
const harness = makeHarness();
return Effect.gen(function* () {
const adapter = yield* ClaudeAdapter;
yield* adapter.startSession({
threadId: THREAD_ID,
provider: ProviderDriverKind.make("claudeAgent"),
modelSelection: createModelSelection(
ProviderInstanceId.make("claudeAgent"),
SYNTHETIC_CLAUDE_CAPABLE_MODEL,
[
{ id: "fastMode", value: true },
{ id: "contextWindow", value: "standard" },
],
),
runtimeMode: "full-access",
});

const createInput = harness.getLastCreateQueryInput();
assert.deepEqual(createInput?.options.settings, {
fastMode: true,
env: { CLAUDE_CODE_DISABLE_1M_CONTEXT: "1" },
});
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Effect.provide(harness.layer),
);
});

it.effect("treats ultrathink as a prompt keyword instead of a session effort", () => {
const harness = makeHarness();
return Effect.gen(function* () {
Expand Down Expand Up @@ -4659,6 +4851,7 @@ describe("ClaudeAdapterLive", () => {
});

assert.deepEqual(harness.query.setModelCalls, []);
assert.deepEqual(harness.query.applyFlagSettingsCalls, []);
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Effect.provide(harness.layer),
Expand Down Expand Up @@ -4702,6 +4895,10 @@ describe("ClaudeAdapterLive", () => {
`${SYNTHETIC_CLAUDE_CAPABLE_MODEL}[expanded]`,
SYNTHETIC_CLAUDE_CAPABLE_MODEL,
]);
assert.deepEqual(harness.query.applyFlagSettingsCalls, [
{ env: { CLAUDE_CODE_DISABLE_1M_CONTEXT: "0" } },
{ env: { CLAUDE_CODE_DISABLE_1M_CONTEXT: "1" } },
]);
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Effect.provide(harness.layer),
Expand Down
Loading
Loading