use crate::pattern::Pattern;
use crate::types::TaskContext;
#[must_use]
pub fn deduplicate_patterns(patterns: Vec<Pattern>) -> Vec<Pattern> {
use std::collections::HashSet;
let mut seen = HashSet::new();
let mut deduplicated = Vec::new();
for pattern in patterns {
let key = pattern.similarity_key();
if seen.insert(key) {
deduplicated.push(pattern);
}
}
deduplicated
}
#[must_use]
pub fn rank_patterns(mut patterns: Vec<Pattern>, context: &TaskContext) -> Vec<Pattern> {
patterns.sort_by(|a, b| {
let score_a = calculate_pattern_score(a, context);
let score_b = calculate_pattern_score(b, context);
score_b
.partial_cmp(&score_a)
.unwrap_or(std::cmp::Ordering::Equal)
});
patterns
}
fn calculate_pattern_score(pattern: &Pattern, current_context: &TaskContext) -> f64 {
let mut score = 0.0;
score += f64::from(pattern.success_rate()) * 100.0;
let sample_size = pattern.sample_size() as f64;
score += (sample_size.min(10.0) / 10.0) * 50.0;
if let Some(pattern_context) = pattern.context() {
score += calculate_context_similarity(pattern_context, current_context) * 100.0;
}
match pattern {
Pattern::ToolSequence { tools, .. } => {
let unique_tools = tools.iter().collect::<std::collections::HashSet<_>>().len();
score += (unique_tools as f64 / tools.len() as f64) * 20.0;
}
Pattern::ErrorRecovery { .. } => {
score += 30.0;
}
Pattern::DecisionPoint { outcome_stats, .. } => {
if outcome_stats.total_count > 5 {
score += 25.0;
}
}
Pattern::ContextPattern { evidence, .. } => {
score += (evidence.len() as f64).min(5.0) * 10.0;
}
}
let effectiveness = pattern.effectiveness();
score += f64::from(effectiveness.effectiveness_score()) * 100.0;
if effectiveness.times_applied > 0 {
let success_rate = effectiveness.application_success_rate();
let usage_confidence = (effectiveness.times_applied as f64).ln().min(3.0) / 3.0;
score += f64::from(success_rate) * usage_confidence * 50.0;
}
if effectiveness.avg_reward_delta > 0.0 {
let capped_delta = effectiveness.avg_reward_delta.min(0.5); score += f64::from(capped_delta) * 100.0; } else if effectiveness.avg_reward_delta < 0.0 {
let capped_penalty = effectiveness.avg_reward_delta.max(-0.5); score += f64::from(capped_penalty) * 100.0; }
if effectiveness.times_applied > 0 {
use chrono::Utc;
let days_since_use = (Utc::now() - effectiveness.last_used).num_days();
if days_since_use < 30 {
score += (30.0 - days_since_use as f64) / 30.0 * 10.0;
}
}
score
}
fn calculate_context_similarity(a: &TaskContext, b: &TaskContext) -> f64 {
let mut similarity = 0.0;
let mut factors = 0.0;
if a.language == b.language {
similarity += 1.0;
}
factors += 1.0;
if a.framework == b.framework {
similarity += 1.0;
}
factors += 1.0;
if a.domain == b.domain {
similarity += 0.8;
}
factors += 1.0;
if a.complexity == b.complexity {
similarity += 0.6;
}
factors += 1.0;
if !a.tags.is_empty() || !b.tags.is_empty() {
let a_tags: std::collections::HashSet<_> = a.tags.iter().collect();
let b_tags: std::collections::HashSet<_> = b.tags.iter().collect();
let intersection = a_tags.intersection(&b_tags).count();
let union = a_tags.union(&b_tags).count();
if union > 0 {
similarity += (intersection as f64 / union as f64) * 0.7;
}
factors += 1.0;
}
if factors > 0.0 {
similarity / factors
} else {
0.0
}
}