LRP-138: Implement Weaviate filter for selected_works in chat search
- Add selected_works parameter to rag_search() function
- Build Weaviate filter using Filter.by_property("workTitle").contains_any()
- Add selected_works parameter to diverse_author_search() function
- Pass selected_works from run_chat_generation to diverse_author_search
- Preserve work filter in fallback search path
- Add logging for applied work filters
The filter allows restricting RAG search to specific works selected by the user.
When selected_works is empty or None, all works are searched (no filter).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -917,7 +917,11 @@ def search() -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def rag_search(query: str, limit: int = 5) -> List[Dict[str, Any]]:
|
def rag_search(
|
||||||
|
query: str,
|
||||||
|
limit: int = 5,
|
||||||
|
selected_works: Optional[List[str]] = None
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
"""Search passages for RAG context with formatted results.
|
"""Search passages for RAG context with formatted results.
|
||||||
|
|
||||||
Wraps the existing search_passages() function but returns results formatted
|
Wraps the existing search_passages() function but returns results formatted
|
||||||
@@ -927,6 +931,9 @@ def rag_search(query: str, limit: int = 5) -> List[Dict[str, Any]]:
|
|||||||
Args:
|
Args:
|
||||||
query: The user's question or search query.
|
query: The user's question or search query.
|
||||||
limit: Maximum number of context chunks to retrieve. Defaults to 5.
|
limit: Maximum number of context chunks to retrieve. Defaults to 5.
|
||||||
|
selected_works: Optional list of work titles to filter results.
|
||||||
|
If provided and non-empty, only chunks from these works are returned.
|
||||||
|
If None or empty, all works are included (no filter).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List of context dictionaries with keys:
|
List of context dictionaries with keys:
|
||||||
@@ -943,10 +950,19 @@ def rag_search(query: str, limit: int = 5) -> List[Dict[str, Any]]:
|
|||||||
'Platon'
|
'Platon'
|
||||||
>>> results[0]["work"]
|
>>> results[0]["work"]
|
||||||
'République'
|
'République'
|
||||||
|
|
||||||
|
>>> # With work filter
|
||||||
|
>>> results = rag_search("la vertu", limit=5, selected_works=["Ménon"])
|
||||||
|
>>> all(r["work"] == "Ménon" for r in results)
|
||||||
|
True
|
||||||
"""
|
"""
|
||||||
import time
|
import time
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
||||||
|
# Normalize selected_works
|
||||||
|
if selected_works is None:
|
||||||
|
selected_works = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with get_weaviate_client() as client:
|
with get_weaviate_client() as client:
|
||||||
if client is None:
|
if client is None:
|
||||||
@@ -955,10 +971,18 @@ def rag_search(query: str, limit: int = 5) -> List[Dict[str, Any]]:
|
|||||||
|
|
||||||
chunks = client.collections.get("Chunk")
|
chunks = client.collections.get("Chunk")
|
||||||
|
|
||||||
|
# Build work filter if selected_works is provided
|
||||||
|
work_filter: Optional[Any] = None
|
||||||
|
if selected_works:
|
||||||
|
# Use contains_any for filtering multiple work titles
|
||||||
|
work_filter = wvq.Filter.by_property("workTitle").contains_any(selected_works)
|
||||||
|
print(f"[RAG Search] Applying work filter: {selected_works}")
|
||||||
|
|
||||||
# Query with properties needed for RAG context
|
# Query with properties needed for RAG context
|
||||||
result = chunks.query.near_text(
|
result = chunks.query.near_text(
|
||||||
query=query,
|
query=query,
|
||||||
limit=limit,
|
limit=limit,
|
||||||
|
filters=work_filter,
|
||||||
return_metadata=wvq.MetadataQuery(distance=True),
|
return_metadata=wvq.MetadataQuery(distance=True),
|
||||||
return_properties=[
|
return_properties=[
|
||||||
"text",
|
"text",
|
||||||
@@ -1001,7 +1025,8 @@ def diverse_author_search(
|
|||||||
limit: int = 10,
|
limit: int = 10,
|
||||||
initial_pool: int = 100,
|
initial_pool: int = 100,
|
||||||
max_authors: int = 5,
|
max_authors: int = 5,
|
||||||
chunks_per_author: int = 2
|
chunks_per_author: int = 2,
|
||||||
|
selected_works: Optional[List[str]] = None
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""Search passages with author diversity to avoid corpus imbalance bias.
|
"""Search passages with author diversity to avoid corpus imbalance bias.
|
||||||
|
|
||||||
@@ -1023,6 +1048,9 @@ def diverse_author_search(
|
|||||||
initial_pool: Size of initial candidate pool (default: 100).
|
initial_pool: Size of initial candidate pool (default: 100).
|
||||||
max_authors: Maximum number of distinct authors to include (default: 5).
|
max_authors: Maximum number of distinct authors to include (default: 5).
|
||||||
chunks_per_author: Number of chunks per selected author (default: 2).
|
chunks_per_author: Number of chunks per selected author (default: 2).
|
||||||
|
selected_works: Optional list of work titles to filter results.
|
||||||
|
If provided and non-empty, only chunks from these works are returned.
|
||||||
|
If None or empty, all works are included (no filter).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List of context dictionaries with keys:
|
List of context dictionaries with keys:
|
||||||
@@ -1049,12 +1077,17 @@ def diverse_author_search(
|
|||||||
import time
|
import time
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
||||||
print(f"[Diverse Search] CALLED with query='{query[:50]}...', initial_pool={initial_pool}, max_authors={max_authors}, chunks_per_author={chunks_per_author}")
|
# Normalize selected_works
|
||||||
|
if selected_works is None:
|
||||||
|
selected_works = []
|
||||||
|
|
||||||
|
works_filter_str = selected_works if selected_works else "all"
|
||||||
|
print(f"[Diverse Search] CALLED with query='{query[:50]}...', initial_pool={initial_pool}, max_authors={max_authors}, chunks_per_author={chunks_per_author}, selected_works={works_filter_str}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Step 1: Retrieve large initial pool
|
# Step 1: Retrieve large initial pool (with work filter if specified)
|
||||||
print(f"[Diverse Search] Calling rag_search with limit={initial_pool}")
|
print(f"[Diverse Search] Calling rag_search with limit={initial_pool}, selected_works={works_filter_str}")
|
||||||
candidates = rag_search(query, limit=initial_pool)
|
candidates = rag_search(query, limit=initial_pool, selected_works=selected_works)
|
||||||
print(f"[Diverse Search] rag_search returned {len(candidates)} candidates")
|
print(f"[Diverse Search] rag_search returned {len(candidates)} candidates")
|
||||||
|
|
||||||
if not candidates:
|
if not candidates:
|
||||||
@@ -1126,9 +1159,9 @@ def diverse_author_search(
|
|||||||
import traceback
|
import traceback
|
||||||
print(f"[Diverse Search] EXCEPTION CAUGHT: {e}")
|
print(f"[Diverse Search] EXCEPTION CAUGHT: {e}")
|
||||||
print(f"[Diverse Search] Traceback: {traceback.format_exc()}")
|
print(f"[Diverse Search] Traceback: {traceback.format_exc()}")
|
||||||
print(f"[Diverse Search] Falling back to standard rag_search with limit={limit}")
|
print(f"[Diverse Search] Falling back to standard rag_search with limit={limit}, selected_works={works_filter_str}")
|
||||||
# Fallback to standard search
|
# Fallback to standard search (preserve work filter)
|
||||||
return rag_search(query, limit)
|
return rag_search(query, limit, selected_works=selected_works)
|
||||||
|
|
||||||
|
|
||||||
def build_prompt_with_context(user_question: str, rag_context: List[Dict[str, Any]]) -> str:
|
def build_prompt_with_context(user_question: str, rag_context: List[Dict[str, Any]]) -> str:
|
||||||
@@ -1707,13 +1740,15 @@ def run_chat_generation(
|
|||||||
# The question parameter here is the final chosen version (original or reformulated)
|
# The question parameter here is the final chosen version (original or reformulated)
|
||||||
|
|
||||||
# Step 1: Diverse author search (avoids corpus imbalance bias)
|
# Step 1: Diverse author search (avoids corpus imbalance bias)
|
||||||
|
# Apply selected_works filter if specified
|
||||||
session["status"] = "searching"
|
session["status"] = "searching"
|
||||||
rag_context = diverse_author_search(
|
rag_context = diverse_author_search(
|
||||||
query=question,
|
query=question,
|
||||||
limit=25, # Get 25 diverse chunks
|
limit=25, # Get 25 diverse chunks
|
||||||
initial_pool=200, # LARGE pool to find all relevant authors (increased from 100)
|
initial_pool=200, # LARGE pool to find all relevant authors (increased from 100)
|
||||||
max_authors=8, # Include up to 8 distinct authors (increased from 6)
|
max_authors=8, # Include up to 8 distinct authors (increased from 6)
|
||||||
chunks_per_author=3 # Max 3 chunks per author for balance
|
chunks_per_author=3, # Max 3 chunks per author for balance
|
||||||
|
selected_works=selected_works # Filter by selected works (empty = all)
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"[Pipeline] diverse_author_search returned {len(rag_context)} chunks")
|
print(f"[Pipeline] diverse_author_search returned {len(rag_context)} chunks")
|
||||||
|
|||||||
Reference in New Issue
Block a user