use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum AuthMethod {
Bearer,
#[serde(rename = "x-api-key")]
XApiKey,
#[serde(rename = "api-key")]
ApiKey,
None,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BuiltinProviderEntry {
pub id: String,
pub display_name: String,
#[serde(default)]
pub aliases: Vec<String>,
pub api: String,
pub env_key: String,
#[serde(default)]
pub extra_env_keys: Vec<String>,
#[serde(default)]
pub base_url: String,
pub auth_method: AuthMethod,
#[serde(default)]
pub extra_headers: Vec<(String, String)>,
pub category: String,
pub description: String,
#[serde(default = "default_enabled")]
pub default_enabled: bool,
}
fn default_enabled() -> bool {
true
}
impl BuiltinProviderEntry {
pub fn all_env_keys(&self) -> impl Iterator<Item = &str> {
std::iter::once(self.env_key.as_str()).chain(self.extra_env_keys.iter().map(|s| s.as_str()))
}
pub fn is_openai_compatible(&self) -> bool {
matches!(self.api.as_str(), "openai-completions" | "openai-responses")
}
}
pub fn load_builtin_providers() -> &'static [BuiltinProviderEntry] {
crate::catalog::CatalogRoot::get().provider.as_slice()
}
pub fn builtin_providers_count() -> usize {
load_builtin_providers().len()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_providers_have_valid_auth_method() {
for p in load_builtin_providers() {
match p.auth_method {
AuthMethod::Bearer
| AuthMethod::XApiKey
| AuthMethod::ApiKey
| AuthMethod::None => {}
}
}
}
#[test]
fn all_providers_have_non_empty_env_key() {
for p in load_builtin_providers() {
assert!(!p.env_key.is_empty(), "Provider {} has empty env_key", p.id);
}
}
#[test]
fn openai_compatible_providers_use_bearer() {
for p in load_builtin_providers() {
if p.is_openai_compatible() {
assert_eq!(
p.auth_method,
AuthMethod::Bearer,
"OpenAI-compatible provider {} should use Bearer auth",
p.id
);
}
}
}
}