use crate::search::{SearchEngine, SearchRequest, SearchResult, SearchResponse, SearchMethod};
use crate::lsp::LspManager;
use super::{
embedding::{SemanticEncoder, CodeEmbedding, EmbeddingConfig},
query_classifier::{QueryClassifier, QueryClassification, QueryIntent, ClassifierConfig},
intent_router::{IntentRouter, LSPRoutingDecision, IntentRouterConfig},
conformal_router::{ConformalRouter, ConformalRouterConfig, RoutingDecision, UpshiftType},
SemanticConfig,
};
use anyhow::{Result, anyhow};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::RwLock;
use tracing::{debug, info, warn, error};
use std::collections::HashMap;
#[derive(Clone)]
pub struct SemanticSearchIntegration {
encoder: Arc<SemanticEncoder>,
classifier: Arc<QueryClassifier>,
intent_router: Arc<IntentRouter>,
conformal_router: Arc<ConformalRouter>,
config: SemanticIntegrationConfig,
metrics: Arc<RwLock<IntegrationMetrics>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SemanticIntegrationConfig {
pub enabled: bool,
pub nl_upshift_threshold: f32,
pub max_processing_time_ms: u64,
pub enable_conformal_routing: bool,
pub fallback_on_error: bool,
pub enable_result_caching: bool,
pub similarity_threshold: f32,
}
impl Default for SemanticIntegrationConfig {
fn default() -> Self {
Self {
enabled: true,
nl_upshift_threshold: 0.7,
max_processing_time_ms: 100, enable_conformal_routing: true,
fallback_on_error: true,
enable_result_caching: true,
similarity_threshold: 0.5,
}
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct IntegrationMetrics {
pub total_requests: u64,
pub successful_enhancements: u64,
pub fallback_count: u64,
pub avg_processing_time_ms: f64,
pub classification_accuracy: f64,
pub upshift_decisions: u64,
pub conformal_routing_decisions: u64,
pub avg_relevance_improvement: f64,
pub cache_hit_rate: f64,
}
#[derive(Debug, Clone)]
pub struct SemanticSearchRequest {
pub base_request: SearchRequest,
pub force_semantic: bool,
pub intent_override: Option<QueryIntent>,
pub skip_conformal: bool,
pub similarity_threshold: Option<f32>,
}
impl From<SearchRequest> for SemanticSearchRequest {
fn from(request: SearchRequest) -> Self {
Self {
base_request: request,
force_semantic: false,
intent_override: None,
skip_conformal: false,
similarity_threshold: None,
}
}
}
impl Default for SemanticSearchRequest {
fn default() -> Self {
Self {
base_request: SearchRequest::default(),
force_semantic: false,
intent_override: None,
skip_conformal: false,
similarity_threshold: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SemanticSearchResponse {
pub base_response: SearchResponse,
pub classification: Option<QueryClassification>,
pub routing_decision: Option<RoutingDecision>,
pub upshift_decision: Option<RoutingDecision>,
pub semantic_metrics: SemanticProcessingMetrics,
pub semantic_enhanced: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SemanticProcessingMetrics {
pub classification_time_ms: u64,
pub routing_time_ms: u64,
pub encoding_time_ms: u64,
pub conformal_time_ms: u64,
pub total_processing_time_ms: u64,
pub results_reranked: usize,
pub avg_similarity_score: f32,
pub within_time_budget: bool,
}
impl Default for SemanticProcessingMetrics {
fn default() -> Self {
Self {
classification_time_ms: 0,
routing_time_ms: 0,
encoding_time_ms: 0,
conformal_time_ms: 0,
total_processing_time_ms: 0,
results_reranked: 0,
avg_similarity_score: 0.0,
within_time_budget: true,
}
}
}
impl SemanticSearchIntegration {
pub async fn new(config: SemanticIntegrationConfig) -> Result<Self> {
info!("Initializing semantic search integration system");
let embedding_config = EmbeddingConfig {
model_type: "sentence-transformers".to_string(),
model_path: "all-MiniLM-L6-v2".to_string(),
embedding_dim: 384,
max_tokens: 512,
batch_size: 16,
device: "cpu".to_string(),
use_simd: true,
memory_pool_mb: 512,
};
let encoder = Arc::new(SemanticEncoder::new(embedding_config).await?);
let classifier_config = ClassifierConfig {
nl_threshold: config.nl_upshift_threshold,
intent_confidence_threshold: 0.8,
enable_language_detection: true,
feature_weights: HashMap::new(),
custom_patterns: vec![],
};
let classifier = Arc::new(QueryClassifier::new(classifier_config)?);
let router_config = IntentRouterConfig {
confidence_threshold: 0.6,
max_primary_results: 20,
enable_lsp_routing: true,
fallback_timeout_ms: 100,
decision_cache_size: 1000,
custom_rules: vec![],
};
let lsp_manager = Arc::new(LspManager::new(crate::lsp::LspConfig::default()).await?);
let classifier_for_router = QueryClassifier::new(ClassifierConfig::default())?;
let intent_router = Arc::new(IntentRouter::new(router_config, classifier_for_router, Some(lsp_manager)).await?);
let conformal_config = ConformalRouterConfig {
risk_threshold: 0.6,
daily_budget_percent: 5.0, confidence_level: 0.95, min_calibration_samples: 100,
p95_headroom_threshold_ms: 10.0,
enabled: true,
calibration_retention_hours: 24,
};
let conformal_router = Arc::new(ConformalRouter::new(conformal_config));
let integration = Self {
encoder,
classifier,
intent_router,
conformal_router,
config,
metrics: Arc::new(RwLock::new(IntegrationMetrics::default())),
};
info!("✅ Semantic search integration system initialized successfully");
Ok(integration)
}
pub async fn from_semantic_config(semantic_config: &SemanticConfig) -> Result<Self> {
let integration_config = SemanticIntegrationConfig {
enabled: true,
nl_upshift_threshold: 0.7,
max_processing_time_ms: 100, enable_conformal_routing: true,
fallback_on_error: true,
enable_result_caching: semantic_config.cross_encoder.enabled,
similarity_threshold: 0.5,
};
Self::new(integration_config).await
}
pub async fn process_search(
&self,
engine: &SearchEngine,
request: SemanticSearchRequest,
) -> Result<SemanticSearchResponse> {
let start_time = Instant::now();
let mut processing_metrics = SemanticProcessingMetrics::default();
let classification_start = Instant::now();
let classification = self.classify_query(&request).await?;
processing_metrics.classification_time_ms = classification_start.elapsed().as_millis() as u64;
debug!("Query classified as: {:?} (confidence: {:.3})",
classification.intent, classification.confidence);
let routing_start = Instant::now();
let routing_decision = self.route_intent(&request, &classification).await?;
processing_metrics.routing_time_ms = routing_start.elapsed().as_millis() as u64;
let conformal_start = Instant::now();
let upshift_decision = if self.config.enable_conformal_routing && !request.skip_conformal {
Some(self.conformal_route(&request, &classification, &routing_decision).await?)
} else {
None
};
processing_metrics.conformal_time_ms = conformal_start.elapsed().as_millis() as u64;
let original_query = request.base_request.query.clone();
let enhanced_request = self.enhance_search_request(request.base_request, &classification, &routing_decision, &upshift_decision);
let base_response = engine.search_comprehensive(enhanced_request).await?;
let (final_response, semantic_enhanced) = if self.should_apply_semantic_reranking(&classification, &routing_decision, &upshift_decision) {
let encoding_start = Instant::now();
let reranked_response = self.apply_semantic_reranking(base_response.clone(), &original_query).await?;
processing_metrics.encoding_time_ms = encoding_start.elapsed().as_millis() as u64;
processing_metrics.results_reranked = reranked_response.results.len();
(reranked_response, true)
} else {
(base_response, false)
};
let total_time = start_time.elapsed().as_millis() as u64;
processing_metrics.total_processing_time_ms = total_time;
processing_metrics.within_time_budget = total_time <= self.config.max_processing_time_ms;
if !processing_metrics.within_time_budget {
warn!("Semantic processing exceeded time budget: {}ms > {}ms",
total_time, self.config.max_processing_time_ms);
}
self.update_metrics(&processing_metrics, semantic_enhanced).await;
Ok(SemanticSearchResponse {
base_response: final_response,
classification: Some(classification),
routing_decision: Some(routing_decision),
upshift_decision,
semantic_metrics: processing_metrics,
semantic_enhanced,
})
}
async fn classify_query(&self, request: &SemanticSearchRequest) -> Result<QueryClassification> {
if let Some(intent_override) = &request.intent_override {
Ok(QueryClassification {
intent: intent_override.clone(),
confidence: 1.0,
characteristics: Vec::new(),
naturalness_score: if matches!(intent_override, QueryIntent::NaturalLanguage) { 0.9 } else { 0.1 },
complexity_score: 0.5,
language_hints: vec![],
})
} else {
Ok(self.classifier.classify(&request.base_request.query))
}
}
async fn route_intent(
&self,
request: &SemanticSearchRequest,
classification: &QueryClassification
) -> Result<RoutingDecision> {
let context = super::intent_router::SearchContext {
query: request.base_request.query.clone(),
mode: "semantic".to_string(),
repo_path: None,
file_context: request.base_request.file_path.as_ref().map(|path| {
super::intent_router::FileContext {
current_file: path.clone(),
current_line: 0,
current_column: 0,
language: request.base_request.language.clone().unwrap_or_default(),
project_root: "/tmp".to_string(), }
}),
user_preferences: None,
};
let intent_result = self.intent_router.route_query(&context).await?;
Ok(RoutingDecision {
should_upshift: intent_result.routing_path.contains(&"lsp_routing".to_string()),
upshift_type: UpshiftType::LSPIntegration, budget_consumed: 1.0, routing_reason: format!("Intent routing via path: {:?}", intent_result.routing_path),
expected_improvement: intent_result.performance_metrics.total_latency_ms as f32,
risk_assessment: super::conformal_router::RiskAssessment {
risk_score: 0.5, confidence_interval: (0.4, 0.6), nonconformity_score: 0.3, calibrated: true, risk_factors: Vec::new(), },
})
}
async fn conformal_route(
&self,
request: &SemanticSearchRequest,
classification: &QueryClassification,
routing_decision: &RoutingDecision,
) -> Result<RoutingDecision> {
let upshift_type = match classification.intent {
QueryIntent::NaturalLanguage => UpshiftType::SemanticReranking,
QueryIntent::SymbolSearch => UpshiftType::LSPIntegration,
QueryIntent::StructuralSearch => UpshiftType::ASTAnalysis,
_ => UpshiftType::CrossLanguageSearch,
};
let input = super::conformal_router::ConformalFeatures {
query_length: request.base_request.query.len() as u32,
word_count: request.base_request.query.split_whitespace().count() as u32,
has_special_chars: request.base_request.query.chars().any(|c| !c.is_alphanumeric() && !c.is_whitespace()),
fuzzy_enabled: false,
structural_mode: matches!(classification.intent, QueryIntent::StructuralSearch),
avg_word_length: request.base_request.query.split_whitespace()
.map(|w| w.len())
.sum::<usize>() as f32 / request.base_request.query.split_whitespace().count().max(1) as f32,
query_entropy: classification.complexity_score,
identifier_density: 0.0, semantic_complexity: classification.naturalness_score,
has_file_context: request.base_request.file_path.is_some(),
language_detected: request.base_request.language.is_some(),
intent_confidence: classification.confidence,
naturalness_score: classification.naturalness_score,
similar_queries_success_rate: 0.8, user_satisfaction_history: 0.75, };
self.conformal_router.make_routing_decision(&input, classification).await
}
fn enhance_search_request(
&self,
mut base_request: SearchRequest,
classification: &QueryClassification,
routing_decision: &RoutingDecision,
upshift_decision: &Option<RoutingDecision>,
) -> SearchRequest {
base_request.search_method = Some(match routing_decision.upshift_type {
UpshiftType::None => SearchMethod::Lexical,
UpshiftType::ASTAnalysis => SearchMethod::Structural,
UpshiftType::SemanticReranking => SearchMethod::Semantic,
UpshiftType::CrossEncoder => SearchMethod::Hybrid,
UpshiftType::LSPIntegration => SearchMethod::Hybrid, _ => SearchMethod::Hybrid, });
if let Some(upshift) = upshift_decision {
if upshift.should_upshift {
match upshift.upshift_type {
UpshiftType::SemanticReranking => {
base_request.search_method = Some(SearchMethod::ForceSemantic);
}
UpshiftType::LSPIntegration => {
base_request.enable_lsp = true;
}
_ => {
}
}
}
}
if classification.complexity_score > 0.8 {
base_request.timeout_ms = (base_request.timeout_ms as f32 * 1.2) as u64; }
base_request
}
fn should_apply_semantic_reranking(
&self,
classification: &QueryClassification,
routing_decision: &RoutingDecision,
upshift_decision: &Option<RoutingDecision>,
) -> bool {
if classification.intent == QueryIntent::NaturalLanguage && classification.confidence > 0.7 {
return true;
}
if let Some(upshift) = upshift_decision {
if upshift.should_upshift && upshift.upshift_type == UpshiftType::SemanticReranking {
return true;
}
}
if matches!(routing_decision.upshift_type, UpshiftType::SemanticReranking) {
return true;
}
false
}
async fn apply_semantic_reranking(
&self,
mut response: SearchResponse,
query: &str,
) -> Result<SearchResponse> {
if response.results.is_empty() {
return Ok(response);
}
let query_embedding = self.encoder.encode_query(query).await?;
let mut similarities = Vec::new();
for result in &response.results {
let content_for_embedding = format!("{} {}", result.file_path, result.content);
match self.encoder.encode_code(&content_for_embedding).await {
Ok(result_embedding) => {
let similarity = query_embedding.cosine_similarity(&result_embedding);
similarities.push(similarity);
}
Err(e) => {
warn!("Failed to encode result for similarity: {}", e);
similarities.push(0.0); }
}
}
for (i, result) in response.results.iter_mut().enumerate() {
let semantic_score = similarities.get(i).cloned().unwrap_or(0.0);
let threshold = self.config.similarity_threshold;
if semantic_score >= threshold {
result.score = 0.7 * semantic_score as f64 + 0.3 * result.score;
}
}
response.results.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal));
Ok(response)
}
async fn update_metrics(&self, processing_metrics: &SemanticProcessingMetrics, semantic_enhanced: bool) {
let mut metrics = self.metrics.write().await;
metrics.total_requests += 1;
if semantic_enhanced {
metrics.successful_enhancements += 1;
} else {
metrics.fallback_count += 1;
}
let total = metrics.total_requests as f64;
metrics.avg_processing_time_ms = (metrics.avg_processing_time_ms * (total - 1.0) + processing_metrics.total_processing_time_ms as f64) / total;
if !processing_metrics.within_time_budget {
debug!("Semantic processing exceeded time budget");
}
}
pub async fn get_metrics(&self) -> IntegrationMetrics {
self.metrics.read().await.clone()
}
pub async fn health_check(&self) -> Result<SemanticHealthStatus> {
let mut status = SemanticHealthStatus::default();
status.encoder_healthy = self.encoder.health_check().await.is_ok();
status.classifier_healthy = true;
status.intent_router_healthy = true;
status.conformal_router_healthy = true;
status.overall_healthy = status.encoder_healthy &&
status.classifier_healthy &&
status.intent_router_healthy &&
status.conformal_router_healthy;
Ok(status)
}
pub async fn shutdown(&self) -> Result<()> {
info!("Shutting down semantic search integration");
info!("Semantic search integration shutdown complete");
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SemanticHealthStatus {
pub overall_healthy: bool,
pub encoder_healthy: bool,
pub classifier_healthy: bool,
pub intent_router_healthy: bool,
pub conformal_router_healthy: bool,
pub last_check: chrono::DateTime<chrono::Utc>,
}
impl Default for SemanticHealthStatus {
fn default() -> Self {
Self {
overall_healthy: false,
encoder_healthy: false,
classifier_healthy: false,
intent_router_healthy: false,
conformal_router_healthy: false,
last_check: chrono::Utc::now(),
}
}
}
pub trait SearchEngineSemanticExt {
async fn search_semantic(&self, request: SemanticSearchRequest, integration: &SemanticSearchIntegration) -> Result<SemanticSearchResponse>;
async fn search_auto_semantic(&self, query: &str, integration: &SemanticSearchIntegration) -> Result<SemanticSearchResponse>;
}
impl SearchEngineSemanticExt for SearchEngine {
async fn search_semantic(&self, request: SemanticSearchRequest, integration: &SemanticSearchIntegration) -> Result<SemanticSearchResponse> {
integration.process_search(self, request).await
}
async fn search_auto_semantic(&self, query: &str, integration: &SemanticSearchIntegration) -> Result<SemanticSearchResponse> {
let base_request = SearchRequest {
query: query.to_string(),
..Default::default()
};
let semantic_request = SemanticSearchRequest::from(base_request);
integration.process_search(self, semantic_request).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
async fn create_test_integration() -> Result<SemanticSearchIntegration> {
let config = SemanticIntegrationConfig {
enabled: true,
nl_upshift_threshold: 0.6,
max_processing_time_ms: 1000, enable_conformal_routing: false, fallback_on_error: true,
enable_result_caching: false,
similarity_threshold: 0.3,
};
SemanticSearchIntegration::new(config).await
}
#[tokio::test]
async fn test_integration_creation() {
let integration = create_test_integration().await;
assert!(integration.is_ok(), "Failed to create semantic integration: {:?}", integration.err());
let integration = integration.unwrap();
let health = integration.health_check().await.unwrap();
assert!(health.encoder_healthy, "Encoder health check failed");
assert!(health.classifier_healthy, "Classifier health check failed");
assert!(health.intent_router_healthy, "Intent router health check failed");
assert!(health.conformal_router_healthy, "Conformal router health check failed");
assert!(health.overall_healthy, "Overall health check failed");
}
#[tokio::test]
async fn test_query_classification_integration() {
let integration = create_test_integration().await.unwrap();
let test_cases = vec![
("how to implement binary search", QueryIntent::NaturalLanguage),
("fn search(&self, query: &str)", QueryIntent::Structural), ("SearchEngine", QueryIntent::Symbol), ];
for (query, expected_intent) in test_cases {
let request = SemanticSearchRequest {
base_request: SearchRequest {
query: query.to_string(),
..Default::default()
},
..Default::default()
};
let classification = integration.classify_query(&request).await.unwrap();
assert_eq!(classification.intent, expected_intent,
"Classification mismatch for query: '{}'", query);
}
}
#[tokio::test]
async fn test_metrics_tracking() {
let integration = create_test_integration().await.unwrap();
let processing_metrics = SemanticProcessingMetrics {
total_processing_time_ms: 50,
within_time_budget: true,
results_reranked: 5,
..Default::default()
};
integration.update_metrics(&processing_metrics, true).await;
let metrics = integration.get_metrics().await;
assert_eq!(metrics.total_requests, 1);
assert_eq!(metrics.successful_enhancements, 1);
assert_eq!(metrics.fallback_count, 0);
assert_eq!(metrics.avg_processing_time_ms, 50.0);
}
#[tokio::test]
async fn test_error_handling_and_fallback() {
let integration = create_test_integration().await.unwrap();
let failed_metrics = SemanticProcessingMetrics {
total_processing_time_ms: 2000, within_time_budget: false,
results_reranked: 0,
..Default::default()
};
integration.update_metrics(&failed_metrics, false).await;
let metrics = integration.get_metrics().await;
assert_eq!(metrics.total_requests, 1);
assert_eq!(metrics.successful_enhancements, 0);
assert_eq!(metrics.fallback_count, 1);
}
#[tokio::test]
async fn test_configuration_edge_cases() {
let config = SemanticIntegrationConfig {
enabled: false, nl_upshift_threshold: 1.1, max_processing_time_ms: 0, enable_conformal_routing: true,
fallback_on_error: false, enable_result_caching: true,
similarity_threshold: -0.1, };
let integration = SemanticSearchIntegration::new(config).await;
assert!(integration.is_ok(), "Should handle invalid configuration gracefully");
}
#[tokio::test]
async fn test_concurrent_processing() {
let integration = create_test_integration().await.unwrap();
let mut handles = vec![];
for i in 0..5 {
let integration_clone = integration.clone();
let handle = tokio::spawn(async move {
let request = SemanticSearchRequest {
base_request: SearchRequest {
query: format!("concurrent test query {}", i),
..Default::default()
},
..Default::default()
};
let result = integration_clone.classify_query(&request).await;
let processing_metrics = SemanticProcessingMetrics {
total_processing_time_ms: 50,
classification_time_ms: 10,
routing_time_ms: 10,
encoding_time_ms: 20,
conformal_time_ms: 10,
results_reranked: 0,
avg_similarity_score: 0.7,
within_time_budget: true,
};
integration_clone.update_metrics(&processing_metrics, result.is_ok()).await;
result
});
handles.push(handle);
}
let mut successful = 0;
for handle in handles {
if let Ok(result) = handle.await {
if result.is_ok() {
successful += 1;
}
}
}
assert!(successful >= 4, "Most concurrent requests should succeed");
let metrics = integration.get_metrics().await;
assert!(metrics.total_requests >= 5);
}
#[tokio::test]
async fn test_health_monitoring_degradation() {
let integration = create_test_integration().await.unwrap();
let initial_health = integration.health_check().await.unwrap();
assert!(initial_health.overall_healthy);
for _ in 0..10 {
let failed_metrics = SemanticProcessingMetrics {
total_processing_time_ms: 5000, within_time_budget: false,
results_reranked: 0,
..Default::default()
};
integration.update_metrics(&failed_metrics, false).await;
}
let degraded_health = integration.health_check().await.unwrap();
assert!(degraded_health.last_check > initial_health.last_check);
}
#[tokio::test]
async fn test_semantic_processing_timeout() {
let config = SemanticIntegrationConfig {
enabled: true,
max_processing_time_ms: 1, ..Default::default()
};
let integration = SemanticSearchIntegration::new(config).await.unwrap();
let request = SemanticSearchRequest {
base_request: SearchRequest {
query: "complex natural language query that might take time to process".to_string(),
..Default::default()
},
..Default::default()
};
let result = integration.classify_query(&request).await;
assert!(result.is_ok(), "Should handle timeout gracefully");
}
#[tokio::test]
async fn test_multiple_intent_classification() {
let integration = create_test_integration().await.unwrap();
let complex_queries = vec![
"find all function definitions that handle authentication",
"class User extends BaseModel",
"TODO: implement caching layer",
"import pandas as pd",
"def calculate_metrics(): pass",
"// This is a comment",
"SELECT * FROM users WHERE active = 1",
];
for query in complex_queries {
let request = SemanticSearchRequest {
base_request: SearchRequest {
query: query.to_string(),
..Default::default()
},
..Default::default()
};
let classification = integration.classify_query(&request).await.unwrap();
assert!(classification.confidence > 0.0);
assert!(classification.confidence <= 1.0);
match classification.intent {
QueryIntent::NaturalLanguage | QueryIntent::Symbol |
QueryIntent::Structural | QueryIntent::Definition |
QueryIntent::References | QueryIntent::Lexical => {
}
_ => panic!("Unexpected query intent: {:?}", classification.intent),
}
}
}
#[tokio::test]
async fn test_metrics_accumulation() {
let integration = create_test_integration().await.unwrap();
let test_scenarios = vec![
(50, true, 3), (150, true, 7), (1200, false, 0), (75, true, 5), ];
for (latency, success, results_count) in test_scenarios {
let metrics = SemanticProcessingMetrics {
total_processing_time_ms: latency,
within_time_budget: success,
results_reranked: results_count,
..Default::default()
};
integration.update_metrics(&metrics, success).await;
}
let final_metrics = integration.get_metrics().await;
assert_eq!(final_metrics.total_requests, 4);
assert_eq!(final_metrics.successful_enhancements, 3);
assert_eq!(final_metrics.fallback_count, 1);
let expected_avg = (50 + 150 + 1200 + 75) as f64 / 4.0;
assert!((final_metrics.avg_processing_time_ms - expected_avg).abs() < 0.1);
}
#[tokio::test]
async fn test_configuration_validation() {
let valid_config = SemanticIntegrationConfig {
enabled: true,
nl_upshift_threshold: 0.7,
max_processing_time_ms: 100,
enable_conformal_routing: true,
fallback_on_error: true,
enable_result_caching: true,
similarity_threshold: 0.5,
};
let integration = SemanticSearchIntegration::new(valid_config).await;
assert!(integration.is_ok(), "Valid configuration should work");
let edge_config = SemanticIntegrationConfig {
enabled: true,
nl_upshift_threshold: 0.0, max_processing_time_ms: 1, enable_conformal_routing: false,
fallback_on_error: false,
enable_result_caching: false,
similarity_threshold: 1.0, };
let edge_integration = SemanticSearchIntegration::new(edge_config).await;
assert!(edge_integration.is_ok(), "Edge configuration should work");
}
#[tokio::test]
async fn test_component_isolation() {
let integration = create_test_integration().await.unwrap();
let request = SemanticSearchRequest {
base_request: SearchRequest {
query: "test isolation".to_string(),
..Default::default()
},
force_semantic: true,
skip_conformal: true, ..Default::default()
};
let classification1 = integration.classify_query(&request).await;
let classification2 = integration.classify_query(&request).await;
assert!(classification1.is_ok());
assert!(classification2.is_ok());
if let (Ok(c1), Ok(c2)) = (classification1, classification2) {
assert_eq!(c1.intent, c2.intent);
}
}
}