feat: add api_headers parameter and callable api_key support#307
Open
cpsievert wants to merge 3 commits into
Open
feat: add api_headers parameter and callable api_key support#307cpsievert wants to merge 3 commits into
cpsievert wants to merge 3 commits into
Conversation
Two improvements for custom authentication on OpenAI-compatible providers: 1. New `api_headers` parameter — accepts a dict of HTTP headers or a zero-argument callable returning one. Applied via `extra_headers` on every API call, enabling token refresh and other dynamic auth patterns. 2. Widen `api_key` to accept `Callable[[], str]` — passed straight through to the openai SDK, which natively supports callable API keys with per-request resolution. Both parameters are added to all OpenAI-compatible Chat functions (ChatOpenAI, ChatOpenAICompletions, ChatGithub, ChatGroq, ChatPerplexity, ChatDeepSeek, ChatOpenRouter, ChatHuggingFace, ChatPortkey, ChatLMStudio, ChatMistral, ChatCloudflare, ChatAzureOpenAI, ChatAzureOpenAICompletions). Closes #190
…rs scope - Sync callable api_key is now wrapped with wrap_async() before passing to AsyncOpenAI/AsyncAzureOpenAI, which expect Callable[[], Awaitable[str]]. Without this, chat_async() would fail when api_key is a sync callable. - Clarified api_headers docstring: applies to chat API requests, not all API requests (list_models, batch, etc. use the client's default auth).
d3e5bbf to
1535c9a
Compare
ChatPortkey needs to serialize the API key into the x-portkey-api-key header at construction time, so callable api_key is not supported — raise a clear TypeError directing users to api_headers instead. Also adds test coverage for callable api_key with the async client, verifying the sync→async wrapping works correctly.
Comment on lines
+223
to
+226
| return self._client.responses.create( # type: ignore | ||
| **kwargs, | ||
| extra_headers=self._get_extra_headers(), | ||
| ) |
Comment on lines
+169
to
+172
| return self._client.chat.completions.create( # type: ignore | ||
| **kwargs, | ||
| extra_headers=self._get_extra_headers(), | ||
| ) |
Comment on lines
+49
to
+67
| class TestProviderApiHeaders: | ||
| """Test that api_headers flow through to the provider correctly.""" | ||
|
|
||
| def test_openai_provider_stores_api_headers(self, monkeypatch): | ||
| from chatlas._provider_openai_completions import OpenAICompletionsProvider | ||
|
|
||
| for k in ("HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"): | ||
| monkeypatch.delenv(k, raising=False) | ||
| monkeypatch.delenv(k.lower(), raising=False) | ||
|
|
||
| headers = {"Authorization": "Bearer dynamic-key"} | ||
| provider = OpenAICompletionsProvider( | ||
| api_key="dummy", | ||
| model="gpt-4o", | ||
| api_headers=headers, | ||
| ) | ||
| assert provider._api_headers is not None | ||
| assert provider._get_extra_headers() == headers | ||
|
|
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.
Closes #190
Why
Users integrating with internal APIs or services that rotate Bearer tokens couldn't easily authenticate with chatlas. The existing
kwargs={"default_headers": {...}}workaround was hard to discover and couldn't handle token refresh without recreating the chat object.What's new
api_headersparameter — a new top-level parameter on all OpenAI-compatible Chat functions. Accepts adict[str, str]of HTTP headers or a zero-argument callable returning one. Callables are invoked on every request, enabling dynamic patterns like token refresh:Callable
api_key—api_keynow acceptsCallable[[], str]in addition tostr, matching the openai SDK's native support. The SDK calls it before every request for per-request key resolution:Both features are available on:
ChatOpenAI,ChatOpenAICompletions,ChatGithub,ChatGroq,ChatPerplexity,ChatDeepSeek,ChatOpenRouter,ChatHuggingFace,ChatPortkey,ChatLMStudio,ChatMistral,ChatCloudflare,ChatAzureOpenAI,ChatAzureOpenAICompletions.Test plan
resolve_api_headers()(static dict, callable, error cases)api_headersflows through provider constructionapi_keyon both Responses and Completions APIs