fix: Prevent context manager conflict in hierarchical_search

## Problem

"generator didn't stop after throw()" error when hierarchical_search
falls back to simple_search. Both functions use 'with get_weaviate_client()',
creating nested context managers on the same generator.

## Solution

- Use ValueError("FALLBACK_TO_SIMPLE") signal instead of calling simple_search()
  inside the context manager
- Catch ValueError in except block and call simple_search() outside context
- Applied to all 3 fallback points:
  1. No Weaviate client
  2. No summaries found (Stage 1)
  3. No sections after filtering

## Result

Fallback now works correctly without context manager conflicts.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-01 15:10:06 +01:00
parent 5ebde24d20
commit 8153ea35a4

View File

@@ -317,16 +317,8 @@ def hierarchical_search(
try: try:
with get_weaviate_client() as client: with get_weaviate_client() as client:
if client is None: if client is None:
# Fallback to simple search only if not forced # Return early if forced, otherwise signal fallback
if not force_hierarchical: if force_hierarchical:
results = simple_search(query, limit, author_filter, work_filter)
return {
"mode": "simple",
"results": results,
"total_chunks": len(results),
}
else:
# Forced hierarchical with no client
return { return {
"mode": "hierarchical", "mode": "hierarchical",
"sections": [], "sections": [],
@@ -334,6 +326,8 @@ def hierarchical_search(
"total_chunks": 0, "total_chunks": 0,
"fallback_reason": "Weaviate client unavailable", "fallback_reason": "Weaviate client unavailable",
} }
# Set flag to fallback outside context manager
raise ValueError("FALLBACK_TO_SIMPLE")
# ═══════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════
# STAGE 1: Search Summary collection for relevant sections # STAGE 1: Search Summary collection for relevant sections
@@ -352,15 +346,7 @@ def hierarchical_search(
if not summaries_result.objects: if not summaries_result.objects:
# No summaries found # No summaries found
if not force_hierarchical: if force_hierarchical:
# Auto-detection: fallback to simple search
results = simple_search(query, limit, author_filter, work_filter)
return {
"mode": "simple",
"results": results,
"total_chunks": len(results),
}
else:
# Forced hierarchical: return empty hierarchical result # Forced hierarchical: return empty hierarchical result
return { return {
"mode": "hierarchical", "mode": "hierarchical",
@@ -369,6 +355,8 @@ def hierarchical_search(
"total_chunks": 0, "total_chunks": 0,
"fallback_reason": f"Aucune section pertinente trouvée (0/{sections_limit} summaries)", "fallback_reason": f"Aucune section pertinente trouvée (0/{sections_limit} summaries)",
} }
# Signal fallback outside context manager
raise ValueError("FALLBACK_TO_SIMPLE")
# Extract section data # Extract section data
sections_data = [] sections_data = []
@@ -418,15 +406,7 @@ def hierarchical_search(
if not sections_data: if not sections_data:
# No sections match filters # No sections match filters
if not force_hierarchical: if force_hierarchical:
# Auto-detection: fallback to simple search
results = simple_search(query, limit, author_filter, work_filter)
return {
"mode": "simple",
"results": results,
"total_chunks": len(results),
}
else:
# Forced hierarchical: return empty hierarchical result # Forced hierarchical: return empty hierarchical result
filters_str = f"author={author_filter}" if author_filter else "" filters_str = f"author={author_filter}" if author_filter else ""
if work_filter: if work_filter:
@@ -438,6 +418,8 @@ def hierarchical_search(
"total_chunks": 0, "total_chunks": 0,
"fallback_reason": f"Aucune section ne correspond aux filtres ({filters_str})", "fallback_reason": f"Aucune section ne correspond aux filtres ({filters_str})",
} }
# Signal fallback outside context manager
raise ValueError("FALLBACK_TO_SIMPLE")
# ═══════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════
# STAGE 2: Search Chunk collection filtered by sections # STAGE 2: Search Chunk collection filtered by sections
@@ -497,6 +479,18 @@ def hierarchical_search(
"total_chunks": len(all_chunks), "total_chunks": len(all_chunks),
} }
except ValueError as e:
# Check if this is our fallback signal
if str(e) == "FALLBACK_TO_SIMPLE":
# Fallback to simple search (outside context manager)
results = simple_search(query, limit, author_filter, work_filter)
return {
"mode": "simple",
"results": results,
"total_chunks": len(results),
}
# Re-raise if not our signal
raise
except Exception as e: except Exception as e:
print(f"Erreur recherche hiérarchique: {e}") print(f"Erreur recherche hiérarchique: {e}")
# Fallback to simple search on error (unless forced) # Fallback to simple search on error (unless forced)