textmachine/eval/exp12_blocks.py

104 lines
5.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
"""exp12 — реконструкция per-chunk инъекционных блоков глоссария из приёмочного store.
$0. Воспроизводит ровно то, что боевой редактор A1 (glm-5 mono) видел:
- editorConstraintBlock (renderEditorConstraintBlock, memory.go): CONFIRMED dst-формы,
строки «- «dst»», дедуп по dst. Заголовок editorConstraintHeader.
- bilingualGlossaryBlock (renderGlossaryBlock, memory.go): «src → dst» (+ ⟨проверить⟩
для не-CONFIRMED). Заголовок glossaryBlockHeader. Для билингв-армов (редактор видит src).
Активные ключи чанка = injected_ids(chunk) injected_ids(chunk0) для chunk>0 (аппрокс sticky
A5 внутри главы; выбранные главы — 2-чанковые, sticky несёт ровно активные chunk0).
Ключ injected_id = «src\x1fsense\x1fsince\x1funtil» → строка glossary по (src,sense,since,until).
CONFIRMED ⇔ status='approved' и окно since/until валидно для главы.
Выход: diag/blocks.json {(ch/chunk): {editor_constraint, bilingual_glossary, keys[]}}
"""
from __future__ import annotations
import json, sqlite3
from pathlib import Path
DIAG = Path("/home/ubuntu/books/gu-zhenren/diag")
DB = DIAG / "store-copy.db"
SEL = [5, 7, 14] # пре-регистрированный выбор (exp12_extract.py)
EDITOR_HEADER = ("КАНОНИЧЕСКИЕ ПЕРЕВОДЫ имён и терминов (в черновике эти сущности должны быть "
"переданы ИМЕННО этими формами — приводи к ним любые расхождения, склоняя по "
"контексту; не вводи иных вариантов и не меняй ничего другого):")
GLOSS_HEADER = ("ГЛОССАРИЙ (используй эти утверждённые переводы имён и терминов последовательно; "
"строки с пометкой ⟨проверить⟩ — неподтверждённые кандидаты):")
def main() -> None:
con = sqlite3.connect(DB)
con.row_factory = sqlite3.Row
# glossary lookup by (src,sense,since,until)
gl = {}
for r in con.execute("SELECT src,sense,since_ch,until_ch,dst,status FROM glossary"):
gl[(r["src"], r["sense"], r["since_ch"], r["until_ch"])] = (r["dst"], r["status"])
# injected_ids per selected chunk
inj = {}
for r in con.execute(
"SELECT chapter,chunk_idx,injected_ids FROM retrieval_state WHERE chapter IN (%s)"
% ",".join(map(str, SEL))
):
inj[(r["chapter"], r["chunk_idx"])] = json.loads(r["injected_ids"] or "[]")
con.close()
def keydec(k: str):
src, sense, since, until = k.split("\x1f")
return (src, sense, int(since), int(until))
out = {}
for ch in SEL:
chunks = sorted(c for (cc, c) in inj if cc == ch)
chunk0 = inj.get((ch, 0), [])
for c in chunks:
active = list(inj.get((ch, c), []))
if c > 0: # sticky: carry chunk0's exact matches (A5 scene inertia within chapter)
for k in chunk0:
if k not in active:
active.append(k)
# map to records
recs = []
for k in active:
src, sense, since, until = keydec(k)
dst, status = gl.get((src, sense, since, until), ("", "auto"))
if not dst.strip():
continue
confirmed = (status == "approved") # windows all 0/0 here → spoiler-valid
recs.append({"src": src, "dst": dst.strip(), "confirmed": confirmed})
# editor constraint block: CONFIRMED only, dedup by dst
seen, elines = set(), []
for r in recs:
if not r["confirmed"] or r["dst"] in seen:
continue
seen.add(r["dst"]); elines.append(f"- «{r['dst']}»")
editor_block = (EDITOR_HEADER + "\n" + "\n".join(elines)) if elines else ""
# bilingual glossary block: src → dst (+ ⟨проверить⟩ if not confirmed)
glines = []
for r in recs:
line = f"{r['src']}{r['dst']}"
if not r["confirmed"]:
line += " ⟨проверить⟩"
glines.append(line)
gloss_block = (GLOSS_HEADER + "\n" + "\n".join(glines)) if glines else ""
out[f"{ch}/{c}"] = {
"editor_constraint": editor_block,
"bilingual_glossary": gloss_block,
"n_constraint_terms": len(elines),
"n_gloss_terms": len(glines),
}
(DIAG / "blocks.json").write_text(json.dumps(out, ensure_ascii=False, indent=1), encoding="utf-8")
for k, v in out.items():
print(f"\n=== chunk {k}: {v['n_constraint_terms']} constraint / {v['n_gloss_terms']} gloss terms ===")
print(v["editor_constraint"][:400])
if __name__ == "__main__":
main()