import assert from "node:assert/strict" import { test } from "node:test" import { accountApiBase, apiForModelId, baseUrlForApi, fetchLiveCatalog, getModelsTimeoutMs, modelsFromCatalog, modelsFromLive, parseLiveCatalog, providerHeaders, thinkingLevelMapFor, toProviderModel, } from "../../src/models.ts" const PROVIDER_API_BASE = "https://api.commandcode.ai/provider/v1" /** Shape of the real GET /provider/v1/models response. */ const liveCatalogResponse = { object: "list", data: [ { id: "deepseek/deepseek-v4.1-flash", object: "model", name: "DeepSeek V4.1 Flash", context_length: 1_000_000 }, { id: "claude-sonnet-4-6", object: "model", name: "Claude Sonnet 4.6", context_length: 1_000_000 }, { id: "vendor/brand-new-model", object: "model", name: "Brand New", context_length: 32_768 }, ], } test("parseLiveCatalog reads id, name and context window", () => { const models = parseLiveCatalog(liveCatalogResponse) assert.deepEqual( models.map((model) => model.id), ["deepseek/deepseek-v4.1-flash", "claude-sonnet-4-6", "vendor/brand-new-model"], ) assert.equal(models[2]?.contextWindow, 32_768) }) test("parseLiveCatalog rejects malformed catalogs", () => { assert.throws(() => parseLiveCatalog({ object: "list", data: [] }), /empty model catalog/) assert.throws(() => parseLiveCatalog({ object: "collection", data: [{}] }), /'list'/) assert.throws( () => parseLiveCatalog({ object: "list", data: [{ id: "x", name: "X" }] }), /context_length/, ) }) test("modelsFromLive uses CLI metadata when the model is known", () => { const [deepseek, claude, unknown] = modelsFromLive(parseLiveCatalog(liveCatalogResponse)) assert.equal(deepseek?.reasoning, true) assert.deepEqual(deepseek?.efforts, ["low", "high", "max"]) assert.deepEqual(deepseek?.input, ["text", "image"]) assert.equal(deepseek?.cost.output, 0.6) assert.equal(claude?.api, "anthropic-messages") // Unknown models stay usable but text-only and unpriced until the catalog syncs. assert.equal(unknown?.reasoning, false) assert.deepEqual(unknown?.input, ["text"]) assert.deepEqual(unknown?.cost, { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }) assert.equal(unknown?.maxTokens, 32_768) }) test("modelsFromCatalog exposes every generated entry", async () => { const { CATALOG } = await import("../../src/catalog.ts") const models = modelsFromCatalog() assert.equal(models.length, CATALOG.length) assert.ok(models.every((model) => model.maxTokens > 0 && model.contextWindow > 0)) }) test("baseline models without a published context window fall back to a usable default", () => { // The CLI reference lists GLM-5.1 as "—" for context, and pi cannot run a model with a 0 window. const glm = modelsFromCatalog().find((model) => model.id === "zai-org/GLM-5.1") assert.equal(glm?.contextWindow, 200_000) assert.ok((glm?.maxTokens ?? 0) > 0) }) test("modelsFromLive prefers the live context window over the CLI snapshot", () => { const [model] = modelsFromLive([ { id: "deepseek/deepseek-v4.1-flash", name: "DeepSeek V4.1 Flash", contextWindow: 512_000 }, ]) assert.equal(model?.contextWindow, 512_000) assert.equal(model?.maxTokens, 65_536) assert.deepEqual(model?.input, ["text", "image"]) }) test("live context windows and DeepSeek V4.1 vision/effort metadata survive the merge", () => { const [model] = modelsFromLive([ { id: "deepseek/deepseek-v4.1-flash", name: "DeepSeek V4.1 Flash", contextWindow: 1_000_000 }, ]) assert.ok(model) const config = toProviderModel(model, PROVIDER_API_BASE) // Vision and the opt-in max effort are what the API actually serves for V4.1. assert.deepEqual(config.input, ["text", "image"]) assert.deepEqual(config.thinkingLevelMap, { minimal: null, low: "low", medium: null, high: "high", xhigh: null, max: "max", }) assert.equal( (config.compat as { supportsReasoningEffort?: boolean }).supportsReasoningEffort, true, ) }) test("api and base URL follow the model family", () => { assert.equal(apiForModelId("claude-opus-5"), "anthropic-messages") assert.equal(apiForModelId("deepseek/deepseek-v4.1-flash"), "openai-completions") assert.equal(baseUrlForApi(PROVIDER_API_BASE, "openai-completions"), PROVIDER_API_BASE) // pi appends /v1/messages to the Anthropic base URL. assert.equal(baseUrlForApi(PROVIDER_API_BASE, "anthropic-messages"), "https://api.commandcode.ai/provider") }) test("accountApiBase strips the provider namespace", () => { assert.equal(accountApiBase(PROVIDER_API_BASE), "https://api.commandcode.ai") assert.equal(accountApiBase("https://example.test/provider/v1/"), "https://example.test") }) test("thinkingLevelMap hides levels the model does not offer", () => { assert.deepEqual(thinkingLevelMapFor(["low", "high", "max"]), { minimal: null, low: "low", medium: null, high: "high", xhigh: null, max: "max", }) }) test("toProviderModel maps a Claude model onto the Anthropic adapter", () => { const [claude] = modelsFromLive(parseLiveCatalog(liveCatalogResponse)).slice(1) assert.ok(claude) const config = toProviderModel(claude, PROVIDER_API_BASE) assert.equal(config.api, "anthropic-messages") assert.equal(config.baseUrl, "https://api.commandcode.ai/provider") assert.equal(config.reasoning, true) assert.deepEqual(config.thinkingLevelMap?.high, "high") assert.equal( (config.compat as { forceAdaptiveThinking?: boolean }).forceAdaptiveThinking, true, ) assert.equal(config.cost.cacheWrite, 3.75) }) test("toProviderModel maps an OpenAI-compatible model onto the completions adapter", () => { const [deepseek] = modelsFromLive(parseLiveCatalog(liveCatalogResponse)) assert.ok(deepseek) const config = toProviderModel(deepseek, PROVIDER_API_BASE) assert.equal(config.api, "openai-completions") assert.equal(config.baseUrl, PROVIDER_API_BASE) assert.deepEqual(config.compat, { supportsStore: false, supportsDeveloperRole: false, supportsReasoningEffort: true, maxTokensField: "max_tokens", }) assert.deepEqual(config.input, ["text", "image"]) }) test("fetchLiveCatalog parses the response and surfaces HTTP failures", async () => { const ok = await fetchLiveCatalog({ fetchImpl: async () => new Response(JSON.stringify(liveCatalogResponse), { status: 200 }), }) assert.equal(ok.length, 3) await assert.rejects( fetchLiveCatalog({ fetchImpl: async () => new Response("nope", { status: 503 }) }), /503/, ) }) test("environment overrides for base URL, headers and timeout", () => { assert.deepEqual(providerHeaders({ CMD_ZDR: "1" }), { "x-cmd-zdr": "1" }) assert.equal(providerHeaders({ COMMANDCODE_ZDR: "1" })?.["x-cmd-zdr"], "1") assert.equal(providerHeaders({}), undefined) assert.equal(getModelsTimeoutMs({ COMMANDCODE_MODELS_TIMEOUT_MS: "2500" }), 2500) assert.equal(getModelsTimeoutMs({ COMMANDCODE_MODELS_TIMEOUT_MS: "-1" }), 10_000) })