fix(agent): honor model.default_headers for custom OpenAI-compatible providers (#40033)#40403
Closed
sanidhyasin wants to merge 1 commit into
Closed
Conversation
…providers (NousResearch#40033) Custom OpenAI-compatible endpoints sitting behind a gateway/WAF can reject the OpenAI Python SDK's default identifying headers (User-Agent: OpenAI/Python, X-Stainless-*) and return an opaque 502/4xx even though the same request body succeeds under curl. There was no supported way to override those headers. Add a model.default_headers config key whose values are merged onto the OpenAI client's default_headers, taking precedence over provider- and SDK-supplied defaults. Applied at client construction and on every credential swap / client rebuild so the override survives reconnects. No-op for native Anthropic / Bedrock modes and when unconfigured.
This was referenced Jun 7, 2026
kshitijk4poor
added a commit
to kshitijk4poor/hermes-agent
that referenced
this pull request
Jun 7, 2026
Adds the AUTHOR_MAP entry for the NousResearch#40403 salvage (model.default_headers for custom OpenAI-compatible providers, fixes NousResearch#40033) so contributor_audit passes when the salvage PR lands.
kshitijk4poor
added a commit
that referenced
this pull request
Jun 7, 2026
Collaborator
|
Merged via #41096 — your commit was cherry-picked with authorship preserved (it's on |
Contributor
Author
|
Thanks for the salvage and for preserving authorship — appreciated. The auxiliary-client follow-up is the right call; I'd scoped only the main client and missed that title/compression/vision build their own OpenAI clients, so the override would've silently applied to the main turn but not aux calls on the same WAF'd endpoint. Good catch. 🙏 |
changman
pushed a commit
to changman/hermes-agent
that referenced
this pull request
Jun 10, 2026
…search#41094) Adds the AUTHOR_MAP entry for the NousResearch#40403 salvage (model.default_headers for custom OpenAI-compatible providers, fixes NousResearch#40033) so contributor_audit passes when the salvage PR lands.
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
Fixes #40033 — a
customOpenAI-compatible provider can fail with an opaqueHTTP 502: Upstream access forbidden(or a 4xx) even though the identical API key, base URL, model, and JSON body succeed withcurl. The reporter traced it to the OpenAI Python SDK's default identifying headers —User-Agent: OpenAI/Python ...and theX-Stainless-*family — being rejected by an upstream gateway/WAF in front of the OpenAI-compatible endpoint.There was no supported way to override those headers, so the only fix was to patch the local install.
Root cause
For OpenAI-wire providers,
default_headersis set from a fixed set of base-URL/provider rules (agent/agent_init.pyat construction;AIAgent._apply_client_headers_for_base_urlon credential swaps / client rebuilds). A genericcustomendpoint matches none of those rules, so it inherits the SDK's built-in identifying headers with no user-facing override.Fix
Add a
model.default_headersconfig key. Its entries are merged onto the OpenAI client'sdefault_headerswith user values taking precedence over provider- and SDK-supplied defaults, so a custom endpoint can swap in a plainUser-Agent(or overrideX-Stainless-*) and reach the upstream.AIAgent._apply_user_default_headers()readsmodel.default_headersvia the existingcfg_get/load_confighelpers and merges it on top of whatever headers were already resolved.agent/agent_init.py) and at the end of_apply_client_headers_for_base_url, so the override survives credential rotation and client rebuilds — not just the first request.Config (now works)
This matches suggested fix option 1 in the issue. Documented in
cli-config.yaml.exampleunder themodel:section.Tests
tests/run_agent/test_provider_attribution_headers.py— 4 new tests:model.default_headersoverrides the SDKUser-Agentand adds extra headers (the Custom OpenAI-compatible provider can fail when upstream blocks OpenAI Python SDK default headers #40033 reproduction)HTTP-Referer) intactFull file passes (13 tests). Ran with the project venv on macOS (Python 3.11).
ruff checkclean on the changed files.Platforms
Logic is platform-independent (header dict merge + config read). Tested on macOS; no OS-specific code paths touched.