fix(hindsight): disable broken local runtime on unsupported CPUs

This commit is contained in:
LeonSGP43 2026-04-17 13:11:59 +08:00 committed by Teknium
parent 7897f65a94
commit df55660e3c
2 changed files with 81 additions and 2 deletions

View file

@ -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)

View file

@ -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"