diff --git a/eval/dovodka/contour.py b/eval/dovodka/contour.py index 3459e180..0ef9fd75 100644 --- a/eval/dovodka/contour.py +++ b/eval/dovodka/contour.py @@ -134,6 +134,12 @@ EXCLUDED = { # и включается ЦЕЛИКОМ: риг, банк, бриф, письменность, теги и источник единиц разом. _PAIR = None _UNITS = None +# ⚠ БЮДЖЕТ ВЫВОДА — ПАРАМЕТР ОСИ. Умолчание рига 16000 хватает китайским единицам, но не японским: +# у DeepSeek размышление тарифицируется и считается ВНУТРИ бюджета, а японский текст плотнее на +# знак и даёт более длинный русский перевод. Замер 14.08: 3 клетки из 27 вернулись ПУСТЫМИ с +# `finish=length`, деньги списаны, содержимого нет. Клетка при этом СУЩЕСТВУЕТ файлом, поэтому +# счёт «9/9» выглядел полным — считались файлы, а не годное содержимое. +_MAX_OUT = 16000 def pair(): @@ -143,9 +149,12 @@ def pair(): return _PAIR -def configure(p, units_provider=None, base_draft_hook=None, base_a0_hook=None) -> None: +def configure(p, units_provider=None, base_draft_hook=None, base_a0_hook=None, + max_out: int | None = None) -> None: """Перевести контур на пару `p`. Для zh — проверенный no-op (селфтест это стережёт).""" - global _PAIR, _UNITS, BASE_DRAFT_HOOK, BASE_A0_HOOK + global _PAIR, _UNITS, BASE_DRAFT_HOOK, BASE_A0_HOOK, _MAX_OUT + if max_out: + _MAX_OUT = max_out BASE_DRAFT_HOOK, BASE_A0_HOOK = base_draft_hook, base_a0_hook _PAIR, _UNITS = p, units_provider B.configure_pair(SOURCE_VALUES_HOOK=p.values_in_source, @@ -570,7 +579,7 @@ def _buy(led, tg: str, msgs: list[dict], **extra) -> dict: вопрос владельцу**. Здесь она наконец механизирована. """ rec = MONEY.purchase(led, tg, FIXER, ED22.client(FIXER), - ROSTER.call_kwargs(FIXER, msgs), **extra) + ROSTER.call_kwargs(FIXER, msgs, max_out=_MAX_OUT), **extra) if rec.get("skipped"): raise SystemExit( f"⛔ ГАРД ОТКАЗАЛ на клетке {tg} — прогон ОСТАНОВЛЕН.\n" @@ -656,6 +665,25 @@ def cmd_run(which: list[str]) -> None: MONEY.report() +def quarantine_length(tags: list[str]) -> int: + """Увести клетки `finish=length` с пустым содержимым в карантин `.LENGTH.json`. + + ⚠ Не удалять: деньги за них списаны и обязаны остаться видимыми кассе (глоб считает + `dv-*.json`, и карантинное имя под него по-прежнему попадает). Удаление спрятало бы трату. + """ + n = 0 + for tg in tags: + f = OUT / f"{tg}.json" + if not f.exists(): + continue + rec = json.loads(f.read_text(encoding="utf-8")) + if rec.get("finish") == "length" and not (rec.get("content") or "").strip(): + f.rename(OUT / f"{tg}.LENGTH.json") + print(f" {tg}: пустая клетка (finish=length) → карантин") + n += 1 + return n + + def buy_bases(phase: str, us: list[dict], pfx: str, draft_arm: str, edit_arm: str, draft_model: str, edit_model: str, pair_key: str, law_fn, expect: float) -> None: """Черновик и боевая связка новой пары — общий механизм для всех неканонных осей. @@ -678,7 +706,8 @@ def buy_bases(phase: str, us: list[dict], pfx: str, draft_arm: str, edit_arm: st rec = MONEY.purchase(led, tg, draft_model, ED22.client(draft_model), ROSTER.call_kwargs(draft_model, PR.translator_msgs(u["source"], None, - pair=pair_key)), + pair=pair_key), + max_out=_MAX_OUT), role="translator", uid=u["uid"], arm=draft_arm) if rec.get("skipped"): raise SystemExit(f"⛔ ГАРД ОТКАЗАЛ на {tg} — СТОП и вопрос владельцу") @@ -694,7 +723,8 @@ def buy_bases(phase: str, us: list[dict], pfx: str, draft_arm: str, edit_arm: st ROSTER.call_kwargs(edit_model, PR.editor_msgs(u["source"], draft, law_fn(u["source"]), - pair=pair_key)), + pair=pair_key), + max_out=_MAX_OUT), role="editor", uid=u["uid"], arm=edit_arm) if rec.get("skipped"): raise SystemExit(f"⛔ ГАРД ОТКАЗАЛ на {tg} — СТОП и вопрос владельцу") diff --git a/eval/dovodka/en_axis.py b/eval/dovodka/en_axis.py index a60281e9..a96484b5 100644 --- a/eval/dovodka/en_axis.py +++ b/eval/dovodka/en_axis.py @@ -61,6 +61,9 @@ N_PER_STRATUM = 8 # 16 единиц — ровно то, ч DRAFT_MODEL = "deepseek-v4-flash" # тот же жилец-черновик, что на китайской оси EDITOR = "deepseek-v4-pro" # боевой редактор EDITOR2 = "glm-5" # второй редактор, H-4 +# ⚠ Английские единицы длиннее китайских (1600 знаков против 1100), и перевод длиннее — умолчания +# рига 16000 может не хватить так же, как не хватило японской оси (3 пустых клетки из 27). +MAX_OUT = 32000 def units() -> list[dict]: @@ -95,7 +98,7 @@ def base_e0(uid: str) -> str: def wire() -> None: """Единственная точка включения пары. Всё остальное — механизмы контура.""" C.configure(PARA.EN, units_provider=units, - base_draft_hook=base_draft, base_a0_hook=base_e0) + base_draft_hook=base_draft, base_a0_hook=base_e0, max_out=MAX_OUT) def _law(src: str) -> str | None: @@ -184,6 +187,17 @@ def cmd_floor() -> None: C.cmd_floor() +def cmd_refill() -> None: + """Карантин пустых клеток оси и повторная покупка с поднятым бюджетом вывода.""" + wire() + tags = ([f"dv-b-{a}-{u['uid']}" for u in units() for a in ("d0", "e0")] + + [C.tag(a, u["uid"]) for u in units() for a in ("E1", "E2", "E3", "E4")] + + [C.tag("E3", u["uid"], "crit") for u in units()] + + [C.tag("E6", u["uid"]) for u in units()]) + n = C.quarantine_length(tags) + print(f"в карантин ушло клеток: {n}") + + def cmd_dry() -> None: """Смета и наличие. $0. Цены — измеренные медианы фазы, а не догадки.""" wire() @@ -247,12 +261,19 @@ def cmd_selftest() -> int: and not any(str(MONEY.OUT) in str(f) for f in old), f"клеток эксп-21 {len(old)}") ck("базы берутся из клеток ФАЗЫ", C.BASE_DRAFT_HOOK is base_draft and C.BASE_A0_HOOK is base_e0) + got = [(a, u["uid"]) for u in us for a in ("E1", "E2", "E4") + if (MONEY.OUT / f"{C.tag(a, u['uid'])}.json").exists()] + empty = [f"{a}-{uid}" for a, uid in got if not text_of(C.tag(a, uid)).strip()] + empty += [f"D0-{u['uid']}" for u in us if not text_of(f"dv-b-d0-{u['uid']}").strip()] + empty += [f"E0-{u['uid']}" for u in us if not text_of(f"dv-b-e0-{u['uid']}").strip()] + ck("купленные клетки НЕПУСТЫ (файл ≠ годное содержимое)", not empty, + f"пустых {len(empty)}: {empty[:3]}") print(f"\n{'ОСЬ ГОТОВА К ПОКУПКЕ' if not bad else f'ПРОВАЛОВ: {bad}'}") return bad if __name__ == "__main__": a = sys.argv[1] if len(sys.argv) > 1 else "--dry" - fn = {"--dry": cmd_dry, "--bases": cmd_bases, "--arms": cmd_arms, "--floor": cmd_floor, "--e6": cmd_e6, + fn = {"--dry": cmd_dry, "--bases": cmd_bases, "--arms": cmd_arms, "--floor": cmd_floor, "--refill": cmd_refill, "--e6": cmd_e6, "--selftest": lambda: sys.exit(1 if cmd_selftest() else 0)}.get(a) fn() if fn else print(__doc__) diff --git a/eval/dovodka/ja_axis.py b/eval/dovodka/ja_axis.py index 8f3924ee..d87b433f 100644 --- a/eval/dovodka/ja_axis.py +++ b/eval/dovodka/ja_axis.py @@ -48,6 +48,9 @@ MONEY, PR, PARA = C.MONEY, C.PR, C.PARA DRAFT_MODEL = "deepseek-v4-flash" EDITOR = "deepseek-v4-pro" PFX = "dv-c" +# ⚠ Японский плотнее китайского на знак, а размышление DeepSeek тарифицируется ВНУТРИ бюджета: +# при умолчании рига 16000 три клетки из 27 вернулись пустыми с `finish=length`. +MAX_OUT = 32000 def units() -> list[dict]: @@ -79,7 +82,7 @@ def base_j0(uid: str) -> str: def wire() -> None: C.configure(PARA.JA, units_provider=units, - base_draft_hook=base_draft, base_a0_hook=base_j0) + base_draft_hook=base_draft, base_a0_hook=base_j0, max_out=MAX_OUT) def _law(src: str) -> str | None: @@ -97,6 +100,17 @@ def cmd_arms() -> None: C.cmd_run(["J2"]) +def cmd_refill() -> None: + """Карантин пустых клеток и повторная покупка с поднятым бюджетом вывода.""" + wire() + tags = [f"{PFX}-{a}-{u['uid']}" for u in units() for a in ("d0", "j0", "j2")] + n = C.quarantine_length(tags) + print(f"в карантин ушло клеток: {n}") + if n: + cmd_bases() + cmd_arms() + + def cmd_floor() -> None: """Свой шумовой пол оси — парой независимых генераций одного лечения. @@ -150,12 +164,16 @@ def cmd_selftest() -> int: bool(PARA.JA.source_script.search(us[0]["source"]))) ck("ось объявлена ОПИСАТЕЛЬНОЙ и это не отменяется арифметикой", "Д6 ja→ru разведка" in _load("power_ja", ZONE / "power.py").FORCED_DESCRIPTIVE) + empty = [f"{a}-{u['uid']}" for u in us for a, t in + (("D0", f"{PFX}-d0-{u['uid']}"), ("J0", f"{PFX}-j0-{u['uid']}"), + ("J2", C.tag("J2", u["uid"]))) if not text_of(t).strip()] + ck("клетки НЕПУСТЫ (файл ≠ годное содержимое)", not empty, f"пустых {len(empty)}: {empty[:3]}") print(f"\n{'РАЗВЕДКА ГОТОВА' if not bad else f'ПРОВАЛОВ: {bad}'}") return bad if __name__ == "__main__": a = sys.argv[1] if len(sys.argv) > 1 else "--dry" - fn = {"--dry": cmd_dry, "--bases": cmd_bases, "--arms": cmd_arms, "--floor": cmd_floor, + fn = {"--dry": cmd_dry, "--bases": cmd_bases, "--arms": cmd_arms, "--floor": cmd_floor, "--refill": cmd_refill, "--selftest": lambda: sys.exit(1 if cmd_selftest() else 0)}.get(a) fn() if fn else print(__doc__)