use crate::episode::Episode;
use crate::types::TaskContext;
use std::collections::HashSet;
use std::sync::Arc;
use super::super::SelfLearningMemory;
impl SelfLearningMemory {
pub(super) fn is_relevant_episode(
&self,
episode: &Arc<Episode>,
context: &TaskContext,
query_tags: &HashSet<&String>,
query_words_gt3: &[&str],
episode_desc_lower: &str,
) -> bool {
if episode.context.domain == context.domain {
return true;
}
if episode.context.language == context.language && episode.context.language.is_some() {
return true;
}
if episode.context.framework == context.framework && episode.context.framework.is_some() {
return true;
}
if episode.context.tags.iter().any(|t| query_tags.contains(t)) {
return true;
}
query_words_gt3
.iter()
.any(|&w| episode_desc_lower.contains(w))
}
pub(super) fn calculate_relevance_score(
&self,
episode: &Arc<Episode>,
context: &TaskContext,
query_tags: &HashSet<&String>,
query_words: &[&str],
query_words_gt3: &[&str],
episode_desc_lower: &str,
) -> f32 {
let episode_ref: &Episode = episode.as_ref();
let mut score = 0.0;
if let Some(reward) = &episode_ref.reward {
score += reward.total * 0.3;
}
let mut context_score = 0.0;
if episode_ref.context.domain == context.domain {
context_score += 0.4;
}
if episode_ref.context.language == context.language
&& episode_ref.context.language.is_some()
{
context_score += 0.3;
}
if episode_ref.context.framework == context.framework
&& episode_ref.context.framework.is_some()
{
context_score += 0.2;
}
let common_tags_count = episode_ref
.context
.tags
.iter()
.filter(|t| query_tags.contains(t))
.count();
if common_tags_count > 0 {
context_score += 0.1 * common_tags_count as f32;
}
score += context_score.min(0.4);
if !query_words.is_empty() {
let common_words_count = query_words_gt3
.iter()
.filter(|&&w| episode_desc_lower.contains(w))
.count();
let similarity = common_words_count as f32 / query_words.len() as f32;
score += similarity * 0.3;
}
score
}
pub(super) fn calculate_heuristic_relevance(
&self,
heuristic: &crate::patterns::Heuristic,
domain_lower: &str,
language_lower: Option<&str>,
framework_lower: Option<&str>,
tags_lower: &[String],
) -> f32 {
let mut score = 0.0;
let condition_lower = heuristic.condition.to_lowercase();
if condition_lower.contains(domain_lower) {
score += 1.0;
}
if let Some(lang) = language_lower {
if condition_lower.contains(lang) {
score += 0.8;
}
}
if let Some(framework) = framework_lower {
if condition_lower.contains(framework) {
score += 0.5;
}
}
for tag in tags_lower {
if condition_lower.contains(tag) {
score += 0.3;
}
}
if score == 0.0 {
score = 0.1;
}
score
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::episode::Episode;
use crate::types::{ComplexityLevel, TaskContext, TaskType};
use std::collections::HashSet;
use uuid::Uuid;
fn create_test_episode(domain: &str, lang: Option<&str>, tags: Vec<&str>) -> Arc<Episode> {
Arc::new(Episode {
episode_id: Uuid::new_v4(),
task_type: TaskType::CodeGeneration,
task_description: "Implement a rust web api with axum".to_string(),
context: TaskContext {
domain: domain.to_string(),
language: lang.map(|s| s.to_string()),
framework: Some("axum".to_string()),
complexity: ComplexityLevel::Moderate,
tags: tags.into_iter().map(|s| s.to_string()).collect(),
},
start_time: chrono::Utc::now(),
end_time: Some(chrono::Utc::now()),
steps: vec![],
outcome: None,
reward: None,
reflection: None,
patterns: vec![],
heuristics: vec![],
applied_patterns: vec![],
salient_features: None,
metadata: std::collections::HashMap::new(),
tags: Vec::new(),
checkpoints: Vec::new(),
})
}
#[test]
fn test_is_relevant_episode() {
let memory = SelfLearningMemory::new();
let episode = create_test_episode("web-api", Some("rust"), vec!["rest", "auth"]);
let context = TaskContext {
domain: "web-api".to_string(),
language: Some("rust".to_string()),
..Default::default()
};
let query_tags_vec = ["rest".to_string()];
let query_tags: HashSet<&String> = query_tags_vec.iter().collect();
let query_words_gt3 = ["axum", "rust"];
let desc_lower = episode.task_description.to_lowercase();
assert!(memory.is_relevant_episode(
&episode,
&context,
&query_tags,
&query_words_gt3,
&desc_lower
));
let context_mismatch = TaskContext {
domain: "data".to_string(),
language: Some("python".to_string()),
..Default::default()
};
let empty_tags = HashSet::new();
let mismatch_words = vec!["data", "science"];
assert!(!memory.is_relevant_episode(
&episode,
&context_mismatch,
&empty_tags,
&mismatch_words,
&desc_lower
));
let context_tag_only = TaskContext {
domain: "other".to_string(),
..Default::default()
};
assert!(memory.is_relevant_episode(
&episode,
&context_tag_only,
&query_tags,
&mismatch_words,
&desc_lower
));
assert!(memory.is_relevant_episode(
&episode,
&context_tag_only,
&empty_tags,
&query_words_gt3,
&desc_lower
));
}
#[test]
fn test_calculate_relevance_score() {
let memory = SelfLearningMemory::new();
let episode = create_test_episode("web-api", Some("rust"), vec!["rest"]);
let context = TaskContext {
domain: "web-api".to_string(),
language: Some("rust".to_string()),
..Default::default()
};
let query_tags_vec = ["rest".to_string()];
let query_tags: HashSet<&String> = query_tags_vec.iter().collect();
let query_words = ["rust", "web", "api"];
let query_words_gt3 = ["rust"];
let desc_lower = episode.task_description.to_lowercase();
let score = memory.calculate_relevance_score(
&episode,
&context,
&query_tags,
&query_words,
&query_words_gt3,
&desc_lower,
);
assert!(score > 0.0);
assert!((0.49..=0.51).contains(&score));
}
#[test]
fn test_calculate_heuristic_relevance() {
let memory = SelfLearningMemory::new();
let heuristic = crate::patterns::Heuristic {
heuristic_id: Uuid::new_v4(),
condition: "In rust web-api using axum".to_string(),
action: "Use middleware".to_string(),
confidence: 0.8,
evidence: crate::Evidence {
episode_ids: vec![],
success_rate: 0.9,
sample_size: 1,
},
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
};
let domain_lower = "web-api";
let lang_lower = Some("rust");
let framework_lower = Some("axum");
let tags_lower = vec!["auth".to_string()];
let score = memory.calculate_heuristic_relevance(
&heuristic,
domain_lower,
lang_lower,
framework_lower,
&tags_lower,
);
assert_eq!(score, 2.3);
let score_mismatch =
memory.calculate_heuristic_relevance(&heuristic, "data", Some("python"), None, &[]);
assert_eq!(score_mismatch, 0.1); }
}