import type { AnthropicMessagesCompat, Api, Model, } from "@earendil-works/pi-ai"; import { getBuiltinModel, getBuiltinModels, } from "@earendil-works/pi-ai/providers/all"; import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent"; import { dbg } from "./debug.js"; const FABLE_FALLBACK_IDS = ["claude-opus-4-8", "claude-opus-5"] as const; /** Dated snapshot ids (e.g. `claude-opus-4-5-20251101`) stay hidden; the * current id of each family is always projected. */ export function isProjectableClaudeModel(model: Model): boolean { return ( model.provider === "anthropic" && !/-\d{8}$/.test(model.id) && !model.id.endsWith("-latest") ); } /** Pi catalog entries may be tuned for the direct Anthropic API, where request * headers unlock capacity that the Claude Agent SDK path does not provide. * Klaus drops those headers, so it must not advertise more than the builtin * catalog promises for the same model. */ /** Typed escape hatch: catalog lookups accept only literal ids at the type * level, but klaus passes runtime catalog ids from enumerations. */ function builtinAnthropic(modelId: string): Model | undefined { return getBuiltinModel( "anthropic", modelId as Parameters[1], ) as Model | undefined; } function clampToBaseline( field: "contextWindow" | "maxTokens", value: number, baseline: Model | undefined, ): number { const ceiling = baseline?.[field]; if (ceiling === undefined || value <= ceiling) return value; dbg?.("models.clamp"); return ceiling; } function fableCompat(model: Model): Model["compat"] { return { ...(structuredClone(model.compat) as AnthropicMessagesCompat), allowedFallbackModels: FABLE_FALLBACK_IDS.flatMap((id) => { const fallback = getBuiltinModel("anthropic", id); return fallback ? [ { provider: "anthropic", model: id, cost: structuredClone(fallback.cost), }, ] : []; }), } satisfies AnthropicMessagesCompat; } /** Project one Pi Anthropic model onto the Klaus provider. Transport-owned * fields (`api`, `baseUrl`, `headers`, sampling parameters) are never * inherited, because the Claude Agent SDK owns the request. */ export function projectModel(model: Model): ProviderModelConfig { dbg?.("models.projectModel"); const baseline = builtinAnthropic(model.id); return { id: model.id, name: model.name, reasoning: model.reasoning, thinkingLevelMap: model.thinkingLevelMap, input: [...model.input], cost: structuredClone(model.cost), compat: model.id === "claude-fable-5-1" ? fableCompat(model) : structuredClone(model.compat), contextWindow: clampToBaseline( "contextWindow", model.contextWindow, baseline, ), maxTokens: clampToBaseline("maxTokens", model.maxTokens, baseline), }; } /** Project every projectable Claude model from a catalog enumeration. */ export function projectModels(models: Model[]): ProviderModelConfig[] { const projected = models.filter(isProjectableClaudeModel).map(projectModel); dbg?.("models.projectModels", { count: projected.length }); return projected; } /** Startup projection from Pi's builtin catalog before any session exists. */ export function projectBuiltinModels(): ProviderModelConfig[] { return projectModels(getBuiltinModels("anthropic")); } /** Stable identity of a projected catalog, used to skip redundant provider * registrations. */ export function modelsSignature(models: ProviderModelConfig[]): string { return JSON.stringify(models); }