| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192 |
- """模型单价与费用计算。
- 单价含义:每 1K token 的价格(与 config 表 billing_pricing 的列注释一致)。
- 骨架自带一组种子单价,启动时写入 billing_pricing(不覆盖已存在的人工调价)。
- """
- from __future__ import annotations
- import config
- from db import billing_store, db_conn
- from obs import get_logger
- logger = get_logger(__name__)
- class UnpricedModelError(RuntimeError):
- """模型在 billing_pricing 里没有单价,且不允许按 0 计费。"""
- # 种子单价:仅为了让骨架开箱可算费,**不是**真实报价,上线前必须按实际
- # 供应商账单校准。单位:元 / 1K token。
- SEED_PRICING: dict[str, tuple[float, float]] = {
- "deepseek-flash": (0.0005, 0.0015),
- "deepseek-v4-flash": (0.0005, 0.0015),
- "deepseek-v4-pro": (0.002, 0.008),
- "deepseek-chat": (0.001, 0.002),
- "deepseek-reasoner": (0.001, 0.002),
- "gpt-4o-mini": (0.001, 0.004),
- # 用户提供的平台报价:输入 0.4 元 / M,输出 3.2 元 / M,换算为每 1K token。
- "qwen3.5-35b-a3b": (0.0004, 0.0032),
- }
- def seed_default_pricing() -> int:
- """把种子单价写入单价表;已存在的模型不动。返回新写入条数。"""
- created = 0
- for model, (input_price, output_price) in SEED_PRICING.items():
- if billing_store.get_pricing(model) is None:
- billing_store.set_pricing(model, input_price, output_price)
- created += 1
- if created:
- logger.info("单价表初始化:新写入 %d 条", created)
- return created
- def compute_cost(model: str, input_tokens: int, output_tokens: int) -> tuple[float, str]:
- """按单价表算本轮费用。
- 返回 ``(cost, currency)``。模型未定价时:
- - 默认 fail-closed —— 抛 :class:`UnpricedModelError`,避免「静默免费」
- - ``BILLING_ALLOW_UNPRICED=1`` 时降级为 0 价并告警(便于本地开发)
- """
- pricing = billing_store.get_pricing(model)
- if pricing is None:
- if config.get_bool("BILLING_ALLOW_UNPRICED", False):
- logger.warning("模型 %s 未定价,按 0 计费(BILLING_ALLOW_UNPRICED=1)", model)
- return 0.0, "CNY"
- raise UnpricedModelError(
- f"模型 {model!r} 未在 billing_pricing 表中定价;"
- f"请补单价,或设 BILLING_ALLOW_UNPRICED=1 临时按 0 计费"
- )
- input_price = float(pricing.get("input_price") or 0)
- output_price = float(pricing.get("output_price") or 0)
- cost = (int(input_tokens) / 1000.0) * input_price + (
- int(output_tokens) / 1000.0
- ) * output_price
- return round(cost, 8), str(pricing.get("currency") or "CNY")
- def pricing_table() -> list[dict]:
- """当前单价表(供 /api/usage 展示)。"""
- rows = billing_store.list_pricing()
- return [
- {
- "model": row.get("model"),
- "input_price": float(row.get("input_price") or 0),
- "output_price": float(row.get("output_price") or 0),
- "currency": row.get("currency"),
- "updated_at": row.get("updated_at"),
- }
- for row in rows
- ]
- __all__ = [
- "SEED_PRICING",
- "UnpricedModelError",
- "compute_cost",
- "pricing_table",
- "seed_default_pricing",
- "db_conn",
- ]
|