mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(hindsight): disable broken local runtime on unsupported CPUs
This commit is contained in:
parent
7897f65a94
commit
df55660e3c
2 changed files with 81 additions and 2 deletions
|
|
@ -23,6 +23,7 @@ Or via $HERMES_HOME/hindsight/config.json (profile-scoped), falling back to
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import importlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
|
@ -54,6 +55,22 @@ _PROVIDER_DEFAULT_MODELS = {
|
|||
}
|
||||
|
||||
|
||||
def _check_local_runtime() -> tuple[bool, str | None]:
|
||||
"""Return whether local embedded Hindsight imports cleanly.
|
||||
|
||||
On older CPUs, importing the local Hindsight stack can raise a runtime
|
||||
error from NumPy before the daemon starts. Treat that as "unavailable"
|
||||
so Hermes can degrade gracefully instead of repeatedly trying to start
|
||||
a broken local memory backend.
|
||||
"""
|
||||
try:
|
||||
importlib.import_module("hindsight")
|
||||
importlib.import_module("hindsight_embed.daemon_embed_manager")
|
||||
return True, None
|
||||
except Exception as exc:
|
||||
return False, str(exc)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Dedicated event loop for Hindsight async calls (one per process, reused).
|
||||
# Avoids creating ephemeral loops that leak aiohttp sessions.
|
||||
|
|
@ -368,7 +385,10 @@ class HindsightMemoryProvider(MemoryProvider):
|
|||
try:
|
||||
cfg = _load_config()
|
||||
mode = cfg.get("mode", "cloud")
|
||||
if mode in ("local", "local_embedded", "local_external"):
|
||||
if mode in ("local", "local_embedded"):
|
||||
available, _ = _check_local_runtime()
|
||||
return available
|
||||
if mode == "local_external":
|
||||
return True
|
||||
has_key = bool(
|
||||
cfg.get("apiKey")
|
||||
|
|
@ -604,6 +624,12 @@ class HindsightMemoryProvider(MemoryProvider):
|
|||
"""Return the cached Hindsight client (created once, reused)."""
|
||||
if self._client is None:
|
||||
if self._mode == "local_embedded":
|
||||
available, reason = _check_local_runtime()
|
||||
if not available:
|
||||
raise RuntimeError(
|
||||
"Hindsight local runtime is unavailable"
|
||||
+ (f": {reason}" if reason else "")
|
||||
)
|
||||
from hindsight import HindsightEmbedded
|
||||
HindsightEmbedded.__del__ = lambda self: None
|
||||
llm_provider = self._config.get("llm_provider", "")
|
||||
|
|
@ -676,6 +702,15 @@ class HindsightMemoryProvider(MemoryProvider):
|
|||
# "local" is a legacy alias for "local_embedded"
|
||||
if self._mode == "local":
|
||||
self._mode = "local_embedded"
|
||||
if self._mode == "local_embedded":
|
||||
available, reason = _check_local_runtime()
|
||||
if not available:
|
||||
logger.warning(
|
||||
"Hindsight local mode disabled because its runtime could not be imported: %s",
|
||||
reason,
|
||||
)
|
||||
self._mode = "disabled"
|
||||
return
|
||||
self._api_key = self._config.get("apiKey") or self._config.get("api_key") or os.environ.get("HINDSIGHT_API_KEY", "")
|
||||
default_url = _DEFAULT_LOCAL_URL if self._mode in ("local_embedded", "local_external") else _DEFAULT_API_URL
|
||||
self._api_url = self._config.get("api_url") or os.environ.get("HINDSIGHT_API_URL", default_url)
|
||||
|
|
|
|||
|
|
@ -705,6 +705,10 @@ class TestAvailability:
|
|||
lambda: tmp_path / "nonexistent",
|
||||
)
|
||||
monkeypatch.setenv("HINDSIGHT_MODE", "local")
|
||||
monkeypatch.setattr(
|
||||
"plugins.memory.hindsight.importlib.import_module",
|
||||
lambda name: object(),
|
||||
)
|
||||
p = HindsightMemoryProvider()
|
||||
assert p.is_available()
|
||||
|
||||
|
|
@ -713,7 +717,7 @@ class TestAvailability:
|
|||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
config_path.write_text(json.dumps({
|
||||
"mode": "cloud",
|
||||
"api_key": "config-key",
|
||||
"api_key": "***",
|
||||
}))
|
||||
monkeypatch.setattr(
|
||||
"plugins.memory.hindsight.get_hermes_home",
|
||||
|
|
@ -723,3 +727,43 @@ class TestAvailability:
|
|||
p = HindsightMemoryProvider()
|
||||
|
||||
assert p.is_available()
|
||||
|
||||
def test_local_mode_unavailable_when_runtime_import_fails(self, tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
"plugins.memory.hindsight.get_hermes_home",
|
||||
lambda: tmp_path / "nonexistent",
|
||||
)
|
||||
monkeypatch.setenv("HINDSIGHT_MODE", "local")
|
||||
|
||||
def _raise(_name):
|
||||
raise RuntimeError(
|
||||
"NumPy was built with baseline optimizations: (x86_64-v2)"
|
||||
)
|
||||
|
||||
monkeypatch.setattr(
|
||||
"plugins.memory.hindsight.importlib.import_module",
|
||||
_raise,
|
||||
)
|
||||
p = HindsightMemoryProvider()
|
||||
assert not p.is_available()
|
||||
|
||||
def test_initialize_disables_local_mode_when_runtime_import_fails(self, tmp_path, monkeypatch):
|
||||
config = {"mode": "local_embedded"}
|
||||
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
|
||||
)
|
||||
|
||||
def _raise(_name):
|
||||
raise RuntimeError("x86_64-v2 unsupported")
|
||||
|
||||
monkeypatch.setattr(
|
||||
"plugins.memory.hindsight.importlib.import_module",
|
||||
_raise,
|
||||
)
|
||||
|
||||
p = HindsightMemoryProvider()
|
||||
p.initialize(session_id="test-session", hermes_home=str(tmp_path), platform="cli")
|
||||
assert p._mode == "disabled"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue