You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Propagates per-provider extra_headers from named providers / custom_providers entries to auxiliary OpenAI-wire clients.
The main agent already applies these headers, but auxiliary calls such as title generation, context compression, vision routing, and web extraction construct separate sync/async clients. A custom gateway that requires a provider-scoped header override can therefore accept the main turn while rejecting the auxiliary request from the same turn.
The selected named-provider identity is carried through client construction instead of being recovered only from base_url. This matters when multiple named providers share an endpoint: each provider must receive only its own routing/auth headers, and a headerless provider must not inherit another provider's headers through URL fallback.
This follows up on #40033 / #41096, which added global model.default_headers support. This PR covers the more specific providers.<name>.extra_headers / custom_providers[].extra_headers path.
Configure a named custom OpenAI-compatible provider with a provider-scoped extra_headers.User-Agent, start a new Hermes process, and verify both the main response and automatic title generation complete successfully.
Manual E2E on macOS: before the change, the main turn succeeded with the provider-scoped header while title generation returned HTTP 502; after the change and a Desktop restart, the same custom provider completed the conversation and title-generation path.
AI code review — automated review for reference, author can ignore or act on any point.
fix(aux): preserve named provider headers in auxiliary clients
agent/auxiliary_client.py — the generic (non-named) fallback in _apply_custom_provider_extra_headers wraps apply_custom_provider_extra_headers_to_client_kwargs in except Exception: logger.debug(...). A config error silently drops the headers at debug level — which is exactly the failure mode this PR fixes (aux calls 502ing behind a WAF). logger.warning would make a config regression visible instead of invisible.
The fix covers _create_openai_client and _to_async_client, but the codex_responses branch wraps the client in CodexAuxiliaryClient (near line ~6535). If that wrapper builds its own OpenAI/Responses client internally rather than wrapping the passed one, the headers may still be lost on the codex path. Verify that route inherits default_headers or thread the headers through there as well.
The named-identity suppression (a headerless named provider must not inherit the URL-matching provider's headers) is a security-relevant behavior change, well covered by the tenant-a/tenant-b tests. One asymmetry worth documenting: the suppression only exists for the named resolution path — a non-named base-url-only resolution hitting a config with two entries sharing a base_url still gets the first URL match's headers arbitrarily. If that scenario is reachable, it deserves the same treatment or an explicit note.
Hit this exact bug with a Cloudflare Access–gated endpoint (named custom provider, OpenAI-compatible, Service Auth policy requiring CF-Access-Client-Id/CF-Access-Client-Secret headers).
The main agent honored providers.<name>.extra_headers and worked, but the auxiliary client (auxiliary_client.py → resolve_provider_client, named-custom arm) silently dropped extra_headers — so compression / title generation / web-extract 401'd the moment Access enforcement went live, while the main chat path stayed fine.
Confirmed the fix direction: merging the resolved provider's extra_headers into the client's default_headers (provider-level last, so it wins over model.* defaults) restores a clean end-to-end 200 through the gate.
Thanks for the fix — it's a silent capability drop for anyone behind a gated gateway with a named custom provider.
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
area/configConfig system, migrations, profilescomp/agentCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpointP3Low — cosmetic, nice to haveprovider/openaiOpenAI / Codex Responses APIsweeper:risk-compatibilitySweeper risk: may break existing users, config, migrations, defaults, or upgradestype/bugSomething isn't working
4 participants
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 does this PR do?
Propagates per-provider
extra_headersfrom namedproviders/custom_providersentries to auxiliary OpenAI-wire clients.The main agent already applies these headers, but auxiliary calls such as title generation, context compression, vision routing, and web extraction construct separate sync/async clients. A custom gateway that requires a provider-scoped header override can therefore accept the main turn while rejecting the auxiliary request from the same turn.
The selected named-provider identity is carried through client construction instead of being recovered only from
base_url. This matters when multiple named providers share an endpoint: each provider must receive only its own routing/auth headers, and a headerless provider must not inherit another provider's headers through URL fallback.This follows up on #40033 / #41096, which added global
model.default_headerssupport. This PR covers the more specificproviders.<name>.extra_headers/custom_providers[].extra_headerspath.Related Issue
Related to #40033 and #41096.
Type of Change
Changes Made
agent/auxiliary_client.pybase_url;tests/agent/test_auxiliary_custom_provider_extra_headers.pyHow to Test
Run the focused CI-parity suite:
Expected result:
27 tests passed, 0 failed.Configure a named custom OpenAI-compatible provider with a provider-scoped
extra_headers.User-Agent, start a new Hermes process, and verify both the main response and automatic title generation complete successfully.Manual E2E on macOS: before the change, the main turn succeeded with the provider-scoped header while title generation returned HTTP 502; after the change and a Desktop restart, the same custom provider completed the conversation and title-generation path.
Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests pass — focused canonical suite passed: 27/27Documentation & Housekeeping
docs/, docstrings) — N/A; no user-facing key or API changedcli-config.yaml.exampleif I added/changed config keys — N/A; uses the existingextra_headerskeyCONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — N/AScreenshots / Logs
Focused canonical test runner: