Fix: Gestion robuste des valeurs None dans .lower()
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>
This commit is contained in:
@@ -56,6 +56,9 @@ def call_llm(
|
|||||||
>>> for token in call_llm("Test", "ollama", "qwen2.5:7b"):
|
>>> for token in call_llm("Test", "ollama", "qwen2.5:7b"):
|
||||||
... print(token, end="")
|
... print(token, end="")
|
||||||
"""
|
"""
|
||||||
|
if not provider:
|
||||||
|
raise LLMError("Provider cannot be None or empty")
|
||||||
|
|
||||||
provider = provider.lower()
|
provider = provider.lower()
|
||||||
|
|
||||||
logger.info(f"[LLM Call] Provider: {provider}, Model: {model}, Stream: {stream}")
|
logger.info(f"[LLM Call] Provider: {provider}, Model: {model}, Stream: {stream}")
|
||||||
|
|||||||
@@ -351,8 +351,8 @@ def is_excluded_section(section: dict[str, Any]) -> bool:
|
|||||||
>>> is_excluded_section({"title": "Introduction", "content": "..."})
|
>>> is_excluded_section({"title": "Introduction", "content": "..."})
|
||||||
False
|
False
|
||||||
"""
|
"""
|
||||||
title: str = section.get("title", "").lower().strip()
|
title: str = (section.get("title") or "").lower().strip()
|
||||||
chapter_title: str = section.get("chapterTitle", "").lower().strip()
|
chapter_title: str = (section.get("chapterTitle") or "").lower().strip()
|
||||||
|
|
||||||
# Vérifier le titre de la section
|
# Vérifier le titre de la section
|
||||||
for excluded in EXCLUDED_SECTION_TITLES:
|
for excluded in EXCLUDED_SECTION_TITLES:
|
||||||
@@ -454,7 +454,7 @@ def filter_indexable_sections(sections: list[dict[str, Any]]) -> list[dict[str,
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Vérifier si le chapitre parent est une TOC
|
# Vérifier si le chapitre parent est une TOC
|
||||||
chapter_title: str = s.get("chapterTitle", "").lower().strip()
|
chapter_title: str = (s.get("chapterTitle") or "").lower().strip()
|
||||||
if any(excluded in chapter_title for excluded in EXCLUDED_SECTION_TITLES):
|
if any(excluded in chapter_title for excluded in EXCLUDED_SECTION_TITLES):
|
||||||
logger.info(f"Section exclue (chapitre TOC): '{s.get('title', 'Sans titre')}' dans '{chapter_title}'")
|
logger.info(f"Section exclue (chapitre TOC): '{s.get('title', 'Sans titre')}' dans '{chapter_title}'")
|
||||||
excluded_count += 1
|
excluded_count += 1
|
||||||
@@ -497,8 +497,8 @@ def validate_classified_sections(sections: list[dict[str, Any]]) -> list[dict[st
|
|||||||
|
|
||||||
for section in sections:
|
for section in sections:
|
||||||
# Vérifier d'abord si le titre du chapitre parent est une TOC
|
# Vérifier d'abord si le titre du chapitre parent est une TOC
|
||||||
chapter_title: str = section.get("chapter_title", "").lower().strip()
|
chapter_title: str = (section.get("chapter_title") or "").lower().strip()
|
||||||
section_title: str = section.get("title", "").lower().strip()
|
section_title: str = (section.get("title") or "").lower().strip()
|
||||||
|
|
||||||
# Exclure si le chapitre parent est une TOC
|
# Exclure si le chapitre parent est une TOC
|
||||||
is_toc_chapter: bool = False
|
is_toc_chapter: bool = False
|
||||||
|
|||||||
@@ -495,7 +495,7 @@ def apply_corrections(
|
|||||||
title: str = metadata["title"]
|
title: str = metadata["title"]
|
||||||
# Si le titre contient des phrases de validation, utiliser le champ "work" à la place
|
# Si le titre contient des phrases de validation, utiliser le champ "work" à la place
|
||||||
validation_phrases: List[str] = ["à confirmer", "confirmer avec", "vérifier"]
|
validation_phrases: List[str] = ["à confirmer", "confirmer avec", "vérifier"]
|
||||||
if any(phrase in title.lower() for phrase in validation_phrases):
|
if title and any(phrase in title.lower() for phrase in validation_phrases):
|
||||||
if "work" in metadata and metadata["work"]:
|
if "work" in metadata and metadata["work"]:
|
||||||
logger.info(f"Titre remplacé par 'work': '{title}' -> '{metadata['work']}'")
|
logger.info(f"Titre remplacé par 'work': '{title}' -> '{metadata['work']}'")
|
||||||
metadata["original_title"] = title
|
metadata["original_title"] = title
|
||||||
|
|||||||
Reference in New Issue
Block a user