1use std::collections::HashSet;
7use terraphim_types::capability::Capability;
8
9#[derive(Debug, Clone)]
11pub struct KeywordRouter {
12 mappings: Vec<KeywordMapping>,
13}
14
15#[derive(Debug, Clone)]
16struct KeywordMapping {
17 keywords: Vec<String>,
18 capability: Capability,
19 priority: u32,
20}
21
22impl KeywordRouter {
23 pub fn new() -> Self {
25 Self {
26 mappings: Self::default_mappings(),
27 }
28 }
29
30 pub fn with_mappings(mappings: Vec<(Vec<String>, Capability, u32)>) -> Self {
32 let mappings = mappings
33 .into_iter()
34 .map(|(keywords, capability, priority)| KeywordMapping {
35 keywords,
36 capability,
37 priority,
38 })
39 .collect();
40
41 Self { mappings }
42 }
43
44 pub fn extract_capabilities(&self, text: &str) -> Vec<Capability> {
46 let text_lower = text.to_lowercase();
47 let mut caps = HashSet::new();
48 let mut matched_keywords = Vec::new();
49
50 for mapping in &self.mappings {
51 for keyword in &mapping.keywords {
52 if text_lower.contains(&keyword.to_lowercase()) {
53 caps.insert(mapping.capability);
54 matched_keywords.push((keyword.clone(), mapping.priority));
55 break; }
57 }
58 }
59
60 matched_keywords.sort_by(|a, b| b.1.cmp(&a.1));
62
63 caps.into_iter().collect()
64 }
65
66 pub fn has_keywords(&self, text: &str) -> bool {
68 !self.extract_capabilities(text).is_empty()
69 }
70
71 fn default_mappings() -> Vec<KeywordMapping> {
73 vec![
74 KeywordMapping {
76 keywords: vec![
77 "think".to_string(),
78 "thinking".to_string(),
79 "reason".to_string(),
80 "reasoning".to_string(),
81 "analyze deeply".to_string(),
82 "complex analysis".to_string(),
83 "deep dive".to_string(),
84 "carefully consider".to_string(),
85 ],
86 capability: Capability::DeepThinking,
87 priority: 100,
88 },
89 KeywordMapping {
91 keywords: vec![
92 "quick".to_string(),
93 "fast".to_string(),
94 "simple".to_string(),
95 "brief".to_string(),
96 "short".to_string(),
97 "summary".to_string(),
98 ],
99 capability: Capability::FastThinking,
100 priority: 50,
101 },
102 KeywordMapping {
104 keywords: vec![
105 "implement".to_string(),
106 "code".to_string(),
107 "write function".to_string(),
108 "create".to_string(),
109 "build".to_string(),
110 "develop".to_string(),
111 "program".to_string(),
112 ],
113 capability: Capability::CodeGeneration,
114 priority: 90,
115 },
116 KeywordMapping {
118 keywords: vec![
119 "review".to_string(),
120 "check".to_string(),
121 "audit".to_string(),
122 "inspect".to_string(),
123 "evaluate code".to_string(),
124 ],
125 capability: Capability::CodeReview,
126 priority: 85,
127 },
128 KeywordMapping {
130 keywords: vec![
131 "design".to_string(),
132 "architecture".to_string(),
133 "structure".to_string(),
134 "system design".to_string(),
135 "pattern".to_string(),
136 ],
137 capability: Capability::Architecture,
138 priority: 88,
139 },
140 KeywordMapping {
142 keywords: vec![
143 "test".to_string(),
144 "testing".to_string(),
145 "unit test".to_string(),
146 "integration test".to_string(),
147 "spec".to_string(),
148 ],
149 capability: Capability::Testing,
150 priority: 80,
151 },
152 KeywordMapping {
154 keywords: vec![
155 "refactor".to_string(),
156 "restructure".to_string(),
157 "clean up".to_string(),
158 "improve".to_string(),
159 "optimize code".to_string(),
160 ],
161 capability: Capability::Refactoring,
162 priority: 75,
163 },
164 KeywordMapping {
166 keywords: vec![
167 "document".to_string(),
168 "documentation".to_string(),
169 "readme".to_string(),
170 "explain how".to_string(),
171 "guide".to_string(),
172 ],
173 capability: Capability::Documentation,
174 priority: 70,
175 },
176 KeywordMapping {
178 keywords: vec![
179 "explain".to_string(),
180 "clarify".to_string(),
181 "describe".to_string(),
182 "what is".to_string(),
183 "how does".to_string(),
184 ],
185 capability: Capability::Explanation,
186 priority: 65,
187 },
188 KeywordMapping {
190 keywords: vec![
191 "security".to_string(),
192 "secure".to_string(),
193 "vulnerability".to_string(),
194 "audit".to_string(),
195 "threat".to_string(),
196 "sanitize".to_string(),
197 ],
198 capability: Capability::SecurityAudit,
199 priority: 95,
200 },
201 KeywordMapping {
203 keywords: vec![
204 "performance".to_string(),
205 "optimize".to_string(),
206 "speed".to_string(),
207 "fast".to_string(),
208 "efficient".to_string(),
209 "benchmark".to_string(),
210 ],
211 capability: Capability::Performance,
212 priority: 78,
213 },
214 ]
215 }
216}
217
218impl Default for KeywordRouter {
219 fn default() -> Self {
220 Self::new()
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn test_extract_deep_thinking() {
230 let router = KeywordRouter::new();
231
232 let caps =
233 router.extract_capabilities("I need you to think carefully about this complex problem");
234
235 assert!(caps.contains(&Capability::DeepThinking));
236 }
237
238 #[test]
239 fn test_extract_code_generation() {
240 let router = KeywordRouter::new();
241
242 let caps = router.extract_capabilities("Please implement a function to parse JSON");
243
244 assert!(caps.contains(&Capability::CodeGeneration));
245 }
246
247 #[test]
248 fn test_extract_security_audit() {
249 let router = KeywordRouter::new();
250
251 let caps = router.extract_capabilities("Audit this code for security vulnerabilities");
252
253 assert!(caps.contains(&Capability::SecurityAudit));
254 }
255
256 #[test]
257 fn test_multiple_capabilities() {
258 let router = KeywordRouter::new();
259
260 let caps = router.extract_capabilities(
261 "Implement a secure authentication system and write tests for it",
262 );
263
264 assert!(caps.contains(&Capability::CodeGeneration));
265 assert!(caps.contains(&Capability::SecurityAudit));
266 assert!(caps.contains(&Capability::Testing));
267 }
268
269 #[test]
270 fn test_no_capabilities() {
271 let router = KeywordRouter::new();
272
273 let caps = router.extract_capabilities("Hello, how are you today?");
274
275 assert!(caps.is_empty());
276 }
277
278 #[test]
279 fn test_case_insensitive() {
280 let router = KeywordRouter::new();
281
282 let caps1 = router.extract_capabilities("IMPLEMENT this feature");
283 let caps2 = router.extract_capabilities("implement this feature");
284 let caps3 = router.extract_capabilities("Implement this feature");
285
286 assert_eq!(caps1, caps2);
287 assert_eq!(caps2, caps3);
288 }
289
290 #[test]
291 fn test_has_keywords() {
292 let router = KeywordRouter::new();
293
294 assert!(router.has_keywords("Think about this problem"));
295 assert!(!router.has_keywords("Hello world"));
296 }
297}
298
299#[cfg(test)]
300mod proptest_tests {
301 use super::*;
302 use proptest::prelude::*;
303
304 proptest! {
305 #[test]
306 fn extract_capabilities_never_panics(text in "\\PC{0,500}") {
307 let router = KeywordRouter::new();
308 let _ = router.extract_capabilities(&text);
310 }
311
312 #[test]
313 fn extract_capabilities_returns_subset_of_all(text in "\\PC{0,200}") {
314 let router = KeywordRouter::new();
315 let all_caps = Capability::all();
316 let extracted = router.extract_capabilities(&text);
317
318 for cap in &extracted {
319 prop_assert!(all_caps.contains(cap));
320 }
321 }
322
323 #[test]
324 fn has_keywords_consistent_with_extract(text in "\\PC{0,200}") {
325 let router = KeywordRouter::new();
326 let has = router.has_keywords(&text);
327 let extracted = router.extract_capabilities(&text);
328
329 prop_assert_eq!(has, !extracted.is_empty());
330 }
331 }
332}