fix(providers): seed Claude context from catalog

This commit is contained in:
Mohamed Boudra
2026-07-01 21:24:46 +02:00
parent 53c9c3c0be
commit 9059f8e0e9
7 changed files with 48 additions and 22 deletions
+1
View File
@@ -77,6 +77,7 @@ export interface AgentModelDefinition {
label: string;
description?: string;
isDefault?: boolean;
contextWindowMaxTokens?: number;
metadata?: AgentMetadata;
thinkingOptions?: AgentSelectOption[];
defaultThinkingOptionId?: string;
@@ -48,6 +48,7 @@ describe("provider snapshot message schemas", () => {
id: "MiniMax-M2.7",
label: "MiniMax-M2.7",
isDefault: true,
contextWindowMaxTokens: 200_000,
thinkingOptions: [
{ id: "off", label: "Off" },
{ id: "max", label: "Max", isDefault: true },
@@ -62,6 +63,7 @@ describe("provider snapshot message schemas", () => {
id: "MiniMax-M2.7",
label: "MiniMax-M2.7",
isDefault: true,
contextWindowMaxTokens: 200_000,
thinkingOptions: [
{ id: "off", label: "Off" },
{ id: "max", label: "Max", isDefault: true },
+1
View File
@@ -242,6 +242,7 @@ const AgentModelDefinitionSchema: z.ZodType<AgentModelDefinition> = z
label: z.string(),
description: z.string().optional(),
isDefault: z.boolean().optional(),
contextWindowMaxTokens: z.number().optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
thinkingOptions: z.array(AgentSelectOptionSchema).optional(),
defaultThinkingOptionId: z.string().optional(),
@@ -78,6 +78,7 @@ export interface AgentModelDefinition {
label: string;
description?: string;
isDefault?: boolean;
contextWindowMaxTokens?: number;
metadata?: AgentMetadata;
thinkingOptions?: AgentSelectOption[];
defaultThinkingOptionId?: string;
@@ -29,7 +29,11 @@ import {
mapTaskNotificationSystemRecordToToolCall,
mapTaskNotificationUserContentToToolCall,
} from "./task-notification-tool-call.js";
import { getClaudeModelsWithSettings, normalizeClaudeRuntimeModelId } from "./models.js";
import {
findClaudeModel,
getClaudeModelsWithSettings,
normalizeClaudeRuntimeModelId,
} from "./models.js";
import { parsePartialJsonObject } from "./partial-json.js";
import { ClaudeSidechainTracker } from "./sidechain-tracker.js";
import { buildClaudeFeatures, claudeModelSupportsFastMode } from "./feature-definitions.js";
@@ -1626,24 +1630,6 @@ function extractContextWindowSize(modelUsage: unknown): number | undefined {
return maxContextWindow;
}
function resolveInitialContextWindowSize(modelId: string | null | undefined): number | undefined {
const normalized = typeof modelId === "string" ? modelId.trim().toLowerCase() : "";
if (!normalized) {
return undefined;
}
if (normalized.includes("[1m]") || normalized.includes("context-1m")) {
return 1_000_000;
}
const catalogModelId = normalizeClaudeRuntimeModelId(normalized);
if (catalogModelId === "claude-fable-5" || catalogModelId === "claude-sonnet-5") {
return 1_000_000;
}
if (/(?:^|[~/_-])(?:claude[-_ ]*)?(opus|sonnet|haiku)(?:$|[-_ ./])/.test(normalized)) {
return 200_000;
}
return undefined;
}
function readStreamRequestInputTokens(event: Record<string, unknown>): number | undefined {
const messageUsage = toObjectRecord(toObjectRecord(event.message)?.usage);
if (!messageUsage) {
@@ -1942,7 +1928,7 @@ class ClaudeAgentSession implements AgentSession {
this.queryFactory = options.queryFactory;
this.resolveBinary = options.resolveBinary;
this.contextUsage = new ClaudeContextUsageState(
resolveInitialContextWindowSize(this.config.model),
findClaudeModel(this.config.model)?.contextWindowMaxTokens,
);
const handle = options.handle;
@@ -2188,7 +2174,7 @@ class ClaudeAgentSession implements AgentSession {
await this.applyFastModeFeature(false, activeQuery);
}
this.contextUsage.setInitialContextWindowMaxTokens(
resolveInitialContextWindowSize(this.config.model),
findClaudeModel(this.config.model)?.contextWindowMaxTokens,
);
this.lastOptionsModel = normalizedModelId ?? this.lastOptionsModel;
this.lastRuntimeModel = null;
@@ -5,7 +5,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import { createTestLogger } from "../../../../test-utils/test-logger.js";
import { ClaudeAgentClient } from "./agent.js";
import { getClaudeModels, normalizeClaudeRuntimeModelId } from "./models.js";
import { findClaudeModel, getClaudeModels, normalizeClaudeRuntimeModelId } from "./models.js";
const createdClaudeConfigDirs: string[] = [];
@@ -56,6 +56,22 @@ describe("getClaudeModels", () => {
expect(defaults[0].id).toBe("claude-opus-4-8");
});
it("keeps fixed context windows on the model entries", () => {
const modelsById = new Map(getClaudeModels().map((model) => [model.id, model]));
expect(modelsById.get("claude-fable-5")?.contextWindowMaxTokens).toBe(1_000_000);
expect(modelsById.get("claude-opus-4-8[1m]")?.contextWindowMaxTokens).toBe(1_000_000);
expect(modelsById.get("claude-opus-4-8")?.contextWindowMaxTokens).toBe(200_000);
expect(modelsById.get("claude-sonnet-5")?.contextWindowMaxTokens).toBe(1_000_000);
expect(modelsById.get("claude-sonnet-4-6")?.contextWindowMaxTokens).toBe(200_000);
expect(modelsById.get("claude-haiku-4-5")?.contextWindowMaxTokens).toBe(200_000);
});
it("finds models through the runtime normalizer", () => {
expect(findClaudeModel("claude-sonnet-5-20260101")?.id).toBe("claude-sonnet-5");
expect(findClaudeModel("claude-sonnet-5[1m]")?.contextWindowMaxTokens).toBe(1_000_000);
});
it("returns fresh copies each call", () => {
const a = getClaudeModels();
const b = getClaudeModels();
@@ -31,6 +31,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [
id: "claude-fable-5",
label: "Fable 5",
description: "Fable 5 · Most powerful model",
contextWindowMaxTokens: 1_000_000,
thinkingOptions: [...CLAUDE_ULTRACODE_THINKING_OPTIONS],
},
{
@@ -38,6 +39,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [
id: "claude-opus-4-8[1m]",
label: "Opus 4.8 1M",
description: "Opus 4.8 with 1M context window",
contextWindowMaxTokens: 1_000_000,
thinkingOptions: [...CLAUDE_ULTRACODE_THINKING_OPTIONS],
},
{
@@ -46,6 +48,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [
label: "Opus 4.8",
description: "Opus 4.8 · Latest release",
isDefault: true,
contextWindowMaxTokens: 200_000,
thinkingOptions: [...CLAUDE_ULTRACODE_THINKING_OPTIONS],
},
{
@@ -53,6 +56,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [
id: "claude-sonnet-5",
label: "Sonnet 5",
description: "Sonnet 5 · Efficient for routine tasks",
contextWindowMaxTokens: 1_000_000,
thinkingOptions: [...CLAUDE_EXTENDED_THINKING_OPTIONS],
},
{
@@ -60,6 +64,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [
id: "claude-opus-4-7[1m]",
label: "Opus 4.7 1M",
description: "Opus 4.7 with 1M context window",
contextWindowMaxTokens: 1_000_000,
thinkingOptions: [...CLAUDE_EXTENDED_THINKING_OPTIONS],
},
{
@@ -67,6 +72,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [
id: "claude-opus-4-7",
label: "Opus 4.7",
description: "Opus 4.7 · Previous release",
contextWindowMaxTokens: 200_000,
thinkingOptions: [...CLAUDE_EXTENDED_THINKING_OPTIONS],
},
{
@@ -74,6 +80,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [
id: "claude-opus-4-6[1m]",
label: "Opus 4.6 1M",
description: "Opus 4.6 with 1M context window",
contextWindowMaxTokens: 1_000_000,
thinkingOptions: [...CLAUDE_THINKING_OPTIONS],
},
{
@@ -81,6 +88,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [
id: "claude-opus-4-6",
label: "Opus 4.6",
description: "Opus 4.6 · Most capable for complex work",
contextWindowMaxTokens: 200_000,
thinkingOptions: [...CLAUDE_THINKING_OPTIONS],
},
{
@@ -88,6 +96,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [
id: "claude-sonnet-4-6[1m]",
label: "Sonnet 4.6 1M",
description: "Sonnet 4.6 with 1M context window",
contextWindowMaxTokens: 1_000_000,
thinkingOptions: [...CLAUDE_THINKING_OPTIONS],
},
{
@@ -95,6 +104,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [
id: "claude-sonnet-4-6",
label: "Sonnet 4.6",
description: "Sonnet 4.6 · Best for everyday tasks",
contextWindowMaxTokens: 200_000,
thinkingOptions: [...CLAUDE_THINKING_OPTIONS],
},
{
@@ -102,6 +112,7 @@ const CLAUDE_MODELS: AgentModelDefinition[] = [
id: "claude-haiku-4-5",
label: "Haiku 4.5",
description: "Haiku 4.5 · Fastest for quick answers",
contextWindowMaxTokens: 200_000,
},
];
@@ -117,6 +128,14 @@ export function getClaudeModels(): AgentModelDefinition[] {
return CLAUDE_MODELS.map((model) => ({ ...model }));
}
export function findClaudeModel(modelId: string | null | undefined): AgentModelDefinition | null {
const normalizedModelId = normalizeClaudeRuntimeModelId(modelId);
if (!normalizedModelId) {
return null;
}
return CLAUDE_MODELS.find((model) => model.id === normalizedModelId) ?? null;
}
export async function getClaudeModelsWithSettings(
logger: Logger,
configDir?: string,