feat: Config-Runtime Contract Registry (Phase 1) + fix #28046 + fix #28863#28995
Open
zccyman wants to merge 1 commit into
Open
feat: Config-Runtime Contract Registry (Phase 1) + fix #28046 + fix #28863#28995zccyman wants to merge 1 commit into
zccyman wants to merge 1 commit into
Conversation
…8046 + fix NousResearch#28863 Adds a declarative config binding registry that maps config.yaml keys to their runtime consumers. On gateway startup, validates all registered bindings are active — catching orphan config fields before users discover them silently. Changes: - New module: hermes_cli/config_contracts.py - ConfigBinding dataclass + CONFIG_BINDINGS registry (29 bindings) - get_nested() resolves dotted/wildcard keys including list-of-dicts - validate_config_bindings() cross-checks config vs registry at startup - get_binding_report() for /info debugging - Fix NousResearch#28046: Read max_tokens from custom_providers per-model config - Extract get_custom_provider_model_field() as generic lookup helper - Add get_custom_provider_max_tokens() symmetric to context_length - Read custom_providers max_tokens in agent_init.py - 10 regression tests - Fix NousResearch#28863: Add docker_extra_args to _terminal_env_map - The key was in DEFAULT_CONFIG but missing from the env var bridge - Gateway startup: inject contract validation (warnings only, no behavior change) - Tests: 17 new (10 max_tokens + 17 contracts = 27 unique, 39 total with existing context_length tests) Implements: NousResearch#28984 (Typed Config-Runtime Contract, Phase 1)
Collaborator
|
Note: The two bug fixes bundled here have existing dedicated PRs:
The Config-Runtime Contract Registry feature implements #28984. |
Contributor
Author
|
Thanks @alt-glitch for the review. Acknowledged on the bundled bug fixes. My reasoning for keeping them together: #28142 and #28891 are still open (not merged), so the fixes aren't yet available to users. The feature work (config_contracts.py) was motivated by these same bugs, so bundling them here provides a complete solution in one PR. However, I'm happy to split if you prefer. The options are:
Which approach do you prefer? If you want a split, let me know and I'll remove the bug fix commits. |
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
Implements Phase 1 of the Typed Config-Runtime Contract — a declarative config binding registry that catches orphan config fields at startup, before users discover them silently.
Along the way, fixes two concrete bugs that motivated this work.
Bug Fixes
Fix #28046:
max_tokenssilently ignored fromcustom_providersper-model configProblem: Only
context_lengthwas read fromcustom_providers[].models.<model>. Themax_tokensfield was silently ignored, always defaulting to 4096.Fix: Extract
get_custom_provider_model_field()as a generic lookup helper, addget_custom_provider_max_tokens()symmetric tocontext_length, and read it inagent_init.pyafter the existing context_length lookup.Fix #28863:
terminal.docker_extra_argssilently droppedProblem:
docker_extra_argswas declared inDEFAULT_CONFIGbut missing from the_terminal_env_mapbridge ingateway/run.py, so it was never passed to the terminal tool.Fix: Add the mapping entry.
Feature: Config-Runtime Contract Registry (Phase 1)
New module:
hermes_cli/config_contracts.pyCONFIG_BINDINGS— declarative registry of 29 config→runtime bindings (all terminal.* env vars, model.max_tokens, model.context_length, custom_providers wildcards)get_nested()— resolves dotted keys with wildcard support, including list-of-dicts (forcustom_providers)validate_config_bindings()— cross-checks loaded config against registry, returns warning listget_binding_report()— human-readable status report for debuggingGateway integration
At startup, after env var bridging, the gateway runs
validate_config_bindings()and logs any warnings. This is purely additive — warnings only, no behavior change, wrapped in try/except so it can never block startup.How this prevents future bugs
When a developer adds a new
terminal.*key toDEFAULT_CONFIG, the contract registry immediately shows it as unregistered. The next person who reviews the code sees the gap before shipping.Test Coverage
test_config_contracts.pytest_custom_provider_max_tokens.pytest_custom_provider_context_length.pyBackward Compatibility
get_custom_provider_context_length()signature unchangedget_custom_provider_max_tokens()is a new function (purely additive)config_contracts.pyis a new module (purely additive)Related
Files Changed
hermes_cli/config_contracts.pytests/hermes_cli/test_config_contracts.pytests/hermes_cli/test_custom_provider_max_tokens.pyagent/agent_init.pyhermes_cli/config.pygateway/run.py