use semantic_memory::SearchResult;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchEvidenceBundle {
pub id: String,
pub items: Vec<EvidenceItem>,
pub aggregate_confidence: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvidenceItem {
pub content: String,
pub source: semantic_memory::SearchSource,
pub score: f64,
pub relevance: EvidenceRelevance,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EvidenceRelevance {
Direct,
Supporting,
Contrasting,
Tangential,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnswerBundle {
pub answer: String,
pub evidence: SearchEvidenceBundle,
pub confidence: f32,
}
pub fn bundle_from_results(results: &[SearchResult]) -> SearchEvidenceBundle {
let items: Vec<EvidenceItem> = results
.iter()
.map(|r| EvidenceItem {
content: r.content.clone(),
source: r.source.clone(),
score: r.score,
relevance: EvidenceRelevance::Direct,
})
.collect();
let aggregate_confidence = if items.is_empty() {
0.0
} else {
let sum: f64 = items.iter().map(|i| i.score).sum();
(sum / items.len() as f64).min(1.0) as f32
};
SearchEvidenceBundle {
id: uuid::Uuid::new_v4().to_string(),
items,
aggregate_confidence,
}
}
#[cfg(test)]
mod tests {
use super::*;
use semantic_memory::{SearchResult, SearchSource};
#[test]
fn bundle_from_empty_results() {
let bundle = bundle_from_results(&[]);
assert!(bundle.items.is_empty());
assert_eq!(bundle.aggregate_confidence, 0.0);
}
#[test]
fn bundle_preserves_order() {
let results = vec![
SearchResult {
content: "first".into(),
source: SearchSource::Fact {
fact_id: "f1".into(),
namespace: "test".into(),
},
score: 0.9,
bm25_rank: None,
vector_rank: None,
cosine_similarity: None,
},
SearchResult {
content: "second".into(),
source: SearchSource::Fact {
fact_id: "f2".into(),
namespace: "test".into(),
},
score: 0.7,
bm25_rank: None,
vector_rank: None,
cosine_similarity: None,
},
];
let bundle = bundle_from_results(&results);
assert_eq!(bundle.items.len(), 2);
assert_eq!(bundle.items[0].content, "first");
assert_eq!(bundle.items[1].content, "second");
}
}