#!/usr/bin/env python3 """$0 сводка расхода сессии ДВУМЯ независимыми путями. Путь 1 — то, что каждый харнесс записал сам (`cost_usd` в своих артефактах). Путь 2 — пере-счёт из СЫРОГО `usage` по прайсу `models.yaml` (кэш-токены по своей ставке). Расхождение двух путей и есть проверка: одинаковые числа, полученные одним способом, — не проверка. """ from __future__ import annotations import json import sqlite3 from pathlib import Path import yaml REPO = Path("/home/ubuntu/projects/textmachine") PL = Path.home() / "books" / "gu-zhenren" / "promptlang" CUTOFF = "2026-07-31 18:40" # первая трата сессии prices = yaml.safe_load((REPO / "backend/configs/models.yaml").read_text(encoding="utf-8"))["models"] def px(model: str) -> tuple[float, float, float]: p = prices[model]["price"] return p["input_per_m"], p["output_per_m"], p.get("cached_per_m", 0.0) rows = [] # — движковые прогоны (задача A арм en; задача B арм a0 в боевой форме до стоп-гейта) for proj in ("A-ru", "A-en", "B-a0", "B-a1", "B-a2"): db = PL / proj / "db.sqlite" if not db.exists(): continue con = sqlite3.connect(db) for r in con.execute("select model_actual, prompt_tokens, cached_tokens, completion_tokens," " cost_usd from request_log where ts > ?", (CUTOFF,)): m, pt, cch, ct, self_cost = r if not m: continue pin, pout, pcached = px(m) recomputed = (pt - cch) / 1e6 * pin + cch / 1e6 * pcached + ct / 1e6 * pout rows.append(("движок " + proj, self_cost, recomputed)) con.close() # — сырые пробы провода и армы for d, label in ((PL / "wire", "wire-пробы"), (PL / "armsraw", "армы задачи B/A")): for f in sorted(d.glob("*.json")): if f.name == "fragments.json": continue j = json.loads(f.read_text(encoding="utf-8")) if "error" in j: continue pin, pout, pcached = px("deepseek-v4-flash") pt, cch, ct = j.get("prompt_tokens", 0), j.get("cached_tokens", 0), j.get("completion_tokens", 0) recomputed = (pt - cch) / 1e6 * pin + cch / 1e6 * pcached + ct / 1e6 * pout rows.append((label, j.get("cost_usd", 0.0), recomputed)) # — судьи (свои леджеры, свои модели; в judge_p4 их ДВА, маршрутизация по семейству пары) for sub, label in (("judge", "судья армов"), ("judge_p4", "судья пар P4")): led = PL / sub / "ledger.jsonl" if not led.exists(): continue for ln in led.read_text().splitlines(): j = json.loads(ln) if "error" in j: continue model = j.get("judge", "grok-4.3") pin, pout, _ = px(model) billed = j.get("out_billed") if billed is None: # judge_p4: reasoning у deepseek subset, у xai additive ct, rt = j.get("completion_tok", 0), j.get("reasoning_tok", 0) billed = ct + rt if model.startswith("grok") and rt > ct else ct recomputed = j.get("prompt_tok", 0) / 1e6 * pin + billed / 1e6 * pout rows.append((f"{label} {model}", j.get("cost_usd", 0.0), recomputed)) agg: dict[str, list[float]] = {} for label, a, b in rows: v = agg.setdefault(label, [0.0, 0.0, 0]) v[0] += a v[1] += b v[2] += 1 print(f"{'источник':<22}{'вызовов':>9}{'путь 1 ($)':>13}{'путь 2 ($)':>13}{'Δ':>12}") t1 = t2 = 0.0 for label, (a, b, n) in sorted(agg.items()): print(f"{label:<22}{n:>9}{a:>13.6f}{b:>13.6f}{a-b:>12.2e}") t1 += a t2 += b print(f"{'ИТОГО':<22}{sum(v[2] for v in agg.values()):>9}{t1:>13.6f}{t2:>13.6f}{t1-t2:>12.2e}") print(f"\nпотолок сессии $0.15 · использовано {t1/0.15*100:.1f}% · остаток ${0.15-t1:.6f}")