Skip to main content

oris_intake/
lib.rs

1//! Oris Intake - Automatic Issue Intake System for Self-Evolution
2//!
3//! This crate provides automatic issue intake capabilities:
4//! - CI/CD webhook listener (GitHub Actions, GitLab CI)
5//! - Monitoring alert integration (Prometheus, Sentry)
6//! - Error log signal extraction
7//! - Automatic mutation/task creation
8//!
9//! ## Architecture
10//!
11//! ```text
12//! External Source (CI/CD, Monitoring, Logs)
13//!     |
14//!     v
15//! IntakeSource (trait)
16//!     |
17//!     v
18//! SignalExtractor
19//!     |
20//!     v
21//! MutationBuilder -> Evolution Store
22//! ```
23
24mod continuous;
25mod mutation;
26mod prioritize;
27mod rules;
28mod signal;
29mod source;
30
31pub use continuous::*;
32pub use mutation::*;
33pub use prioritize::*;
34pub use rules::*;
35pub use signal::*;
36pub use source::*;
37
38use serde::{Deserialize, Serialize};
39use thiserror::Error;
40
41/// Errors that can occur during intake processing
42#[derive(Error, Debug)]
43pub enum IntakeError {
44    #[error("Failed to parse webhook payload: {0}")]
45    ParseError(String),
46
47    #[error("Failed to extract signals: {0}")]
48    SignalExtractionError(String),
49
50    #[error("Failed to create mutation: {0}")]
51    MutationError(String),
52
53    #[error("Configuration error: {0}")]
54    ConfigError(String),
55
56    #[error("Storage error: {0}")]
57    StorageError(String),
58}
59
60/// Result type for intake operations
61pub type IntakeResult<T> = Result<T, IntakeError>;
62
63/// Configuration for the intake system
64#[derive(Clone, Debug, Serialize, Deserialize)]
65pub struct IntakeConfig {
66    /// Enable/disable the intake system
67    pub enabled: bool,
68
69    /// Supported intake sources
70    pub sources: Vec<IntakeSourceConfig>,
71
72    /// Signal extraction settings
73    pub signal_extraction: SignalExtractionConfig,
74
75    /// Intake rate limiting
76    pub rate_limit: RateLimitConfig,
77}
78
79impl Default for IntakeConfig {
80    fn default() -> Self {
81        Self {
82            enabled: true,
83            sources: vec![],
84            signal_extraction: SignalExtractionConfig::default(),
85            rate_limit: RateLimitConfig::default(),
86        }
87    }
88}
89
90/// Configuration for a specific intake source
91#[derive(Clone, Debug, Serialize, Deserialize)]
92pub struct IntakeSourceConfig {
93    /// Source type (github, gitlab, prometheus, sentry, etc.)
94    pub source_type: String,
95
96    /// Whether this source is enabled
97    pub enabled: bool,
98
99    /// Source-specific configuration
100    pub config: serde_json::Value,
101}
102
103/// Signal extraction configuration
104#[derive(Clone, Debug, Serialize, Deserialize)]
105pub struct SignalExtractionConfig {
106    /// Minimum confidence threshold for extracted signals
107    pub min_confidence: f32,
108
109    /// Maximum signals per intake event
110    pub max_signals: usize,
111
112    /// Whether to enable automatic pattern learning
113    pub enable_pattern_learning: bool,
114}
115
116impl Default for SignalExtractionConfig {
117    fn default() -> Self {
118        Self {
119            min_confidence: 0.5,
120            max_signals: 10,
121            enable_pattern_learning: false,
122        }
123    }
124}
125
126/// Rate limiting configuration
127#[derive(Clone, Debug, Serialize, Deserialize)]
128pub struct RateLimitConfig {
129    /// Maximum requests per minute
130    pub max_requests_per_minute: usize,
131
132    /// Maximum concurrent intakes
133    pub max_concurrent: usize,
134
135    /// Backoff duration on rate limit (seconds)
136    pub backoff_seconds: u64,
137}
138
139impl Default for RateLimitConfig {
140    fn default() -> Self {
141        Self {
142            max_requests_per_minute: 60,
143            max_concurrent: 10,
144            backoff_seconds: 60,
145        }
146    }
147}
148
149/// Generate a unique ID for intake events
150pub fn generate_intake_id(prefix: &str) -> String {
151    let uuid = uuid::Uuid::new_v4();
152    format!("{}-{}", prefix, uuid)
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[test]
160    fn test_generate_intake_id() {
161        let id = generate_intake_id("intake");
162        assert!(id.starts_with("intake-"));
163    }
164
165    #[test]
166    fn test_default_config() {
167        let config = IntakeConfig::default();
168        assert!(config.enabled);
169        assert!(config.sources.is_empty());
170    }
171}