126 lines
7.7 KiB
Python
126 lines
7.7 KiB
Python
#!/usr/bin/env python3
|
||
"""КАССА ПАКА «СИЛЬНЫЙ ТИР». $0 сама по себе; через неё идут ВСЕ платные вызовы пака.
|
||
|
||
Не копия кассы эксп-22, а её НАСТРОЙКА. Механизм там проверенный и дорого доставшийся — фриз-гейт,
|
||
атомарный замок клетки, проекционный сторож перед каждым вызовом, два независимых пути счёта, — и
|
||
копирование размножило бы будущие расхождения вместо того, чтобы их ловить. Пак-специфика (зона
|
||
кода, каталог сырья, теги фаз, потолки, прайс-таблица) подставляется через `money.configure`.
|
||
|
||
Потолки — санкция владельца 09.08: пак ≤$4.00. Фазовые суммируются в $3.35, разница — объявленный
|
||
резерв на ре-гены эхо-мины и ретраи, а не запас «на всякий случай»: тратится он только через те же
|
||
теги и виден в отчёте.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||
ZONE = Path(__file__).resolve().parent
|
||
sys.path.insert(0, str(ZONE))
|
||
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 roster` находил ростер эксп-22: три
|
||
`sys.path.insert(0, …)` подряд кладут ПОСЛЕДНИЙ вставленный первым, и своя папка оказывалась
|
||
в хвосте. Поймано печатью селфтеста — «моделей 11» вместо 15, то есть проверка «цена считается
|
||
для ВСЕХ моделей» молча проверяла только старые."""
|
||
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
|
||
|
||
|
||
# ⚠ И касса эксп-22 тоже грузится ПО ПУТИ. `import money` здесь работал лишь по счастливому
|
||
# порядку sys.path: стоило вызвать модуль из файла, который кладёт свою папку первой, как он
|
||
# находил САМ СЕБЯ и падал циклическим импортом. Правило без исключений: чужое — только _load.
|
||
M22 = _load("money22", REPO / "eval" / "tenant_panel" / "money.py")
|
||
ROSTER_ET = _load("roster_et", ZONE / "roster.py")
|
||
|
||
OUT = Path.home() / "books" / "editor-tier"
|
||
|
||
# Теги покупок по фазам. ⚠ Новый тег вписывается СЮДА тем же коммитом, что и код, который его
|
||
# заводит: расход мимо глоба невидим для кассы, и эксп-21 напоролся на это трижды.
|
||
TAGS = {
|
||
"ФA": ("et-scr-*.json",), # скрин сильного тира
|
||
"ФB": ("et-unit-*.json",), # 16 новых единиц для границы glm-5 (черновик + R0 + R2)
|
||
"ФC": ("et-ed-*.json", "et-floor-*.json"), # панель + ШУМОВОЙ ПОЛ своего прохода
|
||
}
|
||
# ⚠ Потолок ФC поднят $1.80 → $2.30 из ОБЪЯВЛЕННОГО В ПЛАНЕ резерва пака ($0.65), объявляется до
|
||
# покупок фазы. Две причины, обе замерные: пре-рег-критерий пропустил ТРОИХ (панель $1.844 вместо
|
||
# сметы на двоих) и плану нужен СВОЙ шумовой пол — вторая независимая генерация самого дешёвого
|
||
# арма панели, ещё $0.29. Пакетный потолок $4.00 (санкция владельца) НЕ двигается: смета пака $2.93.
|
||
CEILINGS = {"ФA": 1.10, "ФB": 0.45, "ФC": 2.30}
|
||
PACK_CEILING = 4.00
|
||
|
||
M22.configure(ZONE=ZONE, OUT=OUT, TAGS=TAGS, CEILINGS=CEILINGS,
|
||
PACK_CEILING=PACK_CEILING, GLOB_ALL="et-*.json", ROSTER=ROSTER_ET)
|
||
|
||
# Всё остальное берётся у настроенной кассы — своих реализаций тут нет намеренно.
|
||
Guarded, Pack, purchase, model_expect = M22.Guarded, M22.Pack, M22.purchase, M22.model_expect
|
||
|
||
|
||
def report() -> None:
|
||
print(f"{'фаза':6s}{'потрачено':>12s}{'потолок':>10s}")
|
||
tot = 0.0
|
||
for ph in TAGS:
|
||
sp = Guarded(ph).spent()
|
||
tot += sp
|
||
mark = " ⛔ ПРОБОЙ" if sp > CEILINGS[ph] + 1e-9 else ""
|
||
print(f"{ph:6s}{sp:12.6f}{CEILINGS[ph]:10.2f}{mark}")
|
||
print(f"{'ПАК':6s}{tot:12.6f}{PACK_CEILING:10.2f}")
|
||
|
||
|
||
def selftest() -> int:
|
||
"""Обязан пройти ДО первой покупки пака."""
|
||
bad = 0
|
||
# 1. касса смотрит в СВОЁ сырьё, а не в сырьё эксп-22
|
||
if M22.OUT != OUT:
|
||
print("[ПРОВАЛ] касса не пере-настроена на своё сырьё")
|
||
bad += 1
|
||
if M22.BUY.OUT != OUT:
|
||
print("[ПРОВАЛ] buy.OUT указывает мимо каталога пака — покупки уедут к соседу")
|
||
bad += 1
|
||
# 2. цена считается для ВСЕХ моделей, включая новые (эксп-22 упал тут на середине скрина)
|
||
for m in ROSTER_ET.CANDIDATES:
|
||
try:
|
||
if not M22.BUY.cost(m, 4500, 0, 2500, 1000, 8000) > 0:
|
||
raise ValueError(m)
|
||
except Exception as e: # noqa: BLE001
|
||
print(f"[ПРОВАЛ] цена не считается: {m} ({type(e).__name__}: {e})")
|
||
bad += 1
|
||
# 3. в таблице кассы ДОЛЖНЫ быть новые кандидаты — иначе подставился чужой ростер
|
||
missing = [m for m in ("gpt-5.6-terra", "kimi-k3", "grok-4.5", "glm-5.2")
|
||
if m not in ROSTER_ET.CANDIDATES]
|
||
if missing:
|
||
print(f"[ПРОВАЛ] в таблице кассы нет новых кандидатов: {missing}")
|
||
bad += 1
|
||
|
||
# 4. потолки фаз не превышают пакетный
|
||
if sum(CEILINGS.values()) > PACK_CEILING + 1e-9:
|
||
print("[ПРОВАЛ] сумма фазовых потолков выше пакетного")
|
||
bad += 1
|
||
# 5. КАЖДЫЙ тег покрыт глобом GLOB_ALL — иначе покупка невидима для `model_expect`
|
||
for globs in TAGS.values():
|
||
for g in globs:
|
||
if not g.startswith("et-"):
|
||
print(f"[ПРОВАЛ] тег вне шаблона пака: {g}")
|
||
bad += 1
|
||
# 6. фриз-гейт обязан отказывать коду вне зоны пака
|
||
import inspect # noqa: PLC0415
|
||
if M22.ZONE != ZONE:
|
||
print("[ПРОВАЛ] фриз-гейт сторожит чужую зону")
|
||
bad += 1
|
||
_ = inspect
|
||
print(f"касса: тегов {sum(len(v) for v in TAGS.values())} · моделей "
|
||
f"{len(ROSTER_ET.CANDIDATES)} · провалов {bad}")
|
||
return bad
|
||
|
||
|
||
if __name__ == "__main__":
|
||
if "--selftest" in sys.argv:
|
||
sys.exit(1 if selftest() else 0)
|
||
report()
|