textmachine/eval/editor_tier/screen.py

164 lines
8.2 KiB
Python
Raw 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
"""ФАЗА A — СКРИН СИЛЬНОГО ТИРА. Платный (потолок $1.10), идёт через кассу пака.
Пороги НЕ свои: берутся у эксп-22 побайтно (`screen.PRICE_CAP/CYR_MIN/ECHO_MAX`, `gates`,
`verdict`). Порог, взятый из прошлого пака без правки, — единственная защита от подгонки состава
под желаемый результат, а состав здесь и есть предмет спора: пак существует потому, что эксп-22
срезал сильный тир.
⚠ ОСОБЫЙ РЕЖИМ `kimi-k3`, объявлен ДО покупок. По отношению «цена клетки / цена выхода» внутри
Kimi он ожидается ~$0.25/ед — вчетверо выше порога роли, — и `k2.6` уже отдал пустой выход на трёх
клетках из четырёх при 100% доли размышления. Полный скрин был бы покупкой предсказуемого провала
примерно за доллар. Берётся ОДНА клетка редакторской роли с жёстким потолком выхода: этого хватает
и на ценовой вердикт, и на второй замер отказного режима. Отдай он против ожидания годный дешёвый
выход — досняли бы полным скрином отдельным решением.
"""
from __future__ import annotations
import json
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 <имя>`. При прямом запуске своя
папка стоит в sys.path первой, и `import screen` находит ЭТОТ файл, а не скрин эксп-22 —
молчаливый само-импорт. Ловилось трижды подряд (ростер, касса, скрин), поэтому правило одно:
всё чужое — только явной загрузкой."""
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
TP = REPO / "eval" / "tenant_panel"
S22 = _load("screen22", TP / "screen.py")
MONEY = _load("money_et", ZONE / "money.py")
ROSTER = MONEY.ROSTER_ET
PHASE = "ФA"
NEW = list(ROSTER.NEW) # gpt-5.6-terra · kimi-k3 · grok-4.5 · glm-5.2
SPECIAL = {"kimi-k3": dict(units=1, roles=("editor",), max_out=4000)}
ROLES = ("translator", "editor")
def tag(model: str, role: str, uid: str) -> str:
return f"et-scr-{model}-{role}-{uid[:6]}"
def plan() -> list[tuple[str, str, dict]]:
"""Что будет куплено. Печатается ДО покупок и лежит во фризе."""
us = S22.units() # те же две единицы, что скринил эксп-22
out = []
for m in ROSTER.by_new_price():
sp = SPECIAL.get(m, {})
for role in sp.get("roles", ROLES):
for u in us[:sp.get("units", len(us))]:
out.append((m, role, u))
return out
def cmd_plan() -> None:
tot = 0.0
print(f"{'модель':16s}{'роль':12s}{'ед.':>4s}{'ожидание $':>12s}")
for m in ROSTER.by_new_price():
sp = SPECIAL.get(m, {})
n = len(sp.get("roles", ROLES)) * sp.get("units", len(S22.units()))
exp = ROSTER.EXPECT_PER_UNIT[m] * n
tot += exp
print(f"{m:16s}{'/'.join(sp.get('roles', ROLES)):12s}{n:4d}{exp:12.3f}"
+ (" ← особый режим" if m in SPECIAL else ""))
print(f"{'ИТОГО':16s}{'':12s}{'':4s}{tot:12.3f} потолок {MONEY.CEILINGS[PHASE]:.2f}")
if tot > MONEY.CEILINGS[PHASE]:
print("⛔ смета выше потолка фазы — СТОП, не резать молча")
raise SystemExit(1)
def cmd_run() -> None:
led = MONEY.Guarded(PHASE)
for m, role, u in plan():
t = tag(m, role, u["uid"])
if (MONEY.OUT / f"{t}.json").exists():
continue
msgs = S22.msgs_for(role, u)
kw = ROSTER.call_kwargs(m, msgs, max_out=SPECIAL.get(m, {}).get("max_out", 16000))
rec = MONEY.purchase(led, t, m, S22.client(m), kw, role=role, uid=u["uid"])
if rec is None:
print(f" {t}: не куплено")
continue
print(f" {t}: ${rec.get('cost_usd', 0):.5f} finish={rec.get('finish')} "
f"знаков={len(rec.get('content') or '')}")
MONEY.report()
def result() -> dict:
"""Вердикт по каждому кандидату — порогами эксп-22, без единой правки."""
res = {}
for m in NEW:
rows = []
for f in MONEY.OUT.glob(f"et-scr-{m}-*.json"):
rec = json.loads(f.read_text(encoding="utf-8"))
u = next((x for x in S22.units() if x["uid"] == rec.get("uid")), None)
rows.append(S22.gates(rec, u["source"] if u else ""))
v, why = S22.verdict(rows)
pt = [r["cost"] for r in rows if r["role"] == "translator"]
pe = [r["cost"] for r in rows if r["role"] == "editor"]
import statistics as st # noqa: PLC0415
res[m] = dict(verdict=v, why=why, n=len(rows),
price_translator=(st.median(pt) if pt else None),
price_editor=(st.median(pe) if pe else None),
reasoning_share=(max((r["reasoning_share"] for r in rows), default=0.0)),
han=sum(r["han_chars"] for r in rows))
return res
def cmd_score() -> None:
print(f"{'модель':16s}{'вердикт':>10s}{'ед.':>4s}{'$/ед transl':>13s}{'$/ед edit':>11s}"
f"{'размышл':>9s} причины")
for m, r in result().items():
print(f"{m:16s}{r['verdict']:>10s}{r['n']:4d}"
f"{(r['price_translator'] or 0):13.5f}{(r['price_editor'] or 0):11.5f}"
f"{r['reasoning_share']:9.1%} {'; '.join(r['why'])}")
ok = [m for m, r in result().items() if r["verdict"] == "прошёл"]
print(f"\nпрошли фазу A: {ok or ''}")
def selfcheck() -> int:
"""До покупок: пороги те же, план в потолке, теги уникальны."""
bad = 0
if (S22.PRICE_CAP, S22.CYR_MIN, S22.ECHO_MAX) != ({"translator": 0.02, "editor": 0.06},
0.5, 0.15):
print("[ПРОВАЛ] пороги эксп-22 изменились — состав пака стал бы подгонкой")
bad += 1
tags = [tag(m, r, u["uid"]) for m, r, u in plan()]
if len(tags) != len(set(tags)):
print("[ПРОВАЛ] теги покупок не уникальны — клетки перетрут друг друга")
bad += 1
if not all(t.startswith("et-scr-") for t in tags):
print("[ПРОВАЛ] тег вне глоба фазы — расход будет невидим кассе")
bad += 1
for m in NEW:
if m not in ROSTER.THINK_ET:
print(f"[ПРОВАЛ] боевая конфигурация размышления не объявлена: {m}")
bad += 1
if "kimi-k3" not in SPECIAL:
print("[ПРОВАЛ] особый режим kimi-k3 потерян")
bad += 1
print(f"скрин: клеток в плане {len(tags)} · провалов {bad}")
return bad
if __name__ == "__main__":
a = sys.argv[1:] or ["--plan"]
fn = {"--plan": cmd_plan, "--run": cmd_run, "--score": cmd_score,
"--selfcheck": lambda: sys.exit(1 if selfcheck() else 0)}.get(a[0])
if fn:
fn()
else:
print(__doc__)