import { assert, describe, it } from "@effect/vitest"; import { ProviderInstanceId } from "@t3tools/contracts"; import { hasValidClaudeManifestAdapters } from "./ClaudeModelManifest.ts"; import type { ModelManifestData } from "./ModelManifest.ts"; import { formatClaudeVersionUpgradeMessage, normalizeClaudeCatalogEffort, resolveClaudeCatalogApiModelId, resolveClaudeCatalogEffort, resolveClaudeModelCatalog, resolveClaudeModelsForVersion, resolveClaudeModelSlug, scopeClaudeModelCatalog, } from "./ClaudeModelCatalog.ts"; /** * Test policy: adding or changing a real Claude model in model-manifest.json * must not add or update tests here. These synthetic fixtures cover resolver * behavior once. Add a test only when Claude adapter semantics change, such * as introducing a new compatibility rule or dispatch mapping type. */ const manifest = (): ModelManifestData => ({ version: 1, currentModels: {}, providers: { claudeAgent: { profiles: { synthetic: { capabilities: { optionDescriptors: [ { id: "effort", label: "Reasoning", type: "select", options: [{ id: "extreme", label: "Extreme", isDefault: true }], }, { id: "contextWindow", label: "Context Window", type: "select", options: [{ id: "large", label: "Large", isDefault: true }], }, ], }, adapter: { claudeCode: { effortMap: { extreme: "high" }, modelSuffixes: { contextWindow: { large: "[large]" } }, }, }, }, }, models: [ { slug: "claude-synthetic-next", name: "Claude Synthetic Next", aliases: ["synthetic"], status: "current", profile: "synthetic", adapter: { claudeCode: { minVersion: "3.2.0" } }, }, ], }, }, }); describe("Claude model catalog", () => { it("filters models at runtime-version boundaries and derives the upgrade message", () => { const catalog = resolveClaudeModelCatalog(manifest()); assert.deepStrictEqual(resolveClaudeModelsForVersion(catalog, "3.1.9"), []); assert.deepStrictEqual( resolveClaudeModelsForVersion(catalog, "3.2.0").map((model) => model.slug), ["claude-synthetic-next"], ); assert.strictEqual( formatClaudeVersionUpgradeMessage(catalog, "3.1.9"), "Claude Code v3.1.9 is too old for Claude Synthetic Next. Upgrade to v3.2.0 or newer to access it.", ); }); it("resolves aliases and declarative adapter mappings", () => { const base = manifest(); const input: ModelManifestData = { ...base, providers: { ...base.providers, claudeAgent: { ...base.providers!.claudeAgent!, models: [ { slug: "claude-synthetic-collision", name: "Claude Synthetic Collision", aliases: ["claude-synthetic-next"], status: "current", }, ...base.providers!.claudeAgent!.models, ], }, }, }; const catalog = resolveClaudeModelCatalog(input); assert.strictEqual(resolveClaudeModelSlug(catalog, "synthetic"), "claude-synthetic-next"); assert.strictEqual( resolveClaudeModelSlug(catalog, "claude-synthetic-next"), "claude-synthetic-next", ); assert.strictEqual(normalizeClaudeCatalogEffort(catalog, "extreme", "synthetic"), "high"); assert.strictEqual( resolveClaudeCatalogApiModelId(catalog, { instanceId: ProviderInstanceId.make("claudeAgent"), model: "synthetic", }), "claude-synthetic-next[large]", ); }); it("rejects malformed adapter mappings", () => { const base = manifest(); const malformed: ModelManifestData = { ...base, providers: { ...base.providers, claudeAgent: { ...base.providers!.claudeAgent!, profiles: { ...base.providers!.claudeAgent!.profiles, synthetic: { ...base.providers!.claudeAgent!.profiles.synthetic!, adapter: { claudeCode: { effortMap: { extreme: 123 } } }, }, }, }, }, }; assert.isFalse(hasValidClaudeManifestAdapters(malformed)); }); it("appends custom models with their own descriptors and keeps bare slugs opaque", () => { const catalog = scopeClaudeModelCatalog(resolveClaudeModelCatalog(manifest()), [ "synthetic", { slug: "claude-custom-tuned", name: "Tuned", capabilities: { optionDescriptors: [ { id: "effort", label: "Reasoning", type: "select", options: [ { id: "gentle", label: "Gentle", isDefault: true }, { id: "brutal", label: "Brutal" }, ], }, ], }, }, ]); // The bare custom slug shadows the built-in alias, so it no longer resolves to it. assert.strictEqual(resolveClaudeModelSlug(catalog, "synthetic"), "synthetic"); assert.strictEqual(resolveClaudeCatalogEffort(catalog, "synthetic", "extreme"), undefined); // The entry with descriptors resolves user-defined effort ids and passes // them through untouched (no effortMap, no model suffix). assert.strictEqual( resolveClaudeCatalogEffort(catalog, "claude-custom-tuned", "brutal"), "brutal", ); assert.strictEqual( resolveClaudeCatalogEffort(catalog, "claude-custom-tuned", "bogus"), "gentle", ); assert.strictEqual( normalizeClaudeCatalogEffort(catalog, "brutal", "claude-custom-tuned"), "brutal", ); assert.strictEqual( resolveClaudeCatalogApiModelId(catalog, { instanceId: ProviderInstanceId.make("claudeAgent"), model: "claude-custom-tuned", options: [{ id: "effort", value: "brutal" }], }), "claude-custom-tuned", ); assert.deepStrictEqual( resolveClaudeModelsForVersion(catalog, "3.2.0").map((model) => model.slug), ["claude-synthetic-next", "claude-custom-tuned"], ); }); });