use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use super::types::{ApiKeyAuth, ApiKeyCredential, AuthLoginCallbacks, AuthModel, AuthResolveInput, AuthResult};
use super::types::{ModelAuth, OAuthAuth};
pub fn env_api_key_auth(name: impl Into<String>, env_vars: Vec<&'static str>) -> ApiKeyAuth {
let name = name.into();
ApiKeyAuth {
name: name.clone(),
resolve: Arc::new(move |input: AuthResolveInput| {
let env_vars = env_vars.clone();
Box::pin(async move {
if let Some(key) = input.credential.and_then(|c| c.key) {
return Some(AuthResult {
auth: ModelAuth {
api_key: Some(key),
headers: None,
base_url: None,
},
env: None,
source: Some("stored credential".to_string()),
});
}
for var in env_vars {
if let Some(value) = input.ctx.env(var).await {
return Some(AuthResult {
auth: ModelAuth {
api_key: Some(value),
headers: None,
base_url: None,
},
env: None,
source: Some(var.to_string()),
});
}
}
None
})
}),
login: Some(Arc::new(move |callbacks: Arc<dyn AuthLoginCallbacks>| {
let name = name.clone();
Box::pin(async move {
let key = callbacks
.prompt(super::types::AuthPrompt::Secret {
message: format!("Enter {name}"),
placeholder: None,
})
.await?;
Ok(ApiKeyCredential::new(key))
})
})),
}
}
pub fn lazy_oauth(name: impl Into<String>, load: OAuthLoader) -> OAuthAuth {
let name = name.into();
let inner: Arc<tokio::sync::Mutex<Option<Arc<OAuthAuth>>>> = Arc::new(tokio::sync::Mutex::new(None));
let load_login = load.clone();
let load_refresh = load.clone();
let load_to_auth = load;
let inner_login = inner.clone();
let inner_refresh = inner.clone();
let inner_to_auth = inner;
OAuthAuth {
name: name.clone(),
login: Arc::new(move |callbacks| {
let inner = inner_login.clone();
let load = load_login.clone();
Box::pin(async move {
let auth = loaded(&inner, &load).await;
(auth.login)(callbacks).await
})
}),
refresh: Arc::new(move |credential| {
let inner = inner_refresh.clone();
let load = load_refresh.clone();
Box::pin(async move {
let auth = loaded(&inner, &load).await;
(auth.refresh)(credential).await
})
}),
to_auth: Arc::new(move |credential| {
let inner = inner_to_auth.clone();
let load = load_to_auth.clone();
Box::pin(async move {
let auth = loaded(&inner, &load).await;
(auth.to_auth)(credential).await
})
}),
}
}
pub type OAuthLoader = Arc<dyn Fn() -> Pin<Box<dyn Future<Output = OAuthAuth> + Send>> + Send + Sync>;
async fn loaded(slot: &Arc<tokio::sync::Mutex<Option<Arc<OAuthAuth>>>>, load: &OAuthLoader) -> Arc<OAuthAuth> {
let mut guard = slot.lock().await;
if guard.is_none() {
*guard = Some(Arc::new(load().await));
}
guard.clone().unwrap()
}
pub fn auth_model_provider(model: &AuthModel) -> &str {
match model {
AuthModel::Chat(m) => &m.provider,
AuthModel::Images(m) => &m.provider,
}
}