Skip to main content

weavatrix_memory/context/
retrieved.rs

1use super::{
2    ContextBundle, ContextCompiler, ContextRequest, FusedRetrievalHit, RetrievalProvider,
3    RetrievalQuery, TokenEstimator, fuse_retrieval,
4};
5use crate::{MemoryError, MemoryProjection, Result};
6
7#[derive(Debug, Clone)]
8pub struct RetrievedContextBundle {
9    pub context: ContextBundle,
10    pub retrieval: Vec<FusedRetrievalHit>,
11}
12
13impl<T> ContextCompiler<T>
14where
15    T: TokenEstimator,
16{
17    /// Resolves free text into exact graph seeds and compiles their context.
18    ///
19    /// Providers can be lexical, BM25, vector, or hybrid implementations from
20    /// separate crates. Rank fusion is deterministic and provider scores never
21    /// need to share a scale.
22    ///
23    /// # Errors
24    ///
25    /// Returns provider, missing-entity, scope, graph, or budget failures.
26    pub fn compile_with_retrieval(
27        &self,
28        projection: &MemoryProjection,
29        request: &ContextRequest,
30        query: &RetrievalQuery,
31        providers: &[&dyn RetrievalProvider],
32    ) -> Result<RetrievedContextBundle> {
33        let retrieval = fuse_retrieval(providers, query).map_err(MemoryError::from)?;
34        let seeds = retrieval
35            .iter()
36            .filter(|hit| {
37                projection
38                    .visible_node(&hit.entity, request.known_at)
39                    .is_some()
40            })
41            .map(|hit| hit.entity.clone())
42            .collect::<Vec<_>>();
43        if seeds.is_empty() {
44            return Err(MemoryError::Retrieval {
45                provider: "fusion".to_owned(),
46                message: "no retrieved entity exists in this projection".to_owned(),
47            });
48        }
49        let mut exact = request.clone();
50        exact.seeds = seeds;
51        Ok(RetrievedContextBundle {
52            context: self.compile(projection, &exact)?,
53            retrieval,
54        })
55    }
56}