aion_store/memory/
assistant.rs1use std::collections::BTreeMap;
17use std::sync::MutexGuard;
18
19use aion_core::{AssistantSessionId, Payload};
20use async_trait::async_trait;
21use chrono::{DateTime, Utc};
22
23use super::InMemoryStore;
24use crate::StoreError;
25use crate::assistant::{
26 AssistantSessionListing, AssistantSessionRecord, AssistantSessionStore,
27 AssistantTranscriptEvent, UndecodableAssistantSession,
28};
29
30impl InMemoryStore {
31 pub fn write_raw_assistant_session(
39 &self,
40 session_id: &str,
41 bytes: Vec<u8>,
42 ) -> Result<(), StoreError> {
43 if session_id.trim().is_empty() {
44 return Err(StoreError::Serialization(
45 "assistant session id must not be empty".to_owned(),
46 ));
47 }
48 self.lock_assistant_sessions()
49 .insert(session_id.to_owned(), bytes);
50 Ok(())
51 }
52
53 fn assistant_session_exists(&self, session_id: &AssistantSessionId) -> bool {
59 self.lock_assistant_sessions()
60 .contains_key(&session_id.to_string())
61 }
62}
63
64#[async_trait]
65impl AssistantSessionStore for InMemoryStore {
66 async fn put_assistant_session(
67 &self,
68 record: AssistantSessionRecord,
69 ) -> Result<(), StoreError> {
70 let bytes = record.encode()?;
71 self.lock_assistant_sessions()
72 .insert(record.session_id.to_string(), bytes);
73 Ok(())
74 }
75
76 async fn get_assistant_session(
77 &self,
78 session_id: &AssistantSessionId,
79 ) -> Result<Option<AssistantSessionRecord>, StoreError> {
80 self.lock_assistant_sessions()
81 .get(&session_id.to_string())
82 .map(|bytes| AssistantSessionRecord::decode(bytes))
83 .transpose()
84 }
85
86 async fn list_assistant_sessions(&self) -> Result<AssistantSessionListing, StoreError> {
87 let mut listing = AssistantSessionListing::default();
88 for (session_id, bytes) in self.lock_assistant_sessions().iter() {
89 match AssistantSessionRecord::decode(bytes) {
90 Ok(record) => listing.sessions.push(record),
91 Err(error) => listing.undecodable.push(UndecodableAssistantSession {
92 session_id: session_id.clone(),
93 error: error.to_string(),
94 }),
95 }
96 }
97 listing.sort();
98 Ok(listing)
99 }
100
101 async fn append_assistant_transcript_event(
102 &self,
103 session_id: &AssistantSessionId,
104 recorded_at: DateTime<Utc>,
105 payload: Payload,
106 ) -> Result<u64, StoreError> {
107 if !self.assistant_session_exists(session_id) {
108 return Err(StoreError::AssistantSessionNotFound {
109 session_id: session_id.to_string(),
110 });
111 }
112 let mut transcripts = self.lock_assistant_transcripts();
116 let events = transcripts.entry(session_id.to_string()).or_default();
117 let index = u64::try_from(events.len()).map_err(|error| {
118 StoreError::Backend(format!(
119 "assistant transcript for {session_id} is longer than a u64 index can name: {error}"
120 ))
121 })?;
122 events.push(AssistantTranscriptEvent {
123 index,
124 recorded_at,
125 payload,
126 });
127 Ok(index)
128 }
129
130 async fn assistant_transcript_head(
131 &self,
132 session_id: &AssistantSessionId,
133 ) -> Result<u64, StoreError> {
134 let head = self
135 .lock_assistant_transcripts()
136 .get(&session_id.to_string())
137 .map_or(0, Vec::len);
138 u64::try_from(head).map_err(|error| {
139 StoreError::Backend(format!(
140 "assistant transcript for {session_id} is longer than a u64 head can name: {error}"
141 ))
142 })
143 }
144
145 async fn put_assistant_default_harness(
146 &self,
147 subject: &str,
148 harness: &str,
149 ) -> Result<(), StoreError> {
150 self.lock_assistant_default_harnesses()
151 .insert(subject.to_owned(), harness.to_owned());
152 Ok(())
153 }
154
155 async fn assistant_default_harness(&self, subject: &str) -> Result<Option<String>, StoreError> {
156 Ok(self
157 .lock_assistant_default_harnesses()
158 .get(subject)
159 .cloned())
160 }
161
162 async fn assistant_transcript(
163 &self,
164 session_id: &AssistantSessionId,
165 after: Option<u64>,
166 ) -> Result<Vec<AssistantTranscriptEvent>, StoreError> {
167 Ok(self
168 .lock_assistant_transcripts()
169 .get(&session_id.to_string())
170 .map(|events| {
171 events
172 .iter()
173 .filter(|event| after.is_none_or(|bound| event.index > bound))
174 .cloned()
175 .collect()
176 })
177 .unwrap_or_default())
178 }
179}
180
181impl InMemoryStore {
182 fn lock_assistant_sessions(&self) -> MutexGuard<'_, BTreeMap<String, Vec<u8>>> {
184 self.assistant_sessions
185 .lock()
186 .unwrap_or_else(std::sync::PoisonError::into_inner)
187 }
188
189 fn lock_assistant_default_harnesses(&self) -> MutexGuard<'_, BTreeMap<String, String>> {
191 self.assistant_default_harnesses
192 .lock()
193 .unwrap_or_else(std::sync::PoisonError::into_inner)
194 }
195
196 fn lock_assistant_transcripts(
198 &self,
199 ) -> MutexGuard<'_, BTreeMap<String, Vec<AssistantTranscriptEvent>>> {
200 self.assistant_transcripts
201 .lock()
202 .unwrap_or_else(std::sync::PoisonError::into_inner)
203 }
204}