//! Registry of model providers supported by Codex. //! //! Providers can be defined in two places: //! 1. Built-in defaults compiled into the binary so Codex works out-of-the-box. //! 2. User-defined entries inside `~/.codex/config.toml` under the `model_providers` //! key. These override or extend the defaults at runtime. use serde::Deserialize; use serde::Serialize; use std::collections::HashMap; use std::env::VarError; use crate::error::EnvVarError; use crate::openai_api_key::get_openai_api_key; /// Wire protocol that the provider speaks. Most third-party services only /// implement the classic OpenAI Chat Completions JSON schema, whereas OpenAI /// itself (and a handful of others) additionally expose the more modern /// *Responses* API. The two protocols use different request/response shapes /// and *cannot* be auto-detected at runtime, therefore each provider entry /// must declare which one it expects. #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum WireApi { /// The experimental “Responses” API exposed by OpenAI at `/v1/responses`. #[default] Responses, /// Regular Chat Completions compatible with `/v1/chat/completions`. Chat, } /// Serializable representation of a provider definition. #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] pub struct ModelProviderInfo { /// Friendly display name. pub name: String, /// Base URL for the provider's OpenAI-compatible API. pub base_url: String, /// Environment variable that stores the user's API key for this provider. pub env_key: Option, /// Optional instructions to help the user get a valid value for the /// variable and set it. pub env_key_instructions: Option, /// Which wire protocol this provider expects. pub wire_api: WireApi, } 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. pub fn api_key(&self) -> crate::error::Result> { match &self.env_key { Some(env_key) => { let env_value = if env_key == crate::openai_api_key::OPENAI_API_KEY_ENV_VAR { get_openai_api_key().map_or_else(|| Err(VarError::NotPresent), Ok) } else { std::env::var(env_key) }; env_value .and_then(|v| { if v.trim().is_empty() { Err(VarError::NotPresent) } else { Ok(Some(v)) } }) .map_err(|_| { crate::error::CodexErr::EnvVar(EnvVarError { var: env_key.clone(), instructions: self.env_key_instructions.clone(), }) }) } None => Ok(None), } } } /// Built-in default provider list. pub fn built_in_model_providers() -> HashMap { use ModelProviderInfo as P; // We do not want to be in the business of adjucating which third-party // providers are bundled with Codex CLI, so we only include the OpenAI // provider by default. Users are encouraged to add to `model_providers` // in config.toml to add their own providers. [ ( "openai", P { name: "OpenAI".into(), base_url: "https://api.openai.com/v1".into(), env_key: Some("OPENAI_API_KEY".into()), env_key_instructions: Some("Create an API key (https://platform.openai.com) and export it as an environment variable.".into()), wire_api: WireApi::Responses, }, ), ] .into_iter() .map(|(k, v)| (k.to_string(), v)) .collect() }