mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-28 07:35:27 -04:00
fix(health): report unhealthy memory vector store as degraded
Keep an unhealthy MemoryVectorStore instance available for health reporting instead of discarding it as disabled. This lets health checks report a degraded/down vector-store state while preserving focused regression coverage for initializer behavior.
This commit is contained in:
@@ -68,8 +68,10 @@ def initialize_managers(base_dir: str, rag_manager=None) -> Dict[str, Any]:
|
||||
logger.info(f"Rebuilt memory vector index from {len(existing)} existing entries")
|
||||
logger.info("MemoryVectorStore initialized")
|
||||
else:
|
||||
# Keep the unhealthy object (do NOT reset to None): consumers gate on
|
||||
# `.healthy`, and service_health.chromadb_health() needs a present
|
||||
# object to report DEGRADED/DOWN instead of DISABLED ("not configured").
|
||||
logger.warning("MemoryVectorStore DEGRADED: ChromaDB vector memory unavailable")
|
||||
memory_vector = None
|
||||
except Exception as e:
|
||||
logger.warning(f"MemoryVectorStore DEGRADED: {e}")
|
||||
memory_vector = None
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
"""Regression: a present-but-unhealthy MemoryVectorStore must survive initialization.
|
||||
|
||||
When MemoryVectorStore._initialize() fails (ChromaDB unavailable / embeddings not
|
||||
installed) it swallows the exception and leaves `.healthy == False` — the object
|
||||
exists but is unhealthy. app_initializer.initialize_managers() previously reset that
|
||||
object to ``None`` in the ``else`` branch, so service_health.chromadb_health() saw
|
||||
``memory_vector is None`` and reported the vector memory as DISABLED ("not
|
||||
configured") instead of DEGRADED/DOWN ("initialization failed") — losing the
|
||||
diagnostic distinction the /api/diagnostics/services probe is built to surface.
|
||||
|
||||
This test fails before the fix (memory_vector is None) and passes after it.
|
||||
"""
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import src.app_initializer as app_init
|
||||
import src.memory_vector as memory_vector_mod
|
||||
import src.service_health as sh
|
||||
|
||||
|
||||
class _UnhealthyVectorStore:
|
||||
"""Stand-in for a MemoryVectorStore whose init failed: present but inert."""
|
||||
healthy = False
|
||||
|
||||
def count(self):
|
||||
return 0
|
||||
|
||||
def search(self, *a, **k):
|
||||
return []
|
||||
|
||||
|
||||
def _neutralize_collaborators(monkeypatch):
|
||||
"""Stub out everything initialize_managers() builds except the vector store,
|
||||
so the test isolates the memory_vector health-handling branch."""
|
||||
for name in [
|
||||
"MemoryManager", "SkillsManager", "SessionManager", "UploadHandler",
|
||||
"PersonalDocsManager", "APIKeyManager", "PresetManager",
|
||||
"MemoryProviderRegistry", "NativeMemoryProvider", "ChatProcessor",
|
||||
"ResearchHandler", "ChatHandler", "ModelDiscovery",
|
||||
]:
|
||||
monkeypatch.setattr(app_init, name, lambda *a, **k: MagicMock())
|
||||
monkeypatch.setattr(app_init, "set_session_manager", lambda *a, **k: None)
|
||||
monkeypatch.setattr(app_init, "update_search_config", lambda *a, **k: None)
|
||||
monkeypatch.setattr(app_init, "create_directories", lambda: None)
|
||||
|
||||
|
||||
def test_failed_memory_vector_init_is_kept_not_discarded(monkeypatch, tmp_path):
|
||||
_neutralize_collaborators(monkeypatch)
|
||||
# initialize_managers does `from src.memory_vector import MemoryVectorStore`
|
||||
# at call time, so patch it on the source module.
|
||||
monkeypatch.setattr(
|
||||
memory_vector_mod, "MemoryVectorStore",
|
||||
lambda *a, **k: _UnhealthyVectorStore(),
|
||||
)
|
||||
|
||||
result = app_init.initialize_managers(str(tmp_path), rag_manager=None)
|
||||
|
||||
mv = result["memory_vector"]
|
||||
assert mv is not None, "unhealthy MemoryVectorStore was discarded (reported as DISABLED, not DEGRADED/DOWN)"
|
||||
assert mv.healthy is False
|
||||
|
||||
|
||||
def test_chromadb_health_reports_down_for_unhealthy_vector_store():
|
||||
# Pins the downstream taxonomy the fix feeds: a present-but-unhealthy vector
|
||||
# store (rag absent) is DOWN, not DISABLED; with a healthy rag it is DEGRADED;
|
||||
# only when both are absent is it DISABLED.
|
||||
store = _UnhealthyVectorStore()
|
||||
healthy_rag = MagicMock(healthy=True)
|
||||
|
||||
assert sh.chromadb_health(None, None)["status"] == sh.DISABLED
|
||||
assert sh.chromadb_health(None, store)["status"] == sh.DOWN
|
||||
assert sh.chromadb_health(healthy_rag, store)["status"] == sh.DEGRADED
|
||||
Reference in New Issue
Block a user