1pub mod affinity;
18pub mod changepoint;
19pub mod clustering;
20pub mod dbscan;
21pub mod effectiveness;
22pub mod extractors;
23pub mod heuristic;
24pub mod optimized_validator;
25pub mod similarity;
26#[cfg(test)]
27mod tests;
28pub mod types;
29pub mod validation;
30
31pub use affinity::{
32 DEFAULT_AFFINITY_THRESHOLD, DEFAULT_MIN_SUCCESS_RATE, EpisodeAssignmentGuard,
33 PatternAffinityClassifier, RejectionReason, RelativeAffinity,
34};
35
36pub use changepoint::{
37 ChangeDirection, ChangeType, Changepoint, ChangepointConfig, ChangepointDetector,
38 SegmentComparison, SegmentComparisonConfig, SegmentStats,
39};
40pub use clustering::{ClusterCentroid, ClusteringConfig, EpisodeCluster, PatternClusterer};
41pub use dbscan::{
42 Anomaly, AnomalyReason, DBSCANAnomalyDetector, DBSCANClusterResult, DBSCANConfig, DBSCANStats,
43 FeatureWeights,
44};
45pub use effectiveness::{EffectivenessTracker, OverallStats, PatternUsage, UsageStats};
46pub use extractors::{
47 ContextPatternExtractor, DecisionPointExtractor, ErrorRecoveryExtractor,
48 HybridPatternExtractor, PatternExtractor, ToolSequenceExtractor,
49};
50pub use heuristic::Heuristic;
51pub use optimized_validator::{
52 EnhancedPatternApplicator, OptimizedPatternValidator, RiskAssessment,
53};
54pub use types::{Pattern, PatternEffectiveness};
55pub use validation::{PatternMetrics, PatternValidator, ValidationConfig};
56
57use crate::types::TaskContext;
58
59impl Pattern {
60 #[must_use]
62 pub fn is_relevant_to(&self, query_context: &TaskContext) -> bool {
63 if let Some(pattern_context) = self.context() {
64 if pattern_context.domain == query_context.domain {
66 return true;
67 }
68
69 if pattern_context.language == query_context.language
71 && pattern_context.language.is_some()
72 {
73 return true;
74 }
75
76 let common_tags: Vec<_> = pattern_context
78 .tags
79 .iter()
80 .filter(|t| query_context.tags.contains(t))
81 .collect();
82
83 if !common_tags.is_empty() {
84 return true;
85 }
86 }
87
88 false
89 }
90
91 #[must_use]
94 pub fn similarity_key(&self) -> String {
95 match self {
96 Pattern::ToolSequence { tools, context, .. } => {
97 format!("tool_seq:{}:{}", tools.join(","), context.domain)
98 }
99 Pattern::DecisionPoint {
100 condition,
101 action,
102 context,
103 ..
104 } => {
105 format!("decision:{}:{}:{}", condition, action, context.domain)
106 }
107 Pattern::ErrorRecovery {
108 error_type,
109 recovery_steps,
110 context,
111 ..
112 } => {
113 format!(
114 "error_recovery:{}:{}:{}",
115 error_type,
116 recovery_steps.join(","),
117 context.domain
118 )
119 }
120 Pattern::ContextPattern {
121 context_features,
122 recommended_approach,
123 ..
124 } => {
125 format!(
126 "context:{}:{}",
127 context_features.join(","),
128 recommended_approach
129 )
130 }
131 }
132 }
133
134 #[must_use]
137 pub fn similarity_score(&self, other: &Self) -> f32 {
138 if std::mem::discriminant(self) != std::mem::discriminant(other) {
140 return 0.0;
141 }
142
143 match (self, other) {
144 (
145 Pattern::ToolSequence {
146 tools: tools1,
147 context: ctx1,
148 ..
149 },
150 Pattern::ToolSequence {
151 tools: tools2,
152 context: ctx2,
153 ..
154 },
155 ) => similarity::tool_sequence_similarity(tools1, ctx1, tools2, ctx2),
156 (
157 Pattern::DecisionPoint {
158 condition: cond1,
159 action: act1,
160 context: ctx1,
161 ..
162 },
163 Pattern::DecisionPoint {
164 condition: cond2,
165 action: act2,
166 context: ctx2,
167 ..
168 },
169 ) => similarity::decision_point_similarity(cond1, act1, ctx1, cond2, act2, ctx2),
170 (
171 Pattern::ErrorRecovery {
172 error_type: err1,
173 recovery_steps: steps1,
174 context: ctx1,
175 ..
176 },
177 Pattern::ErrorRecovery {
178 error_type: err2,
179 recovery_steps: steps2,
180 context: ctx2,
181 ..
182 },
183 ) => similarity::error_recovery_similarity(err1, steps1, ctx1, err2, steps2, ctx2),
184 (
185 Pattern::ContextPattern {
186 context_features: feat1,
187 recommended_approach: rec1,
188 ..
189 },
190 Pattern::ContextPattern {
191 context_features: feat2,
192 recommended_approach: rec2,
193 ..
194 },
195 ) => similarity::context_pattern_similarity(feat1, rec1, feat2, rec2),
196 _ => 0.0,
197 }
198 }
199
200 #[must_use]
203 pub fn confidence(&self) -> f32 {
204 let success_rate = self.success_rate();
205 let sample_size = self.sample_size() as f32;
206
207 if sample_size == 0.0 {
208 return 0.0;
209 }
210
211 success_rate * sample_size.sqrt()
212 }
213
214 pub fn merge_with(&mut self, other: &Self) {
217 if std::mem::discriminant(self) != std::mem::discriminant(other) {
219 return;
220 }
221
222 match (self, other) {
223 (
224 Pattern::ToolSequence {
225 success_rate: sr1,
226 occurrence_count: oc1,
227 avg_latency: lat1,
228 ..
229 },
230 Pattern::ToolSequence {
231 success_rate: sr2,
232 occurrence_count: oc2,
233 avg_latency: lat2,
234 ..
235 },
236 ) => types::merge_tool_sequence(sr1, oc1, lat1, *sr2, *oc2, lat2),
237 (
238 Pattern::DecisionPoint {
239 outcome_stats: stats1,
240 ..
241 },
242 Pattern::DecisionPoint {
243 outcome_stats: stats2,
244 ..
245 },
246 ) => types::merge_decision_point(stats1, stats2),
247 (
248 Pattern::ErrorRecovery {
249 success_rate: sr1, ..
250 },
251 Pattern::ErrorRecovery {
252 success_rate: sr2, ..
253 },
254 ) => types::merge_error_recovery(sr1, *sr2),
255 (
256 Pattern::ContextPattern {
257 evidence: ev1,
258 success_rate: sr1,
259 ..
260 },
261 Pattern::ContextPattern {
262 evidence: ev2,
263 success_rate: sr2,
264 ..
265 },
266 ) => types::merge_context_pattern(ev1, sr1, ev2, *sr2),
267 _ => {}
268 }
269 }
270}