use crate::error::LlmConnectorError;
use async_trait::async_trait;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ServiceTarget {
pub endpoint: Option<String>,
pub api_key: Option<String>,
pub model: String,
pub extra_headers: Option<HashMap<String, String>>,
}
impl ServiceTarget {
pub fn new(model: impl Into<String>) -> Self {
Self {
endpoint: None,
api_key: None,
model: model.into(),
extra_headers: None,
}
}
}
#[async_trait]
pub trait ServiceResolver: Send + Sync {
async fn resolve(&self, model: &str) -> Result<ServiceTarget, LlmConnectorError>;
}
pub struct EnvVarResolver {
mappings: HashMap<String, String>,
}
impl EnvVarResolver {
pub fn new() -> Self {
let mut mappings = HashMap::new();
mappings.insert("gpt".to_string(), "OPENAI_API_KEY".to_string());
mappings.insert("claude".to_string(), "ANTHROPIC_API_KEY".to_string());
mappings.insert("gemini".to_string(), "GOOGLE_API_KEY".to_string());
mappings.insert("deepseek".to_string(), "DEEPSEEK_API_KEY".to_string());
Self { mappings }
}
pub fn with_mapping(mut self, prefix: &str, env_var: &str) -> Self {
self.mappings.insert(prefix.to_string(), env_var.to_string());
self
}
}
#[async_trait]
impl ServiceResolver for EnvVarResolver {
async fn resolve(&self, model: &str) -> Result<ServiceTarget, LlmConnectorError> {
let mut target = ServiceTarget::new(model);
for (prefix, env_var) in &self.mappings {
if model.starts_with(prefix) {
if let Ok(key) = std::env::var(env_var) {
target.api_key = Some(key);
}
break;
}
}
Ok(target)
}
}
pub struct StaticResolver {
target: ServiceTarget,
}
impl StaticResolver {
pub fn new(target: ServiceTarget) -> Self {
Self { target }
}
}
#[async_trait]
impl ServiceResolver for StaticResolver {
async fn resolve(&self, _model: &str) -> Result<ServiceTarget, LlmConnectorError> {
Ok(self.target.clone())
}
}