/** * OpenCode Go 模型价格表 + 每次请求的用量/费用估算记录。 * 零 pi 依赖的纯业务模块。 * * 实测:每次 chat/completions 响应自带 usage(含 cached_tokens / cache_write_tokens), * 但 cost 字段恒为 "0"(Go 包月制,官方不按次结算金额)。因此金额只能自己按 token × 单价估算。 * 价格表来自官方文档 https://opencode.ai/docs/go/(静态数据,需内置)。 */ /** 单价(美元 / 百万 token)。可选峰/谷两档。 */ export interface ModelPrice { input: number; output: number; cacheRead: number; cacheWrite?: number; // 简化的峰谷配置:peak 为 true 时使用 peak 价 peakInput?: number; peakOutput?: number; peakCacheRead?: number; peakCacheWrite?: number; } export const DEFAULT_PRICES: Record = { "deepseek-v4-flash": { input: 0.22, output: 0.66, cacheRead: 0.007, peakInput: 0.44, peakOutput: 1.32, peakCacheRead: 0.014, }, "deepseek-v4-pro": { input: 0.66, output: 1.98, cacheRead: 0.022, peakInput: 1.32, peakOutput: 3.96, peakCacheRead: 0.044, }, // 其余模型:普通单价(来自官方文档),无峰谷时 pe 为同一值 "glm-5.3": { input: 1.4, output: 4.4, cacheRead: 0.26 }, "glm-5.2": { input: 1.4, output: 4.4, cacheRead: 0.26 }, "glm-5.1": { input: 1.4, output: 4.4, cacheRead: 0.26 }, "gpt-5.6-luna": { input: 0.2, output: 1.2, cacheRead: 0.02, cacheWrite: 0.25, peakInput: 0.4, peakOutput: 1.8, peakCacheRead: 0.04, peakCacheWrite: 0.5 }, "kimi-k3": { input: 3.0, output: 15.0, cacheRead: 0.3 }, "kimi-k2.7-code": { input: 0.95, output: 4.0, cacheRead: 0.19 }, "kimi-k2.6": { input: 0.95, output: 4.0, cacheRead: 0.16 }, "mimo-v2.5": { input: 0.14, output: 0.28, cacheRead: 0.0028 }, "mimo-v2.5-pro": { input: 0.435, output: 0.87, cacheRead: 0.003625 }, "minimax-m3": { input: 0.3, output: 1.2, cacheRead: 0.06 }, "minimax-m2.7": { input: 0.3, output: 1.2, cacheRead: 0.06, cacheWrite: 0.375 }, "qwen3.8-max": { input: 2.0, output: 6.0, cacheRead: 0.25, cacheWrite: 2.5 }, "qwen3.7-max": { input: 2.5, output: 7.5, cacheRead: 0.5, cacheWrite: 3.125 }, "qwen3.7-plus": { input: 0.4, output: 1.6, cacheRead: 0.04, cacheWrite: 0.5 }, "qwen3.6-plus": { input: 0.5, output: 3.0, cacheRead: 0.05, cacheWrite: 0.625 }, "hy3": { input: 0.14, output: 0.58, cacheRead: 0.035 }, }; export function isPeakHour(date = new Date()): boolean { // Peak: 01:00-04:00 与 06:00-10:00 UTC const h = date.getUTCHours(); return (h >= 1 && h < 4) || (h >= 6 && h < 10); } export interface CostBreakdown { /** 美元,估算值 */ input: number; output: number; cacheRead: number; cacheWrite: number; total: number; } /** * 估算一次请求的费用(美元)。 * modelId: 如 deepseek-v4-flash;peak: 是否按峰时价;priceMap: 可注入自定义价格表。 */ export function estimateCost( modelId: string | undefined, usage: { prompt?: number; completion?: number; cached?: number; cacheWrite?: number }, peak: boolean, priceMap: Record = DEFAULT_PRICES, ): CostBreakdown { const base = modelId ? priceMap[modelId] : undefined; const inputPrice = peak && base?.peakInput !== undefined ? base.peakInput : base?.input ?? 0; const outputPrice = peak && base?.peakOutput !== undefined ? base.peakOutput : base?.output ?? 0; const cacheReadPrice = peak && base?.peakCacheRead !== undefined ? base.peakCacheRead : base?.cacheRead ?? 0; const cacheWritePrice = peak && base?.peakCacheWrite !== undefined ? base.peakCacheWrite : base?.cacheWrite ?? 0; const prompt = usage.prompt ?? 0; const cached = usage.cached ?? 0; const cacheWrite = usage.cacheWrite ?? 0; const completion = usage.completion ?? 0; // 缓存命中的 token 属于 prompt,但按 cacheRead 计价;未命中部分按正常 input 价。 const nonCachedPrompt = Math.max(0, prompt - cached); const input = (nonCachedPrompt * inputPrice) / 1_000_000; const cacheReadCost = (cached * cacheReadPrice) / 1_000_000; const cacheWriteCost = (cacheWrite * cacheWritePrice) / 1_000_000; const output = (completion * outputPrice) / 1_000_000; return { input: round(input), output: round(output), cacheRead: round(cacheReadCost), cacheWrite: round(cacheWriteCost), total: round(input + output + cacheReadCost + cacheWriteCost), }; } function round(n: number): number { return Math.round(n * 1e6) / 1e6; } /** 一次请求的用量记录(落盘/汇总用)。 */ export interface UsageRecord { ts: number; sessionId?: string; model?: string; prompt: number; completion: number; cached: number; cacheWrite: number; peak: boolean; cost: CostBreakdown; } export interface UsageStats { records: number; prompt: number; completion: number; cached: number; cacheWrite: number; cost: CostBreakdown; /** 缓存命中率 = cached / (cached+nonCachedPrompt),0~1;无记录为 0 */ cacheHitRate: number; } export function summarizeStats(records: UsageRecord[]): UsageStats { let prompt = 0; let completion = 0; let cached = 0; let cacheWrite = 0; const cost: CostBreakdown = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }; for (const r of records) { prompt += r.prompt; completion += r.completion; cached += r.cached; cacheWrite += r.cacheWrite; cost.input += r.cost.input; cost.output += r.cost.output; cost.cacheRead += r.cost.cacheRead; cost.cacheWrite += r.cost.cacheWrite; cost.total += r.cost.total; } const nonCachedPrompt = Math.max(0, prompt - cached); const cacheHitRate = nonCachedPrompt + cached > 0 ? cached / (cached + nonCachedPrompt) : 0; return { records: records.length, prompt, completion, cached, cacheWrite, cost, cacheHitRate: round(cacheHitRate), }; }