Skip to content

fix(hindsight): re-read on-disk config when recreating the embedded client - #110804

Closed
nikkoxgonzales wants to merge 1 commit into
NousResearch:mainfrom
nikkoxgonzales:fix/hindsight-respawn-stale-env
Closed

nikkoxgonzales wants to merge 1 commit into
NousResearch:mainfrom
nikkoxgonzales:fix/hindsight-respawn-stale-env

Conversation

@nikkoxgonzales

Copy link
Copy Markdown

What does this PR do?

Supersedes #44805 (authored by @benegessarit, cherry-picked with authorship preserved — the commit is still Author: David Beyer <bdavey163@gmail.com>). That PR was approved by two reviewers and then closed unmerged by its author; the underlying issue #44804 remains open. This re-lands the same fix, adapted to current main (the client-creation code it patched has since been refactored into _new_embedded_client / _new_cloud_client, and _load_config now dedups file reads through read_json_or_empty).

The bug: a long-running gateway loads hindsight/config.json once at initialize() and holds it in self._config. When the embedded daemon later dies (idle shutdown, crash) and a stale-connection retry recreates the client (_run_hindsight_operation → self._client = None → _get_client()), the embedded manager re-materializes ~/.hindsight/profiles/<p>.env from the initialize()-time snapshot — silently reverting any config.json edits made since (observed: an llm_model bump undone by a pre-restart respawn).

The fix: client creation is the daemon (re)spawn boundary, so re-read the on-disk config there. File-gated via a new _load_file_config() helper (_load_config minus the env fallback): env-only setups keep their existing config, and a respawn only adopts disk state when a config file actually exists. Session-shaping fields (banks, recall tuning) deliberately keep initialize()-time values for in-session stability.

Related Issue

Fixes #44804

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • plugins/memory/hindsight/__init__.py
    • New _load_file_config() -> dict | None: disk-only config read (profile-scoped $HERMES_HOME/hindsight/config.json, then legacy ~/.hindsight/config.json), None when no file parses. _load_config() now delegates to it before the env fallback.
    • _new_embedded_client(): re-reads config.json at client (re)creation — the daemon respawn boundary — refreshing self._config and self._llm_base_url when a file exists.
  • tests/plugins/memory/test_hindsight_provider.py
    • New test_get_client_rereads_disk_config_at_respawn: writes a config, snapshots it as initialize()-time state, edits the file on disk, recreates the client, and asserts the new llm_model/llm_base_url reach HindsightEmbedded.
    • test_get_client_passes_idle_timeout_to_hindsight_embedded: made hermetic — points the config lookup at an empty tmp_path and stubs _ensure_client_dependency so the fake hindsight module stands in for the optional hindsight-client dep.

How to Test

  1. Configure hindsight local_embedded mode; start a long-running gateway so initialize() snapshots config.json.
  2. Edit ~/.hermes/hindsight/config.json (e.g. bump llm_model) while the gateway keeps running.
  3. Let the embedded daemon idle-shutdown (or kill it), then trigger a memory operation — the stale-connection retry recreates the client. Before this fix the respawned daemon's ~/.hindsight/profiles/<p>.env reverted to the old llm_model; after, it picks up the edit.

Automated: scripts/run_tests.sh tests/plugins/memory/test_hindsight_provider.py -q -k test_get_client → 2 passed. Full file: 79 passed; the 7 remaining failures are pre-existing on upstream/main in this environment (hindsight_client_api optional dep not installed — identical failures on a clean upstream/main checkout).

Checklist

Code

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

For New Skills

N/A — not a skill.

Screenshots / Logs

$ scripts/run_tests.sh tests/plugins/memory/test_hindsight_provider.py -q -k test_get_client
[100.0% | 88/~88 | ✓2 | ✗0] ✓ tests\plugins\memory\test_hindsight_provider.py (2✓, 6.8s)
=== Summary: 1 files, 2 tests passed, 0 failed (100% complete) ===

Credit: original fix and regression test by @benegessarit in #44805 — preserved via cherry-pick (git log shows Author: David Beyer <bdavey163@gmail.com>); conflict resolution onto current main and test hermeticity only.

…lient

A long-running gateway loads hindsight config.json once at provider
initialize() and holds it in memory. When the embedded daemon later
dies (idle shutdown, crash) and a stale-connection retry recreates the
client, the embedded manager re-materializes ~/.hindsight/profiles/
<p>.env from the initialize()-time snapshot — silently reverting any
config.json edits made since (observed: an llm_model bump undone by a
pre-restart respawn).

Client creation is the daemon (re)spawn boundary, so re-read the
on-disk config there. File-gated via a new _load_file_config() helper
(_load_config minus the env fallback): env-only setups keep their
existing config, and a respawn only adopts disk state when a config
file actually exists. Session-shaping fields (banks, recall tuning)
deliberately keep initialize()-time values for in-session stability.

Also makes the existing idle-timeout _get_client test hermetic against
a real host config (it previously relied on no config.json existing at
the real HERMES_HOME), and adds a regression test covering the
edit-then-respawn sequence.
@teknium1

Copy link
Copy Markdown
Collaborator

Closing: the bundled Hindsight provider this PR patches has moved out of this repo.

Thanks @nikkoxgonzales for this contribution. In #119888 (merge 9d799e0531c; removal commit 4cbf862abe4) the in-tree plugins/memory/hindsight/ provider was removed — Hindsight now installs from the plugin catalog and its code lives in vectorize-io/hindsight hindsight-integrations/hermes (maintained by @nicoloboschi). There is no longer any code in this repo for the Hindsight half of this PR to patch, so we are closing every open PR against the bundled provider rather than leaving them stranded.

Triage notes:

If you believe this was closed in error, comment and we will reopen.

(Bulk-closed in the hindsight-move close pass.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have tool/memory Memory tool and memory providers type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hindsight embedded: daemon respawn re-materializes profile .env from stale initialize()-time config

3 participants