Download tests/plugins/memory/test_memory_lazy_install.py from SaylorTwift/hermes-agent: direct link, hf CLI and curl.
- Browser
- Download file 10.4 kB
-
https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/tests/plugins/memory/test_memory_lazy_install.py
- Command line
-
hf download hf://SaylorTwift/hermes-agent/tests/plugins/memory/test_memory_lazy_install.py
-
curl -L -o test_memory_lazy_install.py https://huggingface.co/SaylorTwift/hermes-agent/resolve/main/tests/plugins/memory/test_memory_lazy_install.py
10.4 kB
| """Regression tests: supermemory + mem0 memory providers must lazy-install | |
| their SDKs like honcho/hindsight. | |
| Both providers ship a third-party SDK (``supermemory`` / ``mem0ai``) that is | |
| NOT a core dependency. Before this fix they imported the SDK directly with no | |
| ``tools.lazy_deps.ensure()`` preflight and had no ``LAZY_DEPS`` allowlist | |
| entry. On the published Docker image the agent venv is sealed | |
| (``HERMES_DISABLE_LAZY_INSTALLS=1``) and lazy installs are redirected to a | |
| writable durable target (``HERMES_LAZY_INSTALL_TARGET``). honcho/hindsight | |
| route through ``ensure()`` and therefore install fine on a hosted instance; | |
| supermemory/mem0 never called it, so the SDK was never installed there and | |
| the provider silently reported itself unavailable. | |
| These tests pin the contract: | |
| 1. Both features are in the ``LAZY_DEPS`` allowlist (without an entry, | |
| ``ensure()`` raises ``FeatureUnavailable`` — the original silent-dark bug). | |
| 2. Each provider's SDK-import chokepoint actually calls ``ensure(<feature>)``. | |
| 3. supermemory's ``is_available()`` no longer gates on the SDK being | |
| importable (the chicken-and-egg trap that stopped the provider loading at | |
| all on a sealed venv, so ``initialize()``/``ensure()`` never ran). | |
| 4. The real sealed-venv durable-target gate accepts the new features (the | |
| exact hosted-Fly condition the user hit). | |
| The pip subprocess is never actually run — ``_venv_pip_install`` / | |
| ``_is_satisfied`` are stubbed so we exercise the real ``ensure()`` control | |
| flow without touching PyPI. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import pytest | |
| import tools.lazy_deps as ld | |
| MEMORY_FEATURES = ("memory.supermemory", "memory.mem0") | |
| # --------------------------------------------------------------------------- | |
| # 1. Allowlist contract — the core regression. | |
| # --------------------------------------------------------------------------- | |
| class TestAllowlistEntries: | |
| def test_feature_is_allowlisted(self, feature): | |
| # Without an allowlist entry, ensure() raises FeatureUnavailable with | |
| # "not in LAZY_DEPS" — which is exactly why the SDK never installed on | |
| # a hosted instance before this fix. | |
| assert feature in ld.LAZY_DEPS, ( | |
| f"{feature!r} missing from LAZY_DEPS — its SDK can never " | |
| f"lazy-install on a sealed Docker venv." | |
| ) | |
| def test_supermemory_spec_package(self): | |
| specs = ld.LAZY_DEPS["memory.supermemory"] | |
| assert any(ld._pkg_name_from_spec(s) == "supermemory" for s in specs) | |
| def test_mem0_spec_package(self): | |
| # mem0's pip package is ``mem0ai`` (imports as ``mem0``). | |
| specs = ld.LAZY_DEPS["memory.mem0"] | |
| assert any(ld._pkg_name_from_spec(s) == "mem0ai" for s in specs) | |
| def test_unknown_feature_would_raise_without_entry(self, feature, monkeypatch): | |
| # Demonstrate the failure mode the allowlist entry prevents: a feature | |
| # NOT in LAZY_DEPS raises rather than installing. | |
| monkeypatch.setattr(ld, "_allow_lazy_installs", lambda: True) | |
| with pytest.raises(ld.FeatureUnavailable, match="not in LAZY_DEPS"): | |
| ld.ensure(feature + ".typo", prompt=False) | |
| # --------------------------------------------------------------------------- | |
| # 2. Import sites call ensure(). | |
| # --------------------------------------------------------------------------- | |
| class TestSupermemoryEnsureCalled: | |
| def test_client_construction_calls_ensure(self, monkeypatch): | |
| """_SupermemoryClient.__init__ must call ensure('memory.supermemory') | |
| before importing the SDK.""" | |
| from plugins.memory.supermemory import _SupermemoryClient | |
| calls = [] | |
| monkeypatch.setattr( | |
| ld, "ensure", | |
| lambda feature, **kw: calls.append((feature, kw)), | |
| ) | |
| # Stub the SDK so construction doesn't need the real package. The | |
| # client does ``from supermemory import Supermemory`` right after | |
| # ensure(); inject a fake module. | |
| import sys | |
| import types | |
| fake = types.ModuleType("supermemory") | |
| fake.Supermemory = lambda **kw: object() | |
| monkeypatch.setitem(sys.modules, "supermemory", fake) | |
| _SupermemoryClient(api_key="k", timeout=5.0, container_tag="hermes") | |
| assert ("memory.supermemory", {"prompt": False}) in calls, ( | |
| "supermemory client did not call ensure('memory.supermemory', " | |
| f"prompt=False); calls={calls}" | |
| ) | |
| class TestMem0EnsureCalled: | |
| def test_create_backend_calls_ensure(self, monkeypatch): | |
| """SupermemoryMemoryProvider-style mem0 provider must call | |
| ensure('memory.mem0') in _create_backend before importing the SDK.""" | |
| from plugins.memory.mem0 import Mem0MemoryProvider | |
| calls = [] | |
| monkeypatch.setattr( | |
| ld, "ensure", | |
| lambda feature, **kw: calls.append((feature, kw)), | |
| ) | |
| prov = Mem0MemoryProvider() | |
| # Platform mode is the default; force a known mode and stub the backend | |
| # import so we isolate the ensure() call. | |
| prov._mode = "platform" | |
| prov._api_key = "k" | |
| import sys | |
| import types | |
| fake = types.ModuleType("mem0") | |
| fake.MemoryClient = lambda **kw: object() | |
| fake.Memory = object | |
| monkeypatch.setitem(sys.modules, "mem0", fake) | |
| # _backend imports ``from mem0 import MemoryClient`` lazily inside | |
| # PlatformBackend.__init__, so the fake module satisfies it. | |
| prov._create_backend() | |
| assert ("memory.mem0", {"prompt": False}) in calls, ( | |
| f"mem0 _create_backend did not call ensure('memory.mem0', " | |
| f"prompt=False); calls={calls}" | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # 3. supermemory is_available() chicken-and-egg fix. | |
| # --------------------------------------------------------------------------- | |
| class TestSupermemoryIsAvailable: | |
| def test_available_with_key_even_when_sdk_absent(self, monkeypatch): | |
| """With the key set but the SDK not importable, is_available() must | |
| still return True — otherwise the provider never loads on a sealed | |
| venv and ensure() (which installs the SDK) never runs.""" | |
| from plugins.memory.supermemory import SupermemoryMemoryProvider | |
| import builtins | |
| monkeypatch.setenv("SUPERMEMORY_API_KEY", "sk-test") | |
| # Make any attempt to import the SDK fail, simulating the | |
| # not-yet-installed sealed-venv state. | |
| real_import = builtins.__import__ | |
| def _no_supermemory(name, *args, **kwargs): | |
| if name == "supermemory" or name.startswith("supermemory."): | |
| raise ImportError("No module named 'supermemory'") | |
| return real_import(name, *args, **kwargs) | |
| monkeypatch.setattr(builtins, "__import__", _no_supermemory) | |
| prov = SupermemoryMemoryProvider() | |
| assert prov.is_available() is True | |
| def test_unavailable_without_key(self, monkeypatch): | |
| from plugins.memory.supermemory import SupermemoryMemoryProvider | |
| monkeypatch.delenv("SUPERMEMORY_API_KEY", raising=False) | |
| prov = SupermemoryMemoryProvider() | |
| assert prov.is_available() is False | |
| # --------------------------------------------------------------------------- | |
| # 4. Real sealed-venv durable-target gate accepts the new features. | |
| # | |
| # This is the exact hosted-Fly condition: HERMES_DISABLE_LAZY_INSTALLS=1 seals | |
| # the venv, but HERMES_LAZY_INSTALL_TARGET redirects installs to a writable | |
| # durable dir, so installs are still ALLOWED. We exercise the real | |
| # _allow_lazy_installs() + ensure() flow end-to-end with only the pip | |
| # subprocess stubbed. | |
| # --------------------------------------------------------------------------- | |
| class TestSealedVenvDurableTarget: | |
| def test_ensure_installs_into_durable_target_on_sealed_venv( | |
| self, feature, monkeypatch, tmp_path | |
| ): | |
| # Sealed venv + durable target = the published Docker image config. | |
| monkeypatch.setenv("HERMES_DISABLE_LAZY_INSTALLS", "1") | |
| monkeypatch.setenv("HERMES_LAZY_INSTALL_TARGET", str(tmp_path / "lazy")) | |
| # config.yaml kill-switch left at default (allow). | |
| monkeypatch.setattr( | |
| "hermes_cli.config.load_config", | |
| lambda: {"security": {"allow_lazy_installs": True}}, | |
| ) | |
| # Real gate must permit installs because a durable target is set. | |
| assert ld._allow_lazy_installs() is True, ( | |
| "sealed venv WITH a durable target must allow installs — this is " | |
| "the path honcho/hindsight use on hosted Fly instances" | |
| ) | |
| # Drive ensure(): missing first, satisfied after the (stubbed) install. | |
| states = iter([False, True]) | |
| monkeypatch.setattr(ld, "_is_satisfied", lambda spec: next(states)) | |
| captured = {} | |
| def fake_install(specs, **kw): | |
| captured["specs"] = specs | |
| captured["target_env"] = os.environ.get("HERMES_LAZY_INSTALL_TARGET") | |
| return ld._InstallResult(True, "ok", "") | |
| monkeypatch.setattr(ld, "_venv_pip_install", fake_install) | |
| ld.ensure(feature, prompt=False) # must not raise | |
| assert captured.get("specs") == ld.LAZY_DEPS[feature] | |
| assert captured.get("target_env"), ( | |
| "install ran without the durable target env set" | |
| ) | |
| def test_sealed_venv_without_target_blocks(self, feature, monkeypatch): | |
| # Sealed venv and NO durable target → installs blocked (can't mutate | |
| # the sealed venv). Belt-and-suspenders: confirms the gate still | |
| # protects the seal for these features. | |
| monkeypatch.setenv("HERMES_DISABLE_LAZY_INSTALLS", "1") | |
| monkeypatch.delenv("HERMES_LAZY_INSTALL_TARGET", raising=False) | |
| monkeypatch.setattr( | |
| "hermes_cli.config.load_config", | |
| lambda: {"security": {"allow_lazy_installs": True}}, | |
| ) | |
| monkeypatch.setattr(ld, "_is_satisfied", lambda spec: False) | |
| with pytest.raises(ld.FeatureUnavailable, match="lazy installs disabled"): | |
| ld.ensure(feature, prompt=False) | |