feat(delegation): per-child fallback chain via delegation.fallback_providers - #81072
Open
devatnull wants to merge 3 commits into
Open
feat(delegation): per-child fallback chain via delegation.fallback_providers#81072devatnull wants to merge 3 commits into
devatnull wants to merge 3 commits into
Conversation
…oviders
Subagents currently inherit the parent's fallback_providers chain with
no way to override it. This adds three modes for
delegation.fallback_providers in config.yaml:
Not set / inherit → child inherits parent's fallback_providers (default)
[] → no fallback; child fails on primary error
[{provider, model}] → custom per-child chain
Use case: route subagent failures to cheaper providers (e.g. GLM-5.2 →
DeepSeek V4 Flash) while keeping the primary conversation on a more
expensive chain (e.g. Grok 4.5 → GLM-5.2).
The resolution reuses the existing _iter_fallback_entries parser from
hermes_cli/fallback_config.py — same entry shape, same dedup logic.
Changes:
- tools/delegate_tool.py: resolve child fallback chain before building
child agent; three-mode dispatch (inherit / none / custom)
- hermes_cli/config_defaults.py: add fallback_providers key to
delegation section with inline docs
- website/docs/user-guide/features/delegation.md: new Fallback Chain
subsection + Key Properties bullet
- tests/tools/test_delegate_fallback_providers.py: 5 tests covering
all three modes + parent string alias
Backward compatible: when delegation.fallback_providers is absent,
behavior is identical to before (inherits parent chain).
Contributor
There was a problem hiding this comment.
Pull request overview
Adds support for configuring a separate fallback provider/model chain for delegated subagents via delegation.fallback_providers, so subagent failover behavior can differ from the parent conversation while reusing the existing fallback parsing/dedup logic.
Changes:
- Adds child fallback-chain resolution in
delegate_taskchild-agent construction. - Documents the new
delegation.fallback_providersbehavior and examples. - Adds tests intended to cover inherit/none/custom modes for child fallback chains.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
tools/delegate_tool.py |
Implements per-child fallback-chain resolution and updates tool description text. |
hermes_cli/config_defaults.py |
Adds a delegation.fallback_providers default/config documentation entry. |
website/docs/user-guide/features/delegation.md |
Documents the new fallback-chain override behavior for subagents. |
tests/tools/test_delegate_fallback_providers.py |
Adds tests for the new delegation fallback-chain configuration modes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+1581
to
+1583
| _del_cfg = _load_config() | ||
| _del_cfg_root = _del_cfg.get("delegation", {}) if isinstance(_del_cfg, dict) else {} | ||
| _del_fb_raw = _del_cfg_root.get("fallback_providers") if isinstance(_del_cfg_root, dict) else None |
Comment on lines
+39
to
+43
| @patch("tools.delegate_tool._load_config") | ||
| def test_inherit_when_not_set(self, mock_cfg): | ||
| """When delegation.fallback_providers is absent, inherit parent chain.""" | ||
| mock_cfg.return_value = {"delegation": {}} | ||
| parent = self._make_parent_agent( |
| # [] → no fallback (child fails on primary error) | ||
| # [{provider, model}] → custom per-child chain | ||
| # Same entry shape as the top-level fallback_providers key. | ||
| "fallback_providers": [], # e.g. [{"provider":"zai","model":"glm-5.2"}] |
Three issues found by Copilot review:
1. delegate_tool.py: _load_config() returns the delegation sub-dict
directly, but code did .get('delegation', {}).get('fallback_providers')
— double-wrapping meant the key was never read. Fixed to read
.get('fallback_providers') directly.
2. config_defaults.py: fallback_providers: [] in defaults made the key
always present after merge, which would be interpreted as 'disable
fallback' — breaking the documented inherit-by-default behavior.
Removed the default value; key is now absent unless user sets it.
3. Tests: re-implemented resolution logic inline but mocked
_load_config returning a full config dict (with delegation wrapper)
instead of the delegation sub-dict directly. Tests now match the
real return shape. Added two regression tests for both bugs.
hermes config set stores complex values as JSON strings in config.yaml. _iter_fallback_entries only handled dict/list, not str — so setting fallback_providers via CLI silently broke the fallback chain (parsed as empty, no fallback fired on 429/rate-limit). Add str branch that JSON-parses then recurses. Affects both top-level fallback_providers and delegation.fallback_providers.
3 tasks
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.
What
Subagents currently inherit the parent's
fallback_providerschain with no way to override it. This PR addsdelegation.fallback_providersto config.yaml, supporting three modes:"inherit"fallback_providers(default, backward compatible)[][{provider, model}, ...]Why
When running multiple providers with different cost/quota profiles, you often want subagents to fail over to cheaper models than the primary conversation. For example:
How
The resolution reuses the existing
_iter_fallback_entriesparser fromhermes_cli/fallback_config.py— same entry shape, same dedup logic.Resolution order in
_build_child_agent:delegation.fallback_providersfrom config"inherit"or"parent"→ use parent's_fallback_chain[]→None(no fallback)[{...}]→ parse and use custom chainFiles changed
tools/delegate_tool.py— three-mode fallback resolution + tool description updatehermes_cli/config_defaults.py— newfallback_providerskey in delegation sectionwebsite/docs/user-guide/features/delegation.md— new Fallback Chain subsection + Key Properties bullettests/tools/test_delegate_fallback_providers.py— 5 tests covering all three modes + parent string aliasTesting
Pre-existing failure
test_child_inherits_runtime_credentialsis unrelated (fails on cleanmain— credential masking assertion).Backward compatibility
When
delegation.fallback_providersis absent from config, behavior is identical to before — child inherits parent's_fallback_chain.