feat(ai-proxy): configurable credential attachment via auth field - #132
Merged
Conversation
`Provider` conflated two orthogonal concerns: the wire protocol (OpenAI
Chat Completions/Responses vs Anthropic Messages) and the auth header
convention. The credential header was hardcoded in three places (OpenAI
transport, Anthropic transport, and the `/v1/models` aggregator).
Add an `auth` field on targets, routes, and the flat config, decoupled
from `provider`:
- `bearer` -> Authorization: Bearer <key> (OpenAI/Ollama default)
- `api_key` -> x-api-key: <key> (Anthropic default)
- `{ header: "..." }` -> arbitrary credential header
- `{ query: "..." }` -> key in the query string
When omitted, `auth` defaults to the provider's convention, so existing
configs are unchanged. This lets OpenAI-compatible endpoints with
non-standard credential headers (Brave AI Grounding `X-Subscription-Token`,
Azure OpenAI `api-key`) be configured without a dedicated provider type.
The three hardcoded auth sites now share a single `apply_auth` helper;
custom `auth` is also propagated to the `/v1/models` aggregator.
- lib.rs: `Auth` enum, `Provider::default_auth`, `TargetConfig::effective_auth`,
`auth` on `TargetConfig`/`Route`/flat config
- providers/mod.rs: unified `apply_auth` (+ unit tests)
- openai/anthropic transports + responses passthrough + models aggregator
read `apply_auth`
- config-schema.json: `Auth` def + property on the three surfaces;
vacuum rulesets regenerated
- docs (dispatchers guide, extensions reference) + CHANGELOG
Cargo.lock: local SDK path deps resynced 0.7.0 -> 0.8.1 (build side effect).
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.
Motivation
ai-proxy'sProviderconflated two orthogonal concerns: the wire protocol (OpenAI Chat Completions/Responses vs Anthropic Messages) and the auth header convention. The credential header was hardcoded in three separate places (OpenAI transport, Anthropic transport, and the/v1/modelsaggregator), so any OpenAI-compatible upstream with a non-standard credential header could not be used without minting a new provider variant.Concrete trigger: Brave AI Grounding exposes an OpenAI-compatible
/v1/chat/completionssurface (it even runs the agentic search loop server-side), but authenticates withX-Subscription-Tokeninstead ofAuthorization: Bearer. Same story for Azure OpenAI (api-key).What this does
Adds an
authfield on targets, routes, and the flat config, decoupled fromprovider:authbearerAuthorization: Bearer <key>(OpenAI/Ollama default)api_keyx-api-key: <key>(Anthropic default){ header: "Name" }{ query: "param" }When
authis omitted it defaults to the provider's convention, so existing configs are unchanged (fully backward compatible). The three hardcoded auth sites now share a singleapply_authhelper, and customauthis propagated through to the/v1/modelsaggregator (UpstreamProvider.auth) so discovery does not silently fail for such targets.Changes
Authenum,Provider::default_auth,TargetConfig::effective_auth,authonTargetConfig/Route/flat config.apply_auth(single source of auth-attachment logic).apply_auth.Auth$def+ property on the three surfaces; vacuum rulesets regenerated.Testing
Authdeserialization of both string and map forms,effective_authprovider defaults + explicit override,apply_authfor all four variants). Clippy clean (also fixed one pre-existingmatch_like_matches_macrowarning inresponses.rs)..bca+ running gateway (patched wasm), with a WireMock upstream emulating Brave:model: brave-research-> outboundx-subscription-token: <key>, noAuthorizationmodel: gpt-4o-> outboundAuthorization: Bearer <key>(default preserved)/res/v1/chat/completionsviabase_urlNote
Whether Brave's grounding endpoint ultimately wants
BearerorX-Subscription-Tokenis a config choice now covered either way; no code change is needed to switch.