mirror of
https://github.com/openai/codex.git
synced 2026-11-13 22:47:20 +00:00
feat: add opt-in provider runtime abstraction (#17713)
## Summary - Add `codex-model-provider` as the runtime home for model-provider behavior that does not belong in `codex-core`, `codex-login`, or `codex-api`. - The new crate wraps configured `ModelProviderInfo` in a `ModelProvider` trait object that can resolve the API provider config, provider-scoped auth manager, and request auth provider for each call. - This centralizes provider auth behavior in one place today, and gives us an extension point for future provider-specific auth, model listing, request setup, and related runtime behavior. ## Tests Ran tests manually to make sure that provider auth under different configs still work as expected. --------- Co-authored-by: pakrym-oai <pakrym@openai.com>
This commit is contained in:
1 parent
91e8eebd03
commit
a803790a10
45 files changed
+577
-369
No files matched your search
@@ -7,6 +7,7 @@ use codex_api::ModelsClient;
|
||||
use codex_api::RequestTelemetry;
|
||||
use codex_api::ReqwestTransport;
|
||||
use codex_api::TransportError;
|
||||
use codex_api::auth_header_telemetry;
|
||||
use codex_api::map_api_error;
|
||||
use codex_app_server_protocol::AuthMode;
|
||||
use codex_feedback::FeedbackRequestTags;
|
||||
@@ -14,10 +15,10 @@ use codex_feedback::emit_feedback_request_tags_with_auth_env;
|
||||
use codex_login::AuthEnvTelemetry;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::auth_provider_from_auth;
|
||||
use codex_login::collect_auth_env_telemetry;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use codex_login::required_auth_manager_for_provider;
|
||||
use codex_model_provider::SharedModelProvider;
|
||||
use codex_model_provider::create_model_provider;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
use codex_otel::TelemetryAuthMode;
|
||||
use codex_protocol::config_types::CollaborationModeMask;
|
||||
@@ -178,10 +179,9 @@ pub struct ModelsManager {
|
||||
remote_models: RwLock<Vec<ModelInfo>>,
|
||||
catalog_mode: CatalogMode,
|
||||
collaboration_modes_config: CollaborationModesConfig,
|
||||
auth_manager: Arc<AuthManager>,
|
||||
etag: RwLock<Option<String>>,
|
||||
cache_manager: ModelsCacheManager,
|
||||
provider: ModelProviderInfo,
|
||||
provider: SharedModelProvider,
|
||||
}
|
||||
|
||||
impl ModelsManager {
|
||||
@@ -206,14 +206,17 @@ impl ModelsManager {
|
||||
}
|
||||
|
||||
/// Construct a manager with an explicit provider used for remote model refreshes.
|
||||
// TODO(celia-oai): Revisit this ownership direction: the model provider should likely
|
||||
// own or return the models manager instead of requiring the manager to construct and use
|
||||
// a provider from provider info.
|
||||
pub fn new_with_provider(
|
||||
codex_home: PathBuf,
|
||||
auth_manager: Arc<AuthManager>,
|
||||
model_catalog: Option<ModelsResponse>,
|
||||
collaboration_modes_config: CollaborationModesConfig,
|
||||
provider: ModelProviderInfo,
|
||||
provider_info: ModelProviderInfo,
|
||||
) -> Self {
|
||||
let auth_manager = required_auth_manager_for_provider(auth_manager, &provider);
|
||||
let model_provider = create_model_provider(provider_info, Some(auth_manager));
|
||||
let cache_path = codex_home.join(MODEL_CACHE_FILE);
|
||||
let cache_manager = ModelsCacheManager::new(cache_path, DEFAULT_MODEL_CACHE_TTL);
|
||||
let catalog_mode = if model_catalog.is_some() {
|
||||
@@ -228,10 +231,9 @@ impl ModelsManager {
|
||||
remote_models: RwLock::new(remote_models),
|
||||
catalog_mode,
|
||||
collaboration_modes_config,
|
||||
auth_manager,
|
||||
etag: RwLock::new(None),
|
||||
cache_manager,
|
||||
provider,
|
||||
provider: model_provider,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,9 +397,11 @@ impl ModelsManager {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.auth_manager.auth_mode() != Some(AuthMode::Chatgpt)
|
||||
&& !self.provider.has_command_auth()
|
||||
{
|
||||
let auth_mode = self
|
||||
.provider
|
||||
.auth_manager()
|
||||
.and_then(|auth_manager| auth_manager.auth_mode());
|
||||
if auth_mode != Some(AuthMode::Chatgpt) && !self.provider.info().has_command_auth() {
|
||||
if matches!(
|
||||
refresh_strategy,
|
||||
RefreshStrategy::Offline | RefreshStrategy::OnlineIfUncached
|
||||
@@ -432,19 +436,21 @@ impl ModelsManager {
|
||||
async fn fetch_and_update_models(&self) -> CoreResult<()> {
|
||||
let _timer =
|
||||
codex_otel::start_global_timer("codex.remote_models.fetch_update.duration_ms", &[]);
|
||||
let auth = self.auth_manager.auth().await;
|
||||
let auth_manager = self.provider.auth_manager();
|
||||
let codex_api_key_env_enabled = auth_manager
|
||||
.as_ref()
|
||||
.is_some_and(|auth_manager| auth_manager.codex_api_key_env_enabled());
|
||||
let auth = self.provider.auth().await;
|
||||
let auth_mode = auth.as_ref().map(CodexAuth::auth_mode);
|
||||
let api_provider = self.provider.to_api_provider(auth_mode)?;
|
||||
let api_auth = auth_provider_from_auth(auth.clone(), &self.provider)?;
|
||||
let auth_env = collect_auth_env_telemetry(
|
||||
&self.provider,
|
||||
self.auth_manager.codex_api_key_env_enabled(),
|
||||
);
|
||||
let api_provider = self.provider.api_provider().await?;
|
||||
let api_auth = self.provider.api_auth().await?;
|
||||
let auth_env = collect_auth_env_telemetry(self.provider.info(), codex_api_key_env_enabled);
|
||||
let transport = ReqwestTransport::new(build_reqwest_client());
|
||||
let auth_telemetry = auth_header_telemetry(api_auth.as_ref());
|
||||
let request_telemetry: Arc<dyn RequestTelemetry> = Arc::new(ModelsRequestTelemetry {
|
||||
auth_mode: auth_mode.map(|mode| TelemetryAuthMode::from(mode).to_string()),
|
||||
auth_header_attached: api_auth.auth_header_attached(),
|
||||
auth_header_name: api_auth.auth_header_name(),
|
||||
auth_header_attached: auth_telemetry.attached,
|
||||
auth_header_name: auth_telemetry.name,
|
||||
auth_env,
|
||||
});
|
||||
let client = ModelsClient::new(transport, api_provider, api_auth)
|
||||
@@ -520,7 +526,11 @@ impl ModelsManager {
|
||||
remote_models.sort_by(|a, b| a.priority.cmp(&b.priority));
|
||||
|
||||
let mut presets: Vec<ModelPreset> = remote_models.into_iter().map(Into::into).collect();
|
||||
let chatgpt_mode = matches!(self.auth_manager.auth_mode(), Some(AuthMode::Chatgpt));
|
||||
let auth_mode = self
|
||||
.provider
|
||||
.auth_manager()
|
||||
.and_then(|auth_manager| auth_manager.auth_mode());
|
||||
let chatgpt_mode = matches!(auth_mode, Some(AuthMode::Chatgpt));
|
||||
presets = ModelPreset::filter_by_auth(presets, chatgpt_mode);
|
||||
|
||||
ModelPreset::mark_default_by_picker_visibility(&mut presets);
|
||||
|
||||
Reference in new issue
Block a user