sqlite_graphrag/config/
api_keys.rs1use super::store::load_config;
7use super::ResolvedKey;
8use secrecy::SecretBox;
9
10pub fn resolve_api_key(provider: &str, cli_key: Option<&str>) -> Option<ResolvedKey> {
12 if let Some(k) = cli_key {
14 if !k.is_empty() {
15 return Some(ResolvedKey {
16 value: SecretBox::new(Box::new(k.to_owned())),
17 source: "cli",
18 });
19 }
20 }
21
22 if let Ok(cfg) = load_config() {
23 if let Some(entry) = cfg.keys.iter().find(|k| k.provider == provider) {
24 return Some(ResolvedKey {
25 value: SecretBox::new(Box::new(entry.value.clone())),
26 source: "config",
27 });
28 }
29 }
30
31 None
32}
33
34pub fn compute_fingerprint(key: &str) -> String {
36 let hash = blake3::hash(key.as_bytes());
37 hash.to_hex()[..16].to_string()
38}
39
40pub fn mask_key(key: &str) -> String {
42 if key.len() <= 8 {
43 return "****".to_string();
44 }
45 format!("{}...{}", &key[..4], &key[key.len() - 4..])
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51 use secrecy::ExposeSecret;
52 #[test]
53 fn compute_fingerprint_deterministic() {
54 let fp1 = compute_fingerprint("sk-or-v1-test-key-12345");
55 let fp2 = compute_fingerprint("sk-or-v1-test-key-12345");
56 assert_eq!(fp1, fp2);
57 assert_eq!(fp1.len(), 16);
58 }
59
60 #[test]
61 fn compute_fingerprint_differs_for_different_keys() {
62 let fp1 = compute_fingerprint("key-a");
63 let fp2 = compute_fingerprint("key-b");
64 assert_ne!(fp1, fp2);
65 }
66
67 #[test]
68 fn mask_key_short() {
69 assert_eq!(mask_key("abcd"), "****");
70 assert_eq!(mask_key("12345678"), "****");
71 assert_eq!(mask_key(""), "****");
72 }
73
74 #[test]
75 fn mask_key_normal() {
76 assert_eq!(mask_key("sk-or-v1-abcdef1234"), "sk-o...1234");
77 }
78
79 #[test]
80 fn resolve_api_key_cli_wins() {
81 let resolved = resolve_api_key("openrouter", Some("cli-key-value"));
82 assert!(resolved.is_some());
83 let r = resolved.unwrap();
84 assert_eq!(r.source, "cli");
85 assert_eq!(r.value.expose_secret(), "cli-key-value");
86 }
87
88 #[test]
89 fn resolve_api_key_cli_fallback() {
90 let resolved = resolve_api_key("nonexistent-provider", Some("cli-key"));
91 assert!(resolved.is_some());
92 let r = resolved.unwrap();
93 assert_eq!(r.source, "cli");
94 assert_eq!(r.value.expose_secret(), "cli-key");
95 }
96
97 #[test]
98 fn resolve_api_key_none_when_nothing_available() {
99 let resolved = resolve_api_key("totally-unknown-provider-xyz-no-key", None);
100 if let Some(r) = resolved {
102 assert_eq!(r.source, "config");
103 }
104 }
105
106 #[test]
107 fn resolve_api_key_ignores_product_env() {
108 unsafe {
110 std::env::set_var("OPENROUTER_API_KEY", "env-must-be-ignored");
111 }
112 let resolved = resolve_api_key("openrouter-env-ignore-test-provider", None);
113 assert!(resolved.is_none(), "product env must not supply API keys");
114 unsafe {
115 std::env::remove_var("OPENROUTER_API_KEY");
116 }
117 }
118}