Skip to main content

st/smart/
relevance.rs

1//! 📊 Relevance Engine - Advanced Scoring Algorithms
2//!
3//! This module provides sophisticated relevance scoring algorithms that
4//! understand code semantics, project structure, and task context to
5//! achieve maximum token efficiency.
6
7use super::{FocusArea, RelevanceScore, TaskContext};
8use crate::scanner::{FileCategory, FileNode};
9use std::collections::HashMap;
10
11/// 🎯 Advanced relevance scoring engine
12pub struct RelevanceEngine {
13    /// Cached scoring patterns
14    patterns: HashMap<String, f32>,
15    /// File type weights
16    type_weights: HashMap<FileCategory, f32>,
17}
18
19impl RelevanceEngine {
20    /// Create new relevance engine
21    pub fn new() -> Self {
22        let mut engine = Self {
23            patterns: HashMap::new(),
24            type_weights: HashMap::new(),
25        };
26
27        engine.initialize_patterns();
28        engine.initialize_type_weights();
29        engine
30    }
31
32    /// 🔍 Score file relevance with advanced algorithms
33    pub fn score_advanced_relevance(
34        &self,
35        file_node: &FileNode,
36        context: &TaskContext,
37        project_context: Option<&ProjectContext>,
38    ) -> RelevanceScore {
39        let mut score = 0.0;
40        let mut reasons = Vec::new();
41        let mut focus_matches = Vec::new();
42
43        // Base file category score
44        if let Some(weight) = self.type_weights.get(&file_node.category) {
45            score += weight;
46            reasons.push(format!("File category {:?} base score", file_node.category));
47        }
48
49        // Pattern matching score
50        let file_path = file_node.path.to_string_lossy().to_lowercase();
51        for (pattern, pattern_score) in &self.patterns {
52            if file_path.contains(pattern) {
53                score += pattern_score;
54                reasons.push(format!("Matches pattern '{}'", pattern));
55            }
56        }
57
58        // Focus area matching
59        for focus_area in &context.focus_areas {
60            let focus_score = self.calculate_focus_score(file_node, focus_area);
61            if focus_score > 0.0 {
62                score += focus_score;
63                focus_matches.push(focus_area.clone());
64                reasons.push(format!("Relevant to {:?}", focus_area));
65            }
66        }
67
68        // Project context boost
69        if let Some(proj_ctx) = project_context {
70            score += self.calculate_project_context_boost(file_node, proj_ctx);
71        }
72
73        // Normalize score
74        score = score.min(1.0);
75
76        RelevanceScore {
77            score,
78            reasons,
79            focus_matches,
80        }
81    }
82
83    /// Calculate focus-specific scoring
84    fn calculate_focus_score(&self, file_node: &FileNode, focus_area: &FocusArea) -> f32 {
85        let file_name = file_node
86            .path
87            .file_name()
88            .and_then(|name| name.to_str())
89            .unwrap_or("")
90            .to_lowercase();
91        let file_path = file_node.path.to_string_lossy().to_lowercase();
92
93        let keywords = focus_area.keywords();
94        let mut score = 0.0;
95
96        for keyword in keywords {
97            if file_name.contains(keyword) {
98                score += 0.4; // High score for filename match
99            } else if file_path.contains(keyword) {
100                score += 0.2; // Medium score for path match
101            }
102        }
103
104        score
105    }
106
107    /// Calculate project context boost
108    fn calculate_project_context_boost(
109        &self,
110        file_node: &FileNode,
111        context: &ProjectContext,
112    ) -> f32 {
113        let mut boost = 0.0;
114
115        // Recently modified files get boost
116        if context.recent_files.contains(&file_node.path) {
117            boost += 0.3;
118        }
119
120        // Core project files get boost
121        if context.core_files.contains(&file_node.path) {
122            boost += 0.4;
123        }
124
125        boost
126    }
127
128    /// Initialize scoring patterns
129    fn initialize_patterns(&mut self) {
130        // Authentication patterns
131        self.patterns.insert("auth".to_string(), 0.3);
132        self.patterns.insert("login".to_string(), 0.3);
133        self.patterns.insert("session".to_string(), 0.2);
134
135        // API patterns
136        self.patterns.insert("api".to_string(), 0.3);
137        self.patterns.insert("endpoint".to_string(), 0.3);
138        self.patterns.insert("handler".to_string(), 0.2);
139
140        // Configuration patterns
141        self.patterns.insert("config".to_string(), 0.2);
142        self.patterns.insert("env".to_string(), 0.2);
143        self.patterns.insert("settings".to_string(), 0.2);
144
145        // Test patterns
146        self.patterns.insert("test".to_string(), 0.2);
147        self.patterns.insert("spec".to_string(), 0.2);
148
149        // Documentation patterns
150        self.patterns.insert("readme".to_string(), 0.2);
151        self.patterns.insert("doc".to_string(), 0.1);
152    }
153
154    /// Initialize file type weights
155    fn initialize_type_weights(&mut self) {
156        self.type_weights.insert(FileCategory::Rust, 0.8);
157        self.type_weights.insert(FileCategory::Python, 0.8);
158        self.type_weights.insert(FileCategory::JavaScript, 0.7);
159        self.type_weights.insert(FileCategory::TypeScript, 0.7);
160        self.type_weights.insert(FileCategory::Json, 0.5);
161        self.type_weights.insert(FileCategory::Yaml, 0.5);
162        self.type_weights.insert(FileCategory::Markdown, 0.4);
163        self.type_weights.insert(FileCategory::Unknown, 0.3);
164    }
165}
166
167/// 🏗️ Project context for enhanced relevance scoring
168#[derive(Debug, Clone)]
169pub struct ProjectContext {
170    /// Recently modified files
171    pub recent_files: Vec<std::path::PathBuf>,
172    /// Core project files (main.rs, package.json, etc.)
173    pub core_files: Vec<std::path::PathBuf>,
174    /// Project type (rust, node, python, etc.)
175    pub project_type: ProjectType,
176}
177
178/// 🏷️ Project type detection
179#[derive(Debug, Clone, PartialEq)]
180pub enum ProjectType {
181    Rust,
182    Node,
183    Python,
184    Go,
185    Java,
186    Mixed,
187    Unknown,
188}
189
190impl Default for RelevanceEngine {
191    fn default() -> Self {
192        Self::new()
193    }
194}