From 96f0ddc6a946156c3f6d7151055329c0a99fec39 Mon Sep 17 00:00:00 2001 From: Ben Barclay Date: Thu, 4 Jun 2026 09:17:35 +1000 Subject: [PATCH] fix(docker): bake hindsight-client into the image (#38128) (#38530) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The native Hindsight memory provider lazy-installs hindsight-client into /opt/hermes/.venv at first use (tools/lazy_deps.py: memory.hindsight). That venv lives inside the immutable image layer, not the mounted /opt/data volume, so the dependency is wiped on every container recreate / image update. After an update, profile config still points at Hindsight and the Hindsight server is healthy, but recall/retain fails with: ModuleNotFoundError: No module named 'hindsight_client' The manual workaround (uv pip install hindsight-client inside the running container) doesn't survive the next recreate, and pip-install-into-.venv is not an officially supported durable Docker workflow. Fix: add --extra hindsight to the image's uv sync line, same pattern as the --extra anthropic/bedrock/azure-identity providers (#30504) and --extra messaging (#24698) — bake the optional dependency into the build layer so it survives container recreate. The pyproject [hindsight] pin (hindsight-client==0.6.1) already matches tools/lazy_deps.py and uv.lock, so this is a pure additive --extra with no lockfile churn. Verified: 'uv sync --frozen --no-install-project --extra hindsight' against the committed uv.lock installs hindsight-client 0.6.1 and the module imports cleanly. Adds a regression test (mirrors test_dockerfile_preinstalls_gateway_ messaging_dependencies) so a future Dockerfile cleanup can't silently drop the extra. --- Dockerfile | 9 ++++++++- tests/tools/test_dockerfile_pid1_reaping.py | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8a1095941..149ec0376 100644 --- a/Dockerfile +++ b/Dockerfile @@ -157,10 +157,17 @@ RUN npm install --prefer-offline --no-audit && \ # so Docker users can use these providers without requiring runtime # lazy-install access to PyPI (often blocked in containerized envs). # +# The hindsight memory provider's client (hindsight-client) is baked in +# for the same reason: it lazy-installs into /opt/hermes/.venv at first +# use, which lives inside the (immutable) image layer rather than the +# mounted /opt/data volume, so it is lost on every container recreate / +# image update and recall/retain then fails with +# `ModuleNotFoundError: No module named 'hindsight_client'` (#38128). +# # The editable link is created after the source copy below. COPY pyproject.toml uv.lock ./ RUN touch ./README.md -RUN uv sync --frozen --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity +RUN uv sync --frozen --no-install-project --extra all --extra messaging --extra anthropic --extra bedrock --extra azure-identity --extra hindsight # ---------- Source code ---------- # .dockerignore excludes node_modules, so the installs above survive. diff --git a/tests/tools/test_dockerfile_pid1_reaping.py b/tests/tools/test_dockerfile_pid1_reaping.py index 87948d4f3..899ba2d0e 100644 --- a/tests/tools/test_dockerfile_pid1_reaping.py +++ b/tests/tools/test_dockerfile_pid1_reaping.py @@ -172,6 +172,23 @@ def test_dockerfile_preinstalls_gateway_messaging_dependencies(dockerfile_text): ) +def test_dockerfile_preinstalls_hindsight_memory_dependency(dockerfile_text): + sync_steps = [ + step for step in _run_steps(dockerfile_text) + if "uv sync" in step and "--no-install-project" in step + ] + + assert sync_steps, "Dockerfile must install Python dependencies with uv sync" + assert any("--extra hindsight" in step for step in sync_steps), ( + "Published Docker images must preload the [hindsight] extra so the " + "native Hindsight memory provider's client (hindsight-client) is baked " + "into /opt/hermes/.venv. It lazy-installs into the image layer (not the " + "mounted /opt/data volume), so without baking it in recall/retain fails " + "with `ModuleNotFoundError: No module named 'hindsight_client'` after " + "every container recreate / image update (#38128)." + ) + + def test_dockerfile_builds_tui_assets(dockerfile_text): assert any( "ui-tui" in step and "npm" in step and "run build" in step