gemini_memory_rs/runtime/
spec_binding.rs1use std::sync::Arc;
13
14use gemini_adk_fluent_rs::live::Live;
15use gemini_adk_fluent_rs::spec::{MemoryBinding, MemorySpec};
16
17use super::live::LiveMemoryExt;
18use super::turn_extractor::MemorySlot;
19use crate::core::MutationIntent;
20use crate::engine::MemorySession;
21
22pub struct SessionMemoryBinding {
37 session: Arc<MemorySession>,
38}
39
40impl SessionMemoryBinding {
41 pub fn new(session: Arc<MemorySession>) -> Self {
43 Self { session }
44 }
45
46 pub fn session(&self) -> &Arc<MemorySession> {
48 &self.session
49 }
50}
51
52impl MemoryBinding for SessionMemoryBinding {
53 fn install(&self, live: Live, memory: &MemorySpec) -> Live {
54 let slots: Vec<MemorySlot> = memory
55 .slots
56 .iter()
57 .filter_map(|s| {
58 MemorySlot::try_new(&s.predicate, &s.to).ok()
62 })
63 .collect();
64 live.with_memory_slots(self.session.clone(), slots)
65 }
66
67 fn remember(&self, note: String) {
68 let session = self.session.clone();
69 tokio::spawn(async move {
70 let turn = session.current_turn();
71 let _ = session
72 .apply_explicit_command(MutationIntent::Remember, ¬e, turn)
73 .await;
74 });
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use crate::core::{SessionId, TurnId, UserId};
82 use crate::engine::MemoryEngine;
83 use gemini_adk_fluent_rs::spec::MemorySlotSpec;
84
85 fn session() -> Arc<MemorySession> {
86 let engine = MemoryEngine::in_memory(UserId::new("usr_1"));
87 let session = Arc::new(engine.begin_session(SessionId::new("ses_1")));
88 session.begin_turn(TurnId(1));
89 session
90 }
91
92 #[tokio::test]
93 async fn remember_commits_through_the_explicit_command_path() {
94 let session = session();
95 let binding = SessionMemoryBinding::new(session.clone());
96 binding.remember("The caller prefers evening appointments".into());
97 for _ in 0..50 {
99 tokio::time::sleep(std::time::Duration::from_millis(10)).await;
100 if !session.known_statements().is_empty() {
101 break;
102 }
103 }
104 assert!(
105 session
106 .known_statements()
107 .iter()
108 .any(|s| s.contains("evening")),
109 "statements: {:?}",
110 session.known_statements()
111 );
112 }
113
114 #[tokio::test]
115 async fn install_wires_slots_onto_the_builder() {
116 let binding = SessionMemoryBinding::new(session());
117 let memory = MemorySpec {
118 slots: vec![MemorySlotSpec {
119 predicate: "dietary_identity".into(),
120 to: "user:diet".into(),
121 }],
122 };
123 let _ = binding.install(Live::builder(), &memory);
126 }
127}