import { test, describe } from "node:test"; import assert from "node:assert/strict"; import { transformToOmniRoute } from "../../src/lib/pricingSync.ts"; // ─── transformToOmniRoute ──────────────────────────────── describe("transformToOmniRoute", () => { test("converts LiteLLM per-token pricing to OmniRoute per-million format", () => { const raw = { "openai/gpt-4o": { input_cost_per_token: 0.0000025, output_cost_per_token: 0.00001, litellm_provider: "openai", mode: "chat", }, }; const result = transformToOmniRoute(raw); assert.ok(result.openai, "Should have openai provider"); assert.ok(result.openai["gpt-4o"], "Should have gpt-4o model"); assert.strictEqual(result.openai["gpt-4o"].input, 2.5); assert.strictEqual(result.openai["gpt-4o"].output, 10); }); test("maps anthropic provider to cc alias", () => { const raw = { "anthropic/claude-sonnet-4-20250514": { input_cost_per_token: 0.000003, output_cost_per_token: 0.000015, litellm_provider: "anthropic", mode: "chat", }, }; const result = transformToOmniRoute(raw); assert.ok(result.cc, "Should map to cc alias"); assert.ok(result.cc["claude-sonnet-4-20250514"]); assert.strictEqual(result.cc["claude-sonnet-4-20250514"].input, 3); assert.strictEqual(result.cc["claude-sonnet-4-20250514"].output, 15); }); test("maps vertex_ai provider to gemini alias", () => { const raw = { "vertex_ai/gemini-2.5-flash": { input_cost_per_token: 0.0000003, output_cost_per_token: 0.0000025, litellm_provider: "vertex_ai", mode: "chat", }, }; const result = transformToOmniRoute(raw); assert.ok(result.gemini, "Should map to gemini alias"); assert.strictEqual(result.gemini["gemini-2.5-flash"].input, 0.3); }); test("ingests non-chat models (embedding token + image per-image)", () => { // Phase 2 (cost-telemetry parity): non-chat modes are no longer skipped. // Token-priced modes (embedding) are scaled to $/1M like chat; non-token // modes (image) carry their per-image cost through verbatim as absolute USD. const raw = { "openai/text-embedding-3-small": { input_cost_per_token: 0.00000002, output_cost_per_token: 0, litellm_provider: "openai", mode: "embedding", }, "openai/dall-e-3": { input_cost_per_token: 0, output_cost_per_token: 0, litellm_provider: "openai", mode: "image_generation", output_cost_per_image: 0.04, }, }; const result = transformToOmniRoute(raw); const embedding = result.openai?.["text-embedding-3-small"]; assert.ok(embedding, "Should ingest token-priced embedding model"); assert.strictEqual(embedding.input, 0.02); assert.strictEqual(embedding.mode, "embedding"); const image = result.openai?.["dall-e-3"]; assert.ok(image, "Should ingest image model with per-image cost"); assert.strictEqual(image.output_cost_per_image, 0.04); assert.strictEqual(image.mode, "image_generation"); }); test("skips models with no token AND no non-token pricing", () => { const raw = { "openai/metadata-only": { litellm_provider: "openai", mode: "image_generation", }, }; const result = transformToOmniRoute(raw); const openaiModels = result.openai || {}; assert.strictEqual( Object.keys(openaiModels).length, 0, "Should skip models carrying no pricing of any kind" ); }); test("includes cache pricing when available", () => { const raw = { "anthropic/claude-sonnet-4-20250514": { input_cost_per_token: 0.000003, output_cost_per_token: 0.000015, cache_read_input_token_cost: 0.0000003, cache_creation_input_token_cost: 0.00000375, litellm_provider: "anthropic", mode: "chat", }, }; const result = transformToOmniRoute(raw); const model = result.anthropic["claude-sonnet-4-20250514"]; assert.ok(model, "Should have model"); assert.strictEqual(model.cached, 0.3); assert.strictEqual(model.cache_creation, 3.75); }); test("handles models without explicit mode (treated as chat)", () => { const raw = { "deepseek/deepseek-chat": { input_cost_per_token: 0.00000014, output_cost_per_token: 0.00000028, litellm_provider: "deepseek", }, }; const result = transformToOmniRoute(raw); // deepseek maps to "if" alias assert.ok(result.if, "Should map deepseek to if alias"); assert.ok(result.if["deepseek-chat"]); }); test("skips entries without input cost", () => { const raw = { "unknown/model": { litellm_provider: "unknown", mode: "chat", }, }; const result = transformToOmniRoute(raw); const unknownModels = result.unknown || {}; assert.strictEqual(Object.keys(unknownModels).length, 0); }); test("handles zero-cost (free) models", () => { const raw = { "groq/llama-3.3-70b-versatile": { input_cost_per_token: 0, output_cost_per_token: 0, litellm_provider: "groq", mode: "chat", }, }; const result = transformToOmniRoute(raw); assert.ok(result.groq, "Should have groq provider"); assert.strictEqual(result.groq["llama-3.3-70b-versatile"].input, 0); assert.strictEqual(result.groq["llama-3.3-70b-versatile"].output, 0); }); test("uses litellm_provider as-is for unmapped providers", () => { const raw = { "newprovider/some-model": { input_cost_per_token: 0.000001, output_cost_per_token: 0.000002, litellm_provider: "newprovider", mode: "chat", }, }; const result = transformToOmniRoute(raw); assert.ok(result.newprovider, "Should use litellm_provider as-is"); assert.ok(result.newprovider["some-model"]); }); test("strips provider prefix from model key", () => { const raw = { "openai/gpt-4o-mini": { input_cost_per_token: 0.00000015, output_cost_per_token: 0.0000006, litellm_provider: "openai", mode: "chat", }, }; const result = transformToOmniRoute(raw); assert.ok(result.openai["gpt-4o-mini"], "Should strip openai/ prefix"); assert.strictEqual(result.openai["gpt-4o-mini"].input, 0.15); }); test("rounds pricing to 3 decimal places", () => { const raw = { "test/model": { input_cost_per_token: 0.00000033333, output_cost_per_token: 0.00000066666, litellm_provider: "openai", mode: "chat", }, }; const result = transformToOmniRoute(raw); // 0.00000033333 * 1e6 = 0.33333 → rounded to 0.333 assert.strictEqual(result.openai.model.input, 0.333); assert.strictEqual(result.openai.model.output, 0.667); }); }); // ─── Merge precedence ──────────────────────────────────── describe("pricing merge precedence", () => { test("user overrides > synced > defaults conceptual order", () => { // This test validates the conceptual model. // The actual merge is tested via integration with settings.ts. // Here we verify transform doesn't lose data needed for merge. const raw = { "openai/gpt-4o": { input_cost_per_token: 0.0000025, output_cost_per_token: 0.00001, litellm_provider: "openai", mode: "chat", }, }; const synced = transformToOmniRoute(raw); const userOverride = { openai: { "gpt-4o": { input: 999 } } }; // Simulate merge: synced then user const merged = { ...synced.openai["gpt-4o"], ...userOverride.openai["gpt-4o"] }; assert.strictEqual(merged.input, 999, "User override should win"); assert.strictEqual(merged.output, 10, "Non-overridden fields from synced should remain"); }); });