V2: Models, Ops & Remaining Polish (llama.cpp backend, phonetic match, CUDA-gating fix, signs/font audit) #2
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/v2-models-ops"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implements the V2: Models, Ops & Remaining Polish spec (
specs/v2-models-ops/) — the ~35review items deferred from V1. Executed and reviewed unit-by-unit (6 units, one whole-branch review),
grouped by the plan's Phase A–D. Depends on V1 (merged to main first).
Phase A — Models & Accuracy (repair.py, glossary.py, generate.py)
llm()now dispatches onREPAIR_BACKEND(ollama|llamacpp); allamacppbackend posts toREPAIR_LLAMACPP_URL(/completionschema). Split connect/read timeouts (REPAIR_TIMEOUT_CONNECT/
_READ) via stdlibhttp.client; per-linelatency_msin the repair CSV;repair-summary.json.REPAIR_MODEL_SECONDARY(no-op when equal to primary).jellyfish.metaphone), gated on non-English tokens, degradesgracefully if
jellyfishis absent.word_probsindubtitles.conf.json(optional) + a low-prob-word targeting gate inis_target().WHISPER_AUDIO_FILTERhighpass+compand pre-filter inextract_wav(). See "⚠️ Deploy decision" below.WHISPER_MODELis env-configurable,default stays
large-v3.Phase B — Shell & Ops (shell scripts, data/, common.py, plex_refresh.py)
set -eingen_loop.sh; removed the self-healingapt-getfrommerge_pass.sh(fail loud instead).data/extras.txt, read by bothcommon.load_extras()andshell/lib.sh::extras_grep_pattern(all 4 consumers), with inline fallbacks..gitignorepipeline artifacts;plex_refresh.pyenv hardening (also cleared the last 2 tree-wide ruff errors).Phase C — Python Polish (mux.py, repair.py, generate.py, glossary_verify.py, ordering.py, reflow.py, …)
partners()inode cache, explicitHL_ROOTSdefault, dropped redundantsamefile(), removedthe
verify()half-size heuristic (duration-tolerance is the sole truncation canary — a truncationtest was added), identify() reuse.
lastrun.json; CUDA error gating fixed — gates onisinstance(RuntimeError)instead of substring-matching
"cuda", so a non-GPU error no longer permanently poisons an episode(it clears
.failand logscrash.jsonfor retry).glossary_verify.adjudicate()parallelized (VERIFY_WORKERS, default 4);ordering.read_start()default fixed;
anime_library.sh --dry-run; COMMON/BLOCKLIST moved todata/with inline fallback;cross-file
os.chown-failure logging; Authorization sections filled in the older specs; reflow readability.Phase D — Signs/songs + font audit (dub_signs_merge.py, mux.py)
WrapStyledifferences, resolution mismatch (WARN-ONLY —no track drop / no coordinate transform, deferred to V3), and a forced
ScaledBorderAndShadow: yes.mux.verify()font-attachment audit — compares source vs muxed font counts (returnsfont-count-mismatch), strictly additive (never a false"ok").Notable decisions
ordering.read_start()(C4) — the literal task said "no priority file → return 0", but that wouldhave silently dropped the existing
SEASON_STARTenv override. KeptSEASON_STARTworking and madethe "watch-order disabled" log fire only when the result truly resolves to 0. (User-adjudicated.)
verify()half-size removal (C16) — confirmed the duration-tolerance check runs unconditionally onthe sole success path before removing the size proxy; added a truncated-output regression test.
mkvmerge -Jfield (tracksw/type=="attachments"); corrected to the real top-levelattachmentsarray (else it'd be a permanent no-op).Dockerfile.builderneverCOPY'dcommon.py(added in V1) → thecontainer would
ImportErrorat startup. Now copiescommon.py,data/, andshell/. A rebuild ofthe builder image is required on deploy regardless of this PR.
⚠️ Deploy decision required — WHISPER_AUDIO_FILTER
The A8 default applies a
highpass=f=80,compand=...filter to every new transcription going forward(already-stamped episodes are untouched). This is the spec's intent but is unverified on real audio.
Please make a go/no-go before rebuilding. To opt out with zero code change, set
WHISPER_AUDIO_FILTER="".Deploy notes
Dockerfile.builder(closes the V1common.pyImportError; shipsdata/+shell/+jellyfish).wiring only if you want to change defaults:
REPAIR_BACKEND,REPAIR_LLAMACPP_URL,REPAIR_MODEL_SECONDARY,REPAIR_TIMEOUT_CONNECT/READ,VERIFY_WORKERS,WHISPER_AUDIO_FILTER,SEASON_PRIORITY_FILE,HARDLINK_ROOTS.llama.cpp backend (192.168.1.232:8080), and a real
container_run.shend-to-end sweep.Test plan
pytest -q→ 193 passed (baseline 122 at V2 start; +71 across the 6 units). Run viartk proxy python -m pytest tests/(this shell's RTK hook otherwise mangles pytest output).ruff check .→ 0 errors whole-tree (V1 had left 2 inplex_refresh.py; B11 cleared them).verify()font/truncation paths andthe CUDA-gating retry-vs-poison branches have dedicated tests.
Known minor follow-ups (non-blocking)
mux.partners()/HL_ROOTS/DELETE_BROKENare dead code on the current hardlink-safe path (polished butunused);
llm_llamacppignores its model arg, so two-pass underREPAIR_BACKEND=llamacppre-runs the samemodel (wasted compute, not incorrect). Both documented for a later pass.
🤖 Generated with Claude Code
has_low_prob_word(c) returns True if any value in c.get("word_probs", []) (V2 A6) is below LOW_WORD_PROB (0.25) -- catches a single badly-mis-heard word hiding inside a card whose avg_logprob otherwise looks fine. Added as an OR condition in is_target() alongside the existing avg_logprob check. Missing/empty word_probs (conf.json predating A6) -> False, backward-compatible with the existing avg_logprob/name_suspect gate.Bare os.environ["PLEX_URL"]/["PLEX_TOKEN"] raised an opaque KeyError if unset. Now os.environ.get(...) + sys.exit("PLEX_URL not set") / "PLEX_TOKEN not set". Also splits the combined `import os, sys, urllib.parse, urllib.request` line into one-per-line (already alphabetical, so no reordering needed) -- these were the last 2 ruff errors in the tree; `ruff check .` is now 0 errors whole-tree.generate.py::process(), repair.py::process(), and dub_signs_merge.py:: process_one() each had a bare "except OSError: pass" after os.chown -- replaced with log(f"chown failed for {p}: {e}") in all three so a permission/ownership problem shows up in the run log instead of disappearing. mux.py's os.chown is already covered by process()'s outer except-and-log handler -- no bare-pass instance to fix there.