Private
Public Access
0
0

fix(rag): Resolve RAG test failures and race conditions

- Fixed circular import in chromadb by using lazy imports in 
ag_engine.py.
- Moved RAG engine initialization to background threads in AppController to avoid blocking UI.
- Added _rag_engine_lock to prevent race conditions during engine re-initialization.
- Updated Gemini embedding model to gemini-embedding-001 (available) from 	ext-embedding-004 (not found).
- Fixed _rebuild_rag_index to use fresh 
ag_engine instance from self in every iteration.
- Optimized 	est_rag_phase4_final_verify.py and 	est_rag_phase4_stress.py to wait for RAG sync before continuing.
- Added dummy embedding fallback in LocalEmbeddingProvider if sentence-transformers fails to load.
This commit is contained in:
2026-05-14 22:23:48 -04:00
parent 07ddcf068b
commit 2d76381796
5 changed files with 115 additions and 55 deletions
+33 -14
View File
@@ -1,4 +1,3 @@
import chromadb
import copy
import inspect
import json
@@ -750,6 +749,7 @@ class AppController:
self._pending_gui_tasks_lock: threading.Lock = threading.Lock()
self._pending_dialog_lock: threading.Lock = threading.Lock()
self._api_event_queue_lock: threading.Lock = threading.Lock()
self._rag_engine_lock: threading.Lock = threading.Lock()
# --- Internal State ---
self._ai_status: str = "idle"
@@ -1186,6 +1186,31 @@ class AppController:
from src import summarize
return summarize._summary_cache
@property
def rag_enabled(self) -> bool:
return self.rag_config.enabled if self.rag_config else False
def _sync_rag_engine(self):
"""
Re-initializes the RAG engine in a background thread to avoid blocking the UI.
"""
self._set_rag_status("initializing...")
def _task():
try:
from src import rag_engine
engine = rag_engine.RAGEngine(self.rag_config, self.active_project_root)
with self._rag_engine_lock:
self.rag_engine = engine
self._set_rag_status("ready")
# If the engine is empty and we have files, trigger indexing
if self.rag_engine and self.rag_engine.is_empty() and self.files:
self._rebuild_rag_index()
except Exception as e:
self._set_rag_status(f"error: {e}")
sys.stderr.write(f"[DEBUG RAG] Failed to sync engine: {e}\n")
sys.stderr.flush()
threading.Thread(target=_task, daemon=True).start()
@property
def rag_enabled(self) -> bool:
return self.rag_config.enabled if self.rag_config else False
@@ -1193,8 +1218,7 @@ class AppController:
def rag_enabled(self, value: bool) -> None:
if self.rag_config:
self.rag_config.enabled = value
from src import rag_engine
self.rag_engine = rag_engine.RAGEngine(self.rag_config, self.active_project_root)
self._sync_rag_engine()
@property
def rag_source(self) -> str:
@@ -1203,8 +1227,7 @@ class AppController:
def rag_source(self, value: str) -> None:
if self.rag_config:
self.rag_config.vector_store.provider = value
from src import rag_engine
if self.rag_engine: self.rag_engine = rag_engine.RAGEngine(self.rag_config, self.active_project_root)
self._sync_rag_engine()
@property
def rag_emb_provider(self) -> str:
@@ -1213,8 +1236,7 @@ class AppController:
def rag_emb_provider(self, value: str) -> None:
if self.rag_config:
self.rag_config.embedding_provider = value
from src import rag_engine
if self.rag_engine: self.rag_engine = rag_engine.RAGEngine(self.rag_config, self.active_project_root)
self._sync_rag_engine()
@property
def rag_chunk_size(self) -> int:
@@ -1351,9 +1373,11 @@ class AppController:
# 1. Incremental indexing of current files in parallel
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
futures = []
def do_index(p):
if self.rag_engine: self.rag_engine.index_file(p)
for f in self.files:
path = f.path if hasattr(f, "path") else str(f)
futures.append(executor.submit(self.rag_engine.index_file, path))
futures.append(executor.submit(do_index, path))
concurrent.futures.wait(futures)
# 2. Cleanup stale entries (files no longer tracked)
@@ -1596,12 +1620,7 @@ class AppController:
self.rag_engine = None
if self.rag_config.enabled:
def _init_rag_engine():
from src import rag_engine
self.rag_engine = rag_engine.RAGEngine(self.rag_config, self.active_project_root)
if self.rag_engine.is_empty():
self._rebuild_rag_index()
threading.Thread(target=_init_rag_engine, daemon=True).start()
self._sync_rag_engine()
from src.personas import PersonaManager
self.persona_manager = PersonaManager(Path(self.active_project_path).parent if self.active_project_path else None)