Skip to main content

kimi_fann_core/
enhanced_router.rs

1//! Enhanced routing system with market integration
2//!
3//! This module provides an enhanced routing system that integrates with the
4//! Synaptic Market to enable dynamic expert allocation based on market conditions,
5//! pricing, and reputation.
6
7use crate::{ExpertDomain, MicroExpert, ProcessingConfig, NetworkStats};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use wasm_bindgen::prelude::*;
11
12/// Enhanced router with market integration
13#[wasm_bindgen]
14pub struct EnhancedRouter {
15    experts: HashMap<ExpertDomain, Vec<MicroExpert>>,
16    market_integration: Option<MarketIntegration>,
17    #[allow(dead_code)]
18    config: ProcessingConfig,
19    stats: NetworkStats,
20}
21
22/// Market integration configuration
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct MarketIntegration {
25    /// Enable market-based routing
26    pub market_routing_enabled: bool,
27    /// Budget for compute purchases (in tokens)
28    pub compute_budget: u64,
29    /// Minimum reputation score required
30    pub min_reputation: f64,
31    /// Maximum price per compute unit
32    pub max_price_per_unit: u64,
33    /// Preferred expert domains
34    pub preferred_domains: Vec<ExpertDomain>,
35}
36
37/// Expert performance metrics
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct ExpertMetrics {
40    /// Expert domain
41    pub domain: ExpertDomain,
42    /// Total queries processed
43    pub queries_processed: u64,
44    /// Average response time (ms)
45    pub avg_response_time: f64,
46    /// Success rate (0.0 to 1.0)
47    pub success_rate: f64,
48    /// Quality score (0.0 to 1.0)
49    pub quality_score: f64,
50    /// Current load (0.0 to 1.0)
51    pub current_load: f64,
52}
53
54/// Route recommendation from the enhanced router
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct RouteRecommendation {
57    /// Recommended expert domain
58    pub domain: ExpertDomain,
59    /// Confidence in the recommendation (0.0 to 1.0)
60    pub confidence: f64,
61    /// Estimated cost (in tokens)
62    pub estimated_cost: u64,
63    /// Estimated processing time (ms)
64    pub estimated_time: u32,
65    /// Whether to use local or market experts
66    pub use_market: bool,
67    /// Reasoning for the recommendation
68    pub reasoning: String,
69}
70
71/// Query classification result
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct QueryClassification {
74    /// Primary domain for the query
75    pub primary_domain: ExpertDomain,
76    /// Secondary domains that might be needed
77    pub secondary_domains: Vec<ExpertDomain>,
78    /// Complexity score (0.0 to 1.0)
79    pub complexity: f64,
80    /// Urgency level (0.0 to 1.0)
81    pub urgency: f64,
82}
83
84#[wasm_bindgen]
85impl EnhancedRouter {
86    /// Create a new enhanced router
87    #[wasm_bindgen(constructor)]
88    pub fn new(config: ProcessingConfig) -> EnhancedRouter {
89        let mut experts = HashMap::new();
90        
91        // Initialize expert pools for each domain
92        for domain in [
93            ExpertDomain::Reasoning,
94            ExpertDomain::Coding,
95            ExpertDomain::Language,
96            ExpertDomain::Mathematics,
97            ExpertDomain::ToolUse,
98            ExpertDomain::Context,
99        ] {
100            experts.insert(domain, vec![MicroExpert::new(domain)]);
101        }
102
103        let stats = NetworkStats {
104            active_peers: 1,
105            total_queries: 0,
106            average_latency_ms: 0.0,
107            expert_utilization: HashMap::new(),
108            neural_accuracy: 0.85,
109        };
110
111        EnhancedRouter {
112            experts,
113            market_integration: None,
114            config,
115            stats,
116        }
117    }
118
119    /// Enable market integration
120    pub fn enable_market_integration(&mut self, integration_config: &str) -> Result<(), JsValue> {
121        let config: MarketIntegration = serde_json::from_str(integration_config)
122            .map_err(|e| JsValue::from_str(&format!("Invalid config: {}", e)))?;
123        
124        self.market_integration = Some(config);
125        Ok(())
126    }
127
128    /// Classify a query to determine appropriate expert domains
129    pub fn classify_query(&self, query: &str) -> String {
130        let classification = self.internal_classify_query(query);
131        serde_json::to_string(&classification).unwrap_or_default()
132    }
133
134    /// Get routing recommendation for a query
135    pub fn get_route_recommendation(&self, query: &str) -> String {
136        let recommendation = self.internal_get_route_recommendation(query);
137        serde_json::to_string(&recommendation).unwrap_or_default()
138    }
139
140    /// Route a query using enhanced logic
141    pub fn enhanced_route(&mut self, query: &str) -> String {
142        // Classify the query
143        let classification = self.internal_classify_query(query);
144        
145        // Get route recommendation
146        let recommendation = self.internal_get_route_recommendation(query);
147        
148        // Update statistics
149        self.update_stats(&classification, &recommendation);
150        
151        // Process the query
152        self.process_with_recommendation(query, &recommendation)
153    }
154
155    /// Get current expert metrics
156    pub fn get_expert_metrics(&self) -> String {
157        let metrics: Vec<ExpertMetrics> = self.experts.keys().map(|&domain| {
158            let utilization = self.stats.expert_utilization.get(&domain).copied().unwrap_or(0.0);
159            
160            ExpertMetrics {
161                domain,
162                queries_processed: self.stats.total_queries / self.experts.len() as u64,
163                avg_response_time: self.stats.average_latency_ms,
164                success_rate: 0.95, // Would be calculated from actual metrics
165                quality_score: 0.85, // Would be calculated from feedback
166                current_load: utilization,
167            }
168        }).collect();
169        
170        serde_json::to_string(&metrics).unwrap_or_default()
171    }
172
173    /// Get network statistics
174    pub fn get_network_stats(&self) -> String {
175        serde_json::to_string(&self.stats).unwrap_or_default()
176    }
177
178    /// Update expert capacity based on market conditions
179    pub fn update_capacity(&mut self, domain_str: &str, capacity: usize) -> Result<(), JsValue> {
180        let domain: ExpertDomain = serde_json::from_str(&format!("\"{}\"", domain_str))
181            .map_err(|e| JsValue::from_str(&format!("Invalid domain: {}", e)))?;
182        
183        if let Some(expert_pool) = self.experts.get_mut(&domain) {
184            // Adjust the number of experts based on capacity
185            if capacity > expert_pool.len() {
186                // Add more experts
187                for _ in expert_pool.len()..capacity {
188                    expert_pool.push(MicroExpert::new(domain));
189                }
190            } else if capacity < expert_pool.len() {
191                // Remove excess experts
192                expert_pool.truncate(capacity);
193            }
194        }
195        
196        Ok(())
197    }
198}
199
200impl EnhancedRouter {
201    /// Internal query classification logic
202    fn internal_classify_query(&self, query: &str) -> QueryClassification {
203        let query_lower = query.to_lowercase();
204        
205        // Check for arithmetic expressions first (highest priority for math)
206        let has_arithmetic = self.detect_arithmetic_expression(query);
207        
208        // Simple keyword-based classification (would be more sophisticated in practice)
209        let (primary_domain, complexity) = if has_arithmetic {
210            (ExpertDomain::Mathematics, 0.9) // High confidence for arithmetic
211        } else if query_lower.contains("machine learning") || query_lower.contains("deep learning") || query_lower.contains("neural network") || query_lower.contains("ai") || query_lower.contains("artificial intelligence") {
212            (ExpertDomain::Reasoning, 0.9) // AI/ML topics go to reasoning domain
213        } else if query_lower.contains("code") || query_lower.contains("function") || query_lower.contains("algorithm") || query_lower.contains("program") || query_lower.contains("implement") || query_lower.contains("array") || query_lower.contains("loop") || query_lower.contains("data structure") || query_lower.contains("recursion") || query_lower.contains("linked list") || query_lower.contains("binary search") || query_lower.contains("sorting") || (query_lower.contains("explain") && (query_lower.contains("programming") || query_lower.contains("code"))) || (query_lower.contains("write") && (query_lower.contains("function") || query_lower.contains("code") || query_lower.contains("algorithm"))) {
214            (ExpertDomain::Coding, 0.85)
215        } else if query_lower.contains("math") || query_lower.contains("calculate") || query_lower.contains("equation") || query_lower.contains("solve") || query_lower.contains("integral") || query_lower.contains("derivative") || query_lower.contains("algebra") || query_lower.contains("geometry") || query_lower.contains("number") || query_lower.contains("sum") || query_lower.contains("subtract") || query_lower.contains("multiply") || query_lower.contains("divide") || query_lower.contains("calculus") || query_lower.contains("statistics") || query_lower.contains("probability") || query_lower.contains("pythagorean") || query_lower.contains("theorem") {
216            (ExpertDomain::Mathematics, 0.7)
217        } else if query_lower.contains("tool") || query_lower.contains("api") || query_lower.contains("execute") || query_lower.contains("command") {
218            (ExpertDomain::ToolUse, 0.6)
219        } else if query_lower.contains("meaning of life") || query_lower.contains("consciousness") || query_lower.contains("free will") || query_lower.contains("reality") || query_lower.contains("existence") || query_lower.contains("philosophy") || query_lower.contains("purpose of") {
220            (ExpertDomain::Reasoning, 0.95) // Philosophical questions have very high confidence
221        } else if query_lower.contains("reason") || query_lower.contains("logic") || query_lower.contains("analyze") || query_lower.contains("think") || query_lower.contains("explain") {
222            (ExpertDomain::Reasoning, 0.9)
223        } else if query_lower.contains("context") || query_lower.contains("remember") || query_lower.contains("previous") || query_lower.contains("earlier") {
224            (ExpertDomain::Context, 0.5)
225        } else if query_lower.contains("translate") || query_lower.contains("language") || query_lower.contains("grammar") || query_lower.contains("text") || query_lower.contains("nlp") || query_lower.contains("natural language") || (query_lower.contains("write") && !query_lower.contains("function") && !query_lower.contains("code")) {
226            (ExpertDomain::Language, 0.6)
227        } else if query_lower.contains("hello") || query_lower.contains("hi ") || query_lower.starts_with("hi") || query_lower.contains("greet") {
228            (ExpertDomain::Language, 0.8) // Greetings go to language domain
229        } else if query_lower.contains("what is") {
230            // Other "what is" questions go to reasoning
231            (ExpertDomain::Reasoning, 0.7)
232        } else {
233            (ExpertDomain::Reasoning, 0.4) // Default to reasoning for general questions
234        };
235
236        // Determine secondary domains
237        let mut secondary_domains = Vec::new();
238        if complexity > 0.7 {
239            secondary_domains.push(ExpertDomain::Reasoning);
240        }
241        if query_lower.len() > 100 {
242            secondary_domains.push(ExpertDomain::Context);
243        }
244
245        // Calculate urgency based on query characteristics
246        let urgency = if query_lower.contains("urgent") || query_lower.contains("asap") || query_lower.contains("quickly") {
247            0.9
248        } else if query_lower.contains("when possible") || query_lower.contains("sometime") {
249            0.2
250        } else {
251            0.5
252        };
253
254        QueryClassification {
255            primary_domain,
256            secondary_domains,
257            complexity,
258            urgency,
259        }
260    }
261
262    /// Internal route recommendation logic
263    fn internal_get_route_recommendation(&self, query: &str) -> RouteRecommendation {
264        let classification = self.internal_classify_query(query);
265        
266        // Check local expert availability
267        let local_available = self.experts.get(&classification.primary_domain)
268            .map(|experts| !experts.is_empty())
269            .unwrap_or(false);
270
271        // Determine if market should be used
272        let use_market = if let Some(ref market_config) = self.market_integration {
273            market_config.market_routing_enabled && 
274            (classification.complexity > 0.8 || classification.urgency > 0.7 || !local_available)
275        } else {
276            false
277        };
278
279        // Estimate cost and time
280        let base_cost = (classification.complexity * 100.0) as u64;
281        let estimated_cost = if use_market {
282            base_cost * 2 // Market premium
283        } else {
284            base_cost / 10 // Local processing is cheaper
285        };
286
287        let estimated_time = if use_market {
288            (classification.complexity * 5000.0) as u32 // Network latency
289        } else {
290            (classification.complexity * 1000.0) as u32 // Local processing
291        };
292
293        // Calculate confidence
294        let confidence = if local_available && !use_market {
295            0.9
296        } else if use_market {
297            0.7
298        } else {
299            0.5
300        };
301
302        // Generate reasoning
303        let reasoning = if use_market {
304            format!("Using market experts for {} domain due to high complexity/urgency", 
305                   format!("{:?}", classification.primary_domain))
306        } else {
307            format!("Using local {} expert - sufficient capacity available", 
308                   format!("{:?}", classification.primary_domain))
309        };
310
311        RouteRecommendation {
312            domain: classification.primary_domain,
313            confidence,
314            estimated_cost,
315            estimated_time,
316            use_market,
317            reasoning,
318        }
319    }
320
321    /// Update internal statistics
322    fn update_stats(&mut self, classification: &QueryClassification, recommendation: &RouteRecommendation) {
323        self.stats.total_queries += 1;
324        
325        // Update expert utilization
326        let current_util = self.stats.expert_utilization
327            .get(&classification.primary_domain)
328            .copied()
329            .unwrap_or(0.0);
330        
331        let new_util = (current_util * 0.9) + (0.1 * if recommendation.use_market { 0.5 } else { 1.0 });
332        self.stats.expert_utilization.insert(classification.primary_domain, new_util);
333        
334        // Update average latency (simple exponential moving average)
335        let new_latency = recommendation.estimated_time as f64;
336        if self.stats.average_latency_ms == 0.0 {
337            self.stats.average_latency_ms = new_latency;
338        } else {
339            self.stats.average_latency_ms = (self.stats.average_latency_ms * 0.9) + (new_latency * 0.1);
340        }
341    }
342    
343    /// Detect arithmetic expressions in the query
344    fn detect_arithmetic_expression(&self, query: &str) -> bool {
345        // Simple pattern matching without regex
346        let has_operators = query.contains('+') || query.contains('-') || 
347                           query.contains('*') || query.contains('/') || 
348                           query.contains('^') || query.contains('=');
349        let has_numbers = query.chars().any(|c| c.is_numeric());
350        
351        // Direct check for arithmetic patterns
352        if has_operators && has_numbers {
353            // Basic validation: check if we have number-operator-number pattern
354            let chars: Vec<char> = query.chars().collect();
355            for i in 0..chars.len() {
356                if chars[i].is_numeric() {
357                    // Look ahead for operator and another number
358                    for j in i+1..chars.len() {
359                        if matches!(chars[j], '+' | '-' | '*' | '/' | '^') {
360                            // Look for number after operator
361                            for k in j+1..chars.len() {
362                                if chars[k].is_numeric() {
363                                    return true;
364                                }
365                            }
366                        }
367                    }
368                }
369            }
370        }
371        
372        // Check for common arithmetic words with numbers
373        let query_lower = query.to_lowercase();
374        let has_arithmetic_words = query_lower.contains("plus") || 
375                                  query_lower.contains("minus") || 
376                                  query_lower.contains("times") || 
377                                  query_lower.contains("divided") ||
378                                  query_lower.contains("add") ||
379                                  query_lower.contains("subtract") ||
380                                  query_lower.contains("multiply") ||
381                                  query_lower.contains("divide") ||
382                                  query_lower.contains("equals") ||
383                                  query_lower.contains("sum of") ||
384                                  query_lower.contains("difference");
385        
386        has_numbers && has_arithmetic_words
387    }
388
389    /// Process query with the given recommendation
390    fn process_with_recommendation(&self, query: &str, recommendation: &RouteRecommendation) -> String {
391        if recommendation.use_market {
392            // In a real implementation, this would make market calls
393            format!(
394                "Market processing: {} (domain: {:?}, cost: {} tokens, time: {}ms)",
395                query,
396                recommendation.domain,
397                recommendation.estimated_cost,
398                recommendation.estimated_time
399            )
400        } else {
401            // Use local expert
402            if let Some(experts) = self.experts.get(&recommendation.domain) {
403                if let Some(expert) = experts.first() {
404                    expert.process(query)
405                } else {
406                    "No local expert available".to_string()
407                }
408            } else {
409                "Domain not supported".to_string()
410            }
411        }
412    }
413}
414
415/// Market integration utilities
416pub struct MarketUtils;
417
418impl MarketUtils {
419    /// Calculate optimal market strategy based on current conditions
420    pub fn calculate_market_strategy(
421        local_capacity: &HashMap<ExpertDomain, usize>,
422        market_prices: &HashMap<ExpertDomain, u64>,
423        budget: u64,
424    ) -> MarketStrategy {
425        let mut recommendations = HashMap::new();
426        
427        for (&domain, &local_count) in local_capacity {
428            let market_price = market_prices.get(&domain).copied().unwrap_or(100);
429            
430            let action = if local_count == 0 && market_price <= budget / 10 {
431                StrategyAction::BuyFromMarket
432            } else if local_count > 3 && market_price > 50 {
433                StrategyAction::SellToMarket
434            } else {
435                StrategyAction::UseLocal
436            };
437            
438            recommendations.insert(domain, action);
439        }
440        
441        MarketStrategy {
442            recommendations,
443            total_budget: budget,
444            expected_cost: market_prices.values().sum::<u64>() / market_prices.len() as u64,
445        }
446    }
447}
448
449/// Market strategy recommendation
450#[derive(Debug, Clone, Serialize, Deserialize)]
451pub struct MarketStrategy {
452    /// Action recommendations per domain
453    pub recommendations: HashMap<ExpertDomain, StrategyAction>,
454    /// Total available budget
455    pub total_budget: u64,
456    /// Expected cost of strategy
457    pub expected_cost: u64,
458}
459
460/// Strategy action for a domain
461#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
462pub enum StrategyAction {
463    /// Use local experts
464    UseLocal,
465    /// Buy compute from market
466    BuyFromMarket,
467    /// Sell compute to market
468    SellToMarket,
469}
470
471#[cfg(test)]
472mod tests {
473    use super::*;
474
475    #[test]
476    fn test_enhanced_router_creation() {
477        let config = ProcessingConfig::new();
478        let router = EnhancedRouter::new(config);
479        
480        assert_eq!(router.experts.len(), 6);
481        assert!(router.market_integration.is_none());
482    }
483
484    #[test]
485    fn test_query_classification() {
486        let config = ProcessingConfig::new();
487        let router = EnhancedRouter::new(config);
488        
489        let classification = router.internal_classify_query("Write a function to calculate fibonacci numbers");
490        assert_eq!(classification.primary_domain, ExpertDomain::Coding);
491        assert!(classification.complexity > 0.5);
492    }
493
494    #[test]
495    fn test_route_recommendation() {
496        let config = ProcessingConfig::new();
497        let router = EnhancedRouter::new(config);
498        
499        let recommendation = router.internal_get_route_recommendation("Simple greeting");
500        assert_eq!(recommendation.domain, ExpertDomain::Language);
501        assert!(!recommendation.use_market); // Should use local for simple queries
502    }
503
504    #[test]
505    fn test_market_strategy_calculation() {
506        let mut local_capacity = HashMap::new();
507        local_capacity.insert(ExpertDomain::Coding, 0);
508        local_capacity.insert(ExpertDomain::Mathematics, 5);
509        
510        let mut market_prices = HashMap::new();
511        market_prices.insert(ExpertDomain::Coding, 50);
512        market_prices.insert(ExpertDomain::Mathematics, 100);
513        
514        let strategy = MarketUtils::calculate_market_strategy(&local_capacity, &market_prices, 1000);
515        
516        assert!(matches!(
517            strategy.recommendations.get(&ExpertDomain::Coding),
518            Some(StrategyAction::BuyFromMarket)
519        ));
520        assert!(matches!(
521            strategy.recommendations.get(&ExpertDomain::Mathematics),
522            Some(StrategyAction::SellToMarket)
523        ));
524    }
525    
526    #[test]
527    fn test_arithmetic_detection() {
528        let config = ProcessingConfig::new();
529        let router = EnhancedRouter::new(config);
530        
531        // Test basic arithmetic expressions
532        assert!(router.detect_arithmetic_expression("What is 2+2?"));
533        assert!(router.detect_arithmetic_expression("Calculate 5 * 3"));
534        assert!(router.detect_arithmetic_expression("10 - 7 equals what?"));
535        assert!(router.detect_arithmetic_expression("What is 100 / 4"));
536        
537        // Test word-based arithmetic
538        assert!(router.detect_arithmetic_expression("What is 5 plus 3?"));
539        assert!(router.detect_arithmetic_expression("Calculate 10 minus 2"));
540        assert!(router.detect_arithmetic_expression("What is 4 times 6?"));
541        
542        // Test non-arithmetic queries
543        assert!(!router.detect_arithmetic_expression("What is machine learning?"));
544        assert!(!router.detect_arithmetic_expression("How does AI work?"));
545        assert!(!router.detect_arithmetic_expression("Tell me about programming"));
546    }
547    
548    #[test]
549    fn test_mathematics_routing() {
550        let config = ProcessingConfig::new();
551        let router = EnhancedRouter::new(config);
552        
553        // Test that arithmetic expressions route to Mathematics
554        let classification = router.internal_classify_query("What is 2+2?");
555        assert_eq!(classification.primary_domain, ExpertDomain::Mathematics);
556        assert!(classification.complexity >= 0.9); // Should have high confidence
557        
558        let classification2 = router.internal_classify_query("Calculate 10 * 5");
559        assert_eq!(classification2.primary_domain, ExpertDomain::Mathematics);
560        
561        // Test that machine learning routes to Reasoning
562        let classification3 = router.internal_classify_query("What is machine learning?");
563        assert_eq!(classification3.primary_domain, ExpertDomain::Reasoning);
564    }
565}