From c93e2e849462fe100e5fc4374f301d2b73b84b85 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Thu, 4 Jun 2026 14:18:27 +0200 Subject: [PATCH] fix(verify): PUNCT_END generico e frasi spezzate come warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PUNCT_END esteso con terminatori comuni in documenti tecnici: - $ e } per formule LaTeX inline e blocchi - > per tag HTML (, , ecc.) - \\ per line break LaTeX - · (punto centrato, U+00B7) per notazione matematica Caratteri unicode scritti con chr() per evitare SyntaxError Python 3.12+ con curly quotes nel sorgente. Frasi spezzate spostate da blockers a warnings: il chunker le risolve automaticamente con merge_broken_sentences; i casi residui sono perlopiù falsi positivi (frontespizi, entità proprie senza punto finale). Co-Authored-By: Claude Sonnet 4.6 --- chunks/verify_chunks.py | 44 ++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/chunks/verify_chunks.py b/chunks/verify_chunks.py index b20fac7..5a5a467 100644 --- a/chunks/verify_chunks.py +++ b/chunks/verify_chunks.py @@ -32,13 +32,27 @@ import config as cfg MIN_CHARS = cfg.MIN_CHARS MAX_CHARS = cfg.MAX_CHARS +_PUNCT_CLS = ( + "[.!?" + + chr(0xBB) + ")\\]" + + chr(0x2018) + chr(0x2019) + + chr(0x201C) + chr(0x201D) + + "\"'" + + chr(0x2014) + chr(0x2013) + chr(0x2026) + + chr(0xB7) # punto centrato LaTeX + + "]$" +) PUNCT_END = re.compile( - r"[.!?\xbb)\]'’\"“”‘—–…]$" - r"|/$" # URL che finisce con / - r"|\|$" # riga di tabella Markdown - r"|;$" # fine clausola legale - r"|:$" # introduzione a lista o formula - r"|\d[\d.,/]*$" # numero, anno, versione, riferimento normativo + _PUNCT_CLS + + r"|/$" + + r"|\|$" + + r"|;$" + + r"|:$" + + r"|\d[\d.,/]*$" + + r"|\$$" + + r"|\}$" + + r"|>$" + + r"|\\\\$" ) _HEX_END = re.compile(r"[0-9a-fA-F]{8,}$") _URL_TAIL = re.compile(r"(https?://|www\.)\S+(\s+\S+){0,3}$") @@ -312,13 +326,13 @@ def verify_stem(stem: str, project_root: Path, min_chars: int, max_chars: int) - print(f" → Causa probabile: sezioni senza testo nel clean.md") if incomplete: - print(f"\n 🔴 {len(incomplete)} chunk con FRASE SPEZZATA:") + print(f"\n 🟡 {len(incomplete)} chunk con FRASE SPEZZATA (warning):") for c in incomplete[:5]: last_line = c.get("text", "").rstrip().split("\n")[-1][-80:] print(f" [{c.get('chunk_id', '?')}] ...{last_line!r}") if len(incomplete) > 5: print(f" ... e altri {len(incomplete) - 5}") - print(f" → Soluzione: python chunks/fix_chunks.py --stem {stem}") + print(f" → Soluzione: correggi il sorgente .md e rigenera con chunker.py --force") # ── Warnings ────────────────────────────────────────────────────────────── @@ -350,12 +364,12 @@ def verify_stem(stem: str, project_root: Path, min_chars: int, max_chars: int) - print(f" [{e['chunk_id']}] ≡ [{e['duplicate_of']}] «{e['last_text'][:60]}»") if len(duplicates) > 5: print(f" ... e altri {len(duplicates) - 5}") - print(f" → Causa probabile: fix_chunks merge multipli o sezioni ripetute") + print(f" → Causa probabile: sezioni ripetute nel sorgente .md") # ── Report.json ─────────────────────────────────────────────────────────── - blockers = empty_chunks + no_prefix + malformed_prefix + body_empty + incomplete - warnings = too_short + too_long + incomplete_math + broken_tables + blockers = empty_chunks + no_prefix + malformed_prefix + body_empty + warnings = too_short + too_long + incomplete + incomplete_math + broken_tables verdict = "blocked" if blockers else ("warnings_only" if (warnings or duplicates) else "ok") @@ -418,18 +432,16 @@ def verify_stem(stem: str, project_root: Path, min_chars: int, max_chars: int) - print(f" python ingestion/ingest.py --stem {stem}") if too_short or too_long: print() - print(f" Per ottimizzare prima:") - print(f" python chunks/fix_chunks.py --stem {stem} --dry-run") - print(f" python chunks/fix_chunks.py --stem {stem}") + print(f" Per ottimizzare: correggi il sorgente .md e rigenera con --force") else: print(f" 🔴 {len(blockers)} problemi bloccanti — correggi prima di procedere:") if empty_chunks or body_empty: print(f" • chunk vuoti/senza corpo → controlla sources/{stem}/auto/{stem}_clean.md") if no_prefix or malformed_prefix: - print(f" • prefisso mancante/malformato → controlla gli heading in {stem}_clean.md") + print(f" • prefisso mancante/malformato → controlla gli heading in sources/{stem}.md") if incomplete: - print(f" • frasi spezzate → python chunks/fix_chunks.py --stem {stem}") + print(f" • frasi spezzate → correggi il sorgente e rigenera con --force") print() print(f" Dopo le correzioni:") print(f" python chunks/chunker.py --stem {stem} --force")