1use std::sync::Arc;
2
3use anyhow::Result;
4use chrono::{DateTime, Utc};
5
6use crate::domain::contracts::NodeStore;
7use crate::domain::models::{AvecState, ListNodesResult, PsiRange, RetrieveResult, SttpNode};
8
9pub struct ContextQueryService {
10 store: Arc<dyn NodeStore>,
11}
12
13impl ContextQueryService {
14 pub fn new(store: Arc<dyn NodeStore>) -> Self {
16 Self { store }
17 }
18
19 pub async fn get_context_async(
20 &self,
21 session_id: &str,
22 stability: f32,
23 friction: f32,
24 logic: f32,
25 autonomy: f32,
26 limit: usize,
27 ) -> RetrieveResult {
28 self.get_context_scoped_filtered_async(
29 Some(session_id),
30 stability,
31 friction,
32 logic,
33 autonomy,
34 None,
35 None,
36 None,
37 limit,
38 )
39 .await
40 }
41
42 pub async fn get_context_global_async(
44 &self,
45 stability: f32,
46 friction: f32,
47 logic: f32,
48 autonomy: f32,
49 limit: usize,
50 ) -> RetrieveResult {
51 self.get_context_scoped_filtered_async(
52 None, stability, friction, logic, autonomy, None, None, None, limit,
53 )
54 .await
55 }
56
57 pub async fn get_context_global_filtered_async(
58 &self,
59 stability: f32,
60 friction: f32,
61 logic: f32,
62 autonomy: f32,
63 from_utc: Option<DateTime<Utc>>,
64 to_utc: Option<DateTime<Utc>>,
65 tiers: Option<&[String]>,
66 limit: usize,
67 ) -> RetrieveResult {
68 self.get_context_scoped_filtered_async(
69 None, stability, friction, logic, autonomy, from_utc, to_utc, tiers, limit,
70 )
71 .await
72 }
73
74 pub async fn get_context_scoped_async(
76 &self,
77 session_id: Option<&str>,
78 stability: f32,
79 friction: f32,
80 logic: f32,
81 autonomy: f32,
82 limit: usize,
83 ) -> RetrieveResult {
84 self.get_context_scoped_filtered_async(
85 session_id, stability, friction, logic, autonomy, None, None, None, limit,
86 )
87 .await
88 }
89
90 pub async fn get_context_scoped_filtered_async(
92 &self,
93 session_id: Option<&str>,
94 stability: f32,
95 friction: f32,
96 logic: f32,
97 autonomy: f32,
98 from_utc: Option<DateTime<Utc>>,
99 to_utc: Option<DateTime<Utc>>,
100 tiers: Option<&[String]>,
101 limit: usize,
102 ) -> RetrieveResult {
103 let current = AvecState {
104 stability,
105 friction,
106 logic,
107 autonomy,
108 };
109
110 let nodes = match session_id {
111 Some(session_id) => match self
112 .store
113 .get_by_resonance_async(session_id, current, from_utc, to_utc, tiers, limit)
114 .await
115 {
116 Ok(nodes) => nodes,
117 Err(_) => return empty_retrieve_result(),
118 },
119 None => match self
120 .store
121 .get_by_resonance_global_async(current, from_utc, to_utc, tiers, limit)
122 .await
123 {
124 Ok(nodes) => nodes,
125 Err(_) => return empty_retrieve_result(),
126 },
127 };
128
129 to_retrieve_result(nodes)
130 }
131
132 pub async fn get_context_hybrid_async(
133 &self,
134 session_id: &str,
135 stability: f32,
136 friction: f32,
137 logic: f32,
138 autonomy: f32,
139 query_embedding: Option<&[f32]>,
140 alpha: f32,
141 beta: f32,
142 limit: usize,
143 ) -> RetrieveResult {
144 self.get_context_hybrid_scoped_filtered_async(
145 Some(session_id),
146 stability,
147 friction,
148 logic,
149 autonomy,
150 None,
151 None,
152 None,
153 query_embedding,
154 alpha,
155 beta,
156 limit,
157 )
158 .await
159 }
160
161 pub async fn get_context_hybrid_global_async(
163 &self,
164 stability: f32,
165 friction: f32,
166 logic: f32,
167 autonomy: f32,
168 query_embedding: Option<&[f32]>,
169 alpha: f32,
170 beta: f32,
171 limit: usize,
172 ) -> RetrieveResult {
173 self.get_context_hybrid_scoped_filtered_async(
174 None,
175 stability,
176 friction,
177 logic,
178 autonomy,
179 None,
180 None,
181 None,
182 query_embedding,
183 alpha,
184 beta,
185 limit,
186 )
187 .await
188 }
189
190 pub async fn get_context_hybrid_global_filtered_async(
191 &self,
192 stability: f32,
193 friction: f32,
194 logic: f32,
195 autonomy: f32,
196 from_utc: Option<DateTime<Utc>>,
197 to_utc: Option<DateTime<Utc>>,
198 tiers: Option<&[String]>,
199 query_embedding: Option<&[f32]>,
200 alpha: f32,
201 beta: f32,
202 limit: usize,
203 ) -> RetrieveResult {
204 self.get_context_hybrid_scoped_filtered_async(
205 None,
206 stability,
207 friction,
208 logic,
209 autonomy,
210 from_utc,
211 to_utc,
212 tiers,
213 query_embedding,
214 alpha,
215 beta,
216 limit,
217 )
218 .await
219 }
220
221 pub async fn get_context_hybrid_scoped_async(
223 &self,
224 session_id: Option<&str>,
225 stability: f32,
226 friction: f32,
227 logic: f32,
228 autonomy: f32,
229 query_embedding: Option<&[f32]>,
230 alpha: f32,
231 beta: f32,
232 limit: usize,
233 ) -> RetrieveResult {
234 self.get_context_hybrid_scoped_filtered_async(
235 session_id,
236 stability,
237 friction,
238 logic,
239 autonomy,
240 None,
241 None,
242 None,
243 query_embedding,
244 alpha,
245 beta,
246 limit,
247 )
248 .await
249 }
250
251 pub async fn get_context_hybrid_scoped_filtered_async(
253 &self,
254 session_id: Option<&str>,
255 stability: f32,
256 friction: f32,
257 logic: f32,
258 autonomy: f32,
259 from_utc: Option<DateTime<Utc>>,
260 to_utc: Option<DateTime<Utc>>,
261 tiers: Option<&[String]>,
262 query_embedding: Option<&[f32]>,
263 alpha: f32,
264 beta: f32,
265 limit: usize,
266 ) -> RetrieveResult {
267 let current = AvecState {
268 stability,
269 friction,
270 logic,
271 autonomy,
272 };
273
274 let nodes = match session_id {
275 Some(session_id) => match self
276 .store
277 .get_by_hybrid_async(
278 session_id,
279 current,
280 from_utc,
281 to_utc,
282 tiers,
283 query_embedding,
284 alpha,
285 beta,
286 limit,
287 )
288 .await
289 {
290 Ok(nodes) => nodes,
291 Err(_) => return empty_retrieve_result(),
292 },
293 None => match self
294 .store
295 .get_by_hybrid_global_async(
296 current,
297 from_utc,
298 to_utc,
299 tiers,
300 query_embedding,
301 alpha,
302 beta,
303 limit,
304 )
305 .await
306 {
307 Ok(nodes) => nodes,
308 Err(_) => return empty_retrieve_result(),
309 },
310 };
311
312 to_retrieve_result(nodes)
313 }
314
315 pub async fn list_nodes_async(
316 &self,
317 limit: usize,
318 session_id: Option<&str>,
319 ) -> Result<ListNodesResult> {
320 let capped_limit = limit.clamp(1, 200);
321 let nodes = self
322 .store
323 .list_nodes_async(capped_limit, session_id)
324 .await?;
325 Ok(ListNodesResult {
326 retrieved: nodes.len(),
327 nodes,
328 })
329 }
330}
331
332fn empty_retrieve_result() -> RetrieveResult {
333 RetrieveResult {
334 nodes: Vec::new(),
335 retrieved: 0,
336 psi_range: PsiRange {
337 min: 0.0,
338 max: 0.0,
339 average: 0.0,
340 },
341 }
342}
343
344fn to_retrieve_result(nodes: Vec<SttpNode>) -> RetrieveResult {
345 if nodes.is_empty() {
346 return empty_retrieve_result();
347 }
348
349 let mut min = f32::INFINITY;
350 let mut max = f32::NEG_INFINITY;
351 let mut sum = 0.0f32;
352
353 for node in &nodes {
354 min = min.min(node.psi);
355 max = max.max(node.psi);
356 sum += node.psi;
357 }
358
359 let retrieved = nodes.len();
360
361 RetrieveResult {
362 retrieved,
363 nodes,
364 psi_range: PsiRange {
365 min,
366 max,
367 average: sum / (retrieved as f32),
368 },
369 }
370}