use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdaptiveRoutingConfig {
pub enabled: bool,
pub default_level: usize,
pub max_level: usize,
pub keyword_weight: f32,
pub length_weight: f32,
pub entity_weight: f32,
}
impl Default for AdaptiveRoutingConfig {
fn default() -> Self {
Self {
enabled: true,
default_level: 1,
max_level: 3,
keyword_weight: 0.5,
length_weight: 0.3,
entity_weight: 0.2,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum QueryComplexity {
VeryBroad,
Broad,
Medium,
Specific,
VerySpecific,
}
impl QueryComplexity {
pub fn to_level(self, max_level: usize) -> usize {
match self {
QueryComplexity::VeryBroad => max_level.max(2),
QueryComplexity::Broad => (max_level - 1).max(1),
QueryComplexity::Medium => 1,
QueryComplexity::Specific => 0,
QueryComplexity::VerySpecific => 0,
}
}
}
#[derive(Debug)]
pub struct QueryComplexityAnalyzer {
config: AdaptiveRoutingConfig,
broad_keywords: Vec<&'static str>,
specific_keywords: Vec<&'static str>,
}
impl QueryComplexityAnalyzer {
pub fn new(config: AdaptiveRoutingConfig) -> Self {
Self {
config,
broad_keywords: vec![
"overview",
"summary",
"summarize",
"main",
"general",
"all",
"themes",
"topics",
"overall",
"broadly",
"big picture",
"what are",
"list all",
"show me all",
],
specific_keywords: vec![
"relationship between",
"how does",
"why does",
"specific",
"detail",
"exactly",
"precisely",
"what is the connection",
"explain how",
"describe the",
"between",
"and",
],
}
}
pub fn analyze(&self, query: &str) -> QueryComplexity {
let query_lower = query.to_lowercase();
let keyword_score = self.analyze_keywords(&query_lower);
let length_score = self.analyze_length(query);
let entity_score = self.analyze_entity_mentions(&query_lower);
let total_score = keyword_score * self.config.keyword_weight
+ length_score * self.config.length_weight
+ entity_score * self.config.entity_weight;
if total_score >= 0.7 {
QueryComplexity::VeryBroad
} else if total_score >= 0.4 {
QueryComplexity::Broad
} else if total_score >= -0.2 {
QueryComplexity::Medium
} else if total_score >= -0.5 {
QueryComplexity::Specific
} else {
QueryComplexity::VerySpecific
}
}
fn analyze_keywords(&self, query_lower: &str) -> f32 {
let mut score = 0.0;
let mut matches = 0;
for keyword in &self.broad_keywords {
if query_lower.contains(keyword) {
score += 1.0;
matches += 1;
}
}
for keyword in &self.specific_keywords {
if query_lower.contains(keyword) {
score -= 1.0;
matches += 1;
}
}
if matches > 0 {
score / matches as f32
} else {
0.0 }
}
fn analyze_length(&self, query: &str) -> f32 {
let words: Vec<&str> = query.split_whitespace().collect();
let word_count = words.len();
match word_count {
1..=3 => 0.5, 4..=5 => 0.2, 6..=7 => 0.0, 8..=10 => -0.3, _ => -0.5, }
}
fn analyze_entity_mentions(&self, query_lower: &str) -> f32 {
let quoted_count = query_lower.matches('"').count() / 2;
let and_between = query_lower.matches(" and ").count();
let between_count = query_lower.matches("between").count();
let entity_indicators = quoted_count + and_between + between_count;
match entity_indicators {
0 => 0.3, 1 => 0.0, 2 => -0.4, _ => -0.7, }
}
pub fn suggest_level(&self, query: &str) -> usize {
let complexity = self.analyze(query);
complexity.to_level(self.config.max_level)
}
pub fn analyze_detailed(&self, query: &str) -> QueryAnalysis {
let query_lower = query.to_lowercase();
let keyword_score = self.analyze_keywords(&query_lower);
let length_score = self.analyze_length(query);
let entity_score = self.analyze_entity_mentions(&query_lower);
let complexity = self.analyze(query);
let suggested_level = complexity.to_level(self.config.max_level);
QueryAnalysis {
query: query.to_string(),
complexity,
suggested_level,
keyword_score,
length_score,
entity_score,
explanation: self.generate_explanation(complexity, suggested_level),
}
}
fn generate_explanation(&self, complexity: QueryComplexity, level: usize) -> String {
match complexity {
QueryComplexity::VeryBroad => format!(
"Very broad query detected → using level {} for high-level overview",
level
),
QueryComplexity::Broad => format!(
"Broad query detected → using level {} for general understanding",
level
),
QueryComplexity::Medium => format!(
"Medium complexity query → using level {} for balanced detail",
level
),
QueryComplexity::Specific => format!(
"Specific query detected → using level {} for detailed information",
level
),
QueryComplexity::VerySpecific => format!(
"Very specific query detected → using level {} for precise relationships",
level
),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryAnalysis {
pub query: String,
pub complexity: QueryComplexity,
pub suggested_level: usize,
pub keyword_score: f32,
pub length_score: f32,
pub entity_score: f32,
pub explanation: String,
}
impl QueryAnalysis {
pub fn print(&self) {
println!("Query Analysis:");
println!(" Query: \"{}\"", self.query);
println!(" Complexity: {:?}", self.complexity);
println!(" Suggested Level: {}", self.suggested_level);
println!(" Scores:");
println!(" - Keywords: {:.2}", self.keyword_score);
println!(" - Length: {:.2}", self.length_score);
println!(" - Entities: {:.2}", self.entity_score);
println!(" {}", self.explanation);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_broad_query() {
let config = AdaptiveRoutingConfig::default();
let analyzer = QueryComplexityAnalyzer::new(config);
let query = "Give me an overview of AI technologies";
let complexity = analyzer.analyze(query);
let level = analyzer.suggest_level(query);
assert!(matches!(
complexity,
QueryComplexity::VeryBroad | QueryComplexity::Broad
));
assert!(level >= 1);
}
#[test]
fn test_specific_query() {
let config = AdaptiveRoutingConfig::default();
let analyzer = QueryComplexityAnalyzer::new(config);
let query = "What is the relationship between Transformers and GPT?";
let complexity = analyzer.analyze(query);
let level = analyzer.suggest_level(query);
assert!(matches!(
complexity,
QueryComplexity::Specific | QueryComplexity::VerySpecific
));
assert_eq!(level, 0);
}
#[test]
fn test_medium_query() {
let config = AdaptiveRoutingConfig::default();
let analyzer = QueryComplexityAnalyzer::new(config);
let query = "How does machine learning work?";
let level = analyzer.suggest_level(query);
assert!(level <= 1);
}
#[test]
fn test_detailed_analysis() {
let config = AdaptiveRoutingConfig::default();
let analyzer = QueryComplexityAnalyzer::new(config);
let query = "Summarize the main themes";
let analysis = analyzer.analyze_detailed(query);
assert!(analysis.keyword_score > 0.0); assert!(!analysis.explanation.is_empty());
}
}