feat(transport): HERMES_TOOL_USE_ENFORCEMENT=required injection (closes #115)#116
Merged
Merged
Conversation
#115) Companion to devagentic#315 (initiative preamble). When operator sets ``HERMES_TOOL_USE_ENFORCEMENT=required``, the chat_completions transport injects ``tool_choice: "required"`` on every dispatch where tools are attached — the model-layer enforcement that closes the gap devagentic#315's soft-signal preamble leaves open. ## Behavior - Unset / empty / unknown value → default behavior unchanged (no ``tool_choice`` injected by hermes) - ``HERMES_TOOL_USE_ENFORCEMENT=required`` + tools attached → ``tool_choice: "required"`` set on the API kwargs - Tools NOT attached → no injection (sending ``tool_choice=required`` with empty tools is a 400 on most providers) - Caller-supplied ``tool_choice`` already on kwargs → no override (the dispatcher-tier signal wins; env is a session-tier default) Per devagentic#203 §1.3 — hermes owns model-call-shape decisions (per-call enforcement). Devagentic's models.json ``default_tool_choice`` is the dispatcher-tier default; this env is the session-tier override. ## Where it fires Both build_kwargs paths in ``chat_completions.py``: - Legacy fallback path (unregistered providers) - Provider-profile path (known providers via providers/ registry) Shared helper ``_maybe_inject_required_tool_choice(api_kwargs, tools)`` keeps the two sites in sync. ## Doctor probe New ``_check_tool_use_enforcement_env`` surfaces the active setting — silent when unset, ``check_ok`` on ``required``, ``check_warn`` with valid-values hint on typos. Mirrors the silent-when-irrelevant pattern from #95 / #96 / persona-deferred. ## Tests - 18 new tests in tests/agent/test_tool_use_enforcement.py: resolver returns None/required/case-insensitive/unknown (8 parametrized), injection happy path (1), no-inject-when-unset (1), no-inject-when-no-tools (1 covering both None and empty list), does-not-clobber-existing-tool_choice (1), no-inject-on-unknown (1), doctor silent-when-unset (1), doctor check_ok on required (1), doctor check_warn on unknown (1). - 128 total green across affected suites (new + doctor + provider/ intent/persona/tools-subset probes). No regression. ## Sequencing per #115 body The issue says "Land after devagentic#315 Phase 1 has deployed + been observed. If the preamble alone closes the reliability gap to operator satisfaction, this issue may not need to ship." This PR ships the env-knob in opt-in OFF-by-default mode, so: - Operators can enable it the moment they observe NousResearch#315's preamble is insufficient (no further hermes-side dev cycle needed) - Default behavior unchanged → zero risk to non-client-tier sessions - Doctor probe surfaces the active state so operators can confirm enablement at boot Saves the round-trip of waiting + then dev'ing once the signal arrives.
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.
Closes #115. Opt-in env knob (default off) that forces tool_choice: "required" when tools attached. Companion to devagentic#315 (initiative preamble) — preamble tells model what to do; this tells API how to constrain. Both build_kwargs paths in chat_completions transport wired via shared helper. Doctor probe surfaces active setting. 18 new tests + 128 total green. Ships in off-by-default mode per #115's sequencing — operator enables when ready.