fix(primary): preserve routed client headers on auto-routed provider init - #9065
Closed
GabriWar wants to merge 1 commit into
Closed
fix(primary): preserve routed client headers on auto-routed provider init#9065GabriWar wants to merge 1 commit into
GabriWar wants to merge 1 commit into
Conversation
…init When the primary agent client is built via the auto-routed path (provider auto-detected, no explicit API key in config.yaml), the code checked for a ``_default_headers`` attribute on the routed OpenAI client to copy provider-specific headers into ``self._client_kwargs``. The OpenAI SDK actually stores headers passed via ``default_headers=`` on the constructor in ``_custom_headers``. As a result, the check silently failed and ``_client_kwargs`` was saved without any headers. Subsequent request-client rebuilds via ``_create_request_openai_client`` then constructed bare ``OpenAI(api_key, base_url)`` clients that dropped the Copilot-required ``Editor-Version`` and ``Copilot-Integration-Id`` headers, causing ``400 model_not_supported`` errors from GitHub Copilot on every tool-call round after the first request. The gateway path (whatsapp, telegram, etc.) hit this hard because every turn spawns a fresh agent that immediately rebuilds its request client. The fix mirrors PR #6076, which fixed the same class of bug in the fallback activation path: prefer ``_custom_headers`` and fall back to ``_default_headers`` for forward-compat with any routed client type that uses the older attribute name.
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a regression where auto-routed provider initialization failed to persist provider-specific HTTP headers (notably GitHub Copilot headers) into _client_kwargs, causing rebuilt request clients to drop required headers on subsequent turns.
Changes:
- Update auto-routed client initialization to copy headers from the OpenAI SDK’s
_custom_headers(with fallback) intoclient_kwargs["default_headers"]. - Add explanatory comments tying the fix to the prior fallback-path fix (#6076) and documenting the OpenAI SDK behavior.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+923
to
+934
| # Preserve any default_headers the router set. | ||
| # OpenAI SDK stores headers passed via ``default_headers=`` | ||
| # on the client constructor in ``_custom_headers``; the | ||
| # older ``_default_headers`` attribute is not exposed for | ||
| # routed clients, so the original check silently failed | ||
| # and provider-specific headers (Copilot Editor-Version, | ||
| # Copilot-Integration-Id, Kimi User-Agent, etc.) were | ||
| # lost on every request-client rebuild. See PR #6076 | ||
| # for the same fix in the fallback activation path. | ||
| _rh = getattr(_routed_client, '_custom_headers', None) or getattr(_routed_client, '_default_headers', None) | ||
| if _rh: | ||
| client_kwargs["default_headers"] = dict(_rh) |
Comment on lines
+925
to
+932
| # on the client constructor in ``_custom_headers``; the | ||
| # older ``_default_headers`` attribute is not exposed for | ||
| # routed clients, so the original check silently failed | ||
| # and provider-specific headers (Copilot Editor-Version, | ||
| # Copilot-Integration-Id, Kimi User-Agent, etc.) were | ||
| # lost on every request-client rebuild. See PR #6076 | ||
| # for the same fix in the fallback activation path. | ||
| _rh = getattr(_routed_client, '_custom_headers', None) or getattr(_routed_client, '_default_headers', None) |
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.
Bug
When the primary agent client is built via the auto-routed path (provider auto-detected, no explicit API key in
config.yaml),run_agent.pycheckshasattr(_routed_client, '_default_headers')to copy provider-specific headers intoself._client_kwargs. The OpenAI SDK actually stores headers passed viadefault_headers=on the constructor in_custom_headers, so the check silently fails and_client_kwargsis saved without any headers.Every subsequent request-client rebuild via
_create_request_openai_clientthen constructs a bareOpenAI(api_key, base_url)client that drops the Copilot-requiredEditor-VersionandCopilot-Integration-Idheaders, causing hard400 model_not_supportedresponses fromapi.githubcopilot.com/chat/completionson every tool-call round after the first request.The gateway path (WhatsApp, Telegram, Discord, etc.) is the easiest repro because each turn spawns a fresh
AIAgentthat immediately rebuilds its request client before any assistant output. The CLI path can be mostly unaffected for single-shot queries because the first request uses the original client directly.Reproduction
model.default: gemini-3.1-pro-preview+model.provider: copilotinconfig.yamlCOPILOT_API_KEYenv var — rely ongh auth token/ghu_*github user token, so the auto-route path at line ~914 is takenhermes gateway run, send a message via a messaging platform that triggers a tool callInspecting the dumped outgoing request headers shows only
AuthorizationandContent-Type—Editor-Version/Copilot-Integration-Idare missing, confirming the rebuilt client dropped them.Root cause
resolve_provider_client()inauxiliary_client.pycorrectly passes the full copilot header set viadefault_headers=when constructing the OpenAI client, but the OpenAI SDK stashes them on_custom_headers, not_default_headers.Fix
Prefer
_custom_headersand fall back to_default_headersfor forward-compat with any routed client type that uses the older attribute name:This mirrors the pattern used in #6076, which fixed the same class of bug in
_try_activate_fallback(). That PR only covered the fallback activation path — the auto-routed primary path was missed.Verified
After applying the patch +
systemctl --user restart hermes-gateway.service+ clearing__pycache__, repeated multi-tool-call sessions ongemini-3.1-pro-previewvia Copilot now complete cleanly with zero400 model_not_supportederrors. Primary no longer needs to hand off to the fallback chain on every request.Related