fix(delegate): inherit parent api_key when delegation.base_url set without delegation.api_key (#15810)#15853
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes delegation credential inheritance when delegation.base_url is set without delegation.api_key, ensuring subagents inherit the parent agent’s resolved API key (via the provider credential chain) instead of falling back to OPENAI_API_KEY.
Changes:
- Update
_resolve_delegation_credentials()so thebase_urlbranch returnsapi_key=Nonewhen not explicitly configured, enabling inheritance in child construction. - Expand/update delegation credential resolution docstring/comments to describe the intended inheritance behavior.
- Update two delegation credential resolution tests to assert
api_keyisNone(noOPENAI_API_KEYfallback and noValueError).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| tools/delegate_tool.py | Adjusts delegation credential resolution so direct-endpoint delegation inherits the parent API key when delegation.api_key is omitted. |
| tests/tools/test_delegate.py | Updates tests to reflect the new inheritance behavior when base_url is configured without api_key. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| omitted, ``api_key`` is returned as None so ``_spawn_child`` inherits the | ||
| parent agent's key (``effective_api_key = override_api_key or parent_api_key``). | ||
| This lets providers that store their key outside ``OPENAI_API_KEY`` (e.g. |
There was a problem hiding this comment.
The docstring references _spawn_child, but there is no _spawn_child function in this module/repo. The credential inheritance logic (effective_api_key = override_api_key or parent_api_key) lives in _build_child_agent, so these references should be updated to avoid misleading future readers.
| omitted, ``api_key`` is returned as None so ``_spawn_child`` inherits the | |
| parent agent's key (``effective_api_key = override_api_key or parent_api_key``). | |
| This lets providers that store their key outside ``OPENAI_API_KEY`` (e.g. | |
| omitted, ``api_key`` is returned as None so the child-building path keeps | |
| inheriting the parent agent's key in ``_build_child_agent`` | |
| (``effective_api_key = override_api_key or parent_api_key``). This lets | |
| providers that store their key outside ``OPENAI_API_KEY`` (e.g. |
| # When delegation.api_key is not set, return None so _spawn_child falls | ||
| # back to the parent agent's API key via the credential inheritance path | ||
| # (effective_api_key = override_api_key or parent_api_key). This lets | ||
| # providers that store their key in a non-OPENAI_API_KEY env var (e.g. | ||
| # MINIMAX_API_KEY, DASHSCOPE_API_KEY) work without requiring callers to | ||
| # duplicate the key under delegation.api_key. | ||
| api_key = configured_api_key # None → inherited from parent in _spawn_child |
There was a problem hiding this comment.
These inline comments mention _spawn_child, but the actual inheritance logic occurs in _build_child_agent (where effective_api_key is computed). Please rename the referenced function in the comment so it matches the code path and is searchable.
| def test_direct_endpoint_falls_back_to_openai_api_key_env(self): | ||
| def test_direct_endpoint_returns_none_api_key_when_not_configured(self): | ||
| # When base_url is set without api_key, api_key should be None so | ||
| # _spawn_child inherits the parent's key (effective_api_key = override or parent). |
There was a problem hiding this comment.
This test comment refers to _spawn_child, but delegation constructs children via _build_child_agent (which computes effective_api_key = override_api_key or parent_api_key). Updating the comment will keep the test documentation accurate and easier to follow.
| # _spawn_child inherits the parent's key (effective_api_key = override or parent). | |
| # _build_child_agent falls back to the parent's key | |
| # (effective_api_key = override_api_key or parent_api_key). |
| # Even if OPENAI_API_KEY is absent, no ValueError — parent key is the fallback. | ||
| parent = _make_mock_parent(depth=0) |
There was a problem hiding this comment.
This test comment mentions _spawn_child, but the inheritance logic is implemented in _build_child_agent. Consider updating the wording so readers can find the relevant code quickly.
|
Thanks @copilot — all four findings are the same root cause: I wrote All four comments → renamed every occurrence of
|
|
@copilot All four |
…thout api_key (NousResearch#15810) When delegation.base_url is configured without delegation.api_key, the old code fell back to os.getenv("OPENAI_API_KEY"), bypassing the provider credential pool. Providers that store their key outside OPENAI_API_KEY (e.g. MINIMAX_API_KEY, DASHSCOPE_API_KEY) therefore received an empty key → 401 Unauthorized. Return None for api_key when delegation.api_key is absent so that _spawn_child picks up the parent agent's resolved key via the existing inheritance path (effective_api_key = override_api_key or parent_api_key). Explicit delegation.api_key still takes effect as before. Update two tests that asserted the old OPENAI_API_KEY fallback behaviour. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c6a16ed to
0908f17
Compare
|
Salvaged via #19741 onto current main. Docstring conflict resolved by merging both code paths' documentation; two existing tests that locked in the old OPENAI_API_KEY-fallback behavior were updated to expect the new parent-inheritance semantics. Your authorship was preserved. Thanks @briandevans! |
Summary
delegation.base_urlis configured withoutdelegation.api_key, the child agent now inherits the parent's resolved API key instead of falling back toOPENAI_API_KEYdelegation.api_keystill overrides as beforeThe bug
_resolve_delegation_credentials()handled thedelegation.base_urlpath as:This bypassed the provider credential pool entirely. Providers that store their key outside
OPENAI_API_KEY(e.g.MINIMAX_API_KEY,DASHSCOPE_API_KEY) therefore caused the child agent to receive an empty API key → 401 Unauthorized. By contrast, thedelegation.provider-only path correctly resolved credentials viaresolve_runtime_provider().The fix
When
delegation.api_keyis absent, returnapi_key = Noneinstead of theOPENAI_API_KEYfallback._spawn_childalready has the correct inheritance logic:With
override_api_key = None, this naturally falls through toparent_api_key, which holds the key the parent agent resolved through the full provider credential chain.Test plan
test_direct_endpoint_returns_none_api_key_when_not_configuredandtest_direct_endpoint_no_raise_when_only_provider_env_key_present— both FAIL on unpatched code (one gets wrong"env-openai-key", one expects noValueErrorbut gets one)tests/tools/test_delegate.pyRelated
🤖 Generated with Claude Code