Skip to main content

llm_optimizer_processor/analyzer/
traits.rs

1//! Core traits for the analyzer framework
2//!
3//! This module defines the `Analyzer` trait that all analyzers must implement,
4//! along with lifecycle management and configuration traits.
5
6use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8use std::time::Duration;
9use thiserror::Error;
10
11use super::types::{AnalyzerEvent, AnalysisReport, AnalyzerStats};
12
13/// Result type for analyzer operations
14pub type AnalyzerResult<T> = Result<T, AnalyzerError>;
15
16/// Errors that can occur during analysis
17#[derive(Debug, Error, Clone, Serialize, Deserialize)]
18pub enum AnalyzerError {
19    /// Analysis failed due to invalid input
20    #[error("Invalid input: {0}")]
21    InvalidInput(String),
22
23    /// Analysis failed due to insufficient data
24    #[error("Insufficient data: {0}")]
25    InsufficientData(String),
26
27    /// Analysis failed due to configuration error
28    #[error("Configuration error: {0}")]
29    ConfigurationError(String),
30
31    /// Analysis failed due to resource exhaustion
32    #[error("Resource exhausted: {0}")]
33    ResourceExhausted(String),
34
35    /// Analysis failed due to internal error
36    #[error("Internal error: {0}")]
37    InternalError(String),
38
39    /// Analyzer is not in the correct state
40    #[error("Invalid state: {0}")]
41    InvalidState(String),
42
43    /// Timeout occurred during analysis
44    #[error("Timeout: {0}")]
45    Timeout(String),
46
47    /// Analyzer is not running
48    #[error("Analyzer is not running")]
49    NotRunning,
50
51    /// Invalid state transition attempted
52    #[error("Invalid state transition from {from} to {to}")]
53    InvalidStateTransition {
54        from: String,
55        to: String,
56    },
57}
58
59/// Lifecycle state of an analyzer
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
61pub enum AnalyzerState {
62    /// Analyzer is initialized but not started
63    Initialized,
64    /// Analyzer is starting up
65    Starting,
66    /// Analyzer is running and processing events
67    Running,
68    /// Analyzer is draining remaining events before stopping
69    Draining,
70    /// Analyzer has stopped
71    Stopped,
72    /// Analyzer encountered an error
73    Failed,
74}
75
76impl AnalyzerState {
77    /// Check if the analyzer can accept new events
78    pub fn can_accept_events(&self) -> bool {
79        matches!(self, AnalyzerState::Running)
80    }
81
82    /// Check if the analyzer is in a terminal state
83    pub fn is_terminal(&self) -> bool {
84        matches!(self, AnalyzerState::Stopped | AnalyzerState::Failed)
85    }
86
87    /// Convert the state to a string representation
88    pub fn as_str(&self) -> &'static str {
89        match self {
90            AnalyzerState::Initialized => "Initialized",
91            AnalyzerState::Starting => "Starting",
92            AnalyzerState::Running => "Running",
93            AnalyzerState::Draining => "Draining",
94            AnalyzerState::Stopped => "Stopped",
95            AnalyzerState::Failed => "Failed",
96        }
97    }
98}
99
100/// Configuration for an analyzer
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct AnalyzerConfig {
103    /// Unique identifier for this analyzer instance
104    pub id: String,
105
106    /// Whether the analyzer is enabled
107    pub enabled: bool,
108
109    /// Events to filter and process (empty = all events)
110    pub event_filter: Vec<String>,
111
112    /// Window duration for sliding window analysis
113    pub window_duration: Duration,
114
115    /// Slide interval for sliding windows
116    pub slide_interval: Duration,
117
118    /// Sample rate (0.0-1.0) for high-volume events
119    pub sample_rate: f64,
120
121    /// Maximum events to buffer before backpressure
122    pub max_buffer_size: usize,
123
124    /// Maximum memory usage in bytes
125    pub max_memory_bytes: usize,
126
127    /// Analysis interval (how often to generate reports)
128    pub analysis_interval: Duration,
129
130    /// Enable detailed tracing
131    pub enable_tracing: bool,
132}
133
134impl Default for AnalyzerConfig {
135    fn default() -> Self {
136        Self {
137            id: String::new(),
138            enabled: true,
139            event_filter: Vec::new(),
140            window_duration: Duration::from_secs(300), // 5 minutes
141            slide_interval: Duration::from_secs(60),   // 1 minute
142            sample_rate: 1.0,                           // No sampling
143            max_buffer_size: 10_000,
144            max_memory_bytes: 100 * 1024 * 1024, // 100MB
145            analysis_interval: Duration::from_secs(60), // 1 minute
146            enable_tracing: false,
147        }
148    }
149}
150
151/// Core trait that all analyzers must implement
152///
153/// This trait defines the interface for real-time analysis of LLM optimization metrics.
154/// Analyzers receive events, maintain state, perform analysis, and generate reports
155/// with insights and recommendations.
156#[async_trait]
157pub trait Analyzer: Send + Sync {
158    /// Get the analyzer's unique name
159    fn name(&self) -> &str;
160
161    /// Get the analyzer's configuration
162    fn config(&self) -> &AnalyzerConfig;
163
164    /// Get the current state of the analyzer
165    fn state(&self) -> AnalyzerState;
166
167    /// Start the analyzer
168    ///
169    /// This initializes internal state and prepares the analyzer to receive events.
170    ///
171    /// # Errors
172    ///
173    /// Returns an error if the analyzer is already running or if initialization fails.
174    async fn start(&mut self) -> AnalyzerResult<()>;
175
176    /// Stop the analyzer gracefully
177    ///
178    /// This drains any remaining events and performs cleanup.
179    ///
180    /// # Errors
181    ///
182    /// Returns an error if the analyzer is already stopped or if cleanup fails.
183    async fn stop(&mut self) -> AnalyzerResult<()>;
184
185    /// Process a single event
186    ///
187    /// This is the core analysis method that processes incoming events.
188    /// The analyzer updates its internal state and may generate insights
189    /// immediately or batch them for the next report.
190    ///
191    /// # Arguments
192    ///
193    /// * `event` - The event to process
194    ///
195    /// # Returns
196    ///
197    /// Returns `Ok(())` if the event was processed successfully.
198    ///
199    /// # Errors
200    ///
201    /// Returns an error if:
202    /// - The analyzer is not in the `Running` state
203    /// - The event is invalid
204    /// - Internal processing fails
205    async fn process_event(&mut self, event: AnalyzerEvent) -> AnalyzerResult<()>;
206
207    /// Generate a report of current analysis results
208    ///
209    /// This method returns the current state of analysis, including any insights,
210    /// recommendations, and alerts generated since the last report.
211    ///
212    /// # Returns
213    ///
214    /// Returns a report containing:
215    /// - Summary statistics
216    /// - Insights discovered
217    /// - Recommendations generated
218    /// - Alerts triggered
219    ///
220    /// # Errors
221    ///
222    /// Returns an error if report generation fails.
223    async fn generate_report(&self) -> AnalyzerResult<AnalysisReport>;
224
225    /// Get current statistics about the analyzer's operation
226    ///
227    /// This includes metrics like:
228    /// - Events processed
229    /// - Analysis duration
230    /// - Memory usage
231    /// - Error counts
232    fn get_stats(&self) -> AnalyzerStats;
233
234    /// Reset the analyzer's state
235    ///
236    /// This clears all accumulated state and resets counters.
237    /// The analyzer remains in its current lifecycle state.
238    ///
239    /// # Errors
240    ///
241    /// Returns an error if reset fails.
242    async fn reset(&mut self) -> AnalyzerResult<()>;
243
244    /// Health check for the analyzer
245    ///
246    /// Returns `Ok(())` if the analyzer is healthy and able to process events.
247    ///
248    /// # Errors
249    ///
250    /// Returns an error if:
251    /// - Memory usage exceeds limits
252    /// - Error rate is too high
253    /// - Internal state is corrupted
254    async fn health_check(&self) -> AnalyzerResult<()>;
255}
256
257/// Builder for creating analyzer configurations
258pub struct AnalyzerConfigBuilder {
259    config: AnalyzerConfig,
260}
261
262impl AnalyzerConfigBuilder {
263    /// Create a new builder with the given analyzer ID
264    pub fn new(id: impl Into<String>) -> Self {
265        Self {
266            config: AnalyzerConfig {
267                id: id.into(),
268                ..Default::default()
269            },
270        }
271    }
272
273    /// Set whether the analyzer is enabled
274    pub fn enabled(mut self, enabled: bool) -> Self {
275        self.config.enabled = enabled;
276        self
277    }
278
279    /// Set the event filter
280    pub fn event_filter(mut self, filter: Vec<String>) -> Self {
281        self.config.event_filter = filter;
282        self
283    }
284
285    /// Set the window duration
286    pub fn window_duration(mut self, duration: Duration) -> Self {
287        self.config.window_duration = duration;
288        self
289    }
290
291    /// Set the slide interval
292    pub fn slide_interval(mut self, interval: Duration) -> Self {
293        self.config.slide_interval = interval;
294        self
295    }
296
297    /// Set the sample rate
298    pub fn sample_rate(mut self, rate: f64) -> Self {
299        self.config.sample_rate = rate.clamp(0.0, 1.0);
300        self
301    }
302
303    /// Set the maximum buffer size
304    pub fn max_buffer_size(mut self, size: usize) -> Self {
305        self.config.max_buffer_size = size;
306        self
307    }
308
309    /// Set the maximum memory usage
310    pub fn max_memory_bytes(mut self, bytes: usize) -> Self {
311        self.config.max_memory_bytes = bytes;
312        self
313    }
314
315    /// Set the analysis interval
316    pub fn analysis_interval(mut self, interval: Duration) -> Self {
317        self.config.analysis_interval = interval;
318        self
319    }
320
321    /// Enable or disable detailed tracing
322    pub fn enable_tracing(mut self, enabled: bool) -> Self {
323        self.config.enable_tracing = enabled;
324        self
325    }
326
327    /// Build the configuration
328    pub fn build(self) -> AnalyzerConfig {
329        self.config
330    }
331}
332
333#[cfg(test)]
334mod tests {
335    use super::*;
336
337    #[test]
338    fn test_analyzer_state_can_accept_events() {
339        assert!(!AnalyzerState::Initialized.can_accept_events());
340        assert!(!AnalyzerState::Starting.can_accept_events());
341        assert!(AnalyzerState::Running.can_accept_events());
342        assert!(!AnalyzerState::Draining.can_accept_events());
343        assert!(!AnalyzerState::Stopped.can_accept_events());
344        assert!(!AnalyzerState::Failed.can_accept_events());
345    }
346
347    #[test]
348    fn test_analyzer_state_is_terminal() {
349        assert!(!AnalyzerState::Initialized.is_terminal());
350        assert!(!AnalyzerState::Starting.is_terminal());
351        assert!(!AnalyzerState::Running.is_terminal());
352        assert!(!AnalyzerState::Draining.is_terminal());
353        assert!(AnalyzerState::Stopped.is_terminal());
354        assert!(AnalyzerState::Failed.is_terminal());
355    }
356
357    #[test]
358    fn test_analyzer_config_builder() {
359        let config = AnalyzerConfigBuilder::new("test-analyzer")
360            .enabled(true)
361            .window_duration(Duration::from_secs(600))
362            .sample_rate(0.5)
363            .max_buffer_size(5000)
364            .build();
365
366        assert_eq!(config.id, "test-analyzer");
367        assert!(config.enabled);
368        assert_eq!(config.window_duration, Duration::from_secs(600));
369        assert_eq!(config.sample_rate, 0.5);
370        assert_eq!(config.max_buffer_size, 5000);
371    }
372
373    #[test]
374    fn test_sample_rate_clamping() {
375        let config = AnalyzerConfigBuilder::new("test")
376            .sample_rate(1.5) // Should be clamped to 1.0
377            .build();
378        assert_eq!(config.sample_rate, 1.0);
379
380        let config = AnalyzerConfigBuilder::new("test")
381            .sample_rate(-0.5) // Should be clamped to 0.0
382            .build();
383        assert_eq!(config.sample_rate, 0.0);
384    }
385}