feat(principle-distiller): config-gated Phase 3 conversation_loop integration - #78003
feat(principle-distiller): config-gated Phase 3 conversation_loop integration#7800312312ewqdq wants to merge 4 commits into
Conversation
… 59142c9b2
- Lazy import principle_distiller with sys.path setup + exception degradation
- Config gate: principle_distiller.enabled (default false) + HERMES_PRINCIPLE_DISTILLER env override, read once at loop startup
- Turn start: retrieve principles, stash hits for reward hook (W1)
- Turn end: distill + append to response, validated dict/text (D1)
- Tool-error markers scan + _turn_had_tool_error flags
- 41 tests passing (config, enabled-path, robustness)
- Restored from hermes-update-autostash-20260803-104527 (stash@{0}), worker crash recovery
…integration Two one-turn end-to-end checks through AIAgent.run_conversation against the in-process mock provider, reusing the harness from the sibling distiller suites: - enabled: seeded store + HERMES_PRINCIPLE_DISTILLER=1 -> distilled principle appended to final_response and persisted (source self-distilled), gate flag stashed, W1 injection on the wire; - disabled: byte-identical response, no stashes, store untouched, no wire injection.
…t_d1048be1 NousResearch#1/NousResearch#2) - _TOOL_ERROR_CONTENT_CONTAINS: substring scan catches tool_search scope-block error ("'<name>' is not available in this session. Use tool_search ...") which starts with a quote, evading all startswith markers; keeps prefix semantics for the original 6 markers (no false-positive on 'Tool ' inside normal content) - isinstance(original_user_message, str) and non-empty guard on the distiller user_message input (empty/non-str fallback to user_message) - 6 pure-function unit tests for _turn_slice_has_tool_error (previously zero coverage); all 59 distiller tests pass
Local validation scope (Windows host)All verification was done on this Windows host before opening the PR:
|
Rebase & post-rebase validation complete — awaiting orchestrator decision (no push performed)Post-rebase HEAD
Conflict-resolution summary (rebase onto
|
| Suite | Result |
|---|---|
| 59 new distiller tests (5 PR files) | 59 passed in 87.04s (0 skip / 0 fail) |
| 45 regression tests (8 tests/agent files) | 45 passed in 31.87s |
| tests/hermes_cli config tests (7 files) | 96 passed, 1 failed in 15.06s |
The single failure — tests/hermes_cli/test_config.py::TestGetHermesHome::test_default_path — is pre-existing and unrelated to this PR: it asserts the POSIX default ~/.hermes on all platforms, while hermes_constants.get_hermes_home() returns the documented Windows-native default %LOCALAPPDATA%\hermes. Reproduces identically on a clean checkout of the rebase base 42708f8bb; hermes_constants.py / test_config.py are byte-identical base..HEAD.
Merge state
gh pr view 78003 --json mergeStateStatus→UNKNOWN(remote head not updated — will recompute after push)- REST API:
mergeable: true,mergeable_state: **blocked**— expected BLOCKED until the orchestrator approves the force-push/update, per plan.
⚠️ Caveat for the orchestrator
origin/main has since advanced to 3fa318a50 (merge of #68881, relay shared-metrics). That commit does not touch any of the PR's 11 files, but the branch is now 1 commit behind current main — force-pushing a9ee317f4 as-is would present a 27-path diff vs current origin/main (16 of those belong to main). A final git rebase --onto origin/main 42708f8bb feat/principle-distiller-phase3 (zero expected conflicts) + quick test re-run is recommended before force-push. Decision left to the orchestrator; no push, no merge performed here.
Phase 3 principle distiller — config-gated conversation_loop hook
Recovered from
hermes-update-autostash-20260803-104527(worker crash during autostash) and independently re-validated. Four commits onfeat/principle-distiller-phase3(base: currentorigin/main):feat(principle-distiller): restore Phase 3 integration— lazy defensive import ofauto/principle_distiller; config gateprinciple_distiller.enabled(default false) +HERMES_PRINCIPLE_DISTILLERenv override, read once at loop startup; turn-start principle retrieval/stash for the reward hook (W1); turn-end distill with validated dict/text append to final response (D1); tool-error marker scan; exception containment on every path.test(principle-distiller): add enabled/disabled smoke tests— full-loop smoke coverage.test(principle-distiller): config-key e2e switch coverage + off-spelling coercion.fix(principle-distiller): scope-block marker gap + isinstance guard(sibling taskt_d1048be1advisory Terminal tool #1/Support passing morph snapshot id #2) — substring marker for the tool_search scope-block error plusisinstance(original_user_message, str)non-empty guard; 6 pure-function unit tests for_turn_slice_has_tool_error.Validation
New tests (59 total, all passing)
tests/hermes_cli/test_principle_distiller_config.py1/true/TRUE/True/yes/on/" ON "/empty), config-key precedence, malformed-section degradation,hermes config setround-trip, off-spelling coerciontests/integration/test_conversation_loop_distiller_enabled.pytests/integration/test_conversation_loop_distiller_robustness.py_turn_slice_has_tool_errorpure-function cases (startswith markers, mid-content substring, normal content not flagged)tests/integration/test_principle_distiller_smoke.pytests/integration/test_conversation_loop_distiller_config_switch.pyEdge cases covered: env-override precedence (incl. whitespace
" ON "and empty string), malformed config section degrades to false without raising, disabled path byte-identical to model output, distiller exceptions logged at ERROR and loop continues, every malformed return shape is ignored and never leaks into the response, reward-hook failures contained, missingauto/module degrades to skip even when enabled, non-str/empty user_message falls back to the raw turn message.Exact test command
python -m pytest -o addopts="" \ tests/hermes_cli/test_principle_distiller_config.py \ tests/integration/test_conversation_loop_distiller_enabled.py \ tests/integration/test_conversation_loop_distiller_robustness.py \ tests/integration/test_principle_distiller_smoke.py \ tests/integration/test_conversation_loop_distiller_config_switch.pyResult: 59 passed in 91.84s.
The new tests are hermetic (in-process mock HTTP provider, zero network/LLM calls) and carry no
integrationmarker, so they also collect under the repo's default-m 'not integration'deselect (verified: 29 collected with default addopts; 9 collected from smoke+config-switch with default addopts).-o addopts=""is used only for an explicit, reproducible command.Runtime regression
Scoped to the code paths the feature touches (
tests/agentturn-context/loop/finalizer set — 8 files):Result: 45 passed in 29.49s.
The full
tests/agentsuite exceeds this Windows environment's time budget (~10 min and still running), so the run was scoped. Full-suite collection is at parity with the pre-feature baseline (aad8f7412): the same 3 pre-existing gateway/relay collection errors (tests/gateway/test_teams.py,relay/test_relay_going_idle.py,relay/test_ws_transport.py) on both trees — no new collection failures introduced.