fix(memory): Initialize GoalExecutor singleton with username and validate Qdrant deletes (TDD)
This commit is contained in:
@@ -184,6 +184,9 @@ def start_bot(**kwargs):
|
||||
active_inference = ActiveInferenceEngine(username)
|
||||
|
||||
# Core Autonomous Engines
|
||||
from GramAddict.core.goap import GoalExecutor
|
||||
|
||||
GoalExecutor.get_instance(device, username)
|
||||
zero_engine = ZeroLatencyEngine(device)
|
||||
nav_graph = QNavGraph(device)
|
||||
growth_brain = GrowthBrain(username, persona_interests=persona_interests)
|
||||
|
||||
@@ -205,11 +205,13 @@ class QdrantBase:
|
||||
|
||||
point_id = self.generate_uuid(seed_string)
|
||||
try:
|
||||
self.client.delete(collection_name=self.collection_name, points_selector=[point_id])
|
||||
logger.info(
|
||||
f"🗑️ [Qdrant] Purged poisoned memory vector from {self.collection_name} (UUID: {point_id[:8]}...)",
|
||||
extra={"color": "\x1b[31m"},
|
||||
)
|
||||
res = self.client.retrieve(collection_name=self.collection_name, ids=[point_id])
|
||||
if res:
|
||||
self.client.delete(collection_name=self.collection_name, points_selector=[point_id])
|
||||
logger.info(
|
||||
f"🗑️ [Qdrant] Purged poisoned memory vector from {self.collection_name} (UUID: {point_id[:8]}...)",
|
||||
extra={"color": "\x1b[31m"},
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
self._handle_error(e, f"Failed to delete point {point_id}")
|
||||
|
||||
28
tests/unit/test_bot_flow_singleton.py
Normal file
28
tests/unit/test_bot_flow_singleton.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
import pytest
|
||||
|
||||
def test_bot_flow_goal_executor_init_order():
|
||||
# We want to prove that without explicit get_instance(device, username),
|
||||
# QNavGraph(device) initializes GoalExecutor with an empty username.
|
||||
|
||||
# We have to reset GoalExecutor singleton first
|
||||
from GramAddict.core.goap import GoalExecutor
|
||||
from GramAddict.core.q_nav_graph import QNavGraph
|
||||
|
||||
if hasattr(GoalExecutor, "_instance"):
|
||||
GoalExecutor._instance = None
|
||||
|
||||
device = MagicMock()
|
||||
|
||||
# Action: Instantiate QNavGraph
|
||||
# Without the fix, this will initialize GoalExecutor with bot_username=""
|
||||
# and PathMemory will get bound to "goap_paths_v1"
|
||||
|
||||
with patch("GramAddict.core.qdrant_memory.QdrantClient") as mock_qdrant:
|
||||
# FIX: Pre-initialize with username (as done in bot_flow.py)
|
||||
executor_pre = GoalExecutor.get_instance(device, "marisaundmarc")
|
||||
nav_graph = QNavGraph(device)
|
||||
executor = GoalExecutor.get_instance(device, "marisaundmarc")
|
||||
|
||||
# Now it should be bound correctly
|
||||
assert executor.path_memory._db.collection_name == "goap_paths_v1_marisaundmarc", "PathMemory collection name does not contain the username suffix! Initialization leak occurred."
|
||||
42
tests/unit/test_delete_point_logging.py
Normal file
42
tests/unit/test_delete_point_logging.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from unittest.mock import MagicMock, patch, PropertyMock
|
||||
from GramAddict.core.qdrant_memory import UIMemoryDB
|
||||
|
||||
def test_delete_point_logs_only_if_exists(caplog):
|
||||
# Setup
|
||||
db = UIMemoryDB()
|
||||
type(db).is_connected = PropertyMock(return_value=True)
|
||||
db.client = MagicMock()
|
||||
db.collection_name = "test_collection"
|
||||
|
||||
# RED: Force the mock to return an empty list (point does NOT exist)
|
||||
db.client.retrieve.return_value = []
|
||||
|
||||
# Action
|
||||
result = db.delete_point("fake_seed")
|
||||
|
||||
# Assertions
|
||||
assert result is True # Should still return True as it didn't crash
|
||||
# It should NOT call delete if it wasn't found
|
||||
db.client.delete.assert_not_called()
|
||||
# It should NOT log the "Purged poisoned memory" line
|
||||
assert "Purged poisoned memory vector" not in caplog.text
|
||||
|
||||
def test_delete_point_logs_if_found(caplog):
|
||||
# Setup
|
||||
db = UIMemoryDB()
|
||||
type(db).is_connected = PropertyMock(return_value=True)
|
||||
db.client = MagicMock()
|
||||
db.collection_name = "test_collection"
|
||||
|
||||
# RED: Force the mock to return a result (point DOES exist)
|
||||
db.client.retrieve.return_value = [{"id": "some_uuid"}]
|
||||
|
||||
# Action
|
||||
with patch("GramAddict.core.qdrant_memory.logger") as mock_logger:
|
||||
result = db.delete_point("fake_seed")
|
||||
|
||||
# Assertions
|
||||
assert result is True
|
||||
db.client.delete.assert_called_once()
|
||||
mock_logger.info.assert_called_once()
|
||||
assert "Purged poisoned memory vector" in mock_logger.info.call_args[0][0]
|
||||
Reference in New Issue
Block a user