feat(orchestrator): W4薄片①——new-api logs.quota权威成本接通(取代客户端估算为权威)

- newapi_cost.py: 直读new-api PG logs(真实倍率), model×token_name聚合/生成裁判分列/平台vs创作者归属/交叉校验
- 实测merge-prod-20: ¥0.59总/¥0.0103次/约¥0.031款(MiniMax-M2.7+v4-flash裁判分列)——确证文本生成成本可忽略
- report.py成本段加权威成本指针; 模型§5客户端估算降fallback,权威数落账
- 评审整改: per-creator计量/支付/订阅推迟(admin无法替他人铸token+支付通道3-5周阻塞)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
zizi 2026-06-11 07:17:53 +00:00
parent 11009b4064
commit 1ece4a7c43
3 changed files with 190 additions and 1 deletions

View File

@ -0,0 +1,187 @@
#!/usr/bin/env python3
"""new-api 权威成本读取W4 成本侧① · HJ-NEWAPI-BILL-001 薄片①)。
定位评审 v2 整改后的薄片范围
W4 LLM 成本来源从客户端 token×假设单价 估算切到 **new-api `logs.quota` 权威**
logs.quota 已含真实模型倍率new-api 计费引擎算好的比客户端估算准只读零依赖生成路径
零回归风险 new-api 历史日志已在库无需新跑批即可验证
读取路径评审实测裁决
admin HTTP API/api/log鉴权 finicky 127.0.0.1:3000 回环超时status=000实测
**直读 new-api PostgreSQL**内网provenCLAUDE.md 允许内网口令入库 `ssh mini-infra docker exec psql`
连接参数为内网常量可环境变量覆盖非公网密钥
平台系统 vs 创作者成本归属薄片②
logs.token_name 分组`--platform-token <name>` 标记平台铺量 token其余归创作者侧
per-creator 用户/支付/订阅按评审 v2 推迟到支付通道真实化后本工具不涉
用法
python3 newapi_cost.py --batch runs/merge-prod-20 # 从批 ledger 推窗口 + 交叉校验 report.json
python3 newapi_cost.py --since '2026-06-10T14:40:00+08:00' --until '2026-06-10T15:10:00+08:00'
python3 newapi_cost.py --minutes 60 # 最近 60 分钟
环境变量NEWAPI_PG_SSH(默认 mini-infra) / NEWAPI_PG_PW / NEWAPI_USD_CNY(默认 7.2)
"""
from __future__ import annotations
import argparse
import json
import os
import subprocess
import sys
# —— new-api 配额→¥ 换算(评审 F3/F4 修正)——
# QuotaPerUnit 库内无此 key实查 options 仅 9 键),用 new-api 编译默认500000 quota = 1 USD。
QUOTA_PER_UNIT = float(os.environ.get("NEWAPI_QUOTA_PER_UNIT", "500000"))
# USD→¥ 汇率为标注假设(非 new-api 配置项);售卖锁价时须快照,此处仅成本台账折算用。
USD_CNY = float(os.environ.get("NEWAPI_USD_CNY", "7.2"))
# —— 直读 PG 连接内网常量CLAUDE.md 允许;可环境变量覆盖)——
PG_SSH = os.environ.get("NEWAPI_PG_SSH", "mini-infra")
PG_CONTAINER = os.environ.get("NEWAPI_PG_CONTAINER", "infra-postgres")
PG_PW = os.environ.get("NEWAPI_PG_PW", "f6710e2d0294eb1c10e26a805a64bc54")
PG_USER = os.environ.get("NEWAPI_PG_USER", "root")
PG_DB = os.environ.get("NEWAPI_PG_DB", "new-api")
def _psql(sql: str) -> list[list[str]]:
"""经 ssh+docker exec 跑只读 psql返回 `|` 分隔的行(-tA 无表头无对齐,默认分隔符)。
SQL 内禁用双引号远程经 sh -c "..." 传递双引号会破坏分层引用字段分隔用
psql 默认 `|` -F'\t' 字面 tab会让远程 argv 解析挂起实测
"""
inner = (
'docker exec -e PGPASSWORD=%s %s psql -U %s -d %s -tA -c "%s"'
% (PG_PW, PG_CONTAINER, PG_USER, PG_DB, sql)
)
out = subprocess.run(
["ssh", PG_SSH, inner],
capture_output=True, text=True, timeout=30,
)
if out.returncode != 0:
raise RuntimeError("psql 读取失败:%s" % (out.stderr.strip()[:300]))
rows = []
for line in out.stdout.splitlines():
line = line.strip()
if line:
rows.append(line.split("|"))
return rows
def _iso_to_epoch(iso: str) -> int:
"""ISO 时间 → unix 秒logs.created_at 是 epoch int。经 PG 自身转,避开本地 tz 依赖。"""
r = _psql("select extract(epoch from timestamptz '%s')::bigint" % iso)
return int(r[0][0])
def _batch_window(run_dir: str) -> tuple[str, str]:
"""从批 ledger.jsonl 的首/末 ts 推窗口(含前后各 2 分钟余量覆盖收尾调用)。"""
led = os.path.join(run_dir, "ledger.jsonl")
ts = []
with open(led, encoding="utf-8") as f:
for line in f:
try:
ts.append(json.loads(line)["ts"])
except Exception: # noqa: BLE001 - 跳过非法行
continue
if not ts:
raise RuntimeError("ledger 无可用 ts%s" % led)
return min(ts), max(ts)
def _client_estimate(run_dir: str):
"""读批 report.json 的 client-side 估算tokenUsage用于交叉校验。缺则 None。"""
rep = os.path.join(run_dir, "report.json")
if not os.path.exists(rep):
return None
try:
with open(rep, encoding="utf-8") as f:
data = json.load(f)
return (data.get("cost") or {}).get("tokenUsage")
except Exception: # noqa: BLE001
return None
def main() -> int:
ap = argparse.ArgumentParser(description="new-api 权威成本读取W4 成本侧①)")
ap.add_argument("--batch", help="批 run 目录(含 ledger.jsonl/report.json→ 推窗口+交叉校验")
ap.add_argument("--since", help="ISO 起(与 --until 配对)")
ap.add_argument("--until", help="ISO 止")
ap.add_argument("--minutes", type=float, help="最近 N 分钟")
ap.add_argument("--platform-token", default="",
help="标记为平台铺量的 token_name其余归创作者侧薄片②归属")
args = ap.parse_args()
client_est = None
if args.batch:
since_iso, until_iso = _batch_window(args.batch)
client_est = _client_estimate(args.batch)
since = _iso_to_epoch(since_iso) - 120
until = _iso_to_epoch(until_iso) + 120
window_desc = "%s 窗口 %s ~ %s±120s" % (args.batch, since_iso, until_iso)
elif args.since and args.until:
since = _iso_to_epoch(args.since)
until = _iso_to_epoch(args.until)
window_desc = "%s ~ %s" % (args.since, args.until)
elif args.minutes:
now = _psql("select extract(epoch from now())::bigint")[0][0]
until = int(now)
since = until - int(args.minutes * 60)
window_desc = "最近 %g 分钟" % args.minutes
else:
ap.error("需 --batch / (--since 且 --until) / --minutes 之一")
return 2
# type=2 = 消费日志new-api消费日志 type=2按 model × token_name 聚合权威 quota
sql = (
"select model_name, coalesce(token_name,'') tn, count(*), "
"coalesce(sum(quota),0), coalesce(sum(prompt_tokens),0), coalesce(sum(completion_tokens),0) "
"from logs where type=2 and created_at between %d and %d "
"group by model_name, tn order by 4 desc"
) % (since, until)
rows = _psql(sql)
yuan_per_quota = USD_CNY / QUOTA_PER_UNIT
tot_calls = tot_quota = tot_pt = tot_ct = 0
plat = {"calls": 0, "quota": 0}
crea = {"calls": 0, "quota": 0}
print("==== new-api 权威成本logs.quota含真实倍率 ====")
print("窗口:%s" % window_desc)
print("换算QuotaPerUnit=%g/USD库内无此 key编译默认× USD→¥=%g ⇒ ¥%.6g/quota【FX 为假设】"
% (QUOTA_PER_UNIT, USD_CNY, yuan_per_quota))
print("--- 按 model × token_name ---")
for model, tn, calls, quota, pt, ct in rows:
calls, quota, pt, ct = int(calls), int(quota), int(pt), int(ct)
tot_calls += calls; tot_quota += quota; tot_pt += pt; tot_ct += ct
bucket = plat if (args.platform_token and tn == args.platform_token) else crea
bucket["calls"] += calls; bucket["quota"] += quota
print(" %-18s / %-16s : %4d 次, %8d quota, ¥%.4f"
% (model, tn or "", calls, quota, quota * yuan_per_quota))
print("--- 合计 ---")
print(" %d 次调用, %d quota, %d prompt + %d completion token, **¥%.4f**"
% (tot_calls, tot_quota, tot_pt, tot_ct, tot_quota * yuan_per_quota))
if tot_calls:
print("%.0f quota/次 ≈ ¥%.5f/次" % (tot_quota / tot_calls, tot_quota / tot_calls * yuan_per_quota))
# 薄片②:平台铺量 vs 创作者成本归属
if args.platform_token:
print("--- 归属platform-token=%s---" % args.platform_token)
print(" 平台铺量 : %d 次, ¥%.4f" % (plat["calls"], plat["quota"] * yuan_per_quota))
print(" 创作者侧 : %d 次, ¥%.4f" % (crea["calls"], crea["quota"] * yuan_per_quota))
# 交叉校验:客户端估算 vs new-api 权威
if client_est:
print("--- 交叉校验client-side 估算 vs new-api 权威)---")
ce_cost = client_est.get("estCostYuan")
ce_tok = client_est.get("total_tokens")
print(" client 估算:¥%s%s token单价假设 ¥%s/M"
% (ce_cost, ce_tok, client_est.get("pricePerMTokenYuan")))
print(" new-api 权威:¥%.4f%d quota" % (tot_quota * yuan_per_quota, tot_quota))
print(" → 权威为准client 估算保留为 new-api 不可达时的兜底/交叉校验。")
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -316,6 +316,7 @@ def _render_markdown(rep):
% (round(tu["estCostYuan"] / max(1, rep["cost"].get("acceptCount", 1)), 4)))
elif tu is not None:
lines.append("- token 计量W4本批通道未返 usagecalls_counted=0无 token 数据——见 llm_client 计量覆盖率说明")
lines.append("- **权威成本W4 薄片①)**:上为 client-side 估算(单价假设);**new-api `logs.quota` 权威成本**(含真实倍率+生成/裁判分列)运行 `newapi_cost.py --batch <run_dir>`HJ-NEWAPI-BILL-001 薄片①)")
lines.append("")
lines.append("## 9. 金丝雀发布清单≤10/批)")
lines.append("")

View File

@ -44,7 +44,8 @@
结构:单款成本 = LLM 调用(设计 1 + 对抗 1 + 修复 ≤1 + 裁判 1 ≈ 4±1 次【确证:编排器流水线结构】× 每次 ~3-6K token【推断max_tokens=4096 上限,实际未计量】)+ 失败摊销 ×1.25【确证accept 80% 口径】+ 存储MB 级,可忽略)。
- 若单价 2 元/百万 token【假设·待网关账单】→ **≈ 0.03-0.08 元/款**;单价 ×10 也 <1 /
- **文本生成不是成本瓶颈**。真正未计入、可能咬人的两项:**素材生成 GPU**ComfyUI 未部署,零数据)与**人审兜底人力**——列为 W4 测量项,本模型不编数。
- **✅ token 计量埋点已就位2026-06-11**`orchestrator/llm_client.py` 累加全部 chat_json 调用的 usageprompt/completion/total + calls_counted 覆盖率),批报告「## 8. 成本」自动渲染总 token / 均 token/次 / 估算成本 / 单游戏成本;单价 `PRICE_PER_MTOKEN_YUAN=2.0` 为**假设占位**(环境变量 `NEWAPI_PRICE_PER_MTOKEN` 可覆盖,真实网关账单到位即改此一处自动校准全部估算)。**真实 token 数下次批跑自动采集**(历史批无 usage 数据,不回填)。单测 3 绿(累加/缺 usage 不臆造/成本估算)。⚠ 早期观察M2.7 推理型含 reasoning_content每次 token 可能高于本模型「3-6K」推断——以实采为准。
- **✅ token 计量埋点已就位2026-06-11**`orchestrator/llm_client.py` 累加 chat_json 的 usage批报告「## 8. 成本」渲染;单价 `PRICE_PER_MTOKEN_YUAN=2.0` 为假设占位。**此为客户端估算,已降为 fallback**(见下)。
- **✅✅ new-api `logs.quota` 权威成本已接通2026-06-11HJ-NEWAPI-BILL-001 薄片①,取代上面的客户端估算为权威源)**`orchestrator/newapi_cost.py` 直读 new-api PostgreSQL logs含真实模型倍率new-api 计费引擎算好),按 model×token_name 聚合、生成/裁判分列、平台铺量 vs 创作者归属、交叉校验客户端估算。**实测权威数merge-prod-2020 创意/57 调用/19 accept= ¥0.59 总、¥0.0103/次、约 ¥0.031/款**MiniMax-M2.7 53 次 ¥0.58 + deepseek-v4-flash 裁判 4 次 ¥0.005)——**确证文本生成成本可忽略**(与 §3 三线排序结论一致,自有端 IAA 万级 DAU 才回本,近期现金靠 B 端)。换算 `QuotaPerUnit=500000/USD`new-api 编译默认,库内无此 key× USD→¥ 假设 7.2。**历史批无需重跑即可读**new-api logs 已在库)。⚠ 评审整改per-creator 计量/支付/订阅按 `2026-06-11-newapi计费平面集成-review.md` v2 推迟到支付通道真实化后admin API 无法替他人铸 token + 支付通道 ICP/进件 3-5 周阻塞)。
## 6. 口径一致性勘误(随本拍板执行)