agentic_evolve_core/matching/
signature.rs1use crate::types::error::EvolveResult;
4use crate::types::match_result::{MatchContext, MatchResult, MatchScore};
5use crate::types::pattern::{FunctionSignature, Pattern};
6
7#[derive(Debug, Default)]
9pub struct SignatureMatcher;
10
11impl SignatureMatcher {
12 pub fn new() -> Self {
13 Self
14 }
15
16 pub fn find_matches(
17 &self,
18 signature: &FunctionSignature,
19 patterns: &[&Pattern],
20 _context: &MatchContext,
21 limit: usize,
22 ) -> EvolveResult<Vec<MatchResult>> {
23 let mut results: Vec<MatchResult> = patterns
24 .iter()
25 .map(|p| {
26 let score = self.score_match(p, signature);
27 MatchResult {
28 pattern_id: p.id.clone(),
29 pattern: (*p).clone(),
30 score: MatchScore::from_single(score),
31 suggested_bindings: std::collections::HashMap::new(),
32 }
33 })
34 .filter(|r| r.score.combined > 0.0)
35 .collect();
36
37 results.sort_by(|a, b| {
38 b.score
39 .combined
40 .partial_cmp(&a.score.combined)
41 .unwrap_or(std::cmp::Ordering::Equal)
42 });
43 results.truncate(limit);
44 Ok(results)
45 }
46
47 pub fn score_match(&self, pattern: &Pattern, signature: &FunctionSignature) -> f64 {
48 let mut score = 0.0;
49 let mut factors = 0;
50
51 let name_score = string_similarity(&pattern.signature.name, &signature.name);
53 score += name_score;
54 factors += 1;
55
56 if pattern.signature.language == signature.language {
58 score += 1.0;
59 }
60 factors += 1;
61
62 let param_diff =
64 (pattern.signature.params.len() as i32 - signature.params.len() as i32).unsigned_abs();
65 let param_score = if param_diff == 0 {
66 1.0
67 } else {
68 1.0 / (1.0 + param_diff as f64)
69 };
70 score += param_score;
71 factors += 1;
72
73 if pattern.signature.return_type == signature.return_type {
75 score += 1.0;
76 } else if pattern.signature.return_type.is_some() && signature.return_type.is_some() {
77 score += string_similarity(
78 pattern.signature.return_type.as_deref().unwrap_or(""),
79 signature.return_type.as_deref().unwrap_or(""),
80 );
81 }
82 factors += 1;
83
84 if pattern.signature.is_async == signature.is_async {
86 score += 0.5;
87 }
88 factors += 1;
89
90 let type_score = param_type_similarity(&pattern.signature.params, &signature.params);
92 score += type_score;
93 factors += 1;
94
95 if factors > 0 {
96 score / factors as f64
97 } else {
98 0.0
99 }
100 }
101}
102
103fn string_similarity(a: &str, b: &str) -> f64 {
104 if a == b {
105 return 1.0;
106 }
107 let a_lower = a.to_lowercase();
108 let b_lower = b.to_lowercase();
109 if a_lower == b_lower {
110 return 0.95;
111 }
112 if a_lower.contains(&b_lower) || b_lower.contains(&a_lower) {
114 return 0.7;
115 }
116 let max_len = a.len().max(b.len());
118 if max_len == 0 {
119 return 1.0;
120 }
121 let dist = levenshtein_distance(&a_lower, &b_lower);
122 1.0 - (dist as f64 / max_len as f64)
123}
124
125fn levenshtein_distance(a: &str, b: &str) -> usize {
126 let a_chars: Vec<char> = a.chars().collect();
127 let b_chars: Vec<char> = b.chars().collect();
128 let m = a_chars.len();
129 let n = b_chars.len();
130 let mut dp = vec![vec![0usize; n + 1]; m + 1];
131 for (i, row) in dp.iter_mut().enumerate().take(m + 1) {
132 row[0] = i;
133 }
134 for (j, cell) in dp[0].iter_mut().enumerate().take(n + 1) {
135 *cell = j;
136 }
137 for i in 1..=m {
138 for j in 1..=n {
139 let cost = if a_chars[i - 1] == b_chars[j - 1] {
140 0
141 } else {
142 1
143 };
144 dp[i][j] = (dp[i - 1][j] + 1)
145 .min(dp[i][j - 1] + 1)
146 .min(dp[i - 1][j - 1] + cost);
147 }
148 }
149 dp[m][n]
150}
151
152fn param_type_similarity(
153 a_params: &[crate::types::pattern::ParamSignature],
154 b_params: &[crate::types::pattern::ParamSignature],
155) -> f64 {
156 if a_params.is_empty() && b_params.is_empty() {
157 return 1.0;
158 }
159 let max_len = a_params.len().max(b_params.len());
160 if max_len == 0 {
161 return 1.0;
162 }
163 let matches: f64 = a_params
164 .iter()
165 .zip(b_params.iter())
166 .map(|(a, b)| string_similarity(&a.param_type, &b.param_type))
167 .sum();
168 matches / max_len as f64
169}