mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
@@ -217,6 +217,7 @@ model_reasoning_summary = "detailed"
|
|||||||
- `CODEX_HOME`: Override config directory location
|
- `CODEX_HOME`: Override config directory location
|
||||||
- `OPENAI_API_KEY`: Use API key instead of ChatGPT auth
|
- `OPENAI_API_KEY`: Use API key instead of ChatGPT auth
|
||||||
- `OPENAI_BASE_URL`: Use alternative API endpoints
|
- `OPENAI_BASE_URL`: Use alternative API endpoints
|
||||||
|
- `OPENAI_WIRE_API`: Force the built-in OpenAI provider to use `chat` or `responses` wiring
|
||||||
|
|
||||||
 
|
 
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|||||||
+1
-1
@@ -123,7 +123,7 @@ How long Codex will wait for activity on a streaming response before treating th
|
|||||||
|
|
||||||
## model_provider
|
## model_provider
|
||||||
|
|
||||||
Identifies which provider to use from the `model_providers` map. Defaults to `"openai"`. You can override the `base_url` for the built-in `openai` provider via the `OPENAI_BASE_URL` environment variable.
|
Identifies which provider to use from the `model_providers` map. Defaults to `"openai"`. You can override the `base_url` for the built-in `openai` provider via the `OPENAI_BASE_URL` environment variable and force the wire protocol (`"responses"` or `"chat"`) with `OPENAI_WIRE_API`.
|
||||||
|
|
||||||
Note that if you override `model_provider`, then you likely want to override
|
Note that if you override `model_provider`, then you likely want to override
|
||||||
`model`, as well. For example, if you are running ollama with Mistral locally,
|
`model`, as well. For example, if you are running ollama with Mistral locally,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use tokio::time::timeout;
|
|||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
|
use crate::auth::AuthManager;
|
||||||
use crate::ModelProviderInfo;
|
use crate::ModelProviderInfo;
|
||||||
use crate::client_common::Prompt;
|
use crate::client_common::Prompt;
|
||||||
use crate::client_common::ResponseEvent;
|
use crate::client_common::ResponseEvent;
|
||||||
@@ -26,6 +27,7 @@ use crate::model_family::ModelFamily;
|
|||||||
use crate::openai_tools::create_tools_json_for_chat_completions_api;
|
use crate::openai_tools::create_tools_json_for_chat_completions_api;
|
||||||
use crate::util::backoff;
|
use crate::util::backoff;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use codex_protocol::mcp_protocol::AuthMode;
|
||||||
use codex_protocol::models::ContentItem;
|
use codex_protocol::models::ContentItem;
|
||||||
use codex_protocol::models::ReasoningItemContent;
|
use codex_protocol::models::ReasoningItemContent;
|
||||||
use codex_protocol::models::ResponseItem;
|
use codex_protocol::models::ResponseItem;
|
||||||
@@ -38,6 +40,7 @@ pub(crate) async fn stream_chat_completions(
|
|||||||
client: &reqwest::Client,
|
client: &reqwest::Client,
|
||||||
provider: &ModelProviderInfo,
|
provider: &ModelProviderInfo,
|
||||||
debug_logger: &Arc<Mutex<DebugLogger>>,
|
debug_logger: &Arc<Mutex<DebugLogger>>,
|
||||||
|
auth_manager: Option<Arc<AuthManager>>,
|
||||||
) -> Result<ResponseStream> {
|
) -> Result<ResponseStream> {
|
||||||
if prompt.output_schema.is_some() {
|
if prompt.output_schema.is_some() {
|
||||||
return Err(CodexErr::UnsupportedOperation(
|
return Err(CodexErr::UnsupportedOperation(
|
||||||
@@ -333,7 +336,16 @@ pub(crate) async fn stream_chat_completions(
|
|||||||
loop {
|
loop {
|
||||||
attempt += 1;
|
attempt += 1;
|
||||||
|
|
||||||
let req_builder = provider.create_request_builder(client, &None).await?;
|
let auth = auth_manager.as_ref().and_then(|m| m.auth());
|
||||||
|
let mut req_builder = provider.create_request_builder(client, &auth).await?;
|
||||||
|
|
||||||
|
if let Some(auth) = auth.as_ref() {
|
||||||
|
if auth.mode == AuthMode::ChatGPT {
|
||||||
|
if let Some(account_id) = auth.get_account_id() {
|
||||||
|
req_builder = req_builder.header("chatgpt-account-id", account_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let res = req_builder
|
let res = req_builder
|
||||||
.header(reqwest::header::ACCEPT, "text/event-stream")
|
.header(reqwest::header::ACCEPT, "text/event-stream")
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ impl ModelClient {
|
|||||||
&self.client,
|
&self.client,
|
||||||
&self.provider,
|
&self.provider,
|
||||||
&self.debug_logger,
|
&self.debug_logger,
|
||||||
|
self.auth_manager.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use serde::Serialize;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env::VarError;
|
use std::env::VarError;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::error::EnvVarError;
|
use crate::error::EnvVarError;
|
||||||
const DEFAULT_STREAM_IDLE_TIMEOUT_MS: u64 = 300_000;
|
const DEFAULT_STREAM_IDLE_TIMEOUT_MS: u64 = 300_000;
|
||||||
@@ -23,6 +24,31 @@ const MAX_STREAM_MAX_RETRIES: u64 = 100;
|
|||||||
/// Hard cap for user-configured `request_max_retries`.
|
/// Hard cap for user-configured `request_max_retries`.
|
||||||
const MAX_REQUEST_MAX_RETRIES: u64 = 100;
|
const MAX_REQUEST_MAX_RETRIES: u64 = 100;
|
||||||
|
|
||||||
|
fn openai_wire_api_override() -> Option<WireApi> {
|
||||||
|
let raw = std::env::var("OPENAI_WIRE_API").ok()?;
|
||||||
|
parse_openai_wire_api(&raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_openai_wire_api(raw: &str) -> Option<WireApi> {
|
||||||
|
let trimmed = raw.trim();
|
||||||
|
if trimmed.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let normalized = trimmed.to_ascii_lowercase();
|
||||||
|
match normalized.as_str() {
|
||||||
|
"chat" => Some(WireApi::Chat),
|
||||||
|
"responses" => Some(WireApi::Responses),
|
||||||
|
_ => {
|
||||||
|
warn!(
|
||||||
|
override_value = trimmed,
|
||||||
|
"Ignoring invalid OPENAI_WIRE_API; expected 'chat' or 'responses'"
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Wire protocol that the provider speaks. Most third-party services only
|
/// Wire protocol that the provider speaks. Most third-party services only
|
||||||
/// implement the classic OpenAI Chat Completions JSON schema, whereas OpenAI
|
/// implement the classic OpenAI Chat Completions JSON schema, whereas OpenAI
|
||||||
/// itself (and a handful of others) additionally expose the more modern
|
/// itself (and a handful of others) additionally expose the more modern
|
||||||
@@ -278,46 +304,54 @@ pub fn built_in_model_providers() -> HashMap<String, ModelProviderInfo> {
|
|||||||
[
|
[
|
||||||
(
|
(
|
||||||
"openai",
|
"openai",
|
||||||
P {
|
{
|
||||||
name: "OpenAI".into(),
|
let mut provider = P {
|
||||||
// Allow users to override the default OpenAI endpoint by
|
name: "OpenAI".into(),
|
||||||
// exporting `OPENAI_BASE_URL`. This is useful when pointing
|
// Allow users to override the default OpenAI endpoint by
|
||||||
// Codex at a proxy, mock server, or Azure-style deployment
|
// exporting `OPENAI_BASE_URL`. This is useful when pointing
|
||||||
// without requiring a full TOML override for the built-in
|
// Codex at a proxy, mock server, or Azure-style deployment
|
||||||
// OpenAI provider.
|
// without requiring a full TOML override for the built-in
|
||||||
base_url: std::env::var("OPENAI_BASE_URL")
|
// OpenAI provider.
|
||||||
.ok()
|
base_url: std::env::var("OPENAI_BASE_URL")
|
||||||
.filter(|v| !v.trim().is_empty()),
|
.ok()
|
||||||
env_key: None,
|
.filter(|v| !v.trim().is_empty()),
|
||||||
env_key_instructions: None,
|
env_key: None,
|
||||||
wire_api: WireApi::Responses,
|
env_key_instructions: None,
|
||||||
query_params: None,
|
wire_api: WireApi::Responses,
|
||||||
http_headers: Some(
|
query_params: None,
|
||||||
[
|
http_headers: Some(
|
||||||
(
|
[
|
||||||
"version".to_string(),
|
(
|
||||||
codex_version::version().to_string(),
|
"version".to_string(),
|
||||||
),
|
codex_version::version().to_string(),
|
||||||
]
|
),
|
||||||
.into_iter()
|
]
|
||||||
.collect(),
|
.into_iter()
|
||||||
),
|
.collect(),
|
||||||
env_http_headers: Some(
|
),
|
||||||
[
|
env_http_headers: Some(
|
||||||
(
|
[
|
||||||
"OpenAI-Organization".to_string(),
|
(
|
||||||
"OPENAI_ORGANIZATION".to_string(),
|
"OpenAI-Organization".to_string(),
|
||||||
),
|
"OPENAI_ORGANIZATION".to_string(),
|
||||||
("OpenAI-Project".to_string(), "OPENAI_PROJECT".to_string()),
|
),
|
||||||
]
|
("OpenAI-Project".to_string(), "OPENAI_PROJECT".to_string()),
|
||||||
.into_iter()
|
]
|
||||||
.collect(),
|
.into_iter()
|
||||||
),
|
.collect(),
|
||||||
// Use global defaults for retry/timeout unless overridden in config.toml.
|
),
|
||||||
request_max_retries: None,
|
// Use global defaults for retry/timeout unless overridden in config.toml.
|
||||||
stream_max_retries: None,
|
request_max_retries: None,
|
||||||
stream_idle_timeout_ms: None,
|
stream_max_retries: None,
|
||||||
requires_openai_auth: true,
|
stream_idle_timeout_ms: None,
|
||||||
|
requires_openai_auth: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(wire_api) = openai_wire_api_override() {
|
||||||
|
provider.wire_api = wire_api;
|
||||||
|
}
|
||||||
|
|
||||||
|
provider
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(BUILT_IN_OSS_MODEL_PROVIDER_ID, create_oss_provider()),
|
(BUILT_IN_OSS_MODEL_PROVIDER_ID, create_oss_provider()),
|
||||||
@@ -468,6 +502,18 @@ env_http_headers = { "X-Example-Env-Header" = "EXAMPLE_ENV_VAR" }
|
|||||||
assert_eq!(expected_provider, provider);
|
assert_eq!(expected_provider, provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn openai_wire_api_override_parser_handles_values() {
|
||||||
|
assert_eq!(super::parse_openai_wire_api("chat"), Some(WireApi::Chat));
|
||||||
|
assert_eq!(
|
||||||
|
super::parse_openai_wire_api(" responses "),
|
||||||
|
Some(WireApi::Responses)
|
||||||
|
);
|
||||||
|
assert_eq!(super::parse_openai_wire_api(""), None);
|
||||||
|
assert_eq!(super::parse_openai_wire_api(" \t"), None);
|
||||||
|
assert_eq!(super::parse_openai_wire_api("invalid"), None);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn detects_azure_responses_base_urls() {
|
fn detects_azure_responses_base_urls() {
|
||||||
fn provider_for(base_url: &str) -> ModelProviderInfo {
|
fn provider_for(base_url: &str) -> ModelProviderInfo {
|
||||||
|
|||||||
+1
-1
@@ -133,7 +133,7 @@ How long Codex will wait for activity on a streaming response before treating th
|
|||||||
|
|
||||||
## model_provider
|
## model_provider
|
||||||
|
|
||||||
Identifies which provider to use from the `model_providers` map. Defaults to `"openai"`. You can override the `base_url` for the built-in `openai` provider via the `OPENAI_BASE_URL` environment variable.
|
Identifies which provider to use from the `model_providers` map. Defaults to `"openai"`. You can override the `base_url` for the built-in `openai` provider via the `OPENAI_BASE_URL` environment variable and force the wire protocol (`"responses"` or `"chat"`) with `OPENAI_WIRE_API`.
|
||||||
|
|
||||||
Note that if you override `model_provider`, then you likely want to override
|
Note that if you override `model_provider`, then you likely want to override
|
||||||
`model`, as well. For example, if you are running ollama with Mistral locally,
|
`model`, as well. For example, if you are running ollama with Mistral locally,
|
||||||
|
|||||||
Reference in New Issue
Block a user