textmachine/eval/editor_tier/itog23.py

199 lines
9.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""ПЕРЕ-СЧЁТ ВСЕХ НЕСУЩИХ ЧИСЕЛ ПАКА ИЗ СЫРЬЯ. $0.
Число, которое нельзя пере-снять командой из репозитория, ничем не отличается от числа по памяти.
Здесь каждый блок ИТОГа печатается заново из персистированных артефактов, деньги — двумя
независимыми путями (касса и пере-счёт из сырых `usage` × прайс).
Запуск: eval/.venv/bin/python eval/editor_tier/itog23.py
"""
from __future__ import annotations
import json
import statistics as st
import sys
from pathlib import Path
REPO = Path("/home/ubuntu/projects/textmachine")
ZONE = Path(__file__).resolve().parent
sys.path.insert(0, str(REPO / "eval" / "tenant_panel"))
sys.path.insert(0, str(REPO / "eval" / "role_topology"))
def _load(name: str, path: Path):
import importlib.util # noqa: PLC0415
spec = importlib.util.spec_from_file_location(name, path)
mod = importlib.util.module_from_spec(spec)
sys.modules[name] = mod
spec.loader.exec_module(mod)
return mod
JU = _load("judge_et", ZONE / "judge.py")
PAN, SCR, MONEY, MAT = JU.PAN, JU.PAN.SCR, JU.MONEY, JU.MAT
ROSTER = SCR.ROSTER
OUT = MONEY.OUT
def h(t: str) -> None:
print(f"\n═══ {t} ═══")
def material() -> None:
h("МАТЕРИАЛ")
old, new = JU.P22.M.units_zh(), MAT.units_new()
print(f" единиц эксп-22 (приколоты манифестом) {len(old)} · глав "
f"{sorted(u['chapter'] for u in old)}")
ln = sorted(len(u["source"]) for u in new)
print(f" НОВЫХ единиц {len(new)} · глав {sorted(u['chapter'] for u in new)}")
print(f" знаков {ln[0]}{ln[-1]} медиана {ln[len(ln) // 2]} · "
f"макс. близость {JU.P22.M._pairwise_max_sim(new):.3f}") # noqa: SLF001
print(f" снято гейтом прав из своего пула: {MAT.rights_dropped(set()) or ''}"
if False else f" пересечений с эксп-22: "
f"{len({u['uid'] for u in new} & {u['uid'] for u in old})}")
def screen() -> None:
h("ФАЗА A — СКРИН СИЛЬНОГО ТИРА (пороги эксп-22, не свои)")
rs = SCR.result()
print(f" {'модель':16s}{'вердикт':>10s}{'ед.':>4s}{'$/ед edit':>11s}{'размышл':>9s} причины")
for m, r in rs.items():
print(f" {m:16s}{r['verdict']:>10s}{r['n']:4d}{(r['price_editor'] or 0):11.5f}"
f"{r['reasoning_share']:9.1%} {'; '.join(r['why'])}")
print(f" прошли: {[m for m, r in rs.items() if r['verdict'] == 'прошёл']}")
print(" СПРАВКА (полный критерий эксп-22, с переводческим порогом, которого нет в пре-реге):")
for m, r in rs.items():
print(f" {m:16s}{r['verdict_full22']:>10s} $/ед transl "
f"{(r['price_translator'] or 0):.5f}")
def judged(pass_: str) -> None:
h(f"СУДЕЙСТВО, проход {pass_}")
JU.setup(pass_)
import absjudge as AJ # noqa: PLC0415
if not AJ._by_unit(): # noqa: SLF001
print(" голосов нет")
return
AJ.controls()
print()
AJ.score()
def positional() -> None:
h("ПОЗИЦИОННЫЙ НАКЛОН — ЗАМЕРЕН И ВЫЧТЕН (требование ЗАКОНа)")
for pass_ in JU.PASSES:
JU.setup(pass_)
import absjudge as AJ # noqa: PLC0415
if not AJ._by_unit(): # noqa: SLF001
continue
print(f" --- {pass_} ---")
AJ.position()
print()
def money_two_ways() -> None:
h("ДЕНЬГИ — ДВА НЕЗАВИСИМЫХ ПУТИ")
led = {ph: MONEY.Guarded(ph).spent() for ph in MONEY.TAGS}
def indep(rec: dict) -> float:
m = rec.get("model")
if m not in ROSTER.CANDIDATES:
return rec.get("cost_usd", 0.0)
_, _, pin, pc, pout, _t, _s = ROSTER.CANDIDATES[m]
pt, cd = rec.get("prompt_tokens", 0), rec.get("cached_tokens", 0)
out = ROSTER.billed_output(m, rec.get("completion_tokens", 0),
rec.get("reasoning_tokens", 0),
rec.get("total_tokens", 0), pt)
return (pt - cd) / 1e6 * pin + cd / 1e6 * pc + out / 1e6 * pout
tot2 = {ph: 0.0 for ph in led}
empty = []
for ph, globs in MONEY.TAGS.items():
for g in globs:
for f in OUT.glob(g):
r = json.loads(f.read_text(encoding="utf-8"))
tot2[ph] += indep(r)
if r.get("cost_usd", 0) > 0 and not (r.get("content") or "").strip():
empty.append((f.name, r["cost_usd"]))
print(f" {'фаза':6s}{'касса':>12s}{'пере-счёт':>12s}{'расхождение':>13s}{'потолок':>10s}")
for ph in MONEY.TAGS:
mark = " ⛔ ПРОБОЙ" if led[ph] > MONEY.CEILINGS[ph] + 1e-9 else ""
print(f" {ph:6s}{led[ph]:12.6f}{tot2[ph]:12.6f}{led[ph] - tot2[ph]:13.6f}"
f"{MONEY.CEILINGS[ph]:10.2f}{mark}")
print(f" {'ПАК':6s}{sum(led.values()):12.6f}{sum(tot2.values()):12.6f}"
f"{sum(led.values()) - sum(tot2.values()):13.6f}{MONEY.PACK_CEILING:10.2f}")
if empty:
t = sum(c for _n, c in empty)
print(f" оплачено за ПУСТОЙ выход: {len(empty)} вызовов ${t:.5f} "
f"({t / max(1e-9, sum(led.values())) * 100:.1f}% пака)")
for n, c in sorted(empty, key=lambda x: -x[1]):
print(f" {n} ${c:.5f}")
def _cost(f: Path) -> float:
return json.loads(f.read_text(encoding="utf-8")).get("cost_usd", 0.0) if f.exists() else 0.0
def economics() -> None:
"""⚠ Строка R0 считается ИЗ СЫРЬЯ эксп-22, а не через `hasattr(P22,"edit_cost")`.
Прежняя редакция спрашивала у эксп-22 метод, которого у него нет (есть только `draft_cost`), —
строка молча выходила нулевой и отфильтровывалась, а в отчёт попадали числа, снятые где-то ещё
и друг с другом не сходившиеся (0.00545 × 1500 = $8.18, напечатано $13.45). Поймано приёмкой.
Колонки печатаются ДВУМЯ семействами: редактура отдельно и СВЯЗКА черновик+редактор — сравнивать
связку одного арма с редактурой другого нельзя, а именно это отчёт и делал.
"""
h("ЦЕНА РЕДАКТОРСКОЙ КЛЕТКИ, p50 И p95 ПО ЕДИНИЦАМ")
us = JU.P22.M.units_zh()
T22 = JU.P22.OUT
base = [_cost(T22 / f"{JU.P22.tag_draft(PAN.ANCHOR, u['uid'])}.json") for u in us]
rows = [("R0 " + PAN.BATTLE,
[_cost(T22 / f"{JU.P22.tag_edit(PAN.ANCHOR, u['uid'])}.json") for u in us])]
for i, m in enumerate(PAN.survivors(), start=1):
rows.append((f"T{i} {m}", [_cost(OUT / f"{PAN.tag_c(m, u['uid'])}.json") for u in us]))
def q(cs: list[float], p: int) -> float:
s = sorted(c for c in cs if c > 0)
return s[min(len(s) - 1, -(-p * len(s) // 100) - 1)] if s else 0.0
print(f" {'арм':26s}{'ед.':>5s}{'p50 edit':>10s}{'p95 edit':>10s}"
f"{'p50 связка':>12s}{'книга p50':>12s}{'книга p95':>12s}")
for name, cs in rows:
nz = [c for c in cs if c > 0]
if not nz:
continue
link = [c + b for c, b in zip(cs, base, strict=True) if c > 0 and b > 0]
print(f" {name:26s}{len(nz):5d}{st.median(nz):10.5f}{q(nz, 95):10.5f}"
f"{(st.median(link) if link else 0):12.5f}"
f"{(st.median(link) if link else 0) * 1500:12.2f}{q(link, 95) * 1500:12.2f}")
print(f" якорный черновик (общая база связки): p50 {st.median([b for b in base if b > 0]):.5f}"
if any(base) else " якорного черновика в сырье нет")
def agent_runs() -> None:
h("АГЕНТ-ЗАПУСКИ")
for p in JU.PASSES:
v = OUT / f"aj-{p}-votes"
t = OUT / f"aj-{p}-tasks"
print(f" {p:8s} заданий {len(list(t.glob('*.txt'))) if t.exists() else 0} · "
f"голосов принято {len(list(v.glob('*.json'))) if v.exists() else 0}")
def main() -> None:
print("ИТОГ ПАКА «СИЛЬНЫЙ ТИР» — ВСЕ НЕСУЩИЕ ЧИСЛА ИЗ СЫРЬЯ ($0)")
material()
screen()
economics()
for p in JU.PASSES:
try:
judged(p)
except Exception as e: # noqa: BLE001
print(f"\n═══ СУДЕЙСТВО {p} ═══\n не считается: {type(e).__name__}: {e}")
positional()
money_two_ways()
agent_runs()
print("\nВСЁ ВЫШЕ ПЕРЕ-СНЯТО ИЗ СЫРЬЯ.")
if __name__ == "__main__":
main()