From d05e15e61253a80e110ca95ac829bc55bcdf5699 Mon Sep 17 00:00:00 2001 From: Varun Chopra Date: Sat, 4 Apr 2026 23:29:33 +0530 Subject: [PATCH] fix: resolve pip packages from site-packages instead of agent dir When `pip install --target .` is run inside the hermes-agent checkout, third-party package directories (openai/, pydantic/, requests/, etc.) end up alongside real Hermes source files. With the agent dir at the front of sys.path (insert(0)), Python resolves imports from those local directories, breaking whenever the host platform differs from the container (e.g. macOS .so files inside a Linux image). Fix: append agent dir to sys.path instead of prepending. This lets site-packages resolve pip packages correctly while still allowing Hermes-specific modules (run_agent, hermes/, etc.) to resolve since they do not exist in site-packages. Also improves verify_hermes_imports() to surface the actual exception message in startup logs, making it much easier to diagnose why a module failed to import. --- api/config.py | 28 +++++++++++++++++++++++----- server.py | 4 +++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/api/config.py b/api/config.py index 8d1691b..8dcd0af 100644 --- a/api/config.py +++ b/api/config.py @@ -126,10 +126,24 @@ def _discover_python(agent_dir: Path) -> str: _AGENT_DIR = _discover_agent_dir() PYTHON_EXE = _discover_python(_AGENT_DIR) -# ── Inject agent dir into sys.path so Hermes modules are importable ─────────── +# ── Inject agent dir into sys.path so Hermes modules are importable ────────── + +# When users (or CI builds) run `pip install --target .` or +# `pip install -t .` inside the hermes-agent checkout, third-party +# package directories (openai/, pydantic/, requests/, etc.) end up +# alongside real Hermes source files. Putting _AGENT_DIR at the +# FRONT of sys.path means Python resolves `import pydantic` from that +# local directory — which breaks whenever the host platform differs +# from the container (e.g. macOS .so files inside a Linux image). +# +# Fix: insert _AGENT_DIR at the END of sys.path. Python searches +# entries in order, so site-packages resolves pip packages correctly, +# and Hermes-specific modules (run_agent, hermes/, etc.) still +# resolve because they do not exist in site-packages. + if _AGENT_DIR is not None: if str(_AGENT_DIR) not in sys.path: - sys.path.insert(0, str(_AGENT_DIR)) + sys.path.append(str(_AGENT_DIR)) _HERMES_FOUND = True else: _HERMES_FOUND = False @@ -232,16 +246,20 @@ def print_startup_config(): def verify_hermes_imports(): """ Attempt to import the key Hermes modules. - Returns (ok: bool, missing: list[str]). + Returns (ok: bool, missing: list[str], errors: dict[str, str]). """ required = ['run_agent'] missing = [] + errors = {} for mod in required: try: __import__(mod) - except ImportError: + except Exception as e: missing.append(mod) - return (len(missing) == 0), missing + # Capture the full error message so startup logs show WHY + # (e.g. pydantic_core .so mismatch) instead of just the name. + errors[mod] = f"{type(e).__name__}: {e}" + return (len(missing) == 0), missing, errors # ── Limits ─────────────────────────────────────────────────────────────────── MAX_FILE_BYTES = 200_000 diff --git a/server.py b/server.py index d9b4fc6..35a3639 100644 --- a/server.py +++ b/server.py @@ -61,9 +61,11 @@ def main(): print_startup_config() - ok, missing = verify_hermes_imports() + ok, missing, errors = verify_hermes_imports() if not ok and _HERMES_FOUND: print(f'[!!] Warning: Hermes agent found but missing modules: {missing}', flush=True) + for mod, err in errors.items(): + print(f' {mod}: {err}', flush=True) print(' Agent features may not work correctly.', flush=True) STATE_DIR.mkdir(parents=True, exist_ok=True)