Implementa validate() che controlla chunk_id duplicati, fence rotti,
size compliance (esclusi overflow) e calcola metriche aggregate.
Fix fixture cfg: min_chars=40 per allinearla ai chunk da 50 chars nel test.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PUNCT_END esteso con terminatori comuni in documenti tecnici:
- $ e } per formule LaTeX inline e blocchi
- > per tag HTML (</table>, </td>, 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 <noreply@anthropic.com>
Input: sources/<stem>_output/auto/<stem>.md (fallback: sources/<stem>.md)
Output: chunks/<stem>/chunks.json + meta.json
Regole implementate:
- 1 paragrafo = 1 chunk; paragrafi di contesti diversi non si mescolano
- split a confine di frase se paragrafo > MAX_CHARS (mai a metà frase)
- merge_short(): paragrafi < MIN_CHARS fusi col successivo (stesso contesto)
- merge_broken_sentences(): chunk consecutivi con frase spezzata vengono
uniti automaticamente se stesso contesto e corpo senza punteggiatura finale
- parse_paragraphs(): skip sezioni via SKIP_HEADINGS (prefisso case-insensitive)
e skip contenuto pre-heading via SKIP_PRE_HEADING
- blocchi atomici: tabelle, liste, code block non vengono mai spezzati
- nessun overlap tra chunk
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eliminati md_optimizer.py e fix_chunks.py: la pipeline non parte più
da _content_list_v2.json ma da un .md già pulito in sources/.
config.py ridotto da 114 a 64 righe: rimossi tutti i parametri MinerU
(NOISE_TYPES, FRONTMATTER_HEADINGS, MODEL_SKIP_LABELS, ecc.) e aggiunti
i parametri effettivamente utili al chunking: SKIP_HEADINGS,
SKIP_PRE_HEADING, MERGE_SHORT_PARAGRAPHS, ATOMIC_TYPES, CONTEXT_DEPTH.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Nuovi check bloccanti:
- prefisso malformato ([ senza ] o contenuto vuoto)
- corpo vuoto dopo prefisso valido
Nuovi warning:
- tabelle Markdown senza riga separatore |---|
- chunk con corpo identico (duplicati da overlap/merge)
Output migliorato:
- istogramma ASCII con marcatori ← MIN / ← MAX
- top 5 sezioni per volume di chunk
- mediana (p50) nelle statistiche di lunghezza
report.json arricchito: p50_chars, sections, malformed_prefix,
body_empty, broken_tables, duplicate_bodies.
PUNCT_END esteso con \d[\d.,/]*$ per numeri, anni, riferimenti normativi.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _URL_TAIL ora matcha anche www.* (non solo https://) — evita falsi blockers
su watermark tipo www.docsity.com
- README: documenta --collection / --stems per ingestion, retrieve e rag
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- chunker.py: scrive meta.json con strategia e soglie effettive (target,
min_chars, max_chars) per ogni documento chunked
- verify_chunks.py:
* _load_thresholds(): legge min/max da meta.json invece del TARGET_CHARS
globale, eliminando il mismatch tra soglie chunker e verify
(h3_aware target=600 -> range 450-750, non piu' validato a 225-375)
* _ROMAN_END: esclude numeri romani finali (XV, XIV...) dagli incompleti
perche' sono artefatti indice PDF, non frasi spezzate
* PUNCT_END: aggiunge ; come fine valida (clausole legali italiane)
- fix_chunks.py:
* _load_thresholds(): usa max_chars da meta.json per split coerente
* _SECONDARY_END: split secondario su ; per testo legale multi-clausola
* Fase 1 (convergenza): risolve solo blockers (incomplete, empty,
no_prefix) senza toccare warnings -- elimina il ciclo
merge->too_long->split->incomplete->merge
* Fase 2 (finale): una sola passata di merge too_short + split too_long
dopo che i blockers sono azzerati
Risultato su dirittopenale: da blocked (265 incomplete) a warnings_only
in 2 iterazioni, senza cicli infiniti.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduce chunks/config.py come unica fonte di verità per tutti i
parametri della pipeline di chunking. TARGET_CHARS + CHUNK_TOLERANCE
sostituiscono MIN_CHARS/MAX_CHARS: il chunker mira a una dimensione
target e si avvicina il più possibile rispettando il vincolo assoluto
di terminare ogni chunk su un confine di frase (punto/punteggiatura).
- config.py: TARGET_CHARS, CHUNK_TOLERANCE, SPLIT_THRESHOLD_FACTOR,
PROTECT_TABLES, FIX_MAX_ITERATIONS, STRATEGY_OVERRIDES per strategia
- chunker.py: algoritmo target-based (emit quando frase successiva
sfora upper_body = upper - prefix_len), table protection atomica,
override MIN/MAX/overlap per ciascuna delle 4 strategie
- verify_chunks.py: soglie derivate da target*(1±tolerance)
- fix_chunks.py: _split_at_boundary sempre su punteggiatura finale,
loop ricorsivo fix→verify fino a FIX_MAX_ITERATIONS, split solo
per chunk > upper × SPLIT_THRESHOLD_FACTOR
Risultato su bitcoin: 694 chunk, 0 incompleti, 83% in range [450,750],
tutti terminanti su punteggiatura indipendentemente dalla dimensione.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Nuova cartella chunks/ con chunker.py (step 5), verify_chunks.py e
fix_chunks.py (step 6). Tutto l'I/O va in chunks/<stem>/ invece di
step-5/ e step-6/ separati. Input: conversione/<stem>/clean.md