OmniRoute 成本与支出追踪:定价同步、成本计算原理与 Dashboard/CLI 实践指南
本篇围绕 OmniRoute 的成本追踪体系展开:它会为每个请求估算一个 USD 成本,驱动 Costs 仪表盘、omniroute cost / omniroute usage CLI、CSV/JSON 导出与按 API Key 的预算控制。读完后,你将理解"仪表盘成本是节省追踪器而非账单"的设计定位,掌握定价表的三层解析优先级、LiteLLM 外部同步、逐 token 成本公式(含缓存与推理 token 的处理细节),并能熟练使用相关 API 端点与 CLI 命令。
定位:节省追踪器,不是账单
OmniRoute 通过"token 数 × 模型单价"为每次完成请求估算一个 USD 成本。这些数字支撑了 Costs 仪表盘、omniroute cost / omniroute usage CLI、CSV/JSON 导出以及按 API Key 的预算。但必须首先明确它的语义:
- OmniRoute 不向用户收费。它只是把请求路由到你已连接的提供商(你自己的订阅、免费额度、API Key)。
- 仪表盘累计的"$290 总成本"如果全部来自免费模型,含义是你避免了约 $290 的付费 API 开销——这是一个按标准价目表估算的"等效成本",用来观察用量分布与路由到免费/廉价提供商带来的节省。
- 正因为它是估算值:定价表中没有条目的模型成本计为
0(在 Cost Explorer 中显示为 "Legacy / Free" 行);免费额度与订阅流量同样会累计"估算成本",那代表的是节省额,而非欠费。
定价来源:三层解析优先级
成本估算依赖一张定价表,其解析优先级在 src/lib/pricingSync.ts 中定义:
- 用户覆盖(User overrides) — 你在 Dashboard 中设置、或通过
PATCH /api/pricing写入的价格,优先级最高。 - 外部同步价格 — 开启同步后从 LiteLLM 公开的
model_prices_and_context_window.json拉取。同步数据存放在独立的pricing_synced命名空间(SQLitekey_value表),永远不会覆盖你的用户覆盖。 - 内置默认价 — 随 OmniRoute 一起发布的硬编码默认值。
外部同步:LiteLLM 数据如何被摄取
外部定价同步是选择性开启(opt-in)、默认关闭的。相关环境变量(见 .env.example 第 18 节 "PRICING SYNC"):
| 环境变量 | 默认值 | 用途 |
|---|---|---|
PRICING_SYNC_ENABLED |
false |
启动时启用后台 LiteLLM 定价同步。 |
PRICING_SYNC_INTERVAL |
86400 |
同步间隔,单位秒(默认每天一次)。 |
PRICING_SYNC_SOURCES |
litellm |
逗号分隔的来源列表(当前仅支持 litellm)。 |
从源码看(src/lib/pricingSync.ts),同步流程有三个值得注意的实现细节:
- 单位换算:LiteLLM 的
input_cost_per_token/output_cost_per_token是单 token 价格,transformToOmniRoute()将其× 1_000_000转换为 OmniRoute 统一的 美元/百万 token 格式并保留三位小数;cache_read_input_token_cost与cache_creation_input_token_cost分别映射为cached与cache_creation字段。 - 提供商别名映射:LiteLLM 的
litellm_provider经LITELLM_PROVIDER_MAP映射到 OmniRoute 的提供商别名(如openai → ["openai", "cx"]、vertex_ai → ["gemini"]、deepseek → ["ds"]、bedrock → ["kr"])。源码注释特别记录了一个历史 bug:deepseek曾错误地写到别名"if"(实为另一家提供商 Qoder 的 key),导致 DeepSeek 同步价格被悄悄挂到错误的提供商名下——可见映射表必须使用 registry 别名而非提供商id。 - 非 token 计费维度原样透传:按图片(
*_cost_per_image)、按秒(音频/视频)、按字符(TTS)、按检索单元(rerank 的search_unit_cost)等字段以绝对 USD 直接携带,不做 ×1e6 缩放。
同步状态(lastSync、模型数)被持久化到独立的 pricing_sync_status 命名空间而非仅存模块变量——源码注释解释了原因:Next.js standalone 构建会从多个独立 webpack chunk 加载该模块(如 instrumentation hook 与 API route handler 各自一份),模块级变量互不可见,落库后才能跨实例读到真实状态。startPeriodicSync() 首次同步为非阻塞执行,定时器 unref() 以避免挂住进程退出。
成本公式:从 token 数到 USD
核心计算位于 src/lib/usage/costCalculator.ts 的 computeCostFromPricing / calculateCost。所有单价均解释为 USD / 1,000,000 tokens。逐项拆解(对照源码 costCalculator.ts):
| 组成 | 计算方式 |
|---|---|
| 输入 token | (input − cached − cache_creation) × input 单价。prompt_tokens 本身已包含两类缓存 token,因此必须同时减去两者,否则缓存部分会被重复按全价计费(源码注释原样说明了这一点)。 |
| 缓存读取 token | × cached 单价(缺省时回退到 input 单价)。 |
| 缓存创建 token | × cache_creation 单价(缺省回退到 input 单价)。 |
| 输出 token | × output 单价。 |
| 推理 token | completion_tokens 已包含推理部分并先按 output 单价计费;仅当定价表显式配置了 reasoning 价时,再补收差额 reasoningTokens × (reasoning − output) 单价,避免重复计费。 |
两个重要的边界处理:
- 精确成本优先:若 usage 记录里带有
cost_in_usd_ticks(xAI 在响应usage中报告的真实计费成本),则直接用ticks / 10_000_000_000(1e10 ticks/美元)作为成本,跳过一切估算——即使本地还没有该模型的定价行(costCalculator.ts)。 - Codex 服务层级乘数:
getCodexFastCostMultiplier对codex/cx提供商按service_tier调整——flex按 50% 折扣计费(即 ×0.5),在仪表盘表现为 flex savings;fast/priority则对特定模型加价(GPT-5.6 系列 ×1.5、gpt-5.5 ×2.5、gpt-5.4 ×2)。 - 模型名归一化:计算前先经
normalizeModelName剥离openai/、accounts/fireworks/models/等路径前缀(取最后一个/后的段),保证历史行仍能命中价格;对 Codex 还会额外尝试剥离 effort 后缀(如-xhigh、-none)再查价。
除 token 计费外,costCalculator.ts 还实现了多模态计费函数:computeImageCost(按张数)、computeAudioCost(按秒或按字符)、computeRerankCost(按检索单元)、computeVideoCost(按视频秒数),统一由 calculateModalCost 分发——缺失定价时一律返回 0 而不抛错。
支出如何被记录:零延迟写入
成本落盘的设计目标是不给客户端响应路径增加任何延迟:
- 共享配额的消耗记录在 src/lib/quota/spendRecorder.ts 中通过
setImmediate推迟到下一个事件循环 tick 执行(fire-and-forget),错误只记录日志、绝不向调用方传播——源码注释说明少量漂移可接受,会由全局饱和信号在下次请求时自愈。 - API Key 维度的支出由 SpendBatchWriter 缓冲后批量落盘:默认 60 秒刷新间隔、1,000 条缓冲上限,两者达到任一条件即触发
flush()。实现上值得注意:刷新失败时条目会被重新排队回缓冲区(requeued: true)而不是丢弃;被删除的 API Key 通过discardEntries从缓冲与在途批次中剔除。可通过环境变量调整:
| 环境变量 | 默认值 | 用途 |
|---|---|---|
OMNIROUTE_SPEND_FLUSH_INTERVAL_MS |
60000 |
刷新间隔(毫秒)。 |
OMNIROUTE_SPEND_MAX_BUFFER_SIZE |
1000 |
强制刷新前的最大缓冲条数。 |
两个关键结论(直接来自文档并得到源码印证):
- 仪表盘数字不是读取存储的每行金额,而是在每次调用 analytics 端点时,用 token 数 + 当前定价表实时重算。因此修正一个错误价格(覆盖或重新同步)后,历史成本估算会追溯性更新。
- API Key 支出因批量写入天然滞后于实时——需要更及时数字时降低
OMNIROUTE_SPEND_FLUSH_INTERVAL_MS。
Dashboard:Costs 页面
Costs 页面位于 /dashboard/costs(src/app/(dashboard)/dashboard/costs//dashboard/costs/)),主视图是 Cost Overview 选项卡(CostOverviewTab.tsx/dashboard/costs/CostOverviewTab.tsx)),全部数据来自 GET /api/usage/analytics。页面展示内容:
- 支出磁贴 — 今日 (1d)、7 天、30 天 及所选窗口的估算支出;范围选择器支持
7d、30d、90d、all。 - 头部指标 — 窗口内请求数、活跃提供商数、活跃模型数、平均每请求成本。
- Cost Explorer — 可按 provider / model / API key / account / service tier 分组、排序、过滤的表格,列含成本、请求数、token 数、平均单请求成本与占比 %。
- Token 用量 — 总量 / 输入 / 输出 token 及输入:输出比。
- 路由效率 — fallback 次数、fallback 率、请求模型覆盖率。
- 月度预测 — 由近期日均支出外推月末支出;周期对比 — 窗口前后两半的百分比变化。
- 图表 — 每日成本趋势、提供商份额(饼图)、Top 提供商、Top 模型、按 API Key 成本、按账户成本、每周用量模式、活跃度热力图。
- 导出 — 将当前窗口下载为 CSV 或 JSON(按钮在有非零成本数据后出现)。
无定价流量时,行显示 "Legacy / Free" 标签而非 $0,呼应节省追踪器的模型。
相邻子页面
Costs 区域还包含(均在 /dashboard/costs/ 下):
- Pricing(
/dashboard/costs/pricing)— 查看并覆盖每个模型的价格(渲染共享 Pricing 选项卡)。 - Budget(
/dashboard/costs/budget)— 设置各作用域的支出限额(渲染共享 Budget 选项卡)。 - Quota Share(
/dashboard/costs/quota-share)— 共享配额池与燃尽率视图。
API 端点
以下端点除特别注明外均需管理鉴权(loopback/JWT,经 requireManagementAuth 校验,见 src/app/api/usage/budget/route.ts 中的实际实现)。
用量与成本分析
| 方法 | 端点 | 用途 |
|---|---|---|
GET |
/api/usage/analytics |
完整成本/用量分析:汇总、每日趋势、按 provider/model/API key/account/tier 分组。查询参数:range、startDate、endDate、apiKeyIds、presets。 |
GET |
/api/usage/utilization |
各提供商随时间的配额利用率。查询:range(1h/24h/7d/30d)、provider。 |
GET |
/api/usage/history |
原始用量历史行。 |
GET |
/api/usage/call-logs |
每请求调用日志(模型、token、成本、延迟、状态)。 |
GET |
/api/usage/quota |
提供商配额状态。 |
GET |
/api/usage/proxy-logs |
代理请求日志。 |
路由实现位于 src/app/api/usage/(analytics、utilization、history、call-logs、quota、proxy-logs 等目录均已确认存在)。
预算(Budgets)
| 方法 | 端点 | 用途 |
|---|---|---|
GET |
/api/usage/budget |
单个 API Key 的成本汇总 + 预算检查(apiKeyId 查询参数必填)。 |
POST |
/api/usage/budget |
为某 API Key 设置日/周/月 USD 限额与告警阈值。 |
GET |
/api/usage/budget/bulk |
跨 API Key 的批量预算汇总。 |
预算 API 以 API Key(apiKeyId) 为作用域。从 src/app/api/usage/budget/route.ts 的响应实现看,返回字段包括 dailyLimitUsd、weeklyLimitUsd、monthlyLimitUsd、warningThreshold,以及运行累计值(totalCostToday、totalCostMonth、totalCostPeriod)与重置相关字段(resetInterval、budgetResetAt、nextResetAt)。
定价(Pricing)
| 方法 | 端点 | 用途 |
|---|---|---|
GET |
/api/pricing |
当前合并后的定价(用户覆盖 + 同步 + 默认)。?includeSources=1 可显示每条来源。 |
PATCH |
/api/pricing |
覆盖定价,格式 { provider: { model: { input, output, cached, … } } }。 |
DELETE |
/api/pricing |
重置定价为默认(可用 ?provider=&model= 限定范围)。 |
GET |
/api/pricing/defaults |
查看默认每 1M token 回退单价。 |
GET |
/api/pricing/models |
按模型组织的定价。 |
POST |
/api/pricing/sync |
手动触发外部来源(LiteLLM)同步。 |
GET |
/api/pricing/sync |
当前同步状态(enabled/lastSync/nextSync/intervalMs/sources)。 |
DELETE |
/api/pricing/sync |
清空全部已同步定价数据。 |
对应路由实现见 src/app/api/pricing/。
其他成本相关端点
| 方法 | 端点 | 用途 |
|---|---|---|
GET |
/api/free-tier/summary |
免费模型 token 总量、本月已用与剩余免费额度。 |
GET |
/api/quota/pools/[id]/usage |
共享配额池用量。 |
CLI 命令
CLI 的成本、用量与定价命令注册于 bin/cli/commands/registry.mjs(registerPricing、registerCost、registerUsage 三个注册函数)。
omniroute cost
从 /api/usage/analytics 聚合的成本报表。从 bin/cli/commands/cost.mjs 的注册实现看:--period 默认 30d,--group-by 默认 provider,--limit 默认 100。
omniroute cost # 最近 30 天,按 provider 分组
omniroute cost --period 7d # 最近 7 天
omniroute cost --group-by model # 分组维度:provider | model | combo | api-key | day
omniroute cost --since 2026-06-01 --until 2026-06-13
omniroute cost --api-key <key> --limit 50
表格列:分组、请求数、输入/输出 token、成本(USD,4 位小数)、占总成本百分比。结尾打印总计行(--quiet 或 --output json 时抑制)。--since/--until 与 --period 互斥:设置了日期区间时以 startDate/endDate 传参,否则走 range。
omniroute usage
omniroute usage analytics --period 30d [--provider <id>] # 按提供商的成本汇总
omniroute usage logs [--limit 100] [--follow] [--api-key <k>] [--search <q>]
omniroute usage quota [--provider <id>] [--check]
omniroute usage utilization [--api-key <k>]
omniroute usage history [--limit 100]
omniroute usage proxy-logs [--limit 100]
# 预算
omniroute usage budget list
omniroute usage budget get [scope]
omniroute usage budget set <amount> [--scope global] [--period monthly]
omniroute usage budget reset [scope]
omniroute pricing
omniroute pricing list [--provider <p>] [--model <m>] [--limit 200]
omniroute pricing get <model>
omniroute pricing sync [--provider <p>] [--force] # POST /api/pricing/sync
omniroute pricing diff [--model <m>]
omniroute pricing defaults show
omniroute pricing defaults set [--input <p>] [--output <p>] [--cache-read <p>] [--cache-write <p>]
pricing defaults show读取GET /api/pricing/defaults。若要编辑单个模型价格,请使用 Pricing 仪表盘页面或PATCH /api/pricing。
故障排查
- 所有成本显示 $0 / "Legacy / Free":所用模型没有定价条目。开启外部同步(
PRICING_SYNC_ENABLED=true)并执行omniroute pricing sync,或通过 Pricing 页面 /PATCH /api/pricing手动设置价格。 - 某个历史模型价格不对:修正价格(覆盖或重新同步)即可——成本在每次 analytics 读取时基于 token 数重算,估算值会追溯性更新。
- 支出滞后于实时:API Key 支出是批量写入的;需要更及时数字时调低
OMNIROUTE_SPEND_FLUSH_INTERVAL_MS。
小结
OmniRoute 的成本追踪是一个"估算—记录—重算"三阶段闭环:定价表按 用户覆盖 > LiteLLM 同步 > 内置默认 的优先级解析;成本按 token 维度(含缓存、推理、Codex 层级乘数与多模态计费)实时计算并支持 xAI 精确成本直通;写入路径通过 setImmediate 与批量缓冲做到零延迟、可重放。由于所有展示层都在读取时重算,修正价格即可追溯修正全部历史估算——这正是把仪表盘数字当作"节省追踪器"而非账单的技术基础。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00