mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
a803790a10
## 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>
127 lines
4.2 KiB
Rust
127 lines
4.2 KiB
Rust
use std::fmt;
|
|
use std::sync::Arc;
|
|
|
|
use codex_api::Provider;
|
|
use codex_api::SharedAuthProvider;
|
|
use codex_login::AuthManager;
|
|
use codex_login::CodexAuth;
|
|
use codex_model_provider_info::ModelProviderInfo;
|
|
|
|
use crate::auth::auth_manager_for_provider;
|
|
use crate::auth::resolve_provider_auth;
|
|
|
|
/// Runtime provider abstraction used by model execution.
|
|
///
|
|
/// Implementations own provider-specific behavior for a model backend. The
|
|
/// `ModelProviderInfo` returned by `info` is the serialized/configured provider
|
|
/// metadata used by the default OpenAI-compatible implementation.
|
|
#[async_trait::async_trait]
|
|
pub trait ModelProvider: fmt::Debug + Send + Sync {
|
|
/// Returns the configured provider metadata.
|
|
fn info(&self) -> &ModelProviderInfo;
|
|
|
|
/// Returns the provider-scoped auth manager, when this provider uses one.
|
|
///
|
|
/// TODO(celia-oai): Make auth manager access internal to this crate so callers
|
|
/// resolve provider-specific auth only through `ModelProvider`. We first need
|
|
/// to think through whether Codex should have a unified provider-specific auth
|
|
/// manager throughout the codebase; that is a larger refactor than this change.
|
|
fn auth_manager(&self) -> Option<Arc<AuthManager>>;
|
|
|
|
/// Returns the current provider-scoped auth value, if one is configured.
|
|
async fn auth(&self) -> Option<CodexAuth>;
|
|
|
|
/// Returns provider configuration adapted for the API client.
|
|
async fn api_provider(&self) -> codex_protocol::error::Result<Provider> {
|
|
let auth = self.auth().await;
|
|
self.info()
|
|
.to_api_provider(auth.as_ref().map(CodexAuth::auth_mode))
|
|
}
|
|
|
|
/// Returns the auth provider used to attach request credentials.
|
|
async fn api_auth(&self) -> codex_protocol::error::Result<SharedAuthProvider> {
|
|
let auth = self.auth().await;
|
|
resolve_provider_auth(auth.as_ref(), self.info())
|
|
}
|
|
}
|
|
|
|
/// Shared runtime model provider handle.
|
|
pub type SharedModelProvider = Arc<dyn ModelProvider>;
|
|
|
|
/// Creates the default runtime model provider for configured provider metadata.
|
|
pub fn create_model_provider(
|
|
provider_info: ModelProviderInfo,
|
|
auth_manager: Option<Arc<AuthManager>>,
|
|
) -> SharedModelProvider {
|
|
let auth_manager = auth_manager_for_provider(auth_manager, &provider_info);
|
|
Arc::new(ConfiguredModelProvider {
|
|
info: provider_info,
|
|
auth_manager,
|
|
})
|
|
}
|
|
|
|
/// Runtime model provider backed by configured `ModelProviderInfo`.
|
|
#[derive(Clone, Debug)]
|
|
struct ConfiguredModelProvider {
|
|
info: ModelProviderInfo,
|
|
auth_manager: Option<Arc<AuthManager>>,
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl ModelProvider for ConfiguredModelProvider {
|
|
fn info(&self) -> &ModelProviderInfo {
|
|
&self.info
|
|
}
|
|
|
|
fn auth_manager(&self) -> Option<Arc<AuthManager>> {
|
|
self.auth_manager.clone()
|
|
}
|
|
|
|
async fn auth(&self) -> Option<CodexAuth> {
|
|
match self.auth_manager.as_ref() {
|
|
Some(auth_manager) => auth_manager.auth().await,
|
|
None => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::num::NonZeroU64;
|
|
|
|
use codex_protocol::config_types::ModelProviderAuthInfo;
|
|
|
|
use super::*;
|
|
|
|
fn provider_info_with_command_auth() -> ModelProviderInfo {
|
|
ModelProviderInfo {
|
|
auth: Some(ModelProviderAuthInfo {
|
|
command: "print-token".to_string(),
|
|
args: Vec::new(),
|
|
timeout_ms: NonZeroU64::new(5_000).expect("timeout should be non-zero"),
|
|
refresh_interval_ms: 300_000,
|
|
cwd: std::env::current_dir()
|
|
.expect("current dir should be available")
|
|
.try_into()
|
|
.expect("current dir should be absolute"),
|
|
}),
|
|
requires_openai_auth: false,
|
|
..ModelProviderInfo::create_openai_provider(/*base_url*/ None)
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn create_model_provider_builds_command_auth_manager_without_base_manager() {
|
|
let provider = create_model_provider(
|
|
provider_info_with_command_auth(),
|
|
/*auth_manager*/ None,
|
|
);
|
|
|
|
let auth_manager = provider
|
|
.auth_manager()
|
|
.expect("command auth provider should have an auth manager");
|
|
|
|
assert!(auth_manager.has_external_auth());
|
|
}
|
|
}
|