diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index a50df961..8e516d79 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed Kimi Coding subscription models to report API-equivalent implied costs when models.dev reports zero pricing. + ## [0.80.10] - 2026-07-16 ### Fixed diff --git a/packages/ai/scripts/generate-models.ts b/packages/ai/scripts/generate-models.ts index 16af169e..8d30e9ce 100644 --- a/packages/ai/scripts/generate-models.ts +++ b/packages/ai/scripts/generate-models.ts @@ -241,6 +241,15 @@ const KIMI_K3_COST = { cacheRead: 0.3, cacheWrite: 0, } as const; +// Kimi Coding is subscription-backed, so models.dev reports zero cost. Use the +// equivalent Moonshot API rates to estimate the value of subscription usage. +const KIMI_CODING_IMPLIED_COSTS: Record["cost"]> = { + k2p7: { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite: 0 }, + k3: KIMI_K3_COST, + "kimi-for-coding": { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite: 0 }, + "kimi-for-coding-highspeed": { input: 1.9, output: 8, cacheRead: 0.38, cacheWrite: 0 }, + "kimi-k2-thinking": { input: 0.6, output: 2.5, cacheRead: 0.15, cacheWrite: 0 }, +}; const OPENROUTER_KIMI_K3_MODEL_IDS = new Set(["moonshotai/kimi-k3", "~moonshotai/kimi-latest"]); const ANT_LING_RING_THINKING_LEVEL_MAP = { @@ -1642,6 +1651,7 @@ async function loadModelsDevData(): Promise[]> { const normalizedName = kimiAliases.has(modelId) ? "Kimi For Coding" : m.name || normalizedId; const isKimiK3 = normalizedId === "k3"; const allowEmptySignature = isKimiK3 || normalizedId === "kimi-for-coding"; + const impliedCost = KIMI_CODING_IMPLIED_COSTS[normalizedId]; models.push({ id: normalizedId, @@ -1659,10 +1669,10 @@ async function loadModelsDevData(): Promise[]> { ...(isKimiK3 ? { thinkingLevelMap: KIMI_K3_THINKING_LEVEL_MAP } : {}), input: m.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], cost: { - input: m.cost?.input || 0, - output: m.cost?.output || 0, - cacheRead: m.cost?.cache_read || 0, - cacheWrite: m.cost?.cache_write || 0, + input: m.cost?.input || impliedCost?.input || 0, + output: m.cost?.output || impliedCost?.output || 0, + cacheRead: m.cost?.cache_read || impliedCost?.cacheRead || 0, + cacheWrite: m.cost?.cache_write || impliedCost?.cacheWrite || 0, }, contextWindow: m.limit?.context || 4096, maxTokens: m.limit?.output || 4096, diff --git a/packages/ai/src/providers/kimi-coding.models.ts b/packages/ai/src/providers/kimi-coding.models.ts index 7bdf8e86..584f6f35 100644 --- a/packages/ai/src/providers/kimi-coding.models.ts +++ b/packages/ai/src/providers/kimi-coding.models.ts @@ -15,9 +15,9 @@ export const KIMI_CODING_MODELS = { reasoning: true, input: ["text", "image"], cost: { - input: 0, - output: 0, - cacheRead: 0, + input: 0.95, + output: 4, + cacheRead: 0.19, cacheWrite: 0, }, contextWindow: 262144, @@ -35,9 +35,9 @@ export const KIMI_CODING_MODELS = { thinkingLevelMap: {"off":null,"minimal":null,"low":null,"medium":null,"high":null,"xhigh":null,"max":"max"}, input: ["text", "image"], cost: { - input: 0, - output: 0, - cacheRead: 0, + input: 3, + output: 15, + cacheRead: 0.3, cacheWrite: 0, }, contextWindow: 1048576, @@ -54,9 +54,9 @@ export const KIMI_CODING_MODELS = { reasoning: true, input: ["text", "image"], cost: { - input: 0, - output: 0, - cacheRead: 0, + input: 0.95, + output: 4, + cacheRead: 0.19, cacheWrite: 0, }, contextWindow: 262144, @@ -73,9 +73,9 @@ export const KIMI_CODING_MODELS = { reasoning: true, input: ["text", "image"], cost: { - input: 0, - output: 0, - cacheRead: 0, + input: 1.9, + output: 8, + cacheRead: 0.38, cacheWrite: 0, }, contextWindow: 262144, @@ -92,9 +92,9 @@ export const KIMI_CODING_MODELS = { reasoning: true, input: ["text"], cost: { - input: 0, - output: 0, - cacheRead: 0, + input: 0.6, + output: 2.5, + cacheRead: 0.15, cacheWrite: 0, }, contextWindow: 262144, diff --git a/packages/ai/test/providers.test.ts b/packages/ai/test/providers.test.ts index a8bc68e4..ff664446 100644 --- a/packages/ai/test/providers.test.ts +++ b/packages/ai/test/providers.test.ts @@ -56,6 +56,21 @@ describe("builtin providers", () => { } }); + it("uses API-equivalent implied pricing for Kimi Coding subscription models", () => { + const models = builtinModels(); + const expectedCosts = { + k2p7: { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite: 0 }, + k3: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 0 }, + "kimi-for-coding": { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite: 0 }, + "kimi-for-coding-highspeed": { input: 1.9, output: 8, cacheRead: 0.38, cacheWrite: 0 }, + "kimi-k2-thinking": { input: 0.6, output: 2.5, cacheRead: 0.15, cacheWrite: 0 }, + }; + + for (const [modelId, cost] of Object.entries(expectedCosts)) { + expect(models.getModel("kimi-coding", modelId)?.cost).toEqual(cost); + } + }); + it("resolves anthropic auth from env with OAuth token precedence", async () => { const models = createModels({ authContext: fakeAuthContext({ ANTHROPIC_API_KEY: "key", ANTHROPIC_OAUTH_TOKEN: "oauth-token" }), diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 3eefbc25..7bc933f4 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixed - Fixed obsolete custom UI, custom tool, and custom editor examples in the extension documentation ([#6735](https://github.com/earendil-works/pi/issues/6735)). +- Fixed Kimi Coding sessions to show API-equivalent implied costs with the subscription indicator. ## [0.80.10] - 2026-07-16 diff --git a/packages/coding-agent/src/modes/interactive/components/footer.ts b/packages/coding-agent/src/modes/interactive/components/footer.ts index a781f893..fa947480 100644 --- a/packages/coding-agent/src/modes/interactive/components/footer.ts +++ b/packages/coding-agent/src/modes/interactive/components/footer.ts @@ -137,8 +137,10 @@ export class FooterComponent implements Component { if ((totalCacheRead > 0 || totalCacheWrite > 0) && latestCacheHitRate !== undefined) { statsParts.push(`CH${latestCacheHitRate.toFixed(1)}%`); } - // Show cost with "(sub)" indicator if using OAuth subscription - const usingSubscription = state.model ? this.session.modelRuntime.isUsingOAuth(state.model.provider) : false; + // Kimi Coding is subscription-backed despite using API-key authentication. + const usingSubscription = state.model + ? state.model.provider === "kimi-coding" || this.session.modelRuntime.isUsingOAuth(state.model.provider) + : false; if (totalCost || usingSubscription) { const costStr = `$${totalCost.toFixed(3)}${usingSubscription ? " (sub)" : ""}`; statsParts.push(costStr); diff --git a/packages/coding-agent/test/footer-width.test.ts b/packages/coding-agent/test/footer-width.test.ts index c3d707a9..85956d53 100644 --- a/packages/coding-agent/test/footer-width.test.ts +++ b/packages/coding-agent/test/footer-width.test.ts @@ -141,4 +141,21 @@ describe("FooterComponent width handling", () => { const statsLine = stripAnsi(footer.render(120)[1]); expect(statsLine).toContain("CH25.0%"); }); + + it("marks Kimi Coding costs as subscription estimates", () => { + const session = createSession({ + sessionName: "", + provider: "kimi-coding", + usage: { + input: 100, + output: 10, + cacheRead: 0, + cacheWrite: 0, + cost: { total: 1.234 }, + }, + }); + const footer = new FooterComponent(session, createFooterData(1)); + + expect(stripAnsi(footer.render(120)[1])).toContain("$1.234 (sub)"); + }); });