auto_commit/api/
mod.rs

1mod client;
2mod gemini;
3mod openai_compatible;
4mod provider;
5
6pub use client::LlmClient;
7pub use gemini::GeminiClient;
8pub use openai_compatible::OpenAiCompatibleClient;
9pub use provider::Provider;
10
11use anyhow::{Context, Result};
12
13/// Create an LLM client based on available API keys
14///
15/// Priority order: OPENAI_API_KEY > DEEPSEEK_API_KEY > GEMINI_API_KEY
16pub fn create_client() -> Result<Box<dyn LlmClient>> {
17    let (provider, api_key) =
18        Provider::detect().context("No API key found. Set OPENAI_API_KEY, DEEPSEEK_API_KEY, or GEMINI_API_KEY")?;
19
20    Ok(create_client_for_provider(provider, api_key))
21}
22
23/// Create a client for a specific provider
24pub fn create_client_for_provider(provider: Provider, api_key: String) -> Box<dyn LlmClient> {
25    match provider {
26        Provider::OpenAi | Provider::DeepSeek => {
27            Box::new(OpenAiCompatibleClient::new(provider, api_key))
28        }
29        Provider::Gemini => Box::new(GeminiClient::new(api_key)),
30    }
31}
32
33// Re-export for backward compatibility
34pub use client::{build_prompt, parse_commit_message};
35
36// Legacy types for backward compatibility (deprecated)
37#[deprecated(since = "2.0.0", note = "Use OpenAiCompatibleClient or GeminiClient instead")]
38pub type DeepSeekClient = OpenAiCompatibleClient;
39
40#[deprecated(since = "2.0.0", note = "Use OpenAiCompatibleClient or GeminiClient instead")]
41pub type DeepSeekRequest = ();
42
43#[deprecated(since = "2.0.0", note = "Use OpenAiCompatibleClient or GeminiClient instead")]
44pub type DeepSeekResponse = ();
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use serial_test::serial;
50
51    fn clear_env_keys() {
52        std::env::remove_var("OPENAI_API_KEY");
53        std::env::remove_var("DEEPSEEK_API_KEY");
54        std::env::remove_var("GEMINI_API_KEY");
55    }
56
57    #[test]
58    #[serial]
59    fn test_create_client_openai() {
60        clear_env_keys();
61        std::env::set_var("OPENAI_API_KEY", "sk-test");
62
63        let client = create_client().unwrap();
64        assert_eq!(client.provider_name(), "OpenAI");
65
66        clear_env_keys();
67    }
68
69    #[test]
70    #[serial]
71    fn test_create_client_deepseek() {
72        clear_env_keys();
73        std::env::set_var("DEEPSEEK_API_KEY", "sk-deepseek");
74
75        let client = create_client().unwrap();
76        assert_eq!(client.provider_name(), "DeepSeek");
77
78        clear_env_keys();
79    }
80
81    #[test]
82    #[serial]
83    fn test_create_client_gemini() {
84        clear_env_keys();
85        std::env::set_var("GEMINI_API_KEY", "AIza-test");
86
87        let client = create_client().unwrap();
88        assert_eq!(client.provider_name(), "Gemini");
89
90        clear_env_keys();
91    }
92
93    #[test]
94    #[serial]
95    fn test_create_client_no_key() {
96        clear_env_keys();
97
98        let result = create_client();
99        assert!(result.is_err());
100
101        clear_env_keys();
102    }
103
104    #[test]
105    fn test_create_client_for_provider() {
106        let openai = create_client_for_provider(Provider::OpenAi, "key".into());
107        assert_eq!(openai.provider_name(), "OpenAI");
108
109        let deepseek = create_client_for_provider(Provider::DeepSeek, "key".into());
110        assert_eq!(deepseek.provider_name(), "DeepSeek");
111
112        let gemini = create_client_for_provider(Provider::Gemini, "key".into());
113        assert_eq!(gemini.provider_name(), "Gemini");
114    }
115}