diff --git a/src/usage/expected-prices.ts b/src/usage/expected-prices.ts index cbe24e4aa4..351b6a00d2 100644 --- a/src/usage/expected-prices.ts +++ b/src/usage/expected-prices.ts @@ -124,6 +124,16 @@ const META_MUSE_SPARK_13_CONTRIBUTOR: Cost4 = { input: 0.1, output: 0.2, cacheRe const META_SPARK_SOURCE = `Meta Model API published price ${META_MODEL_PRICING}`; const META_SPARK_CONTRIBUTOR_SOURCE = `Meta Model API published Contributor-tier price ${META_MODEL_PRICING}; data-sharing discount tier`; const DEEPSEEK_PRICING = "https://api-docs.deepseek.com/quick_start/pricing-details-usd; V4 Flash alias transition scheduled 2026-07-24 — re-verify after"; +/* + * DeepSeek V4.1-Flash list prices (USD / 1M tokens), verified 2026-09-17 against + * https://api-docs.deepseek.com/quick_start/pricing. The page prices a peak window + * (09:30-24:00 Beijing) and an off-peak window; the tuple below is the peak-window + * list rate and the off-peak discount (0.15 / 0.60, cache-hit 0.003) is deliberately + * not baked in — the same rule as the Devin time-boxed promos. cacheWrite=0 follows + * the existing deepseek-chat / deepseek-reasoner rows: DeepSeek publishes no + * cache-write charge. + */ +const DEEPSEEK_V41_FLASH: Cost4 = { input: 0.3, output: 1.2, cacheRead: 0.006, cacheWrite: 0 }; // Kimi official tables publish input/output/cache-hit only; cacheWrite is mapped to the // cache-miss input price (Kimi auto-caches with no separate write billing). 2026-07-20 re-verified. const KIMI_PRICING = "https://platform.kimi.ai/docs/pricing (official table; cacheWrite derived = input, Kimi auto-cache has no write billing)"; @@ -142,6 +152,14 @@ const BIGMODEL_NOTE = "z.ai international list price shown as estimate; domestic // anywhere. Cache stays 0 rather than inheriting the reseller's 0.15 — a reseller number // under a vendor-price label would be a wrong value wearing a verified badge. const QWEN38_MAX_PRICING = "https://qwen.ai/blog?id=qwen3.8 (Qwen release announcement; no Model Studio billing row yet; cache rates unpublished -> 0)"; +// Qwen-published qwen3.8-flash rate ($0.16 in / $0.47 out per 1M tokens) via the +// Qwen3.8 release announcement, corroborated by API-vendor price tables. No cache +// rate is published anywhere, so cache stays 0 rather than borrowing a reseller's +// number — the same hold QWEN38_MAX takes. The announcement marks API availability +// as coming soon, but the id is already served (and logged) on OpenCode Go, so the +// estimate applies to real usage rows now. +const QWEN38_FLASH: Cost4 = { input: 0.16, output: 0.47, cacheRead: 0, cacheWrite: 0 }; +const QWEN38_FLASH_PRICING = "https://qwen.ai/blog?id=qwen3.8-2026 (Qwen release announcement; API marked coming soon at announcement; cache rates unpublished -> 0; input/output corroborated by https://docs.b.ai/guides/models/qwen/qwen3.8-flash)"; /* * Cognition/Devin list prices (USD / 1M tokens), verified 2026-09-13 against the @@ -289,6 +307,16 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [ // for what that source does and does not cover. { provider: "alibaba-token-plan", modelId: "qwen3.8-max", cost4: QWEN38_MAX, source: QWEN38_MAX_PRICING, verifiedAt: "2026-08-04", status: "verified" }, { provider: "alibaba-token-plan-intl", modelId: "qwen3.8-max", cost4: QWEN38_MAX, source: QWEN38_MAX_PRICING, verifiedAt: "2026-08-04", status: "verified" }, + // OpenCode Go — five served ids with no jawcode bundle row and no vendor-level + // fallback (the fallback only searches jawcode metadata, never overlays), so the + // Usage estimated-cost column and the per-model breakdown rendered an em dash for + // every request through them. Each row reuses the vendor's own published list + // price as an estimate: Go itself is subscription-billed, hence verified-derived. + { provider: "opencode-go", modelId: "qwen3.8-max", cost4: QWEN38_MAX, source: `vendor list price applied to the OpenCode Go surface; ${QWEN38_MAX_PRICING}`, verifiedAt: "2026-09-17", status: "verified-derived" }, + { provider: "opencode-go", modelId: "qwen3.8-flash", cost4: QWEN38_FLASH, source: `vendor list price applied to the OpenCode Go surface; ${QWEN38_FLASH_PRICING}`, verifiedAt: "2026-09-17", status: "verified-derived" }, + { provider: "opencode-go", modelId: "deepseek-v4.1-flash", cost4: DEEPSEEK_V41_FLASH, source: `peak-window list rate applied to the OpenCode Go surface (off-peak 0.15/0.60 + cache-hit 0.003 not baked in); ${DEEPSEEK_PRICING}`, verifiedAt: "2026-09-17", status: "verified-derived" }, + { provider: "opencode-go", modelId: "glm-5.3-flash", cost4: GLM_53_FLASH, source: `z.ai list price applied to the OpenCode Go surface as an estimate; ${ZAI_PRICING}`, verifiedAt: "2026-09-17", status: "verified-derived" }, + { provider: "opencode-go", modelId: "muse-spark-1.3-contributor", cost4: META_MUSE_SPARK_13_CONTRIBUTOR, source: `Meta Model API Contributor-tier price applied to the OpenCode Go surface as an estimate; ${META_SPARK_CONTRIBUTOR_SOURCE}`, verifiedAt: "2026-09-17", status: "verified-derived" }, // Cursor Auto router — Cursor's published fixed token price (verified). { provider: "cursor", modelId: "auto", cost4: { input: 1.25, output: 6, cacheRead: 0.25, cacheWrite: 1.25 }, source: "https://docs.cursor.com/account/pricing + https://cursor.com/blog/aug-2025-pricing", verifiedAt: "2026-07-20", status: "verified" }, // Z.AI GLM family — the zai bundle's rows are all-zero upstream, and the four diff --git a/tests/usage/usage-cost.test.ts b/tests/usage/usage-cost.test.ts index 4614303ef8..dffe93f2ab 100644 --- a/tests/usage/usage-cost.test.ts +++ b/tests/usage/usage-cost.test.ts @@ -258,6 +258,36 @@ describe("resolveMatchedPrice", () => { } }); + // OpenCode Go serves five ids with no jawcode bundle row and no vendor-level + // fallback, so every request through them resolved to null and the Usage cost + // column rendered an em dash. Each row reuses the vendor's published list price + // as a verified-derived estimate (Go itself is subscription-billed). + test("17h. OpenCode Go ids resolve vendor list prices as estimates", () => { + for (const [modelId, cost4] of [ + ["qwen3.8-max", { input: 2, output: 6, cacheRead: 0, cacheWrite: 0 }], + ["qwen3.8-flash", { input: 0.16, output: 0.47, cacheRead: 0, cacheWrite: 0 }], + ["deepseek-v4.1-flash", { input: 0.3, output: 1.2, cacheRead: 0.006, cacheWrite: 0 }], + ["glm-5.3-flash", { input: 0.15, output: 0.5, cacheRead: 0.03, cacheWrite: 0 }], + ["muse-spark-1.3-contributor", { input: 0.1, output: 0.2, cacheRead: 0.002, cacheWrite: 0 }], + ] as const) { + const price = resolveMatchedPrice("opencode-go", modelId); + expect(price, `opencode-go/${modelId}`).toMatchObject({ cost4, source: "expected", status: "verified-derived" }); + } + // The Flash rows must keep carrying what the vendor figure does NOT cover: + // Qwen publishes no cache rate, DeepSeek's off-peak window is not baked in. + expect(resolveMatchedPrice("opencode-go", "qwen3.8-flash")?.sourceRef).toContain("cache rates unpublished"); + expect(resolveMatchedPrice("opencode-go", "deepseek-v4.1-flash")?.sourceRef).toContain("off-peak"); + // End to end: a real request through a previously unpriced id now estimates + // instead of resolving to null. + const estimate = estimateRequestCost({ + provider: "opencode-go", + model: "qwen3.8-flash", + usage: { inputTokens: 1700, outputTokens: 0 }, + usageStatus: "estimated", + }); + expect(estimate?.cost.total).toBeCloseTo(1700 * 0.16 / 1e6, 12); + }); + test("6. unmatched exact key is null", () => { expect(resolveMatchedPrice("no-such-provider", "no-such-model")).toBeNull(); expect(resolveMatchedPrice("openai", "definitely-not-a-model")).toBeNull(); @@ -298,8 +328,8 @@ describe("resolveMatchedPrice", () => { expect(resolveMatchedPrice("openrouter", "anthropic-claude-3.5-sonnet")).toBeNull(); }); - test("16. shipped overlay membership: 121 keys, including canonical Fable 5.1, Opus 5 and compatibility prices", () => { - expect(EXPECTED_PRICE_OVERLAYS.length).toBe(121); + test("16. shipped overlay membership: 126 keys, including canonical Fable 5.1, Opus 5, OpenCode Go and compatibility prices", () => { + expect(EXPECTED_PRICE_OVERLAYS.length).toBe(126); expect(EXPECTED_PRICE_OVERLAYS.some(row => row.status === "unverified")).toBe(false); const keys = new Set(EXPECTED_PRICE_OVERLAYS.map(row => `${row.provider}/${row.modelId}`)); for (const expected of [ @@ -369,6 +399,13 @@ describe("resolveMatchedPrice", () => { "kimi-code/kimi-for-coding", "alibaba-token-plan/qwen3.8-max", "alibaba-token-plan-intl/qwen3.8-max", + // OpenCode Go — served ids with no jawcode bundle row; each reuses the + // vendor's published list price as a verified-derived estimate. + "opencode-go/qwen3.8-max", + "opencode-go/qwen3.8-flash", + "opencode-go/deepseek-v4.1-flash", + "opencode-go/glm-5.3-flash", + "opencode-go/muse-spark-1.3-contributor", "cursor/auto", // Z.AI GLM family — the zai bundle is all-zero upstream, so each exposing // provider surface carries its own verified-derived rows (z.ai USD list).