subx_cli/services/ai/factory.rs
1//! AI client factory for creating provider instances based on configuration.
2//!
3//! This module provides a factory pattern implementation for creating AI service
4//! provider instances. It supports multiple AI providers and handles the creation
5//! and configuration of appropriate client instances based on the provided configuration.
6//!
7//! # Supported Providers
8//!
9//! - **OpenAI**: GPT-3.5, GPT-4, and other OpenAI models
10//! - **Anthropic**: Claude models (planned)
11//! - **Google**: Gemini models (planned)
12//! - **Local Models**: Local AI models via Ollama (planned)
13//!
14//! # Examples
15//!
16//! ```rust,ignore
17//! use subx_cli::services::ai::AIClientFactory;
18//! use subx_cli::config::AIConfig;
19//!
20//! // Create OpenAI client from configuration
21//! let config = AIConfig {
22//! provider: "openai".to_string(),
23//! api_key: Some("sk-...".to_string()),
24//! model: "gpt-4.1".to_string(),
25//! // ... other fields
26//! };
27//!
28//! let client = AIClientFactory::create_client(&config)?;
29//! let result = client.analyze_content(request).await?;
30//! ```
31use crate::config::AIConfig;
32use crate::error::SubXError;
33use crate::services::ai::{AIProvider, OpenAIClient};
34
35/// AI client factory for creating provider instances.
36///
37/// The `AIClientFactory` provides a centralized way to create AI provider
38/// instances based on configuration. It supports multiple AI service providers
39/// and handles the complexity of client initialization and configuration.
40///
41/// # Design Pattern
42///
43/// This factory follows the Abstract Factory pattern, providing a single
44/// interface for creating different types of AI clients while hiding the
45/// specific implementation details from the consumer.
46///
47/// # Provider Selection
48///
49/// The factory automatically selects the appropriate provider implementation
50/// based on the `provider` field in the configuration:
51/// - `"openai"` - Creates an OpenAI client instance
52/// - `"anthropic"` - Creates an Anthropic client (future support)
53/// - `"google"` - Creates a Google AI client (future support)
54/// - `"local"` - Creates a local model client (future support)
55///
56/// # Error Handling
57///
58/// The factory returns detailed errors for:
59/// - Unsupported provider names
60/// - Invalid configuration parameters
61/// - Missing required credentials
62/// - Network connectivity issues during client creation
63///
64/// # Examples
65///
66/// ```rust,ignore
67/// use subx_cli::services::ai::AIClientFactory;
68/// use subx_cli::config::AIConfig;
69///
70/// // Create a configured OpenAI client
71/// let config = AIConfig {
72/// provider: "openai".to_string(),
73/// api_key: Some("sk-your-api-key".to_string()),
74/// model: "gpt-4.1".to_string(),
75/// base_url: "https://api.openai.com/v1".to_string(),
76/// max_sample_length: 2000,
77/// temperature: 0.3,
78/// retry_attempts: 3,
79/// retry_delay_ms: 1000,
80/// };
81///
82/// let client = AIClientFactory::create_client(&config)?;
83/// // Client is ready for content analysis
84/// ```
85pub struct AIClientFactory;
86
87impl AIClientFactory {
88 /// Creates an AI provider instance based on the provided configuration.
89 ///
90 /// This method examines the `provider` field in the configuration and
91 /// instantiates the appropriate AI client implementation. The returned
92 /// client is fully configured and ready for use.
93 ///
94 /// # Arguments
95 ///
96 /// * `config` - AI configuration containing provider details and credentials
97 ///
98 /// # Returns
99 ///
100 /// Returns a boxed trait object implementing `AIProvider` that can be used
101 /// for content analysis and subtitle matching operations.
102 ///
103 /// # Errors
104 ///
105 /// This method returns an error if:
106 /// - The specified provider is not supported
107 /// - Required configuration fields are missing or invalid
108 /// - API credentials are invalid or missing
109 /// - Network connectivity issues prevent client initialization
110 ///
111 /// # Examples
112 ///
113 /// ```rust,ignore
114 /// use subx_cli::services::ai::AIClientFactory;
115 /// use subx_cli::config::AIConfig;
116 ///
117 /// let config = AIConfig {
118 /// provider: "openai".to_string(),
119 /// api_key: Some("sk-key".to_string()),
120 /// model: "gpt-4.1".to_string(),
121 /// // ... other configuration
122 /// };
123 ///
124 /// match AIClientFactory::create_client(&config) {
125 /// Ok(client) => {
126 /// // Use client for analysis
127 /// let result = client.analyze_content(request).await?;
128 /// }
129 /// Err(e) => {
130 /// eprintln!("Failed to create AI client: {}", e);
131 /// }
132 /// }
133 /// ```
134 ///
135 /// # Supported Providers
136 ///
137 /// Current supported providers:
138 /// - `"openai"` - OpenAI GPT models with chat completion API
139 pub fn create_client(config: &AIConfig) -> crate::Result<Box<dyn AIProvider>> {
140 match config.provider.as_str() {
141 "openai" => Ok(Box::new(OpenAIClient::from_config(config)?)),
142 other => Err(SubXError::config(format!(
143 "Unsupported AI provider: {}",
144 other
145 ))),
146 }
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use crate::config::AIConfig;
154
155 #[test]
156 fn test_ai_factory_openai_provider() {
157 let config = AIConfig {
158 provider: "openai".to_string(),
159 api_key: Some("key".to_string()),
160 model: "m".to_string(),
161 base_url: "https://api.openai.com/v1".to_string(),
162 max_sample_length: 100,
163 temperature: 0.1,
164 max_tokens: 1000,
165 retry_attempts: 1,
166 retry_delay_ms: 10,
167 request_timeout_seconds: 30,
168 };
169 // Should successfully create OpenAIClient instance
170 let res = AIClientFactory::create_client(&config);
171 assert!(res.is_ok());
172 }
173
174 #[test]
175 fn test_ai_factory_invalid_provider() {
176 let config = AIConfig {
177 provider: "unknown".to_string(),
178 api_key: Some("key".to_string()),
179 model: "m".to_string(),
180 base_url: "https://api.openai.com/v1".to_string(),
181 max_sample_length: 100,
182 temperature: 0.1,
183 max_tokens: 1000,
184 retry_attempts: 1,
185 retry_delay_ms: 10,
186 request_timeout_seconds: 30,
187 };
188 // Unsupported provider should return error
189 let res = AIClientFactory::create_client(&config);
190 assert!(res.is_err());
191 }
192}