bitrouter_config/
detect.rs1use std::collections::HashMap;
2
3use crate::registry::builtin_provider_defs;
4
5#[derive(Debug, Clone)]
7pub struct DetectedProvider {
8 pub name: String,
10 pub env_prefix: String,
12 pub api_key_var: String,
14 pub has_base_url: bool,
16}
17
18pub fn detect_providers(env: &HashMap<String, String>) -> Vec<DetectedProvider> {
22 let defs = builtin_provider_defs();
23 let mut detected: Vec<DetectedProvider> = defs
24 .iter()
25 .filter_map(|(name, bp)| {
26 let prefix = bp.config.env_prefix.as_deref()?;
27 let key_var = format!("{prefix}_API_KEY");
28 let key_value = env.get(&key_var).map(|v| v.as_str()).unwrap_or("");
29 if key_value.is_empty() {
30 return None;
31 }
32 let base_var = format!("{prefix}_BASE_URL");
33 let has_base_url = env.get(&base_var).map(|v| !v.is_empty()).unwrap_or(false);
34 Some(DetectedProvider {
35 name: name.clone(),
36 env_prefix: prefix.to_owned(),
37 api_key_var: key_var,
38 has_base_url,
39 })
40 })
41 .collect();
42 detected.sort_by(|a, b| a.name.cmp(&b.name));
43 detected
44}
45
46pub fn detect_providers_from_env() -> Vec<DetectedProvider> {
48 let env: HashMap<String, String> = std::env::vars().collect();
49 detect_providers(&env)
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn detects_provider_with_key() {
58 let env = HashMap::from([("OPENAI_API_KEY".into(), "sk-test".into())]);
59 let detected = detect_providers(&env);
60 assert_eq!(detected.len(), 1);
61 assert_eq!(detected[0].name, "openai");
62 assert_eq!(detected[0].api_key_var, "OPENAI_API_KEY");
63 assert!(!detected[0].has_base_url);
64 }
65
66 #[test]
67 fn detects_base_url() {
68 let env = HashMap::from([
69 ("OPENAI_API_KEY".into(), "sk-test".into()),
70 ("OPENAI_BASE_URL".into(), "https://proxy.com/v1".into()),
71 ]);
72 let detected = detect_providers(&env);
73 assert!(detected[0].has_base_url);
74 }
75
76 #[test]
77 fn empty_key_not_detected() {
78 let env = HashMap::from([("OPENAI_API_KEY".into(), "".into())]);
79 let detected = detect_providers(&env);
80 assert!(detected.is_empty());
81 }
82
83 #[test]
84 fn no_keys_returns_empty() {
85 let env = HashMap::new();
86 let detected = detect_providers(&env);
87 assert!(detected.is_empty());
88 }
89
90 #[test]
91 fn multiple_providers() {
92 let env = HashMap::from([
93 ("OPENAI_API_KEY".into(), "sk-test".into()),
94 ("ANTHROPIC_API_KEY".into(), "sk-ant-test".into()),
95 ("GOOGLE_API_KEY".into(), "goog-test".into()),
96 ]);
97 let detected = detect_providers(&env);
98 assert_eq!(detected.len(), 3);
99 assert_eq!(detected[0].name, "anthropic");
101 assert_eq!(detected[1].name, "google");
102 assert_eq!(detected[2].name, "openai");
103 }
104}