use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
mod analysis_helpers;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompatibilityAssessment {
pub id: String,
pub pattern_id: String,
pub tool_name: String,
pub compatibility_score: f64,
pub confidence: f64,
pub risk_factors: Vec<RiskFactor>,
pub recommendations: Vec<String>,
pub risk_level: RiskLevel,
pub confidence_interval: (f64, f64),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskFactor {
pub factor_type: RiskFactorType,
pub severity: f64,
pub description: String,
pub mitigation: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RiskFactorType {
DataQuality,
ModelPerformance,
DomainMismatch,
TemporalDrift,
ResourceConstraint,
Compatibility,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RiskLevel {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone)]
pub struct AssessmentConfig {
pub low_risk_threshold: f64,
pub medium_risk_threshold: f64,
pub confidence_level: f64,
pub min_occurrences: usize,
}
impl Default for AssessmentConfig {
fn default() -> Self {
Self {
low_risk_threshold: 0.8,
medium_risk_threshold: 0.6,
confidence_level: 0.95,
min_occurrences: 3,
}
}
}
pub struct CompatibilityAssessor {
config: AssessmentConfig,
tool_capabilities: HashMap<String, ToolCapabilities>,
}
#[derive(Debug, Clone)]
struct ToolCapabilities {
_supported_types: HashSet<String>,
min_data_quality: f64,
max_memory_mb: usize,
supported_domains: HashSet<String>,
_avg_latency_ms: f64,
success_rate: f64,
}
impl CompatibilityAssessor {
pub fn new(config: AssessmentConfig) -> Self {
let mut assessor = Self {
config,
tool_capabilities: HashMap::new(),
};
assessor.initialize_tool_registry();
assessor
}
pub fn default_config() -> Self {
Self::new(AssessmentConfig::default())
}
fn initialize_tool_registry(&mut self) {
self.tool_capabilities.insert(
"query_memory".to_string(),
ToolCapabilities {
_supported_types: vec!["episodic", "semantic", "temporal"]
.into_iter()
.map(String::from)
.collect(),
min_data_quality: 0.5,
max_memory_mb: 100,
supported_domains: vec!["web-api", "cli", "data-processing"]
.into_iter()
.map(String::from)
.collect(),
_avg_latency_ms: 10.0,
success_rate: 0.98,
},
);
self.tool_capabilities.insert(
"analyze_patterns".to_string(),
ToolCapabilities {
_supported_types: vec!["statistical", "predictive", "causal"]
.into_iter()
.map(String::from)
.collect(),
min_data_quality: 0.7,
max_memory_mb: 200,
supported_domains: vec!["data-processing", "analytics"]
.into_iter()
.map(String::from)
.collect(),
_avg_latency_ms: 50.0,
success_rate: 0.92,
},
);
self.tool_capabilities.insert(
"advanced_pattern_analysis".to_string(),
ToolCapabilities {
_supported_types: vec!["time_series", "multivariate", "temporal"]
.into_iter()
.map(String::from)
.collect(),
min_data_quality: 0.8,
max_memory_mb: 500,
supported_domains: vec!["analytics", "forecasting", "anomaly_detection"]
.into_iter()
.map(String::from)
.collect(),
_avg_latency_ms: 100.0,
success_rate: 0.88,
},
);
}
pub fn assess_compatibility(
&self,
pattern_id: &str,
tool_name: &str,
pattern_context: &PatternContext,
) -> Result<CompatibilityAssessment> {
let tool_caps = self
.tool_capabilities
.get(tool_name)
.ok_or_else(|| anyhow::anyhow!("Unknown tool: {}", tool_name))?;
let compatibility_score = self.compute_compatibility_score(tool_caps, pattern_context);
let confidence = self.compute_confidence(tool_caps, pattern_context);
let risk_factors = self.identify_risk_factors(tool_caps, pattern_context);
let risk_level = self.determine_risk_level(compatibility_score, &risk_factors);
let recommendations = self.generate_recommendations(&risk_factors, tool_name);
let confidence_interval = self.compute_confidence_interval(
compatibility_score,
confidence,
pattern_context.occurrences,
);
Ok(CompatibilityAssessment {
id: format!("{}_{}", pattern_id, tool_name),
pattern_id: pattern_id.to_string(),
tool_name: tool_name.to_string(),
compatibility_score,
confidence,
risk_factors,
recommendations,
risk_level,
confidence_interval,
})
}
fn compute_compatibility_score(
&self,
tool_caps: &ToolCapabilities,
context: &PatternContext,
) -> f64 {
let mut score = 0.0;
let mut total_weight = 0.0;
let quality_score = if context.data_quality >= tool_caps.min_data_quality {
1.0
} else {
context.data_quality / tool_caps.min_data_quality
};
score += 0.3 * quality_score;
total_weight += 0.3;
let domain_score = if tool_caps.supported_domains.contains(&context.domain) {
1.0
} else {
0.5 };
score += 0.25 * domain_score;
total_weight += 0.25;
let occurrence_score = if context.occurrences >= self.config.min_occurrences {
1.0
} else {
context.occurrences as f64 / self.config.min_occurrences as f64
};
score += 0.2 * occurrence_score;
total_weight += 0.2;
let stability_score = context.temporal_stability;
score += 0.15 * stability_score;
total_weight += 0.15;
let resource_score = if context.available_memory_mb >= tool_caps.max_memory_mb {
1.0
} else {
context.available_memory_mb as f64 / tool_caps.max_memory_mb as f64
};
score += 0.1 * resource_score;
total_weight += 0.1;
if total_weight > 0.0 {
score / total_weight
} else {
0.5 }
}
fn compute_confidence(&self, tool_caps: &ToolCapabilities, context: &PatternContext) -> f64 {
let mut confidence = 0.5;
confidence += 0.2 * tool_caps.success_rate;
let occurrence_confidence = if context.occurrences >= 10 {
1.0
} else {
context.occurrences as f64 / 10.0
};
confidence += 0.2 * occurrence_confidence;
confidence += 0.1 * context.data_quality;
confidence.clamp(0.0, 1.0)
}
pub fn batch_assess(
&self,
pattern_id: &str,
tool_names: &[String],
context: &PatternContext,
) -> Result<Vec<CompatibilityAssessment>> {
let mut assessments = Vec::new();
for tool_name in tool_names {
let assessment = self.assess_compatibility(pattern_id, tool_name, context)?;
assessments.push(assessment);
}
Ok(assessments)
}
pub fn get_best_tool(
&self,
pattern_id: &str,
tool_names: &[String],
context: &PatternContext,
) -> Result<Option<(String, CompatibilityAssessment)>> {
let assessments = self.batch_assess(pattern_id, tool_names, context)?;
let best = assessments
.into_iter()
.filter(|a| matches!(a.risk_level, RiskLevel::Low | RiskLevel::Medium))
.max_by(|a, b| {
a.compatibility_score
.partial_cmp(&b.compatibility_score)
.unwrap_or(std::cmp::Ordering::Equal)
});
Ok(best.map(|assessment| (assessment.tool_name.clone(), assessment)))
}
}
#[derive(Debug, Clone)]
pub struct PatternContext {
pub domain: String,
pub data_quality: f64,
pub occurrences: usize,
pub temporal_stability: f64,
pub available_memory_mb: usize,
pub complexity: f64,
}
#[cfg(test)]
mod tests;