use crate::core::metadata::{MetadataService, SkillMetadata};
use crate::core::service::ServiceError;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoutedSkill {
pub skill_id: String,
pub relevance_score: f32,
}
#[async_trait]
pub trait RoutingService: Send + Sync {
async fn find_relevant_skills(
&self,
query: &str,
context: Option<QueryContext>,
) -> Result<Vec<RoutedSkill>, ServiceError>;
}
#[derive(Debug, Clone)]
pub struct QueryContext {
pub available_tokens: Option<usize>,
pub conversation_history: Option<Vec<String>>,
pub user_preferences: Option<std::collections::HashMap<String, String>>,
}
pub struct RoutingServiceImpl {
metadata_service: Arc<dyn MetadataService>,
}
impl RoutingServiceImpl {
pub fn new(metadata_service: Arc<dyn MetadataService>) -> Self {
Self { metadata_service }
}
async fn score_skills_for_query(
&self,
skills: &[SkillMetadata],
query: &str,
context: Option<&QueryContext>,
) -> Vec<(f32, SkillMetadata)> {
let mut scored_skills = Vec::new();
for skill in skills {
let score = self.calculate_relevance_score(skill, query, context);
scored_skills.push((score, skill.clone()));
}
scored_skills.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
scored_skills
}
fn calculate_relevance_score(
&self,
skill: &SkillMetadata,
query: &str,
context: Option<&QueryContext>,
) -> f32 {
let query_lower = query.to_lowercase();
let mut score = 0.0;
if skill.name.to_lowercase() == query_lower {
score += 1.0;
} else if skill.name.to_lowercase().contains(&query_lower) {
score += 0.8;
}
if skill.description.to_lowercase().contains(&query_lower) {
score += 0.6;
}
if let Some(context) = context {
if let Some(available_tokens) = context.available_tokens {
if available_tokens < 2000 && skill.token_estimate < 100 {
score += 0.2; }
}
}
score
}
}
#[async_trait]
impl RoutingService for RoutingServiceImpl {
async fn find_relevant_skills(
&self,
query: &str,
context: Option<QueryContext>,
) -> Result<Vec<RoutedSkill>, ServiceError> {
let all_metadata = self.metadata_service.discover_skills("").await?;
let scored_skills = self
.score_skills_for_query(&all_metadata, query, context.as_ref())
.await;
let routed_skills: Vec<RoutedSkill> = scored_skills
.into_iter()
.take(20) .map(|(score, metadata)| RoutedSkill {
skill_id: metadata.id.to_string(),
relevance_score: score,
})
.collect();
Ok(routed_skills)
}
}