首页
/ OmniRoute 成本与支出追踪:定价同步、成本计算原理与 Dashboard/CLI 实践指南

OmniRoute 成本与支出追踪:定价同步、成本计算原理与 Dashboard/CLI 实践指南

2026-09-07 17:46:47作者:苗圣禹Peter

本篇围绕 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 中定义:

  1. 用户覆盖(User overrides) — 你在 Dashboard 中设置、或通过 PATCH /api/pricing 写入的价格,优先级最高。
  2. 外部同步价格 — 开启同步后从 LiteLLM 公开的 model_prices_and_context_window.json 拉取。同步数据存放在独立的 pricing_synced 命名空间(SQLite key_value 表),永远不会覆盖你的用户覆盖
  3. 内置默认价 — 随 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_costcache_creation_input_token_cost 分别映射为 cachedcache_creation 字段。
  • 提供商别名映射:LiteLLM 的 litellm_providerLITELLM_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.tscomputeCostFromPricing / 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 服务层级乘数getCodexFastCostMultipliercodex/cx 提供商按 service_tier 调整——flex 按 50% 折扣计费(即 ×0.5),在仪表盘表现为 flex savingsfast/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 强制刷新前的最大缓冲条数。

两个关键结论(直接来自文档并得到源码印证):

  1. 仪表盘数字不是读取存储的每行金额,而是在每次调用 analytics 端点时,用 token 数 + 当前定价表实时重算。因此修正一个错误价格(覆盖或重新同步)后,历史成本估算会追溯性更新
  2. 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 天 及所选窗口的估算支出;范围选择器支持 7d30d90dall
  • 头部指标 — 窗口内请求数、活跃提供商数、活跃模型数、平均每请求成本。
  • Cost Explorer — 可按 provider / model / API key / account / service tier 分组、排序、过滤的表格,列含成本、请求数、token 数、平均单请求成本与占比 %。
  • Token 用量 — 总量 / 输入 / 输出 token 及输入:输出比。
  • 路由效率 — fallback 次数、fallback 率、请求模型覆盖率。
  • 月度预测 — 由近期日均支出外推月末支出;周期对比 — 窗口前后两半的百分比变化。
  • 图表 — 每日成本趋势、提供商份额(饼图)、Top 提供商、Top 模型、按 API Key 成本、按账户成本、每周用量模式、活跃度热力图。
  • 导出 — 将当前窗口下载为 CSVJSON(按钮在有非零成本数据后出现)。

无定价流量时,行显示 "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 分组。查询参数:rangestartDateendDateapiKeyIdspresets
GET /api/usage/utilization 各提供商随时间的配额利用率。查询:range1h/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/analyticsutilizationhistorycall-logsquotaproxy-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 的响应实现看,返回字段包括 dailyLimitUsdweeklyLimitUsdmonthlyLimitUsdwarningThreshold,以及运行累计值(totalCostTodaytotalCostMonthtotalCostPeriod)与重置相关字段(resetIntervalbudgetResetAtnextResetAt)。

定价(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.mjsregisterPricingregisterCostregisterUsage 三个注册函数)。

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 与批量缓冲做到零延迟、可重放。由于所有展示层都在读取时重算,修正价格即可追溯修正全部历史估算——这正是把仪表盘数字当作"节省追踪器"而非账单的技术基础。

登录后查看全文
热门项目推荐
相关项目推荐