import { describe, expect, it } from "vitest"; import { GPT_CONTEXT_WINDOW_CAP, resolveModelMetadata, type KnownModelMetadata, type PresetModelSource, } from "@/lib/modelMetadata"; import type { ModelsDevResponse } from "@/lib/modelsDev"; const modelsDev: ModelsDevResponse = { zhipuai: { id: "zhipuai", api: "https://open.bigmodel.cn/api/paas/v4", models: { "glm-5": { id: "glm-5", limit: { context: 204800, output: 131072 }, reasoning: true, reasoning_options: [ { type: "toggle" }, { type: "effort", values: ["high"] }, ], modalities: { input: ["text"], output: ["text"] }, cost: { input: 1, output: 3.2 }, }, }, }, zai: { id: "zai", api: "https://api.z.ai/api/paas/v4", models: { "glm-5": { id: "glm-5", limit: { context: 200000, output: 128000 } }, }, }, "zai-coding-plan": { id: "zai-coding-plan", api: "https://api.z.ai/api/coding/paas/v4", models: { "glm-5": { id: "glm-5", limit: { context: 202752, output: 98304 }, cost: { input: 0, output: 0 }, }, }, }, "opencode-go": { id: "opencode-go", api: "https://opencode.ai/zen/go/v1", models: { "glm-5.3": { id: "glm-5.3", limit: { context: 1000000, output: 131072 }, reasoning_options: [{ type: "effort", values: ["low", "high", "max"] }], }, }, }, openrouter: { id: "openrouter", api: "https://openrouter.ai/api/v1", models: { "z-ai/glm-5": { id: "z-ai/glm-5", limit: { context: 202800 }, canonical_model_id: "zhipuai/glm-5", }, "someone/kimi-k9": { id: "someone/kimi-k9", limit: { context: 65536 }, canonical_model_id: "moonshotai/kimi-k9", }, "minimax/minimax-m9": { id: "minimax/minimax-m9", canonical_model_id: "minimax/MiniMax-M9", }, "nvidia/nemotron-9": { id: "nvidia/nemotron-9", canonical_model_id: "nvidia/nemotron-9", }, }, }, minimax: { id: "minimax", models: { "MiniMax-M9": { id: "MiniMax-M9", limit: { context: 204800 } }, }, }, // 本身是原厂(有条目指向它),也托管别家的模型,且这些条目不指向原厂。 nvidia: { id: "nvidia", models: { "nemotron-9": { id: "nemotron-9" }, "minimaxai/minimax-m9": { id: "minimaxai/minimax-m9", limit: { context: 196608 }, }, }, }, moonshotai: { id: "moonshotai", api: "https://api.moonshot.ai/v1", models: { "kimi-k9": { id: "kimi-k9", limit: { context: 262144 } }, }, }, other: { id: "other", models: { "kimi-k9": { id: "kimi-k9", limit: { context: 100000 }, canonical_model_id: "elsewhere/kimi-k9", }, }, }, elsewhere: { id: "elsewhere", models: { "kimi-k9": { id: "kimi-k9", limit: { context: 50000 } } }, }, // 原厂名下没有 deepseek-v9-flash-1017,各家条目都指向它却落空。 deepseek: { id: "deepseek", models: { "deepseek-flash": { id: "deepseek-flash" } }, }, "ds-relay": { id: "ds-relay", models: { "deepseek-v9-flash-1017": { id: "deepseek-v9-flash-1017", limit: { context: 1000000 }, modalities: { input: ["text", "image"], output: ["text"] }, canonical_model_id: "deepseek/deepseek-v9-flash-1017", }, "tinker/inkling-9": { id: "tinker/inkling-9", canonical_model_id: "tinker/inkling-9", }, }, }, // 原厂(有条目指向它),也托管别家的微调条目,这些条目指回别家原厂。 tinker: { id: "tinker", models: { "inkling-9": { id: "inkling-9" }, "deepseek-ai/DeepSeek-V9-Flash-1017:peft:262144": { id: "deepseek-ai/DeepSeek-V9-Flash-1017:peft:262144", limit: { context: 262144 }, modalities: { input: ["text"], output: ["text"] }, canonical_model_id: "deepseek/deepseek-v9-flash-1017", }, "poolside/Laguna-9": { id: "poolside/Laguna-9", limit: { context: 65536 }, canonical_model_id: "poolside/laguna-9", }, }, }, // 原厂自己的条目指向自己,但 ID 写法和指向的不一致,指向也落空。 poolside: { id: "poolside", models: { "poolside/laguna-9": { id: "poolside/laguna-9", limit: { context: 262144 }, canonical_model_id: "poolside/laguna-9", }, }, }, }; const preset = ( baseUrl: string, models: Record, ): PresetModelSource => ({ baseUrl, models: new Map(Object.entries(models)) }); describe("resolveModelMetadata", () => { it("uses the provider recognized by its API address", () => { expect( resolveModelMetadata("glm-5.3", { baseUrl: "https://opencode.ai/zen/go/v1/", modelsDev, }), ).toMatchObject({ contextWindow: 1000000, maxOutputTokens: 131072, reasoningEfforts: ["low", "high", "max"], }); }); it("prefers the provider whose path matches the address best", () => { expect( resolveModelMetadata("glm-5", { baseUrl: "https://api.z.ai/api/coding/paas/v4", modelsDev, })?.contextWindow, ).toBe(202752); expect( resolveModelMetadata("glm-5", { baseUrl: "https://api.z.ai/api/paas/v4", modelsDev, })?.contextWindow, ).toBe(200000); }); it("matches ids case-insensitively and across namespaces", () => { expect( resolveModelMetadata("Z-AI/GLM-5", { baseUrl: "https://openrouter.ai/api/v1", modelsDev, })?.contextWindow, ).toBe(202800); expect( resolveModelMetadata("zai-org/glm-5", { baseUrl: "https://open.bigmodel.cn/api/paas/v4", modelsDev, })?.contextWindow, ).toBe(204800); }); it("falls back to the vendor entry without carrying its price", () => { const metadata = resolveModelMetadata("glm-5", { baseUrl: "https://relay.example.com/v1", modelsDev, }); expect(metadata).toMatchObject({ contextWindow: 204800, reasoning: true, inputModalities: ["text"], reasoningEfforts: ["high"], sources: ["models-dev"], }); expect(metadata?.cost).toBeUndefined(); }); it("keeps the price when the provider itself is recognized", () => { expect( resolveModelMetadata("glm-5", { baseUrl: "https://open.bigmodel.cn/api/paas/v4", modelsDev, }), ).toMatchObject({ cost: { input: 1, output: 3.2 }, reasoningEfforts: ["high"], }); }); it("ignores a vendor hosting another vendor's model", () => { expect( resolveModelMetadata("MiniMax-M9", { baseUrl: "https://relay.example.com/v1", modelsDev, })?.contextWindow, ).toBe(204800); }); it("does not borrow an entry that points at another vendor", () => { expect( resolveModelMetadata("DeepSeek-v9-flash-1017", { baseUrl: "https://relay.example.com/v1", modelsDev, }), ).toBeNull(); }); it("still uses the vendor's own entry when its pointer is dangling", () => { expect( resolveModelMetadata("laguna-9", { baseUrl: "https://relay.example.com/v1", modelsDev, })?.contextWindow, ).toBe(262144); }); it("gives up when the same name points at different vendors", () => { expect( resolveModelMetadata("kimi-k9", { baseUrl: "https://relay.example.com/v1", modelsDev, }), ).toBeNull(); }); it("lets the matching preset row win field by field", () => { const presets = [ preset("https://api.z.ai/api/coding/paas/v4", { "glm-5": { contextWindow: 500000 }, }), preset("https://relay.example.com/v1", { "glm-5": { contextWindow: 1 }, }), ]; expect( resolveModelMetadata("glm-5", { baseUrl: "https://api.z.ai/api/coding/paas/v4", presets, modelsDev, }), ).toMatchObject({ contextWindow: 500000, maxOutputTokens: 98304, sources: ["preset", "models-dev"], }); }); it("does not take another plan's preset on the same host", () => { expect( resolveModelMetadata("glm-5", { baseUrl: "https://api.z.ai/api/paas/v4", presets: [ preset("https://api.z.ai/api/coding/paas/v4", { "glm-5": { contextWindow: 500000 }, }), ], modelsDev, }), ).toMatchObject({ contextWindow: 200000, sources: ["models-dev"] }); }); it("accepts the same endpoint written with or without a version suffix", () => { expect( resolveModelMetadata("glm-5.3", { baseUrl: "https://opencode.ai/zen/go", modelsDev, })?.contextWindow, ).toBe(1000000); }); it("matches the old OpenCode address to the entry models.dev moved to /inference", () => { const moved: ModelsDevResponse = { "opencode-go": { id: "opencode-go", api: "https://opencode.ai/inference/go/openai/v1", models: { "deepseek-v4.1-flash": { id: "deepseek-v4.1-flash", limit: { context: 1000000 }, reasoning: true, modalities: { input: ["text", "image"], output: ["text"] }, }, }, }, opencode: { id: "opencode", api: "https://opencode.ai/inference/openai/v1", models: { "deepseek-v4.1-flash": { id: "deepseek-v4.1-flash", limit: { context: 200000 }, }, }, }, }; expect( resolveModelMetadata("deepseek-v4.1-flash", { baseUrl: "https://opencode.ai/zen/go/v1", modelsDev: moved, }), ).toMatchObject({ contextWindow: 1000000, reasoning: true, inputModalities: ["text", "image"], }); expect( resolveModelMetadata("deepseek-v4.1-flash", { baseUrl: "https://opencode.ai/zen/v1", modelsDev: moved, })?.contextWindow, ).toBe(200000); }); it("does not treat a parent path as the plans nested under it", () => { expect( resolveModelMetadata("glm-5.3", { baseUrl: "https://opencode.ai/zen", presets: [ preset("https://opencode.ai/zen/go/v1", { "glm-5.3": { contextWindow: 1 }, }), ], modelsDev, }), ).toBeNull(); }); it("ignores a pasted request path after the base address", () => { expect( resolveModelMetadata("glm-5", { baseUrl: "https://api.z.ai/api/coding/paas/v4/chat/completions", modelsDev, })?.contextWindow, ).toBe(202752); }); it("fills fields the provider entry leaves out from the vendor entry", () => { const metadata = resolveModelMetadata("glm-5", { baseUrl: "https://api.z.ai/api/paas/v4", modelsDev, }); expect(metadata).toMatchObject({ contextWindow: 200000, maxOutputTokens: 128000, reasoning: true, inputModalities: ["text"], }); expect(metadata?.cost).toBeUndefined(); }); it("uses presets alone when models.dev is unavailable", () => { expect( resolveModelMetadata("glm-5", { baseUrl: "https://api.z.ai/api/coding/paas/v4", presets: [ preset("https://api.z.ai/api/coding/paas/v4", { "glm-5": { contextWindow: 500000 }, }), ], }), ).toEqual({ contextWindow: 500000, sources: ["preset"] }); expect( resolveModelMetadata("glm-5", { baseUrl: "https://api.z.ai/v1" }), ).toBeNull(); }); it("caps GPT windows only when they are borrowed from the vendor entry", () => { const openai: ModelsDevResponse = { openai: { id: "openai", api: "https://api.openai.com/v1", models: { "gpt-6-astra": { id: "gpt-6-astra", limit: { context: 1050000, output: 128000 }, }, "gpt-4o": { id: "gpt-4o", limit: { context: 128000 } }, "o9-big": { id: "o9-big", limit: { context: 1050000 } }, }, }, relay: { id: "relay", models: { "openai/gpt-6-astra": { id: "openai/gpt-6-astra", canonical_model_id: "openai/gpt-6-astra", }, }, }, gateway: { id: "gateway", api: "https://gateway.example.com/v1", models: { "gpt-6-astra": { id: "gpt-6-astra", canonical_model_id: "openai/gpt-6-astra", }, }, }, }; const resolve = (id: string, baseUrl: string) => resolveModelMetadata(id, { baseUrl, modelsDev: openai })?.contextWindow; // 认不出地址、借原厂条目的中转封顶,带命名空间的写法也一样。 expect(resolve("gpt-6-astra", "https://relay.example.com/v1")).toBe( GPT_CONTEXT_WINDOW_CAP, ); expect(resolve("openai/GPT-6-Astra", "https://relay.example.com/v1")).toBe( GPT_CONTEXT_WINDOW_CAP, ); // 认出了地址但那家没写窗口,借来的原厂窗口同样封顶。 expect(resolve("gpt-6-astra", "https://gateway.example.com/v1")).toBe( GPT_CONTEXT_WINDOW_CAP, ); // 按地址认出的那家、同地址预设照用自己的值。 expect(resolve("gpt-6-astra", "https://api.openai.com/v1")).toBe(1050000); expect( resolveModelMetadata("openai-gpt-6-astra", { baseUrl: "https://relay.example.com/v1", presets: [ preset("https://relay.example.com/v1", { "openai-gpt-6-astra": { contextWindow: 1050000 }, }), ], modelsDev: openai, })?.contextWindow, ).toBe(1050000); // 本来就小的 GPT 不往上抬,其他模型不受影响。 expect(resolve("gpt-4o", "https://api.openai.com/v1")).toBe(128000); expect(resolve("o9-big", "https://api.openai.com/v1")).toBe(1050000); }); });