Fixes batch upload ingestion that was failing silently due to schema mismatches:
Schema Fixes:
- Update collection names from "Chunk" to "Chunk_v2"
- Update collection names from "Summary" to "Summary_v2"
Object Structure Fixes:
- Replace nested objects (work: {title, author}) with flat fields
- Use workTitle and workAuthor instead of nested work object
- Add year field to chunks
- Remove document nested object (not used in current schema)
- Disable nested objects validation for flat schema
Impact:
- Batch upload now successfully ingests chunks to Weaviate
- Single-file upload also benefits from fixes
- All new documents will be properly indexed and searchable
Testing:
- Verified with 2-file batch upload (7 + 11 chunks = 18 total)
- Total chunks increased from 5,304 to 5,322
- All chunks properly searchable with workTitle/workAuthor filters
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements comprehensive batch upload system with real-time progress tracking:
Backend Infrastructure:
- Add batch_jobs global dict for batch orchestration
- Add BatchFileInfo and BatchJob TypedDicts to utils/types.py
- Create run_batch_sequential() worker function with thread.join() synchronization
- Modify /upload POST route to detect single vs multi-file uploads
- Add 3 batch API routes: /upload/batch/progress, /status, /result
- Add timestamp_to_date Jinja2 template filter
Frontend:
- Update upload.html with 'multiple' attribute and file counter
- Create upload_batch_progress.html: Real-time dashboard with SSE per file
- Create upload_batch_result.html: Final summary with statistics
Architecture:
- Backward compatible: single-file upload unchanged
- Sequential processing: one file after another (respects API limits)
- N parallel SSE connections: one per file for real-time progress
- Polling mechanism to discover job IDs as files start processing
- 1-hour timeout per file with error handling and continuation
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implemented chunking optimization to resolve oversized chunks and improve
semantic search quality:
CHUNKING IMPROVEMENTS:
- Added strict 1000-word max limit (vs previous 1500-2000)
- Implemented 100-word overlap between consecutive chunks
- Created llm_chunker_improved.py with overlap functionality
- Added 3 fallback points in llm_chunker.py for robustness
RE-CHUNKING RESULTS:
- Identified and re-chunked 31 oversized chunks (>2000 tokens)
- Split into 92 optimally-sized chunks (max 1995 tokens)
- Preserved all metadata (workTitle, workAuthor, sectionPath, etc.)
- 0 chunks now exceed 2000 tokens (vs 31 before)
VECTORIZATION:
- Created manual vectorization script for chunks without vectors
- Successfully vectorized all 92 new chunks (100% coverage)
- All 5,304 chunks now have BGE-M3 embeddings
DOCKER CONFIGURATION:
- Exposed text2vec-transformers port 8090 for manual vectorization
- Added cluster configuration to fix "No private IP address found"
- Increased worker timeout to 600s for large chunks
TESTING:
- Created comprehensive search quality test suite
- Tests distribution, overlap detection, and semantic search
- Modified to use near_vector() (Chunk_v2 has no vectorizer)
Scripts:
- 08_fix_summaries_properties.py - Add missing Work metadata to summaries
- 09_rechunk_oversized.py - Re-chunk giant chunks with overlap
- 10_test_search_quality.py - Validate search improvements
- 11_vectorize_missing_chunks.py - Manual vectorization via API
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Problèmes corrigés:
1. TITRE INCORRECT → Maintenant utilise TITRE: de la première page
2. CONCEPTS EN FRANÇAIS → Désactivé l'enrichissement LLM
Avant:
- Titre: "An Historical Sketch..." (mauvais, titre du chapitre)
- Concepts: ['immuabilité des espèces', 'création séparée'] (français)
- Résultat: 3/37 chunks ingérés dans Weaviate
Après:
- Titre: "On the Origin of Species BY MEANS OF..." (correct!)
- Concepts: [] (vides, pas de problème d'encoding)
- Résultat: 14/37 chunks ingérés (mieux mais pas parfait)
Changements word_pipeline.py:
1. STEP 5 - Métadonnées simplifiées (ligne 241-262):
- Supprimé l'appel à extract_metadata() du LLM
- Utilise directement raw_meta de extract_word_metadata()
- Le LLM prenait le titre du chapitre au lieu du livre
2. STEP 9 - Désactivé enrichissement concepts (ligne 410-423):
- Skip enrich_chunks_with_concepts()
- Raison: LLM génère concepts en FRANÇAIS pour texte ANGLAIS
- Accents français causent échecs Weaviate
Note TOC:
Le document n'a que 2 Heading 2, donc la TOC est limitée.
C'est normal pour un extrait de 10 pages.
Reste à investiguer: Pourquoi 14/37 au lieu de 37/37 chunks?
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Problème:
- Erreur: "expected string or bytes-like object, got 'dict'"
- À l'étape "Chunk Cleaning", on passait chunk (dict) au lieu de chunk["text"] (str)
Correction word_pipeline.py (ligne 434):
AVANT:
```python
cleaned = clean_chunk(chunk) # chunk est un dict!
```
APRÈS:
```python
text: str = chunk.get("text", "")
cleaned_text = clean_chunk(text, use_llm=False)
if is_chunk_valid(cleaned_text, min_chars=30, min_words=8):
chunk["text"] = cleaned_text
cleaned_chunks.append(chunk)
```
Pattern copié depuis pdf_pipeline.py:765-771 où la même logique
extrait le texte, le nettoie, puis met à jour le dict.
Test réussi:
✅ 48 paragraphes extraits
✅ 37 chunks créés
✅ Nettoyage OK
✅ Validation OK
✅ Pipeline complet fonctionnel avec Mistral API
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Problème:
- AttributeError: 'NoneType' object has no attribute 'lower'
- Se produisait quand section.get("title") retournait None au lieu de ""
Corrections:
- llm_classifier.py:
* is_excluded_section(): (section.get("title") or "").lower()
* filter_indexable_sections(): (s.get("chapterTitle") or "").lower()
* validate_classified_sections(): Idem pour chapter_title et section_title
- llm_validator.py:
* apply_corrections(): Ajout de vérification "if title and ..."
- llm_chat.py:
* call_llm(): Ajout d'une exception si provider est None/vide
Pattern de correction:
AVANT: section.get("title", "").lower() # Échoue si None
APRÈS: (section.get("title") or "").lower() # Sûr avec None
Raison:
.get(key, default) retourne le default SEULEMENT si la clé n'existe pas.
Si la clé existe avec valeur None, .get() retourne None, pas le default!
Donc: {"title": None}.get("title", "") -> None (pas "")
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Nouvelle fonction _clean_markdown() pour supprimer le formatage markdown
- Supprime headers (#), bold (**), italic (*), code blocks (```)
- Supprime liens [text](url), citations (>), marqueurs de listes (-)
- Nettoie les espaces multiples pour un texte propre
- Évite la lecture à voix haute des caractères markdown
- Tests validés: tous les patterns markdown correctement nettoyés
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Ajout de TTS>=0.22.0 aux dépendances
- Création du module utils/tts_generator.py avec Coqui XTTS v2
* Support GPU avec mixed precision (FP16)
* Lazy loading avec singleton pattern
* Chunking automatique pour textes longs
* Support multilingue (fr, en, es, de, etc.)
- Ajout de la route /chat/export-audio dans flask_app.py
- Ajout du bouton Audio dans chat.html (côté Word/PDF)
- Génération audio WAV téléchargeable depuis les réponses
Optimisé pour GPU 4070 (8GB VRAM) : utilise 4-6GB, génération rapide
Qualité : voix naturelle française avec prosodie expressive
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Ajout de python-docx et reportlab aux dépendances
- Création du module utils/word_exporter.py pour l'export Word
- Création du module utils/pdf_exporter.py pour l'export PDF
- Ajout des routes /chat/export-word et /chat/export-pdf dans flask_app.py
- Ajout des boutons d'export (Word et PDF) dans chat.html
- Les boutons apparaissent après chaque réponse de l'assistant
- Support des questions reformulées avec question originale
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>