#!/usr/bin/env python3 """Проба C (промт POLYGON_EDITOR_WIRE_PROBE §C): фронтир-пасс по голду — «сколько докупает ум». Тот же терминолог-протокол, что research/24 §B3 (`consilium_probe.py`: ростер 63 строки, 5 батчей, engine-faithful RenderBatch, боевой terminologist.md, добавка CONF_NUM), против чемпиона glm-5 25/45. Кандидаты (пре-рег §0C, слаги live /models 04.08): deepseek-v4-pro · gpt-5.6-terra · gpt-5.6-luna. Цены — прайс-страница вендора, короткий контекст-тир (батчи ≈4.5k ток.). ⚠ Wire-квирк GPT-5.6 НЕ гадаем: `reasoning.mode`/`reasoning.effort` задокументированы для Responses API; плоский `reasoning_effort` в Chat Completions вендором не заявлен, а прецедент exp08 (xAI) — вложенная форма МОЛЧА ГЛОТАЕТСЯ. Поэтому `--wirecheck` меряет ручку живьём (none против high по reasoning_tokens) ДО основного прогона, и транспорт выбирается по замеру. """ from __future__ import annotations import argparse import json import os import sys import time from pathlib import Path from dotenv import load_dotenv from openai import OpenAI REPO = Path("/home/ubuntu/projects/textmachine") HERE = Path(__file__).resolve().parent RAW = Path.home() / "books" / "gu-zhenren" / "bank-arbitration" load_dotenv(REPO / "eval" / ".env") sys.path.insert(0, str(HERE)) from consilium_probe import (build_roster, batches_of, block_of, render_terminologist, # noqa: E402 CONF_NUM) from arbitrate import GOLD, parse_pass, eq # noqa: E402 CEILING_USD = 0.50 # потолок пробы C (промт §C) HARD_STOP_USD = 0.45 # пре-рег: живой стоп ниже потолка # Пер-модельные потолки вывода. Гард ПРОЕКЦИОННЫЙ: вызов не делается, если ХУДШИЙ его исход # (упор в cap) пробил бы CEILING_USD — иначе стоп «по факту» ловил бы уже потраченное. CAPS = {"deepseek-v4-pro": 16000, "gpt-5.6-luna": 16000, "gpt-5.6-terra": 8000} ORDER = ["deepseek-v4-pro", "gpt-5.6-luna", "gpt-5.6-terra"] # дешёвые первыми # (base_url, env, in, cached_in, out, openai_family) CAND = { "deepseek-v4-pro": ("https://api.deepseek.com/v1", "DEEPSEEK_API_KEY", 0.435, 0.003625, 0.87, False), "gpt-5.6-terra": ("https://api.openai.com/v1", "OPENAI_API_KEY", 2.00, 0.20, 12.00, True), "gpt-5.6-luna": ("https://api.openai.com/v1", "OPENAI_API_KEY", 0.20, 0.02, 1.20, True), } TAG = {"deepseek-v4-pro": "fdp", "gpt-5.6-terra": "fte", "gpt-5.6-luna": "flu"} WIRE = HERE / "gold" / "frontier_wire.json" # результат --wirecheck: как слать reasoning def client(model: str) -> OpenAI: base, env, *_ = CAND[model] return OpenAI(api_key=os.environ[env], base_url=base, timeout=900) def price(model: str, pt: int, cached: int, ct: int) -> float: _, _, pin, pcache, pout, _ = CAND[model] return (pt - cached) / 1e6 * pin + cached / 1e6 * pcache + ct / 1e6 * pout def usage_of(u): pt = getattr(u, "prompt_tokens", 0) or 0 ct = getattr(u, "completion_tokens", 0) or 0 cached = getattr(getattr(u, "prompt_tokens_details", None), "cached_tokens", 0) or 0 rt = getattr(getattr(u, "completion_tokens_details", None), "reasoning_tokens", 0) or 0 return pt, cached, ct, rt def call(model: str, system: str, user: str, tag: str, effort: str | None, cap: int) -> dict: _, _, _, _, _, is_openai = CAND[model] cl = client(model) msgs = [{"role": "system", "content": system}, {"role": "user", "content": user}] kw: dict = dict(model=model, messages=msgs) if is_openai: kw["max_completion_tokens"] = cap # quirks 00: не max_tokens if effort: # temperature не шлём (reasoning-модель) kw["reasoning_effort"] = effort else: kw["max_tokens"] = cap kw["temperature"] = 0 # v4-pro: reasoning-параметр НЕ шлём (low маппится в high; ручки бюджета нет) t0 = time.time() r = cl.chat.completions.create(**kw) ch = r.choices[0] pt, cached, ct, rt = usage_of(r.usage) cost = price(model, pt, cached, ct) rec = dict(tag=tag, model=model, model_returned=r.model, effort=effort or "default", finish=ch.finish_reason, ts=time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), cap=cap, prompt_tokens=pt, cached_tokens=cached, completion_tokens=ct, reasoning_tokens=rt, cost_usd=round(cost, 6), latency_s=round(time.time() - t0, 1), content=ch.message.content or "", system=system, user=user) (RAW / f"{tag}.json").write_text(json.dumps(rec, ensure_ascii=False, indent=1), encoding="utf-8") print(f"[{tag}] {model} eff={effort} finish={ch.finish_reason} in={pt}(c{cached}) out={ct}" f"(r{rt}) ${cost:.6f} {rec['latency_s']}s content={len(rec['content'])}c") return rec def cmd_slugcheck(): for model in CAND: base, env, *_ = CAND[model] ids = [m.id for m in OpenAI(api_key=os.environ[env], base_url=base, timeout=120).models.list().data] print(f"/models {model}: {'OK' if model in ids else 'ОТСУТСТВУЕТ'} (всего {len(ids)})") if model not in ids: raise SystemExit(f"слаг {model} не листится — СТОП+релей") def cmd_wirecheck(): """Живой замер ручки reasoning на OpenAI-кандидатах: none против high по reasoning_tokens. Ручка ЖИВАЯ ⇔ reasoning_tokens различаются. Плюс проверка, что Chat Completions вообще принимает.""" probe = "Ответь ровно одним словом: сколько будет 17*23?" out = {} total = 0.0 for model in CAND: if not CAND[model][5]: out[model] = {"transport": "chat", "effort": None, "note": "v4-pro: параметр не шлём (дефолт high)"} continue res = {} for eff in ("none", "high"): try: r = call(model, "Ты — калькулятор.", probe, f"wire-{TAG[model]}-{eff}", eff, 2000) res[eff] = r["reasoning_tokens"] total += r["cost_usd"] except Exception as e: res[eff] = f"ERR {type(e).__name__}: {str(e)[:200]}" live = isinstance(res.get("none"), int) and isinstance(res.get("high"), int) and res["none"] != res["high"] out[model] = {"transport": "chat" if live else "НЕ ПОДТВЕРЖДЕНО", "reasoning_tokens": res, "knob_live": live, "effort": "low" if live else None} print(f" ⇒ {model}: ручка {'ЖИВАЯ' if live else 'НЕ подтверждена'} {res}") WIRE.write_text(json.dumps(out, ensure_ascii=False, indent=1), encoding="utf-8") print(f"wirecheck стоил ${total:.6f}; вердикты → {WIRE}") def cmd_plan(): roster = build_roster() batches = batches_of(roster) print(f"ростер={len(roster)} строк, батчей={len(batches)}") # вход берём фактический из сырья §B3 (тот же промпт и те же батчи) est_in = sum(json.load(open(RAW / f"glm1-b{i}.json", encoding="utf-8"))["prompt_tokens"] for i in range(len(batches))) for model in CAND: for out_est in (5000, 15000, 40000): print(f" {model:18s} вход {est_in} ток. + выход {out_est:6d} → ${price(model, est_in, 0, out_est):.4f}") print(f"ПОТОЛОК ПРОБЫ $0.50; живой стоп харнесса ${HARD_STOP_USD}") def cmd_run(models: list[str]): wire = json.load(open(WIRE, encoding="utf-8")) if WIRE.exists() else {} roster = build_roster() batches = batches_of(roster) total = sum(json.load(open(f, encoding="utf-8"))["cost_usd"] for m in CAND for f in RAW.glob(f"{TAG[m]}-b*.json")) if total: print(f"уже потрачено пробой C по сырью: ${total:.6f}") for model in [m for m in ORDER if m in models]: eff = wire.get(model, {}).get("effort") cap = CAPS[model] for i, b in enumerate(batches): tag = f"{TAG[model]}-b{i}" if (RAW / f"{tag}.json").exists(): print(f"[{tag}] уже персистирован — пропуск") continue system, user = render_terminologist("\n\n".join(block_of(c) for c in b)) worst = price(model, len(user) // 2 + 2000, 0, cap) # худший исход ЭТОГО вызова if total + worst > CEILING_USD or total >= HARD_STOP_USD: print(f"СТОП (проекционный гард): накоплено ${total:.4f}, худший исход вызова " f"${worst:.4f} → пробил бы потолок ${CEILING_USD}. Остаток арма не гоню.") return total += call(model, system, user + CONF_NUM, tag, eff, cap)["cost_usd"] time.sleep(1.0) print(f"TOTAL пробы C: ${total:.6f}") def cmd_score(): gb = {r["bank_src"]: r for r in GOLD if r["in_bank"]} passes = {"glm1 (чемпион)": "glm1", "ds1 (flash low)": "ds1", "mis1": "mis1"} passes.update({m: TAG[m] for m in CAND}) acc, cost, rows = {}, {}, {} for name, tag in passes.items(): p = parse_pass(tag) if not p["map"]: continue acc[name] = {s for s, g in gb.items() if eq(p["map"].get(s, ("", None))[0], g["gold_dst"])} cost[name] = p["cost"] rows[name] = len(p["map"]) print("=== Точность против голда (45) и деньги пасса ===") for n in acc: print(f" {n:22s} {len(acc[n])}/45 строк={rows[n]} ${cost[n]:.6f} " f"пер-терм ${cost[n]/63:.6f}") print("\n=== Парное против чемпиона glm-5 (критерий пре-рега: ≥ +5) ===") base = acc.get("glm1 (чемпион)") for n in acc: if n == "glm1 (чемпион)": continue x, y = len(acc[n] - base), len(base - acc[n]) verdict = "ОПРАВДАН" if x - y >= 5 else "не оправдан" print(f" {n:22s} +{x} / -{y} (net {x-y:+d}) → {verdict}") print("\n=== Цена/книга (методика research/24 §B5: 2000 различных строк банка) ===") for n in acc: print(f" {n:22s} ${cost[n]/63*2000:.2f}/книга") print("\n=== Единицы: что кандидат берёт, а glm-5 теряет (и наоборот) ===") for n in acc: if n == "glm1 (чемпион)": continue for s in sorted(acc[n] - base): print(f" +{n} {s}: gold={gb[s]['gold_dst']!r}") for s in sorted(base - acc[n]): print(f" -{n} {s}: gold={gb[s]['gold_dst']!r}") def main(): ap = argparse.ArgumentParser() ap.add_argument("--slugcheck", action="store_true") ap.add_argument("--wirecheck", action="store_true") ap.add_argument("--plan", action="store_true") ap.add_argument("--run", nargs="*", default=None) ap.add_argument("--score", action="store_true") a = ap.parse_args() if a.slugcheck: cmd_slugcheck() elif a.wirecheck: cmd_wirecheck() elif a.plan: cmd_plan() elif a.run is not None: cmd_run(a.run or list(CAND)) elif a.score: cmd_score() else: ap.print_help() if __name__ == "__main__": main()