mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-28 18:19:28 +00:00
fix(update): refresh active memory provider deps after venv rebuild
Surgical reapply of #53505 by @LeonSGP43 onto current main (stale-base cherry-pick conflicted in main.py): - _refresh_active_memory_provider_dependencies() in hermes_cli/main.py, wired into BOTH the git-pull update path (after lazy refresh) and the ZIP update path — the provider's plugin.yaml bridge packages are not in extras or LAZY_DEPS, so the core reinstall could strip/downgrade them and the update flow never healed them (#53272 mem0ai). - _install_dependencies(force=True) in memory_setup.py: hand every declared spec to the resolver on update so missing AND version-drifted packages are restored (no-op when satisfied). - Skip guards: no provider / builtin store / memory.enabled=false. Widened beyond the original PR for the #70636 half: - _provider_pip_dependencies(): mode-aware expansion — Hindsight in local/local_embedded mode needs hindsight-all (daemon + embedder), not just the declared hindsight-client; setup installs it but plugin.yaml can't express it, so update-time healing previously missed hindsight-embed and the daemon stayed broken. - Spec-aware import probing: version ranges in pip_dependencies (mem0ai>=2.0.10,<3) no longer break the pip-name -> import-name mapping. Fixes #53272. Fixes the hindsight-embed half of #70636.
This commit is contained in:
parent
ab2c9289ca
commit
5645169c8f
3 changed files with 192 additions and 5 deletions
|
|
@ -8,6 +8,7 @@ the provider's config schema. Writes config to config.yaml + .env.
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
|
|
@ -18,6 +19,32 @@ from hermes_cli.secret_prompt import masked_secret_prompt
|
|||
_CANCELLED = -1
|
||||
|
||||
|
||||
def _provider_pip_dependencies(provider_name: str, declared: list) -> list:
|
||||
"""Return the pip deps a provider actually needs on THIS install.
|
||||
|
||||
``plugin.yaml`` declares the provider's baseline bridge packages, but
|
||||
some providers install mode-dependent extras at setup time that the
|
||||
manifest can't express. Hindsight's ``local_embedded`` mode installs
|
||||
``hindsight-all`` (daemon + embedder + client) during
|
||||
``hermes memory setup`` — if the update-time refresh only reinstalled
|
||||
the declared ``hindsight-client``, the embedded daemon would stay
|
||||
broken after a venv rebuild stripped ``hindsight-embed`` (#70636).
|
||||
"""
|
||||
deps = list(declared or [])
|
||||
if provider_name == "hindsight":
|
||||
try:
|
||||
import json
|
||||
cfg_path = get_hermes_home() / "hindsight" / "config.json"
|
||||
cfg = json.loads(cfg_path.read_text(encoding="utf-8")) if cfg_path.exists() else {}
|
||||
mode = cfg.get("mode", "")
|
||||
# "local" is a legacy alias for "local_embedded"
|
||||
if mode in {"local", "local_embedded"}:
|
||||
deps.append("hindsight-all")
|
||||
except Exception:
|
||||
pass
|
||||
return deps
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Curses-based interactive picker (same pattern as hermes tools)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -77,8 +104,16 @@ def _prompt(label: str, default: str | None = None, secret: bool = False) -> str
|
|||
# Provider discovery
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _install_dependencies(provider_name: str) -> None:
|
||||
"""Install pip dependencies declared in plugin.yaml."""
|
||||
def _install_dependencies(provider_name: str, *, force: bool = False) -> None:
|
||||
"""Install pip dependencies declared in ``plugin.yaml``.
|
||||
|
||||
When ``force`` is true, every declared dependency is handed to the
|
||||
installer even if its import currently succeeds — the resolver then
|
||||
reinstalls anything missing or version-drifted and no-ops on satisfied
|
||||
ranges. This is how ``hermes update`` heals the active memory provider
|
||||
after a venv rebuild/sync removed or downgraded its bridge packages
|
||||
(#53272, #70636).
|
||||
"""
|
||||
import subprocess
|
||||
from plugins.memory import find_provider_dir
|
||||
|
||||
|
|
@ -96,7 +131,7 @@ def _install_dependencies(provider_name: str) -> None:
|
|||
except Exception:
|
||||
return
|
||||
|
||||
pip_deps = meta.get("pip_dependencies", [])
|
||||
pip_deps = _provider_pip_dependencies(provider_name, meta.get("pip_dependencies", []))
|
||||
if not pip_deps:
|
||||
return
|
||||
|
||||
|
|
@ -108,10 +143,15 @@ def _install_dependencies(provider_name: str) -> None:
|
|||
"hindsight-all": "hindsight",
|
||||
}
|
||||
|
||||
# Check which packages are missing
|
||||
# Check which packages need installation.
|
||||
missing = []
|
||||
for dep in pip_deps:
|
||||
import_name = _IMPORT_NAMES.get(dep, dep.replace("-", "_").split("[")[0])
|
||||
if force:
|
||||
missing.append(dep)
|
||||
continue
|
||||
dep_name = re.match(r"^[A-Za-z0-9_][A-Za-z0-9_.\-]*", dep)
|
||||
base = dep_name.group(0) if dep_name else dep
|
||||
import_name = _IMPORT_NAMES.get(base, base.replace("-", "_").split("[")[0])
|
||||
try:
|
||||
__import__(import_name)
|
||||
except ImportError:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue