yantrikdb-hermes-plugin/tests/conftest.py

179 lines
5.1 KiB
Python

"""Pytest fixtures + module shims.
When running tests in isolation (before the plugin lands inside Hermes),
we stub out the two Hermes-provided imports and load the plugin files as
a package, mirroring how Hermes' plugins_memory/__init__.py does it.
"""
from __future__ import annotations
import importlib.util
import json
import sys
import types
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Any
import pytest
_ROOT = Path(__file__).resolve().parent.parent / "yantrikdb"
_PKG = "yantrikdb_plugin_under_test"
def _ensure_hermes_stubs() -> None:
if "agent.memory_provider" in sys.modules:
return
sys.modules["agent"] = types.ModuleType("agent")
mp_mod = types.ModuleType("agent.memory_provider")
class MemoryProvider(ABC):
@property
@abstractmethod
def name(self) -> str: ...
@abstractmethod
def is_available(self) -> bool: ...
@abstractmethod
def initialize(self, session_id: str, **kwargs: Any) -> None: ...
def system_prompt_block(self) -> str:
return ""
def prefetch(self, query: str, *, session_id: str = "") -> str:
return ""
def queue_prefetch(self, query: str, *, session_id: str = "") -> None:
return None
def sync_turn(
self, user_content: str, assistant_content: str, *, session_id: str = "",
) -> None:
return None
@abstractmethod
def get_tool_schemas(self) -> list[dict[str, Any]]: ...
def handle_tool_call(
self, tool_name: str, args: dict[str, Any], **kwargs: Any,
) -> str:
raise NotImplementedError
def shutdown(self) -> None:
return None
def on_turn_start(self, turn_number: int, message: str, **kwargs: Any) -> None:
return None
def on_session_end(self, messages: list[dict[str, Any]]) -> None:
return None
def on_pre_compress(self, messages: list[dict[str, Any]]) -> str:
return ""
def on_delegation(
self, task: str, result: str, *, child_session_id: str = "", **kwargs: Any,
) -> None:
return None
def get_config_schema(self) -> list[dict[str, Any]]:
return []
def save_config(self, values: dict[str, Any], hermes_home: str) -> None:
return None
def on_memory_write(self, action: str, target: str, content: str) -> None:
return None
mp_mod.MemoryProvider = MemoryProvider
sys.modules["agent.memory_provider"] = mp_mod
sys.modules["tools"] = types.ModuleType("tools")
registry_mod = types.ModuleType("tools.registry")
def tool_error(message: str) -> str:
return json.dumps({"error": message})
registry_mod.tool_error = tool_error
sys.modules["tools.registry"] = registry_mod
def _load_plugin() -> tuple[types.ModuleType, types.ModuleType]:
_ensure_hermes_stubs()
if _PKG in sys.modules and hasattr(sys.modules[_PKG], "YantrikDBMemoryProvider"):
return sys.modules[_PKG], sys.modules[f"{_PKG}.client"]
pkg_mod = types.ModuleType(_PKG)
pkg_mod.__path__ = [str(_ROOT)]
sys.modules[_PKG] = pkg_mod
client_spec = importlib.util.spec_from_file_location(
f"{_PKG}.client", str(_ROOT / "client.py"),
)
assert client_spec and client_spec.loader
client_mod = importlib.util.module_from_spec(client_spec)
sys.modules[f"{_PKG}.client"] = client_mod
client_spec.loader.exec_module(client_mod)
init_spec = importlib.util.spec_from_file_location(
_PKG, str(_ROOT / "__init__.py"),
submodule_search_locations=[str(_ROOT)],
)
assert init_spec and init_spec.loader
provider_mod = importlib.util.module_from_spec(init_spec)
sys.modules[_PKG] = provider_mod
init_spec.loader.exec_module(provider_mod)
return provider_mod, client_mod
@pytest.fixture(scope="session")
def plugin() -> tuple[types.ModuleType, types.ModuleType]:
return _load_plugin()
@pytest.fixture
def provider_module(plugin):
return plugin[0]
@pytest.fixture
def client_module(plugin):
return plugin[1]
@pytest.fixture(autouse=True)
def _clean_yantrikdb_env(monkeypatch):
for var in (
"YANTRIKDB_URL",
"YANTRIKDB_TOKEN",
"YANTRIKDB_NAMESPACE",
"YANTRIKDB_TOP_K",
"YANTRIKDB_READ_TIMEOUT",
"YANTRIKDB_CONNECT_TIMEOUT",
"YANTRIKDB_RETRY_TOTAL",
"YANTRIKDB_MAX_TEXT_LEN",
"YANTRIKDB_MODE",
"YANTRIKDB_DB_PATH",
"YANTRIKDB_EMBEDDER",
"YANTRIKDB_EMBEDDER_CLASS",
"YANTRIKDB_EMBEDDER_MODEL2VEC",
"YANTRIKDB_EMBEDDER_HF",
"YANTRIKDB_EMBEDDING_DIM",
"YANTRIKDB_SKILLS_ENABLED",
"YANTRIKDB_SYNC_USER_MESSAGES",
"YANTRIKDB_AUTO_THINK_ON_SESSION_END",
"YANTRIKDB_AUTO_ACKNOWLEDGE_TRIGGERS",
"YANTRIKDB_OWNER_SCOPING",
"YANTRIKDB_INCLUDE_BASE_NAMESPACE_RECALL",
"YANTRIKDB_INCLUDE_LEGACY_ACTOR_NAMESPACE_RECALL",
"YANTRIKDB_IDENTITY_MAP_PATH",
"YANTRIKDB_IDENTITY_MAP_JSON",
):
monkeypatch.delenv(var, raising=False)
yield