1use crate::{
7 Agent, Document, EmbeddingService, MemoryService, Pipeline, RetrievalService, RragError,
8 RragResult, SearchResult, StorageService,
9};
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::sync::Arc;
13use std::time::Instant;
14use tokio::sync::RwLock;
15use tracing::info;
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct RragSystemConfig {
20 pub name: String,
22
23 pub version: String,
25
26 pub environment: String,
28
29 pub components: ComponentConfigs,
31
32 pub performance: PerformanceConfig,
34
35 pub monitoring: MonitoringConfig,
37
38 pub features: FeatureFlags,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct ComponentConfigs {
45 pub embedding: EmbeddingConfig,
47
48 pub storage: StorageConfig,
50
51 pub retrieval: RetrievalConfig,
53
54 pub memory: MemoryConfig,
56
57 pub agent: AgentConfig,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct EmbeddingConfig {
64 pub provider: String,
65 pub model: String,
66 pub batch_size: usize,
67 pub timeout_seconds: u64,
68 pub max_retries: usize,
69 pub api_key_env: String,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct StorageConfig {
75 pub backend: String,
76 pub connection_string: Option<String>,
77 pub max_connections: Option<usize>,
78 pub timeout_seconds: u64,
79 pub enable_compression: bool,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct RetrievalConfig {
85 pub index_type: String,
86 pub similarity_threshold: f32,
87 pub max_results: usize,
88 pub enable_reranking: bool,
89 pub cache_results: bool,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct MemoryConfig {
95 pub memory_type: String,
96 pub max_messages: usize,
97 pub max_tokens: Option<usize>,
98 pub enable_summarization: bool,
99 pub persistence_enabled: bool,
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct AgentConfig {
105 pub model_provider: String,
106 pub model_name: String,
107 pub temperature: f32,
108 pub max_tokens: usize,
109 pub max_tool_calls: usize,
110 pub enable_streaming: bool,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct PerformanceConfig {
116 pub max_concurrency: usize,
118
119 pub request_timeout_seconds: u64,
121
122 pub connection_pool_size: usize,
124
125 pub cache_size: usize,
127 pub cache_ttl_seconds: u64,
128
129 pub rate_limit_per_second: Option<u32>,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct MonitoringConfig {
136 pub enable_metrics: bool,
138
139 pub enable_tracing: bool,
141
142 pub log_level: String,
144
145 pub health_check_interval_seconds: u64,
147
148 pub metrics_endpoint: Option<String>,
150 pub tracing_endpoint: Option<String>,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct FeatureFlags {
156 pub enable_experimental: bool,
158
159 pub enable_async_processing: bool,
161
162 pub enable_auto_retry: bool,
164
165 pub enable_validation: bool,
167
168 pub enable_caching: bool,
170}
171
172impl Default for RragSystemConfig {
173 fn default() -> Self {
174 Self {
175 name: "RRAG System".to_string(),
176 version: "1.0.0".to_string(),
177 environment: "development".to_string(),
178 components: ComponentConfigs::default(),
179 performance: PerformanceConfig::default(),
180 monitoring: MonitoringConfig::default(),
181 features: FeatureFlags::default(),
182 }
183 }
184}
185
186impl Default for ComponentConfigs {
187 fn default() -> Self {
188 Self {
189 embedding: EmbeddingConfig::default(),
190 storage: StorageConfig::default(),
191 retrieval: RetrievalConfig::default(),
192 memory: MemoryConfig::default(),
193 agent: AgentConfig::default(),
194 }
195 }
196}
197
198impl Default for EmbeddingConfig {
199 fn default() -> Self {
200 Self {
201 provider: "openai".to_string(),
202 model: "text-embedding-ada-002".to_string(),
203 batch_size: 100,
204 timeout_seconds: 30,
205 max_retries: 3,
206 api_key_env: "OPENAI_API_KEY".to_string(),
207 }
208 }
209}
210
211impl Default for StorageConfig {
212 fn default() -> Self {
213 Self {
214 backend: "in_memory".to_string(),
215 connection_string: None,
216 max_connections: Some(10),
217 timeout_seconds: 30,
218 enable_compression: false,
219 }
220 }
221}
222
223impl Default for RetrievalConfig {
224 fn default() -> Self {
225 Self {
226 index_type: "in_memory".to_string(),
227 similarity_threshold: 0.7,
228 max_results: 10,
229 enable_reranking: true,
230 cache_results: false,
231 }
232 }
233}
234
235impl Default for MemoryConfig {
236 fn default() -> Self {
237 Self {
238 memory_type: "buffer".to_string(),
239 max_messages: 100,
240 max_tokens: Some(4000),
241 enable_summarization: false,
242 persistence_enabled: false,
243 }
244 }
245}
246
247impl Default for AgentConfig {
248 fn default() -> Self {
249 Self {
250 model_provider: "openai".to_string(),
251 model_name: "gpt-3.5-turbo".to_string(),
252 temperature: 0.7,
253 max_tokens: 2048,
254 max_tool_calls: 10,
255 enable_streaming: true,
256 }
257 }
258}
259
260impl Default for PerformanceConfig {
261 fn default() -> Self {
262 Self {
263 max_concurrency: 10,
264 request_timeout_seconds: 300,
265 connection_pool_size: 10,
266 cache_size: 1000,
267 cache_ttl_seconds: 3600,
268 rate_limit_per_second: None,
269 }
270 }
271}
272
273impl Default for MonitoringConfig {
274 fn default() -> Self {
275 Self {
276 enable_metrics: true,
277 enable_tracing: false,
278 log_level: "info".to_string(),
279 health_check_interval_seconds: 30,
280 metrics_endpoint: None,
281 tracing_endpoint: None,
282 }
283 }
284}
285
286impl Default for FeatureFlags {
287 fn default() -> Self {
288 Self {
289 enable_experimental: false,
290 enable_async_processing: true,
291 enable_auto_retry: true,
292 enable_validation: true,
293 enable_caching: true,
294 }
295 }
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct SystemMetrics {
301 pub uptime_seconds: u64,
303
304 pub request_counts: RequestCounts,
306
307 pub performance: PerformanceMetrics,
309
310 pub component_health: HashMap<String, HealthStatus>,
312
313 pub resource_usage: ResourceUsage,
315
316 pub last_updated: chrono::DateTime<chrono::Utc>,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct RequestCounts {
322 pub total_requests: u64,
323 pub successful_requests: u64,
324 pub failed_requests: u64,
325 pub embedding_requests: u64,
326 pub retrieval_requests: u64,
327 pub agent_requests: u64,
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize)]
331pub struct PerformanceMetrics {
332 pub average_response_time_ms: f64,
333 pub p95_response_time_ms: f64,
334 pub p99_response_time_ms: f64,
335 pub requests_per_second: f64,
336 pub error_rate: f64,
337}
338
339#[derive(Debug, Clone, Serialize, Deserialize)]
340pub struct ResourceUsage {
341 pub memory_usage_mb: f64,
342 pub cpu_usage_percent: f64,
343 pub storage_usage_mb: f64,
344 pub network_bytes_sent: u64,
345 pub network_bytes_received: u64,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
349pub enum HealthStatus {
350 Healthy,
351 Degraded,
352 Unhealthy,
353 Unknown,
354}
355
356pub struct RragSystem {
358 config: RragSystemConfig,
360
361 services: SystemServices,
363
364 metrics: Arc<RwLock<SystemMetrics>>,
366
367 start_time: Instant,
369
370 health_checkers: HashMap<String, Box<dyn HealthChecker>>,
372}
373
374pub struct SystemServices {
376 pub embedding: Option<Arc<EmbeddingService>>,
378
379 pub storage: Option<Arc<StorageService>>,
381
382 pub retrieval: Option<Arc<RetrievalService>>,
384
385 pub memory: Option<Arc<MemoryService>>,
387
388 pub agents: HashMap<String, Arc<Agent>>,
390
391 pub pipelines: HashMap<String, Arc<Pipeline>>,
393}
394
395trait HealthChecker: Send + Sync {
397 fn check_health(
398 &self,
399 ) -> Box<dyn std::future::Future<Output = RragResult<HealthStatus>> + Send + '_>;
400}
401
402impl RragSystem {
403 pub async fn new(config: RragSystemConfig) -> RragResult<Self> {
405 let services = SystemServices {
406 embedding: None,
407 storage: None,
408 retrieval: None,
409 memory: None,
410 agents: HashMap::new(),
411 pipelines: HashMap::new(),
412 };
413
414 let metrics = Arc::new(RwLock::new(SystemMetrics {
415 uptime_seconds: 0,
416 request_counts: RequestCounts {
417 total_requests: 0,
418 successful_requests: 0,
419 failed_requests: 0,
420 embedding_requests: 0,
421 retrieval_requests: 0,
422 agent_requests: 0,
423 },
424 performance: PerformanceMetrics {
425 average_response_time_ms: 0.0,
426 p95_response_time_ms: 0.0,
427 p99_response_time_ms: 0.0,
428 requests_per_second: 0.0,
429 error_rate: 0.0,
430 },
431 component_health: HashMap::new(),
432 resource_usage: ResourceUsage {
433 memory_usage_mb: 0.0,
434 cpu_usage_percent: 0.0,
435 storage_usage_mb: 0.0,
436 network_bytes_sent: 0,
437 network_bytes_received: 0,
438 },
439 last_updated: chrono::Utc::now(),
440 }));
441
442 Ok(Self {
443 config,
444 services,
445 metrics,
446 start_time: Instant::now(),
447 health_checkers: HashMap::new(),
448 })
449 }
450
451 pub async fn initialize(&mut self) -> RragResult<()> {
453 info!("Initializing RRAG System: {}", self.config.name);
457 info!("Environment: {}", self.config.environment);
458 info!("Version: {}", self.config.version);
459
460 let mut metrics = self.metrics.write().await;
462 metrics
463 .component_health
464 .insert("system".to_string(), HealthStatus::Healthy);
465 metrics.last_updated = chrono::Utc::now();
466
467 Ok(())
468 }
469
470 pub async fn process_document(&self, document: Document) -> RragResult<ProcessingResult> {
472 let start_time = Instant::now();
473 let mut result = ProcessingResult::new();
474
475 {
477 let mut metrics = self.metrics.write().await;
478 metrics.request_counts.total_requests += 1;
479 }
480
481 result.processing_time_ms = start_time.elapsed().as_millis() as u64;
488 result.success = true;
489 result.metadata.insert(
490 "document_id".to_string(),
491 serde_json::Value::String(document.id.clone()),
492 );
493
494 {
496 let mut metrics = self.metrics.write().await;
497 metrics.request_counts.successful_requests += 1;
498 }
499
500 Ok(result)
501 }
502
503 pub async fn search(&self, query: String, _limit: Option<usize>) -> RragResult<SearchResponse> {
505 let start_time = Instant::now();
506
507 {
509 let mut metrics = self.metrics.write().await;
510 metrics.request_counts.total_requests += 1;
511 metrics.request_counts.retrieval_requests += 1;
512 }
513
514 let response = SearchResponse {
520 query: query.clone(),
521 results: Vec::new(), processing_time_ms: start_time.elapsed().as_millis() as u64,
523 total_results: 0,
524 metadata: HashMap::new(),
525 };
526
527 {
529 let mut metrics = self.metrics.write().await;
530 metrics.request_counts.successful_requests += 1;
531 }
532
533 Ok(response)
534 }
535
536 pub async fn chat(
538 &self,
539 agent_id: &str,
540 message: String,
541 conversation_id: Option<String>,
542 ) -> RragResult<ChatResponse> {
543 let start_time = Instant::now();
544
545 {
547 let mut metrics = self.metrics.write().await;
548 metrics.request_counts.total_requests += 1;
549 metrics.request_counts.agent_requests += 1;
550 }
551
552 let response = ChatResponse {
559 agent_id: agent_id.to_string(),
560 response: format!("Echo: {}", message), conversation_id: conversation_id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
562 processing_time_ms: start_time.elapsed().as_millis() as u64,
563 tool_calls: Vec::new(),
564 metadata: HashMap::new(),
565 };
566
567 {
569 let mut metrics = self.metrics.write().await;
570 metrics.request_counts.successful_requests += 1;
571 }
572
573 Ok(response)
574 }
575
576 pub async fn get_metrics(&self) -> SystemMetrics {
578 let mut metrics = self.metrics.read().await.clone();
579 metrics.uptime_seconds = self.start_time.elapsed().as_secs();
580 metrics.last_updated = chrono::Utc::now();
581 metrics
582 }
583
584 pub async fn health_check(&self) -> RragResult<HealthCheckResult> {
586 let mut result = HealthCheckResult {
587 overall_status: HealthStatus::Healthy,
588 component_status: HashMap::new(),
589 check_time: chrono::Utc::now(),
590 uptime_seconds: self.start_time.elapsed().as_secs(),
591 version: self.config.version.clone(),
592 };
593
594 result
596 .component_status
597 .insert("system".to_string(), HealthStatus::Healthy);
598
599 if let Some(_embedding_service) = &self.services.embedding {
601 result
602 .component_status
603 .insert("embedding".to_string(), HealthStatus::Healthy);
604 }
605
606 if let Some(_storage_service) = &self.services.storage {
607 result
608 .component_status
609 .insert("storage".to_string(), HealthStatus::Healthy);
610 }
611
612 if let Some(_retrieval_service) = &self.services.retrieval {
613 result
614 .component_status
615 .insert("retrieval".to_string(), HealthStatus::Healthy);
616 }
617
618 let has_unhealthy = result
620 .component_status
621 .values()
622 .any(|status| *status == HealthStatus::Unhealthy);
623 let has_degraded = result
624 .component_status
625 .values()
626 .any(|status| *status == HealthStatus::Degraded);
627
628 result.overall_status = if has_unhealthy {
629 HealthStatus::Unhealthy
630 } else if has_degraded {
631 HealthStatus::Degraded
632 } else {
633 HealthStatus::Healthy
634 };
635
636 Ok(result)
637 }
638
639 pub async fn shutdown(&self) -> RragResult<()> {
641 info!("Shutting down RRAG System gracefully...");
642
643 info!("RRAG System shutdown complete");
651 Ok(())
652 }
653
654 pub fn get_config(&self) -> &RragSystemConfig {
656 &self.config
657 }
658
659 pub async fn update_config(&mut self, new_config: RragSystemConfig) -> RragResult<()> {
661 self.validate_config(&new_config)?;
663
664 self.config = new_config;
666
667 info!("System configuration updated");
668 Ok(())
669 }
670
671 fn validate_config(&self, config: &RragSystemConfig) -> RragResult<()> {
673 if config.name.is_empty() {
675 return Err(RragError::validation("name", "non-empty", "empty"));
676 }
677
678 if config.version.is_empty() {
679 return Err(RragError::validation("version", "non-empty", "empty"));
680 }
681
682 if config.performance.max_concurrency == 0 {
683 return Err(RragError::validation("max_concurrency", "> 0", "0"));
684 }
685
686 Ok(())
687 }
688}
689
690#[derive(Debug, Clone, Serialize, Deserialize)]
692pub struct ProcessingResult {
693 pub success: bool,
694 pub processing_time_ms: u64,
695 pub items_processed: usize,
696 pub errors: Vec<String>,
697 pub metadata: HashMap<String, serde_json::Value>,
698}
699
700impl ProcessingResult {
701 pub fn new() -> Self {
702 Self {
703 success: false,
704 processing_time_ms: 0,
705 items_processed: 0,
706 errors: Vec::new(),
707 metadata: HashMap::new(),
708 }
709 }
710}
711
712#[derive(Debug, Clone, Serialize, Deserialize)]
714pub struct SearchResponse {
715 pub query: String,
716 pub results: Vec<SearchResult>,
717 pub processing_time_ms: u64,
718 pub total_results: usize,
719 pub metadata: HashMap<String, serde_json::Value>,
720}
721
722#[derive(Debug, Clone, Serialize, Deserialize)]
724pub struct ChatResponse {
725 pub agent_id: String,
726 pub response: String,
727 pub conversation_id: String,
728 pub processing_time_ms: u64,
729 pub tool_calls: Vec<String>, pub metadata: HashMap<String, serde_json::Value>,
731}
732
733#[derive(Debug, Clone, Serialize, Deserialize)]
735pub struct HealthCheckResult {
736 pub overall_status: HealthStatus,
737 pub component_status: HashMap<String, HealthStatus>,
738 pub check_time: chrono::DateTime<chrono::Utc>,
739 pub uptime_seconds: u64,
740 pub version: String,
741}
742
743pub struct RragSystemBuilder {
745 config: RragSystemConfig,
746}
747
748impl RragSystemBuilder {
749 pub fn new() -> Self {
750 Self {
751 config: RragSystemConfig::default(),
752 }
753 }
754
755 pub fn with_name(mut self, name: impl Into<String>) -> Self {
756 self.config.name = name.into();
757 self
758 }
759
760 pub fn with_environment(mut self, environment: impl Into<String>) -> Self {
761 self.config.environment = environment.into();
762 self
763 }
764
765 pub fn with_embedding_config(mut self, config: EmbeddingConfig) -> Self {
766 self.config.components.embedding = config;
767 self
768 }
769
770 pub fn with_storage_config(mut self, config: StorageConfig) -> Self {
771 self.config.components.storage = config;
772 self
773 }
774
775 pub fn with_performance_config(mut self, config: PerformanceConfig) -> Self {
776 self.config.performance = config;
777 self
778 }
779
780 pub fn enable_feature(mut self, feature: &str, enabled: bool) -> Self {
781 match feature {
782 "experimental" => self.config.features.enable_experimental = enabled,
783 "async_processing" => self.config.features.enable_async_processing = enabled,
784 "auto_retry" => self.config.features.enable_auto_retry = enabled,
785 "validation" => self.config.features.enable_validation = enabled,
786 "caching" => self.config.features.enable_caching = enabled,
787 _ => {} }
789 self
790 }
791
792 pub async fn build(self) -> RragResult<RragSystem> {
793 let mut system = RragSystem::new(self.config).await?;
794 system.initialize().await?;
795 Ok(system)
796 }
797}
798
799impl Default for RragSystemBuilder {
800 fn default() -> Self {
801 Self::new()
802 }
803}
804
805#[cfg(test)]
806mod tests {
807 use super::*;
808
809 #[tokio::test]
810 async fn test_system_creation() {
811 let config = RragSystemConfig::default();
812 let system = RragSystem::new(config).await.unwrap();
813
814 assert_eq!(system.config.name, "RRAG System");
815 assert_eq!(system.config.environment, "development");
816 }
817
818 #[tokio::test]
819 async fn test_system_builder() {
820 let system = RragSystemBuilder::new()
821 .with_name("Test System")
822 .with_environment("test")
823 .enable_feature("experimental", true)
824 .build()
825 .await
826 .unwrap();
827
828 assert_eq!(system.config.name, "Test System");
829 assert_eq!(system.config.environment, "test");
830 assert!(system.config.features.enable_experimental);
831 }
832
833 #[tokio::test]
834 async fn test_health_check() {
835 let system = RragSystemBuilder::new().build().await.unwrap();
836 let health = system.health_check().await.unwrap();
837
838 assert_eq!(health.overall_status, HealthStatus::Healthy);
839 assert!(health.component_status.contains_key("system"));
840 assert!(health.uptime_seconds >= 0);
841 }
842
843 #[tokio::test]
844 async fn test_metrics() {
845 let system = RragSystemBuilder::new().build().await.unwrap();
846 let metrics = system.get_metrics().await;
847
848 assert_eq!(metrics.request_counts.total_requests, 0);
849 assert!(metrics.uptime_seconds >= 0);
850 }
851
852 #[tokio::test]
853 async fn test_document_processing() {
854 let system = RragSystemBuilder::new().build().await.unwrap();
855 let doc = Document::new("Test document");
856
857 let result = system.process_document(doc).await.unwrap();
858
859 assert!(result.success);
860 assert!(result.processing_time_ms > 0);
861 }
862
863 #[tokio::test]
864 async fn test_search() {
865 let system = RragSystemBuilder::new().build().await.unwrap();
866
867 let response = system
868 .search("test query".to_string(), Some(5))
869 .await
870 .unwrap();
871
872 assert_eq!(response.query, "test query");
873 assert!(response.processing_time_ms > 0);
874 }
875
876 #[tokio::test]
877 async fn test_chat() {
878 let system = RragSystemBuilder::new().build().await.unwrap();
879
880 let response = system
881 .chat("test_agent", "Hello".to_string(), None)
882 .await
883 .unwrap();
884
885 assert_eq!(response.agent_id, "test_agent");
886 assert!(response.response.contains("Hello"));
887 assert!(response.processing_time_ms > 0);
888 }
889
890 #[test]
891 fn test_config_validation() {
892 let system = RragSystemBuilder::new().build();
893 let mut invalid_config = RragSystemConfig::default();
896 invalid_config.name = "".to_string();
897
898 assert!(invalid_config.name.is_empty());
900 }
901
902 #[test]
903 fn test_feature_flags() {
904 let mut config = RragSystemConfig::default();
905 config.features.enable_experimental = true;
906 config.features.enable_caching = false;
907
908 assert!(config.features.enable_experimental);
909 assert!(!config.features.enable_caching);
910 }
911}