Restore API key and query param overrides (#1826)

Addresses https://github.com/openai/codex/issues/1796
This commit is contained in:
pakrym-oai
2025-08-04 18:07:49 -07:00
committed by GitHub
parent e38ce39c51
commit 84bcadb8d9
5 changed files with 144 additions and 53 deletions

View File

@@ -5,8 +5,11 @@
//! 2. User-defined entries inside `~/.codex/config.toml` under the `model_providers`
//! key. These override or extend the defaults at runtime.
use codex_login::AuthMode;
use codex_login::CodexAuth;
use serde::Deserialize;
use serde::Serialize;
use std::borrow::Cow;
use std::collections::HashMap;
use std::env::VarError;
use std::time::Duration;
@@ -88,25 +91,30 @@ impl ModelProviderInfo {
/// When `require_api_key` is true and the provider declares an `env_key`
/// but the variable is missing/empty, returns an [`Err`] identical to the
/// one produced by [`ModelProviderInfo::api_key`].
pub fn create_request_builder<'a>(
pub async fn create_request_builder<'a>(
&'a self,
client: &'a reqwest::Client,
auth: &Option<CodexAuth>,
) -> crate::error::Result<reqwest::RequestBuilder> {
let url = self.get_full_url();
let auth: Cow<'_, Option<CodexAuth>> = if auth.is_some() {
Cow::Borrowed(auth)
} else {
Cow::Owned(self.get_fallback_auth()?)
};
let url = self.get_full_url(&auth);
let mut builder = client.post(url);
let api_key = self.api_key()?;
if let Some(key) = api_key {
builder = builder.bearer_auth(key);
if let Some(auth) = auth.as_ref() {
builder = builder.bearer_auth(auth.get_token().await?);
}
Ok(self.apply_http_headers(builder))
}
pub(crate) fn get_full_url(&self) -> String {
let query_string = self
.query_params
fn get_query_string(&self) -> String {
self.query_params
.as_ref()
.map_or_else(String::new, |params| {
let full_params = params
@@ -115,16 +123,29 @@ impl ModelProviderInfo {
.collect::<Vec<_>>()
.join("&");
format!("?{full_params}")
});
})
}
pub(crate) fn get_full_url(&self, auth: &Option<CodexAuth>) -> String {
let default_base_url = if matches!(
auth,
Some(CodexAuth {
mode: AuthMode::ChatGPT,
..
})
) {
"https://chatgpt.com/backend-api/codex"
} else {
"https://api.openai.com/v1"
};
let query_string = self.get_query_string();
let base_url = self
.base_url
.clone()
.unwrap_or("https://api.openai.com/v1".to_string());
.unwrap_or(default_base_url.to_string());
match self.wire_api {
WireApi::Responses => {
format!("{base_url}/responses{query_string}")
}
WireApi::Responses => format!("{base_url}/responses{query_string}"),
WireApi::Chat => format!("{base_url}/chat/completions{query_string}"),
}
}
@@ -132,10 +153,7 @@ impl ModelProviderInfo {
/// Apply provider-specific HTTP headers (both static and environment-based)
/// onto an existing `reqwest::RequestBuilder` and return the updated
/// builder.
pub fn apply_http_headers(
&self,
mut builder: reqwest::RequestBuilder,
) -> reqwest::RequestBuilder {
fn apply_http_headers(&self, mut builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
if let Some(extra) = &self.http_headers {
for (k, v) in extra {
builder = builder.header(k, v);
@@ -157,7 +175,7 @@ impl ModelProviderInfo {
/// If `env_key` is Some, returns the API key for this provider if present
/// (and non-empty) in the environment. If `env_key` is required but
/// cannot be found, returns an error.
fn api_key(&self) -> crate::error::Result<Option<String>> {
pub fn api_key(&self) -> crate::error::Result<Option<String>> {
match &self.env_key {
Some(env_key) => {
let env_value = std::env::var(env_key);
@@ -198,6 +216,14 @@ impl ModelProviderInfo {
.map(Duration::from_millis)
.unwrap_or(Duration::from_millis(DEFAULT_STREAM_IDLE_TIMEOUT_MS))
}
fn get_fallback_auth(&self) -> crate::error::Result<Option<CodexAuth>> {
let api_key = self.api_key()?;
if let Some(api_key) = api_key {
return Ok(Some(CodexAuth::from_api_key(api_key)));
}
Ok(None)
}
}
/// Built-in default provider list.