Repository navigation
fix(hermes): fall back to ruamel.yaml when the image ships no PyYAML - #289
Open
brandon-nuttall wants to merge 1 commit into
Open
brandon-nuttall wants to merge 1 commit into
brandon-nuttall wants to merge 1 commit into
Conversation
configure-provider.sh imports PyYAML to persist HERMES_PROVIDER_BASE_URL into config.yaml. The hermes-agent image dropped PyYAML from its venv in the 2026-10-08 rebuild and ships ruamel.yaml only — which is also what Hermes itself uses to read the file. There is no fallback interpreter (python3 resolves to the venv, /usr/bin/python3 has no yaml either) and no pip in the image. Because the wrapper runs before the image entrypoint under `set -e`, the failed import aborts the script before `exec entrypoint-dispatch.sh`, so the gateway never starts: the container exits 1 ~170ms after launch with nothing on stderr but the traceback. Resolve the writer at runtime instead — PyYAML when present, so older images keep byte-for-byte output, ruamel.yaml otherwise. The indent call reproduces the block-sequence indentation Hermes writes, so switching writers does not restyle the file. Only reachable when a cross-integration file sets HERMES_PROVIDER_BASE_URL (hermes + llamacpp/ollama/prismml/litellm); a bare `harbor up hermes` skips the block and starts fine.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
configure-provider.shruns before the image entrypoint, so its imports sit on hermes' critical path:The hermes-agent image dropped PyYAML from
/opt/hermes/.venvin its 2026-10-08 rebuild. That venv is Python 3.14.7 withruamel.yaml 0.18.16only — which is also what Hermes itself readsconfig.yamlwith. There is no fallback interpreter (python3resolves to the venv;/usr/bin/python3has noyamleither) and nopipin the image, so nothing can self-heal at boot.Symptom:
harbor.hermesexits 1 ~170 ms after start, and the entire container log is the traceback — s6-overlay never boots, so there is no gateway and nothing else to read.if [ -n "${HERMES_PROVIDER_BASE_URL:-}" ]; then python3 - <<'EOF' import os -import yaml +try: + import yaml +except ImportError: + from ruamel.yaml import YAMLReachable only when a cross-integration file sets
HERMES_PROVIDER_BASE_URL(hermes + llamacpp/ollama/prismml/litellm). A bareharbor up hermesskips the block and starts fine, which is why this stayed hidden.Adds a
# services > hermesfact asserting the writer is resolved at runtime.Evidence
ModuleNotFoundError: No module named 'yaml',EXIT=1, entrypoint never reached.After: patched bytes on the same image → provider step succeeds,
entrypoint-dispatch.shexec'd,modelblock written.config.yaml, the rewrite changes 4 quoting-only lines ('on'→on,'off'→off,'cache-capy', a cursor glyph); all four re-parse to identical strings under Hermes' own reader._yaml.indent(mapping=2, sequence=4, offset=2)is load-bearing — without it the fallback restyles the entire file (56 changed lines instead of 4).Considered shelling out to
hermes config set model.provider custominstead, which would drop the YAML dependency entirely. It works pre-entrypoint, but three sets cost 5.2 s at boot and it performs a schema migration as a side effect (_config_version46 → 50, dropping thetirith_*keys), so it is not a drop-in for a boot-time wrapper.Merge Danger
Door: two-way — one script, no interface or config change, single-commit revert.
Blast Radius: hermes startup, only when a provider cross-file is active.
On images that still ship PyYAML, output is unchanged. On ruamel-only images
config.yamlgains 4 quoting-only diffs. One consequence worth stating: bareon/offare booleans to a YAML 1.1 parser, so anything outside the image that parses this file with PyYAML would read them differently. Nothing inside the image does — PyYAML is no longer present, and Hermes reads the file with ruamel.