use crate::config::{Config, ModelCfg};
use crate::providers::{anthropic::AnthropicProvider, openai::OpenAiProvider, Provider};
use crate::secrets::SecretStore;
use std::collections::HashMap;
use std::sync::Arc;
pub struct Registry {
by_id: HashMap<String, Arc<dyn Provider>>,
}
impl Registry {
pub fn from_config(cfg: &Config, secrets: &SecretStore) -> anyhow::Result<Self> {
let mut by_id: HashMap<String, Arc<dyn Provider>> = HashMap::new();
for m in &cfg.models {
let api_key = m
.api_key_env
.as_ref()
.and_then(|name| secrets.resolve(name));
let provider: Arc<dyn Provider> = match m.provider.as_str() {
"openai" | "ollama" | "http" => Arc::new(OpenAiProvider::new(m, api_key)?),
"anthropic" => Arc::new(AnthropicProvider::new(m, api_key)?),
other => anyhow::bail!("unknown provider '{}' for model '{}'", other, m.id),
};
by_id.insert(m.id.clone(), provider);
}
if cfg.cmf.manage_server
&& !cfg.cmf.local_model.trim().is_empty()
&& !by_id.contains_key(&cfg.cmf.model_id)
{
let m = ModelCfg {
id: cfg.cmf.model_id.clone(),
provider: "openai".into(),
base_url: format!("http://{}:{}/v1", cfg.cmf.local_host, cfg.cmf.local_port),
model: "cortiq".into(),
cost_tier: "local".into(),
price_in: 0.0,
price_out: 0.0,
kind: "chat".into(),
api_key_env: None,
caps: Vec::new(),
};
by_id.insert(m.id.clone(), Arc::new(OpenAiProvider::new(&m, None)?));
}
Ok(Self { by_id })
}
pub fn any_id(&self) -> Option<String> {
self.by_id.keys().next().cloned()
}
pub fn get(&self, model_id: &str) -> Option<Arc<dyn Provider>> {
self.by_id.get(model_id).cloned()
}
pub fn ids(&self) -> impl Iterator<Item = &String> {
self.by_id.keys()
}
}