apollo/memory/
context_inject.rs1use std::path::Path;
4use std::sync::Arc;
5
6use super::brief::{format_brief_xml, load_brief};
7use super::graph::GraphNode;
8use super::principal::{load_chat_aliases, resolve_history_chat_ids};
9use super::recall::build_supporting_recall;
10use super::surreal::SurrealMemory;
11use crate::memory::MemoryBackend;
12
13pub struct InjectConfig<'a> {
14 pub workspace: &'a Path,
15 pub principal_id: Option<&'a str>,
16 pub graph_recall_limit: usize,
17}
18
19pub async fn merged_history(
20 memory: &Arc<dyn MemoryBackend>,
21 chat_id: &str,
22 principal_id: Option<&str>,
23 per_chat_limit: usize,
24) -> anyhow::Result<Vec<(String, String)>> {
25 let Some(pid) = principal_id.filter(|s| !s.is_empty()) else {
26 return memory
27 .get_conversation_history(chat_id, per_chat_limit)
28 .await;
29 };
30
31 let aliases = load_chat_aliases(memory, pid).await;
32 let chat_ids = resolve_history_chat_ids(chat_id, pid, &aliases);
33 let mut merged: Vec<(String, String)> = Vec::new();
34
35 for cid in chat_ids {
36 let rows = memory
37 .get_conversation_history(&cid, per_chat_limit)
38 .await?;
39 for row in rows {
40 if !merged.contains(&row) {
41 merged.push(row);
42 }
43 }
44 }
45
46 if merged.len() > per_chat_limit {
47 merged = merged.split_off(merged.len().saturating_sub(per_chat_limit));
48 }
49 Ok(merged)
50}
51
52pub async fn personal_context_blocks(
53 memory: &Arc<dyn MemoryBackend>,
54 cfg: InjectConfig<'_>,
55 user_text: &str,
56) -> Vec<String> {
57 let mut blocks = Vec::new();
58
59 let (tc, ol) = load_brief(memory).await;
60 if let Some(b) = format_brief_xml(&tc, &ol) {
61 blocks.push(b);
62 }
63
64 if let Some(r) = build_supporting_recall(cfg.workspace, memory, user_text, 3).await {
65 blocks.push(r);
66 }
67
68 if let Some(surreal) = memory.as_any().downcast_ref::<SurrealMemory>() {
69 if let Ok(seed) = surreal
70 .graph_match_text(user_text, cfg.graph_recall_limit)
71 .await
72 {
73 let mut nodes: Vec<GraphNode> = seed;
74 if let Some(first) = nodes.first() {
75 if let Ok(neighbors) = surreal.graph_neighbors(&first.id, 3).await {
76 for n in neighbors {
77 if !nodes.iter().any(|x| x.id == n.id) {
78 nodes.push(n);
79 }
80 }
81 }
82 }
83 if let Some(g) = surreal.format_graph_context(&nodes) {
84 blocks.push(g);
85 }
86 }
87 }
88
89 blocks
90}