Skip to content

Commit 12da466

Browse files
committed
fix(rag): let runtime adopt the recovered document store
1 parent 42da399 commit 12da466

5 files changed

Lines changed: 86 additions & 12 deletions

File tree

routes/personal_routes.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,10 @@ def setup_personal_routes(personal_docs_manager, rag_manager, rag_available):
143143

144144
def _rag():
145145
"""Get the current RAG manager, retrying init if needed."""
146-
return get_rag_manager()
146+
recovered = get_rag_manager()
147+
if recovered is not None:
148+
personal_docs_manager.rag_manager = recovered
149+
return recovered
147150

148151
def _resolve_allowed_personal_dir(directory: str) -> str:
149152
"""Resolve a user-supplied personal-docs path under the allowed root."""

src/ai_interaction.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,18 @@ def set_rag_manager(rag_mgr, personal_docs_mgr=None):
6767
_personal_docs_manager = personal_docs_mgr
6868

6969

70+
def _get_live_rag_manager():
71+
"""Resolve startup-degraded RAG through the shared personal-doc manager."""
72+
global _rag_manager
73+
74+
get_rag = getattr(_personal_docs_manager, "get_rag_manager", None)
75+
if callable(get_rag):
76+
recovered = get_rag()
77+
if recovered is not None:
78+
_rag_manager = recovered
79+
return _rag_manager
80+
81+
7082
# ---------------------------------------------------------------------------
7183
# Model resolution
7284
# ---------------------------------------------------------------------------
@@ -571,11 +583,12 @@ async def do_manage_rag(content: str, session_id: Optional[str] = None) -> Dict:
571583
if not os.path.isdir(directory):
572584
return {"error": f"Directory not found: {directory}"}
573585

574-
if not _rag_manager:
586+
rag_manager = _get_live_rag_manager()
587+
if not rag_manager:
575588
return {"error": "RAG manager not available"}
576589

577590
try:
578-
result = _rag_manager.index_personal_documents(directory)
591+
result = rag_manager.index_personal_documents(directory)
579592
indexed = result.get("indexed", 0) if isinstance(result, dict) else 0
580593
return {"action": "add_directory", "directory": directory,
581594
"results": f"Directory '{directory}' added to RAG index ({indexed} files indexed)"}

src/chat_processor.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,12 @@ def build_context_preface(
361361
# RAG: search if enabled and rag_manager available, inject only above threshold
362362
if use_rag:
363363
try:
364-
rag_manager = getattr(self.personal_docs_manager, 'rag_manager', None)
364+
get_rag = getattr(self.personal_docs_manager, "get_rag_manager", None)
365+
rag_manager = (
366+
get_rag()
367+
if callable(get_rag)
368+
else getattr(self.personal_docs_manager, "rag_manager", None)
369+
)
365370
if rag_manager:
366371
results = rag_manager.search(message, k=5, owner=owner)
367372
# Filter by similarity threshold

src/personal_docs.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,25 @@ def __init__(self, personal_dir: str, rag_manager=None):
220220
self._load_excluded()
221221
self.refresh_index()
222222

223+
def get_rag_manager(self):
224+
"""Return the live RAG manager and adopt lazy startup recovery.
225+
226+
The application may start while Chroma is unavailable. Personal routes
227+
already retry the singleton later, but the long-lived manager retained
228+
the startup ``None`` and chat/agent retrieval stayed keyword-only. Keep
229+
the recovered singleton on this shared manager so every caller sees the
230+
same live provider.
231+
"""
232+
if self.rag_manager is not None:
233+
return self.rag_manager
234+
235+
from src.rag_singleton import get_rag_manager
236+
237+
recovered = get_rag_manager()
238+
if recovered is not None:
239+
self.rag_manager = recovered
240+
return recovered
241+
223242
def load_directories(self):
224243
"""Load the list of indexed directories from persistent storage."""
225244
try:
@@ -297,9 +316,10 @@ def add_directory(self, directory: str, *, index: bool = True, owner: str = None
297316
# If RAG manager is available, index the directory immediately.
298317
# Callers that already indexed with owner metadata can pass
299318
# index=False so we do not create a second ownerless copy.
300-
if index and self.rag_manager:
319+
rag_manager = self.get_rag_manager() if index else None
320+
if rag_manager:
301321
try:
302-
result = self.rag_manager.index_personal_documents(directory, owner=owner)
322+
result = rag_manager.index_personal_documents(directory, owner=owner)
303323
logger.info(f"Indexed {result.get('indexed_count', 0)} chunks from {directory}")
304324
except Exception as e:
305325
logger.error(f"Failed to index directory {directory}: {e}")
@@ -328,9 +348,10 @@ def remove_directory(self, directory: str):
328348
# re-indexed only the remaining tracked dirs — ownerless and never
329349
# personal_dir — a catastrophic wipe (#1660). remove_directory now
330350
# removes exactly this directory's chunks and leaves the rest intact.
331-
if self.rag_manager:
351+
rag_manager = self.get_rag_manager()
352+
if rag_manager:
332353
try:
333-
self.rag_manager.remove_directory(directory)
354+
rag_manager.remove_directory(directory)
334355
except Exception as e:
335356
logger.error(f"Failed to remove directory from RAG index: {e}")
336357
else:
@@ -417,7 +438,7 @@ def refresh_index(self):
417438

418439
def retrieve(self, query: str, k: int = 5) -> List[str]:
419440
"""Retrieve relevant documents for a query."""
420-
return retrieve_personal(self.index, query, k, self.rag_manager)
441+
return retrieve_personal(self.index, query, k, self.get_rag_manager())
421442

422443
def get_file_list(self) -> List[Dict[str, Any]]:
423444
"""Get list of indexed files with metadata."""
@@ -447,7 +468,8 @@ def get_stats(self) -> Dict[str, Any]:
447468

448469
def index_all_directories(self):
449470
"""Re-index all tracked directories in the RAG system."""
450-
if not self.rag_manager:
471+
rag_manager = self.get_rag_manager()
472+
if not rag_manager:
451473
logger.warning("No RAG manager available for indexing")
452474
return
453475

@@ -456,7 +478,7 @@ def index_all_directories(self):
456478

457479
# Index the base personal directory
458480
try:
459-
result = self.rag_manager.index_personal_documents(self.personal_dir)
481+
result = rag_manager.index_personal_documents(self.personal_dir)
460482
if result.get('success'):
461483
success_count += 1
462484
logger.info(f"Indexed base directory: {self.personal_dir}")
@@ -472,7 +494,7 @@ def index_all_directories(self):
472494
continue
473495

474496
try:
475-
result = self.rag_manager.index_personal_documents(directory)
497+
result = rag_manager.index_personal_documents(directory)
476498
if result.get('success'):
477499
success_count += 1
478500
logger.info(f"Indexed directory: {directory}")
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from src import ai_interaction
2+
from src import personal_docs
3+
4+
5+
class _RecoveredRag:
6+
def search(self, query, k=5, owner=None):
7+
return []
8+
9+
10+
def test_personal_docs_manager_adopts_recovered_singleton(monkeypatch):
11+
recovered = _RecoveredRag()
12+
manager = personal_docs.PersonalDocsManager.__new__(personal_docs.PersonalDocsManager)
13+
manager.rag_manager = None
14+
monkeypatch.setattr("src.rag_singleton.get_rag_manager", lambda: recovered)
15+
16+
assert manager.get_rag_manager() is recovered
17+
assert manager.rag_manager is recovered
18+
19+
20+
def test_ai_interaction_uses_shared_recovered_manager(monkeypatch):
21+
recovered = _RecoveredRag()
22+
personal_manager = type(
23+
"PersonalManager",
24+
(),
25+
{"get_rag_manager": lambda self: recovered},
26+
)()
27+
monkeypatch.setattr(ai_interaction, "_rag_manager", None)
28+
monkeypatch.setattr(ai_interaction, "_personal_docs_manager", personal_manager)
29+
30+
assert ai_interaction._get_live_rag_manager() is recovered
31+
assert ai_interaction._rag_manager is recovered

0 commit comments

Comments
 (0)