From 6c44471bfdb8a243200abe7aac44371727fda5ca Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 19 Jun 2026 09:38:39 -0400 Subject: [PATCH] fix(hindsight): lazy-install cloud client dependency --- plugins/memory/hindsight/__init__.py | 12 ++++ .../plugins/memory/test_hindsight_provider.py | 68 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/plugins/memory/hindsight/__init__.py b/plugins/memory/hindsight/__init__.py index 03ebda28eca..900f177d391 100644 --- a/plugins/memory/hindsight/__init__.py +++ b/plugins/memory/hindsight/__init__.py @@ -100,6 +100,17 @@ def _check_local_runtime() -> tuple[bool, str | None]: return False, str(exc) +def _ensure_cloud_client_dependency() -> None: + """Install the Hindsight cloud client lazily before importing it.""" + try: + from tools.lazy_deps import ensure as _lazy_ensure + _lazy_ensure("memory.hindsight", prompt=False) + except ImportError: + pass + except Exception as exc: + raise ImportError(str(exc)) from exc + + # --------------------------------------------------------------------------- # Hindsight API capability probe — mirrors hindsight-integrations/openclaw. # --------------------------------------------------------------------------- @@ -990,6 +1001,7 @@ class HindsightMemoryProvider(MemoryProvider): kwargs["idle_timeout"] = idle_timeout self._client = HindsightEmbedded(**kwargs) else: + _ensure_cloud_client_dependency() from hindsight_client import Hindsight timeout = self._timeout or _DEFAULT_TIMEOUT kwargs = {"base_url": self._api_url, "timeout": float(timeout)} diff --git a/tests/plugins/memory/test_hindsight_provider.py b/tests/plugins/memory/test_hindsight_provider.py index bbcb151baa9..5cd485d4c1a 100644 --- a/tests/plugins/memory/test_hindsight_provider.py +++ b/tests/plugins/memory/test_hindsight_provider.py @@ -83,6 +83,66 @@ def _make_mock_client(): return client +def _provider_for_mode(tmp_path, monkeypatch, mode: str): + """Create an initialized provider without pre-seeding its client.""" + config = { + "mode": mode, + "apiKey": "test-key", + "api_url": "http://localhost:9999", + "bank_id": "test-bank", + "budget": "mid", + "memory_mode": "hybrid", + } + config_path = tmp_path / "hindsight" / "config.json" + config_path.parent.mkdir(parents=True, exist_ok=True) + config_path.write_text(json.dumps(config)) + + monkeypatch.setattr( + "plugins.memory.hindsight.get_hermes_home", lambda: tmp_path + ) + + provider = HindsightMemoryProvider() + provider.initialize(session_id="test-session", hermes_home=str(tmp_path), platform="cli") + return provider + + +def _assert_cloud_client_lazy_installed_before_import(tmp_path, monkeypatch, mode: str): + """Cloud/local-external clients must ensure lazy deps before importing.""" + import builtins + + provider = _provider_for_mode(tmp_path, monkeypatch, mode) + ensure_calls = [] + + def fake_ensure(feature, prompt=True): + ensure_calls.append((feature, prompt)) + + class FakeHindsight: + def __init__(self, **kwargs): + self.kwargs = kwargs + + real_import = builtins.__import__ + + def guarded_import(name, globals=None, locals=None, fromlist=(), level=0): + if name == "hindsight_client": + if ensure_calls != [("memory.hindsight", False)]: + raise ModuleNotFoundError("No module named 'hindsight_client'") + return SimpleNamespace(Hindsight=FakeHindsight) + return real_import(name, globals, locals, fromlist, level) + + monkeypatch.setattr("tools.lazy_deps.ensure", fake_ensure) + monkeypatch.setattr(builtins, "__import__", guarded_import) + + client = provider._get_client() + + assert ensure_calls == [("memory.hindsight", False)] + assert isinstance(client, FakeHindsight) + assert client.kwargs == { + "base_url": "http://localhost:9999", + "timeout": 120.0, + "api_key": "test-key", + } + + class _FakeSessionDB: def __init__(self, messages=None): self._messages = list(messages or []) @@ -232,6 +292,14 @@ class TestSchemas: class TestConfig: + def test_cloud_client_lazy_installs_dependency_before_import(self, tmp_path, monkeypatch): + _assert_cloud_client_lazy_installed_before_import(tmp_path, monkeypatch, "cloud") + + def test_local_external_client_lazy_installs_dependency_before_import(self, tmp_path, monkeypatch): + _assert_cloud_client_lazy_installed_before_import( + tmp_path, monkeypatch, "local_external" + ) + def test_default_values(self, provider): assert provider._auto_retain is True assert provider._auto_recall is True