subx_cli/services/ai/mod.rs
1//! AI service integration for intelligent subtitle matching and content analysis.
2//!
3//! This module provides a comprehensive AI service abstraction layer for SubX's
4//! intelligent content analysis capabilities. It enables AI-powered subtitle-video
5//! file matching through semantic analysis, content understanding, and confidence
6//! scoring across multiple AI service providers.
7//!
8//! # Architecture Overview
9//!
10//! The AI service layer is built around a provider pattern that supports:
11//! - **Multi-Provider Support**: OpenAI, Anthropic, and other AI backends
12//! - **Content Analysis**: Deep understanding of video and subtitle content
13//! - **Semantic Matching**: Intelligent file pairing beyond filename similarity
14//! - **Confidence Scoring**: Quantitative match quality assessment
15//! - **Caching Layer**: Persistent caching of expensive AI analysis results
16//! - **Retry Logic**: Robust error handling with exponential backoff
17//!
18//! # Core Capabilities
19//!
20//! ## Content Analysis Engine
21//! - **Video Metadata Extraction**: Title, series, episode, language detection
22//! - **Subtitle Content Analysis**: Dialogue patterns, character names, themes
23//! - **Cross-Reference Matching**: Semantic similarity between content types
24//! - **Language Identification**: Automatic detection and verification
25//! - **Quality Assessment**: Content quality scoring and recommendations
26//!
27//! ## Intelligent Matching Algorithm
28//! 1. **Content Sampling**: Extract representative samples from subtitle files
29//! 2. **Metadata Analysis**: Parse video filenames and directory structures
30//! 3. **Semantic Analysis**: AI-powered content understanding and comparison
31//! 4. **Confidence Scoring**: Multi-factor confidence calculation
32//! 5. **Conflict Resolution**: Resolve ambiguous matches with user preferences
33//! 6. **Verification**: Optional human-in-the-loop verification workflow
34//!
35//! ## Provider Management
36//! - **Dynamic Provider Selection**: Choose optimal provider based on content type
37//! - **Automatic Failover**: Seamless fallback between service providers
38//! - **Cost Optimization**: Smart routing to minimize API usage costs
39//! - **Rate Limiting**: Respect provider-specific rate limits and quotas
40//! - **Usage Tracking**: Detailed usage statistics and cost monitoring
41//!
42//! # Usage Examples
43//!
44//! ## Basic Content Analysis
45//! ```rust,ignore
46//! use subx_cli::core::ComponentFactory;
47//! use subx_cli::config::ProductionConfigService;
48//! use subx_cli::Result;
49//! use std::sync::Arc;
50//!
51//! async fn analyze_content() -> Result<()> {
52//! // Create AI client using component factory
53//! let config_service = Arc::new(ProductionConfigService::new()?);
54//! let factory = ComponentFactory::new(config_service.as_ref())?;
55//! let ai_client = factory.create_ai_provider()?;
56//!
57//! // AI client is ready for content analysis
58//! println!("AI client created and configured");
59//! Ok(())
60//! }
61//! ```
62//!
63//! ## Match Verification Workflow
64//! ```rust,ignore
65//! use subx_cli::services::ai::{AIProvider, VerificationRequest};
66//!
67//! async fn verify_matches(ai_client: Box<dyn AIProvider>) -> Result<()> {
68//! let verification = VerificationRequest {
69//! video_file: "movie.mp4".to_string(),
70//! subtitle_file: "movie_subtitles.srt".to_string(),
71//! match_factors: vec![
72//! "title_similarity".to_string(),
73//! "content_correlation".to_string(),
74//! ],
75//! };
76//!
77//! let confidence = ai_client.verify_match(verification).await?;
78//!
79//! if confidence.score > 0.9 {
80//! println!("Verification successful: {:.2}%", confidence.score * 100.0);
81//! } else {
82//! println!("Verification failed. Factors: {:?}", confidence.factors);
83//! }
84//!
85//! Ok(())
86//! }
87//! ```
88//!
89//! ## Advanced Provider Configuration
90//! ```rust,ignore
91//! use subx_cli::core::ComponentFactory;
92//! use subx_cli::config::ProductionConfigService;
93//! use std::sync::Arc;
94//!
95//! async fn configure_ai_services() -> Result<()> {
96//! // Create component factory with configuration service
97//! let config_service = Arc::new(ProductionConfigService::new()?);
98//! let factory = ComponentFactory::new(config_service.as_ref())?;
99//!
100//! // Create AI client with factory-injected configuration
101//! let client = factory.create_ai_provider()?;
102//!
103//! // Use configured client...
104//! println!("AI client configured with all settings from config service");
105//! Ok(())
106//! }
107//! ```
108//!
109//! # Performance Characteristics
110//!
111//! ## Processing Speed
112//! - **Analysis Time**: 2-5 seconds per content analysis request
113//! - **Batch Processing**: Concurrent processing of multiple file pairs
114//! - **Caching Benefits**: 10-100x speedup for cached results
115//! - **Network Latency**: Optimized for high-latency connections
116//!
117//! ## Resource Usage
118//! - **Memory Footprint**: ~50-200MB for typical analysis sessions
119//! - **API Costs**: $0.001-0.01 per analysis depending on content size
120//! - **Cache Storage**: ~1-10KB per cached analysis result
121//! - **Network Bandwidth**: 1-50KB per API request
122//!
123//! ## Accuracy Metrics
124//! - **Match Accuracy**: >95% for properly named content
125//! - **False Positive Rate**: <2% with confidence threshold >0.8
126//! - **Language Detection**: >99% accuracy for supported languages
127//! - **Content Understanding**: Context-aware matching for complex scenarios
128//!
129//! # Error Handling and Recovery
130//!
131//! The AI service layer provides comprehensive error handling:
132//! - **Network Failures**: Automatic retry with exponential backoff
133//! - **API Rate Limits**: Intelligent backoff and queue management
134//! - **Service Unavailability**: Graceful fallback to alternative providers
135//! - **Invalid Responses**: Response validation and error recovery
136//! - **Timeout Handling**: Configurable timeout with partial result recovery
137//!
138//! # Security and Privacy
139//!
140//! - **Data Privacy**: Content samples are processed with privacy-focused prompts
141//! - **API Key Management**: Secure credential storage and rotation
142//! - **Content Filtering**: No permanent storage of user content on AI providers
143//! - **Request Sanitization**: Input validation and safe prompt construction
144
145use async_trait::async_trait;
146use serde::{Deserialize, Serialize};
147
148/// AI provider trait for content analysis and subtitle matching.
149///
150/// This trait defines the interface for AI services that can analyze
151/// video and subtitle content to determine optimal matches.
152#[async_trait]
153pub trait AIProvider: Send + Sync {
154 /// Analyze multimedia files and subtitle files for matching results.
155 ///
156 /// # Arguments
157 ///
158 /// * `request` - Analysis request containing files and content samples
159 ///
160 /// # Returns
161 ///
162 /// A `MatchResult` containing potential matches with confidence scores
163 async fn analyze_content(&self, request: AnalysisRequest) -> crate::Result<MatchResult>;
164
165 /// Verify file matching confidence.
166 ///
167 /// # Arguments
168 ///
169 /// * `verification` - Verification request for existing matches
170 ///
171 /// # Returns
172 ///
173 /// A confidence score for the verification request
174 async fn verify_match(
175 &self,
176 verification: VerificationRequest,
177 ) -> crate::Result<ConfidenceScore>;
178
179 /// Send a raw chat completion request to the provider.
180 ///
181 /// This provides a provider-neutral entry point for callers (such as the
182 /// translation engine) that need to issue prompts that do not fit the
183 /// matching/verification request shape. Each provider implementation is
184 /// responsible for handling authentication, retries, response size limits,
185 /// and error sanitization, mirroring its specialized methods.
186 ///
187 /// # Arguments
188 ///
189 /// * `messages` - OpenAI-compatible chat messages, e.g.
190 /// `[{"role":"system","content":"..."},{"role":"user","content":"..."}]`.
191 ///
192 /// # Returns
193 ///
194 /// The assistant message text from the first choice in the response.
195 ///
196 /// # Errors
197 ///
198 /// Returns [`crate::error::SubXError::AiService`] for HTTP, parsing, or
199 /// upstream provider errors. The default implementation returns
200 /// [`crate::error::SubXError::AiService`] indicating the provider does not
201 /// support raw chat completion; only providers that override this method
202 /// support translation prompts.
203 async fn chat_completion(&self, _messages: Vec<serde_json::Value>) -> crate::Result<String> {
204 Err(crate::error::SubXError::ai_service(
205 "AI provider does not support chat_completion",
206 ))
207 }
208}
209
210/// Analysis request structure for AI content analysis.
211///
212/// Contains all necessary information for AI services to analyze
213/// and match video files with subtitle files.
214#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
215pub struct AnalysisRequest {
216 /// List of video file paths to analyze
217 pub video_files: Vec<String>,
218 /// List of subtitle file paths to analyze
219 pub subtitle_files: Vec<String>,
220 /// Content samples from subtitle files for analysis
221 pub content_samples: Vec<ContentSample>,
222}
223
224/// Subtitle content sample for AI analysis.
225///
226/// Represents a sample of subtitle content that helps AI services
227/// understand the content and context for matching purposes.
228#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
229pub struct ContentSample {
230 /// Filename of the subtitle file
231 pub filename: String,
232 /// Stable file ID (`file_<uuid-v7>`) of the originating subtitle.
233 ///
234 /// Mirrors the `id` of the [`crate::core::matcher::MediaFile`] this
235 /// sample was extracted from, so the AI prompt can unambiguously map
236 /// `<sample subtitle_file_id="…">` previews back to a single subtitle —
237 /// even when two subtitles share a filename across different
238 /// directories.
239 pub subtitle_file_id: String,
240 /// Preview of the subtitle content
241 pub content_preview: String,
242 /// Size of the subtitle file in bytes
243 pub file_size: u64,
244}
245
246/// AI analysis result containing potential file matches.
247///
248/// The primary result structure returned by AI services containing
249/// matched files with confidence scores and reasoning.
250#[derive(Debug, Deserialize, Clone, PartialEq)]
251pub struct MatchResult {
252 /// List of potential file matches
253 pub matches: Vec<FileMatch>,
254 /// Overall confidence score for the analysis (0.0 to 1.0)
255 pub confidence: f32,
256 /// AI reasoning explanation for the matches
257 pub reasoning: String,
258}
259
260/// Individual file match information using unique file IDs.
261///
262/// Represents a single video-subtitle file pairing suggested by the AI
263/// identified by unique IDs with associated confidence metrics and reasoning factors.
264#[derive(Debug, Deserialize, Clone, PartialEq)]
265pub struct FileMatch {
266 /// Unique ID of the matched video file
267 pub video_file_id: String,
268 /// Unique ID of the matched subtitle file
269 pub subtitle_file_id: String,
270 /// Confidence score for this specific match (0.0 to 1.0)
271 pub confidence: f32,
272 /// List of factors that contributed to this match
273 pub match_factors: Vec<String>,
274 /// Optional language code inferred by the AI from filename + content.
275 ///
276 /// Short language code such as `"tc"`, `"sc"`, `"en"`, `"ja"`, or
277 /// `"und"` when the model could not decide. Marked
278 /// `#[serde(default)]` so legacy cached responses without this field
279 /// continue to deserialize. Consumers SHOULD pass the value through
280 /// [`crate::core::language::LanguageDetector::normalize`] before use.
281 #[serde(default)]
282 pub language: Option<String>,
283 /// Optional disambiguating filename suffix supplied by the AI.
284 ///
285 /// When the model decides two subtitles for the same video would
286 /// otherwise produce identical target filenames, it MAY hand back a
287 /// short tag (typically a language code such as `"tc"` or a numeric
288 /// fallback such as `"2"`) to splice between the video base name and
289 /// the subtitle extension. Marked `#[serde(default)]` for backward
290 /// compatibility with legacy cached responses. Callers MUST sanitize
291 /// the value before using it as a path component.
292 #[serde(default)]
293 pub target_filename_suffix: Option<String>,
294}
295
296/// Confidence score for AI matching decisions.
297///
298/// Represents the AI system's confidence in a particular match along
299/// with the reasoning factors that led to that decision.
300#[derive(Debug, Deserialize, Clone, PartialEq)]
301pub struct ConfidenceScore {
302 /// Numerical confidence score (typically 0.0 to 1.0)
303 pub score: f32,
304 /// List of factors that influenced the confidence score
305 pub factors: Vec<String>,
306}
307
308/// Verification request structure for AI validation.
309///
310/// Used to request verification of a potential match between
311/// a video file and subtitle file from the AI system.
312#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
313pub struct VerificationRequest {
314 /// Path to the video file
315 pub video_file: String,
316 /// Path to the subtitle file
317 pub subtitle_file: String,
318 /// Factors to consider when matching subtitles to video content
319 pub match_factors: Vec<String>,
320}
321
322/// AI usage statistics.
323#[derive(Debug, Clone)]
324pub struct AiUsageStats {
325 /// Name of the model used.
326 pub model: String,
327 /// Number of prompt tokens used.
328 pub prompt_tokens: u32,
329 /// Number of completion tokens used.
330 pub completion_tokens: u32,
331 /// Total number of tokens used.
332 pub total_tokens: u32,
333}
334
335/// AI response content and usage statistics.
336#[derive(Debug, Clone)]
337pub struct AiResponse {
338 /// Response content text.
339 pub content: String,
340 /// Usage statistics.
341 pub usage: Option<AiUsageStats>,
342}
343
344/// Caching functionality for AI analysis results
345pub mod cache;
346
347/// OpenAI integration and client implementation
348pub mod openai;
349/// OpenRouter AI service provider client implementation
350pub mod openrouter;
351
352/// Azure OpenAI service provider client implementation
353pub mod azure_openai;
354
355/// Local / OpenAI-compatible LLM provider client implementation
356pub mod local;
357
358/// AI prompt templates and management
359pub mod prompts;
360
361/// Prompt and response helpers for AI-driven subtitle translation.
362pub mod translation_prompts;
363
364/// Retry logic and backoff strategies for AI services
365pub mod retry;
366
367/// Helpers for sanitizing upstream error payloads before surfacing them.
368pub mod error_sanitizer;
369
370/// Shared security helpers for AI client implementations.
371pub mod security;
372
373/// Hint-emission helpers shared by hosted-provider clients (`openai`,
374/// `openrouter`, `azure-openai`).
375pub mod hosted_hint;
376
377pub use cache::AICache;
378pub use openai::OpenAIClient;
379pub use retry::{RetryConfig, retry_with_backoff};
380pub use security::local_provider_hint;