use std::sync::OnceLock;
use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)]
#[allow(dead_code)]
pub struct ProviderManifest {
pub id: String,
pub label: String,
pub icon: String,
#[serde(default)]
pub docs_path: Option<String>,
#[serde(default)]
pub hook_aliases: Vec<String>,
#[serde(default)]
pub env_vars: Vec<String>,
#[serde(default)]
pub default_model: Option<String>,
#[serde(default)]
pub models: Vec<String>,
#[serde(default)]
pub catalog_url: Option<String>,
#[serde(default)]
pub auth: Vec<ManifestAuth>,
}
#[derive(Debug, Clone, Deserialize)]
#[allow(dead_code)]
pub struct ManifestAuth {
pub id: String,
pub label: String,
#[serde(default)]
pub hint: Option<String>,
pub kind: AuthKind,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AuthKind {
ApiKey,
ApiGlobal,
EnvVar,
Custom,
}
const PROVIDER_MANIFESTS: &[(&str, &str)] = &[
(
"anthropic",
include_str!("../../providers/anthropic.json"),
),
("openai", include_str!("../../providers/openai.json")),
("google", include_str!("../../providers/google.json")),
(
"deepseek",
include_str!("../../providers/deepseek.json"),
),
(
"openrouter",
include_str!("../../providers/openrouter.json"),
),
("azure", include_str!("../../providers/azure.json")),
("groq", include_str!("../../providers/groq.json")),
(
"together",
include_str!("../../providers/together.json"),
),
(
"minimax",
include_str!("../../providers/minimax.json"),
),
("kimi", include_str!("../../providers/kimi.json")),
("zai", include_str!("../../providers/zai.json")),
];
static MANIFESTS: OnceLock<Vec<ProviderManifest>> = OnceLock::new();
pub fn load_manifests() -> &'static Vec<ProviderManifest> {
MANIFESTS.get_or_init(|| {
let mut results = Vec::new();
for &(id, json) in PROVIDER_MANIFESTS {
match serde_json::from_str::<ProviderManifest>(json) {
Ok(m) => results.push(m),
Err(e) => eprintln!("Warning: failed to parse '{}': {}", id, e),
}
}
results
})
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_JSON: &str = r#"{
"id": "test",
"label": "Test",
"icon": "✅",
"models": ["model-a"],
"auth": [{"id": "key", "label": "API Key", "kind": "api_key"}]
}"#;
#[test]
fn test_parse_manifest() {
let m: ProviderManifest = serde_json::from_str(TEST_JSON).unwrap();
assert_eq!(m.id, "test");
assert_eq!(m.models.len(), 1);
}
}