1use serde::{Deserialize, Serialize};
2
3pub type VectorId = String;
5
6pub type NamespaceId = String;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Vector {
12 pub id: VectorId,
13 pub values: Vec<f32>,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub metadata: Option<serde_json::Value>,
16 #[serde(skip_serializing_if = "Option::is_none")]
18 pub ttl_seconds: Option<u64>,
19 #[serde(skip_serializing_if = "Option::is_none")]
21 pub expires_at: Option<u64>,
22}
23
24impl Vector {
25 pub fn is_expired(&self) -> bool {
27 if let Some(expires_at) = self.expires_at {
28 let now = std::time::SystemTime::now()
29 .duration_since(std::time::UNIX_EPOCH)
30 .unwrap_or_default()
31 .as_secs();
32 now >= expires_at
33 } else {
34 false
35 }
36 }
37
38 #[inline]
41 pub fn is_expired_at(&self, now_secs: u64) -> bool {
42 self.expires_at.is_some_and(|exp| now_secs >= exp)
43 }
44
45 pub fn apply_ttl(&mut self) {
47 if let Some(ttl) = self.ttl_seconds {
48 let now = std::time::SystemTime::now()
49 .duration_since(std::time::UNIX_EPOCH)
50 .unwrap_or_default()
51 .as_secs();
52 self.expires_at = Some(now + ttl);
53 }
54 }
55
56 pub fn remaining_ttl(&self) -> Option<u64> {
58 self.expires_at.and_then(|expires_at| {
59 let now = std::time::SystemTime::now()
60 .duration_since(std::time::UNIX_EPOCH)
61 .unwrap_or_default()
62 .as_secs();
63 if now < expires_at {
64 Some(expires_at - now)
65 } else {
66 None
67 }
68 })
69 }
70}
71
72#[derive(Debug, Deserialize)]
74pub struct UpsertRequest {
75 pub vectors: Vec<Vector>,
76}
77
78#[derive(Debug, Serialize, Deserialize)]
80pub struct UpsertResponse {
81 pub upserted_count: usize,
82}
83
84#[derive(Debug, Deserialize)]
87pub struct ColumnUpsertRequest {
88 pub ids: Vec<VectorId>,
90 pub vectors: Vec<Vec<f32>>,
92 #[serde(default)]
95 pub attributes: std::collections::HashMap<String, Vec<serde_json::Value>>,
96 #[serde(default)]
98 pub ttl_seconds: Option<u64>,
99 #[serde(default)]
101 pub dimension: Option<usize>,
102}
103
104impl ColumnUpsertRequest {
105 pub fn to_vectors(&self) -> Result<Vec<Vector>, String> {
107 let count = self.ids.len();
108
109 if self.vectors.len() != count {
111 return Err(format!(
112 "vectors array length ({}) doesn't match ids array length ({})",
113 self.vectors.len(),
114 count
115 ));
116 }
117
118 for (attr_name, attr_values) in &self.attributes {
119 if attr_values.len() != count {
120 return Err(format!(
121 "attribute '{}' array length ({}) doesn't match ids array length ({})",
122 attr_name,
123 attr_values.len(),
124 count
125 ));
126 }
127 }
128
129 let expected_dim = if let Some(dim) = self.dimension {
132 Some(dim)
133 } else {
134 self.vectors.first().map(|v| v.len())
135 };
136
137 if let Some(expected) = expected_dim {
138 for (i, vec) in self.vectors.iter().enumerate() {
139 if vec.len() != expected {
140 return Err(format!(
141 "vectors[{}] has dimension {} but expected {}",
142 i,
143 vec.len(),
144 expected
145 ));
146 }
147 }
148 }
149
150 let mut vectors = Vec::with_capacity(count);
152 for i in 0..count {
153 let metadata = if self.attributes.is_empty() {
155 None
156 } else {
157 let mut meta = serde_json::Map::new();
158 for (attr_name, attr_values) in &self.attributes {
159 let value = &attr_values[i];
160 if !value.is_null() {
161 meta.insert(attr_name.clone(), value.clone());
162 }
163 }
164 if meta.is_empty() {
165 None
166 } else {
167 Some(serde_json::Value::Object(meta))
168 }
169 };
170
171 let mut vector = Vector {
172 id: self.ids[i].clone(),
173 values: self.vectors[i].clone(),
174 metadata,
175 ttl_seconds: self.ttl_seconds,
176 expires_at: None,
177 };
178 vector.apply_ttl();
179 vectors.push(vector);
180 }
181
182 Ok(vectors)
183 }
184}
185
186#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq)]
188#[serde(rename_all = "snake_case")]
189pub enum DistanceMetric {
190 #[default]
191 Cosine,
192 Euclidean,
193 DotProduct,
194}
195
196#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq)]
203#[serde(rename_all = "snake_case")]
204pub enum ReadConsistency {
205 Strong,
207 #[default]
209 Eventual,
210 #[serde(rename = "bounded_staleness")]
212 BoundedStaleness,
213}
214
215#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq)]
217pub struct StalenessConfig {
218 #[serde(default = "default_max_staleness_ms")]
220 pub max_staleness_ms: u64,
221}
222
223fn default_max_staleness_ms() -> u64 {
224 5000 }
226
227#[derive(Debug, Deserialize)]
229pub struct QueryRequest {
230 pub vector: Vec<f32>,
231 #[serde(default = "default_top_k")]
232 pub top_k: usize,
233 #[serde(default)]
234 pub distance_metric: DistanceMetric,
235 #[serde(default = "default_true")]
236 pub include_metadata: bool,
237 #[serde(default)]
238 pub include_vectors: bool,
239 #[serde(default)]
241 pub filter: Option<FilterExpression>,
242 #[serde(default)]
244 pub cursor: Option<String>,
245 #[serde(default)]
248 pub consistency: ReadConsistency,
249 #[serde(default)]
251 pub staleness_config: Option<StalenessConfig>,
252}
253
254fn default_top_k() -> usize {
255 10
256}
257
258fn default_true() -> bool {
259 true
260}
261
262#[derive(Debug, Serialize, Deserialize)]
264pub struct SearchResult {
265 pub id: VectorId,
266 pub score: f32,
267 #[serde(skip_serializing_if = "Option::is_none")]
268 pub metadata: Option<serde_json::Value>,
269 #[serde(skip_serializing_if = "Option::is_none")]
270 pub vector: Option<Vec<f32>>,
271}
272
273#[derive(Debug, Serialize, Deserialize)]
275pub struct QueryResponse {
276 pub results: Vec<SearchResult>,
277 #[serde(skip_serializing_if = "Option::is_none")]
279 pub next_cursor: Option<String>,
280 #[serde(skip_serializing_if = "Option::is_none")]
282 pub has_more: Option<bool>,
283 #[serde(default)]
285 pub search_time_ms: u64,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize)]
294pub struct PaginationCursor {
295 pub last_score: f32,
297 pub last_id: String,
299}
300
301impl PaginationCursor {
302 pub fn new(last_score: f32, last_id: String) -> Self {
304 Self {
305 last_score,
306 last_id,
307 }
308 }
309
310 pub fn encode(&self) -> String {
312 use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
313 let json = serde_json::to_string(self).unwrap_or_default();
314 URL_SAFE_NO_PAD.encode(json.as_bytes())
315 }
316
317 pub fn decode(cursor: &str) -> Option<Self> {
319 use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
320 let bytes = URL_SAFE_NO_PAD.decode(cursor).ok()?;
321 let json = String::from_utf8(bytes).ok()?;
322 serde_json::from_str(&json).ok()
323 }
324}
325
326#[derive(Debug, Deserialize)]
328pub struct DeleteRequest {
329 pub ids: Vec<VectorId>,
330}
331
332#[derive(Debug, Serialize)]
334pub struct DeleteResponse {
335 pub deleted_count: usize,
336}
337
338#[derive(Debug, Clone, Deserialize)]
344pub struct BatchQueryItem {
345 #[serde(default)]
347 pub id: Option<String>,
348 pub vector: Vec<f32>,
350 #[serde(default = "default_batch_top_k")]
352 pub top_k: u32,
353 #[serde(default)]
355 pub filter: Option<FilterExpression>,
356 #[serde(default)]
358 pub include_metadata: bool,
359 #[serde(default)]
361 pub consistency: ReadConsistency,
362 #[serde(default)]
364 pub staleness_config: Option<StalenessConfig>,
365}
366
367fn default_batch_top_k() -> u32 {
368 10
369}
370
371#[derive(Debug, Deserialize)]
373pub struct BatchQueryRequest {
374 pub queries: Vec<BatchQueryItem>,
376}
377
378#[derive(Debug, Serialize)]
380pub struct BatchQueryResult {
381 #[serde(skip_serializing_if = "Option::is_none")]
383 pub id: Option<String>,
384 pub results: Vec<SearchResult>,
386 pub latency_ms: f64,
388 #[serde(skip_serializing_if = "Option::is_none")]
390 pub error: Option<String>,
391}
392
393#[derive(Debug, Serialize)]
395pub struct BatchQueryResponse {
396 pub results: Vec<BatchQueryResult>,
398 pub total_latency_ms: f64,
400 pub query_count: usize,
402}
403
404#[derive(Debug, Deserialize)]
410pub struct MultiVectorSearchRequest {
411 pub positive_vectors: Vec<Vec<f32>>,
413 #[serde(default)]
415 pub positive_weights: Option<Vec<f32>>,
416 #[serde(default)]
418 pub negative_vectors: Option<Vec<Vec<f32>>>,
419 #[serde(default)]
421 pub negative_weights: Option<Vec<f32>>,
422 #[serde(default = "default_top_k")]
424 pub top_k: usize,
425 #[serde(default)]
427 pub distance_metric: DistanceMetric,
428 #[serde(default)]
430 pub score_threshold: Option<f32>,
431 #[serde(default)]
433 pub enable_mmr: bool,
434 #[serde(default = "default_mmr_lambda")]
436 pub mmr_lambda: f32,
437 #[serde(default = "default_true")]
439 pub include_metadata: bool,
440 #[serde(default)]
442 pub include_vectors: bool,
443 #[serde(default)]
445 pub filter: Option<FilterExpression>,
446 #[serde(default)]
448 pub consistency: ReadConsistency,
449 #[serde(default)]
451 pub staleness_config: Option<StalenessConfig>,
452}
453
454fn default_mmr_lambda() -> f32 {
455 0.5
456}
457
458#[derive(Debug, Serialize, Deserialize)]
460pub struct MultiVectorSearchResult {
461 pub id: VectorId,
462 pub score: f32,
464 #[serde(skip_serializing_if = "Option::is_none")]
466 pub mmr_score: Option<f32>,
467 #[serde(skip_serializing_if = "Option::is_none")]
469 pub original_rank: Option<usize>,
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub metadata: Option<serde_json::Value>,
472 #[serde(skip_serializing_if = "Option::is_none")]
473 pub vector: Option<Vec<f32>>,
474}
475
476#[derive(Debug, Serialize, Deserialize)]
478pub struct MultiVectorSearchResponse {
479 pub results: Vec<MultiVectorSearchResult>,
480 #[serde(skip_serializing_if = "Option::is_none")]
482 pub computed_query_vector: Option<Vec<f32>>,
483}
484
485#[derive(Debug, Serialize, Deserialize)]
491pub struct IndexDocumentRequest {
492 pub id: String,
493 pub text: String,
494 #[serde(skip_serializing_if = "Option::is_none")]
495 pub metadata: Option<serde_json::Value>,
496}
497
498#[derive(Debug, Deserialize)]
500pub struct IndexDocumentsRequest {
501 pub documents: Vec<IndexDocumentRequest>,
502}
503
504#[derive(Debug, Serialize, Deserialize)]
506pub struct IndexDocumentsResponse {
507 pub indexed_count: usize,
508}
509
510#[derive(Debug, Deserialize)]
512pub struct FullTextSearchRequest {
513 pub query: String,
514 #[serde(default = "default_top_k")]
515 pub top_k: usize,
516 #[serde(default)]
518 pub filter: Option<FilterExpression>,
519}
520
521#[derive(Debug, Serialize, Deserialize)]
523pub struct FullTextSearchResult {
524 pub id: String,
525 pub score: f32,
526 #[serde(skip_serializing_if = "Option::is_none")]
527 pub metadata: Option<serde_json::Value>,
528}
529
530#[derive(Debug, Serialize, Deserialize)]
532pub struct FullTextSearchResponse {
533 pub results: Vec<FullTextSearchResult>,
534 #[serde(default)]
536 pub search_time_ms: u64,
537}
538
539#[derive(Debug, Deserialize)]
541pub struct DeleteDocumentsRequest {
542 pub ids: Vec<String>,
543}
544
545#[derive(Debug, Serialize)]
547pub struct DeleteDocumentsResponse {
548 pub deleted_count: usize,
549}
550
551#[derive(Debug, Serialize)]
553pub struct FullTextIndexStats {
554 pub document_count: u32,
555 pub unique_terms: usize,
556 pub avg_doc_length: f32,
557}
558
559#[derive(Debug, Deserialize)]
565pub struct HybridSearchRequest {
566 #[serde(default)]
569 pub vector: Option<Vec<f32>>,
570 pub text: String,
572 #[serde(default = "default_top_k")]
574 pub top_k: usize,
575 #[serde(default = "default_vector_weight")]
578 pub vector_weight: f32,
579 #[serde(default)]
581 pub distance_metric: DistanceMetric,
582 #[serde(default = "default_true")]
584 pub include_metadata: bool,
585 #[serde(default)]
587 pub include_vectors: bool,
588 #[serde(default)]
590 pub filter: Option<FilterExpression>,
591}
592
593fn default_vector_weight() -> f32 {
594 0.5 }
596
597#[derive(Debug, Serialize, Deserialize)]
599pub struct HybridSearchResult {
600 pub id: String,
601 pub score: f32,
603 pub vector_score: f32,
605 pub text_score: f32,
607 #[serde(skip_serializing_if = "Option::is_none")]
608 pub metadata: Option<serde_json::Value>,
609 #[serde(skip_serializing_if = "Option::is_none")]
610 pub vector: Option<Vec<f32>>,
611}
612
613#[derive(Debug, Serialize, Deserialize)]
615pub struct HybridSearchResponse {
616 pub results: Vec<HybridSearchResult>,
617 #[serde(default)]
619 pub search_time_ms: u64,
620}
621
622#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
628#[serde(untagged)]
629pub enum FilterValue {
630 String(String),
631 Number(f64),
632 Integer(i64),
633 Boolean(bool),
634 StringArray(Vec<String>),
635 NumberArray(Vec<f64>),
636}
637
638impl FilterValue {
639 pub fn as_f64(&self) -> Option<f64> {
641 match self {
642 FilterValue::Number(n) => Some(*n),
643 FilterValue::Integer(i) => Some(*i as f64),
644 _ => None,
645 }
646 }
647
648 pub fn as_str(&self) -> Option<&str> {
650 match self {
651 FilterValue::String(s) => Some(s.as_str()),
652 _ => None,
653 }
654 }
655
656 pub fn as_bool(&self) -> Option<bool> {
658 match self {
659 FilterValue::Boolean(b) => Some(*b),
660 _ => None,
661 }
662 }
663}
664
665#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
667#[serde(rename_all = "snake_case")]
668pub enum FilterCondition {
669 #[serde(rename = "$eq")]
671 Eq(FilterValue),
672 #[serde(rename = "$ne")]
674 Ne(FilterValue),
675 #[serde(rename = "$gt")]
677 Gt(FilterValue),
678 #[serde(rename = "$gte")]
680 Gte(FilterValue),
681 #[serde(rename = "$lt")]
683 Lt(FilterValue),
684 #[serde(rename = "$lte")]
686 Lte(FilterValue),
687 #[serde(rename = "$in")]
689 In(Vec<FilterValue>),
690 #[serde(rename = "$nin")]
692 NotIn(Vec<FilterValue>),
693 #[serde(rename = "$exists")]
695 Exists(bool),
696 #[serde(rename = "$contains")]
701 Contains(String),
702 #[serde(rename = "$icontains")]
704 IContains(String),
705 #[serde(rename = "$startsWith")]
707 StartsWith(String),
708 #[serde(rename = "$endsWith")]
710 EndsWith(String),
711 #[serde(rename = "$glob")]
713 Glob(String),
714 #[serde(rename = "$regex")]
716 Regex(String),
717 #[serde(rename = "$arrayContains")]
722 ArrayContains(FilterValue),
723 #[serde(rename = "$arrayContainsAll")]
725 ArrayContainsAll(Vec<FilterValue>),
726 #[serde(rename = "$arrayContainsAny")]
728 ArrayContainsAny(Vec<FilterValue>),
729}
730
731#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
733#[serde(untagged)]
734pub enum FilterExpression {
735 And {
737 #[serde(rename = "$and")]
738 conditions: Vec<FilterExpression>,
739 },
740 Or {
742 #[serde(rename = "$or")]
743 conditions: Vec<FilterExpression>,
744 },
745 Field {
747 #[serde(flatten)]
748 field: std::collections::HashMap<String, FilterCondition>,
749 },
750}
751
752#[derive(Debug, Clone, Serialize, Deserialize, Default)]
758pub struct QuotaConfig {
759 #[serde(skip_serializing_if = "Option::is_none")]
761 pub max_vectors: Option<u64>,
762 #[serde(skip_serializing_if = "Option::is_none")]
764 pub max_storage_bytes: Option<u64>,
765 #[serde(skip_serializing_if = "Option::is_none")]
767 pub max_dimensions: Option<usize>,
768 #[serde(skip_serializing_if = "Option::is_none")]
770 pub max_metadata_bytes: Option<usize>,
771 #[serde(default)]
773 pub enforcement: QuotaEnforcement,
774}
775
776#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
778#[serde(rename_all = "snake_case")]
779pub enum QuotaEnforcement {
780 None,
782 Soft,
784 #[default]
786 Hard,
787}
788
789#[derive(Debug, Clone, Serialize, Deserialize, Default)]
791pub struct QuotaUsage {
792 pub vector_count: u64,
794 pub storage_bytes: u64,
796 pub avg_dimensions: Option<usize>,
798 pub avg_metadata_bytes: Option<usize>,
800 pub last_updated: u64,
802}
803
804impl QuotaUsage {
805 pub fn new(vector_count: u64, storage_bytes: u64) -> Self {
807 let now = std::time::SystemTime::now()
808 .duration_since(std::time::UNIX_EPOCH)
809 .unwrap_or_default()
810 .as_secs();
811 Self {
812 vector_count,
813 storage_bytes,
814 avg_dimensions: None,
815 avg_metadata_bytes: None,
816 last_updated: now,
817 }
818 }
819
820 pub fn touch(&mut self) {
822 self.last_updated = std::time::SystemTime::now()
823 .duration_since(std::time::UNIX_EPOCH)
824 .unwrap_or_default()
825 .as_secs();
826 }
827}
828
829#[derive(Debug, Clone, Serialize, Deserialize)]
831pub struct QuotaStatus {
832 pub namespace: String,
834 pub config: QuotaConfig,
836 pub usage: QuotaUsage,
838 #[serde(skip_serializing_if = "Option::is_none")]
840 pub vector_usage_percent: Option<f32>,
841 #[serde(skip_serializing_if = "Option::is_none")]
843 pub storage_usage_percent: Option<f32>,
844 pub is_exceeded: bool,
846 #[serde(skip_serializing_if = "Vec::is_empty")]
848 pub exceeded_quotas: Vec<String>,
849}
850
851impl QuotaStatus {
852 pub fn new(namespace: String, config: QuotaConfig, usage: QuotaUsage) -> Self {
854 let vector_usage_percent = config
855 .max_vectors
856 .map(|max| (usage.vector_count as f32 / max as f32) * 100.0);
857
858 let storage_usage_percent = config
859 .max_storage_bytes
860 .map(|max| (usage.storage_bytes as f32 / max as f32) * 100.0);
861
862 let mut exceeded_quotas = Vec::new();
863
864 if let Some(max) = config.max_vectors {
865 if usage.vector_count > max {
866 exceeded_quotas.push("max_vectors".to_string());
867 }
868 }
869
870 if let Some(max) = config.max_storage_bytes {
871 if usage.storage_bytes > max {
872 exceeded_quotas.push("max_storage_bytes".to_string());
873 }
874 }
875
876 let is_exceeded = !exceeded_quotas.is_empty();
877
878 Self {
879 namespace,
880 config,
881 usage,
882 vector_usage_percent,
883 storage_usage_percent,
884 is_exceeded,
885 exceeded_quotas,
886 }
887 }
888}
889
890#[derive(Debug, Deserialize)]
892pub struct SetQuotaRequest {
893 pub config: QuotaConfig,
895}
896
897#[derive(Debug, Serialize)]
899pub struct SetQuotaResponse {
900 pub success: bool,
902 pub namespace: String,
904 pub config: QuotaConfig,
906 pub message: String,
908}
909
910#[derive(Debug, Clone, Serialize)]
912pub struct QuotaCheckResult {
913 pub allowed: bool,
915 #[serde(skip_serializing_if = "Option::is_none")]
917 pub reason: Option<String>,
918 pub usage: QuotaUsage,
920 #[serde(skip_serializing_if = "Option::is_none")]
922 pub exceeded_quota: Option<String>,
923}
924
925#[derive(Debug, Serialize)]
927pub struct QuotaListResponse {
928 pub quotas: Vec<QuotaStatus>,
930 pub total: u64,
932 #[serde(skip_serializing_if = "Option::is_none")]
934 pub default_config: Option<QuotaConfig>,
935}
936
937#[derive(Debug, Serialize)]
939pub struct DefaultQuotaResponse {
940 pub config: Option<QuotaConfig>,
942}
943
944#[derive(Debug, Deserialize)]
946pub struct SetDefaultQuotaRequest {
947 pub config: Option<QuotaConfig>,
949}
950
951#[derive(Debug, Deserialize)]
953pub struct QuotaCheckRequest {
954 pub vector_ids: Vec<String>,
956 #[serde(default)]
958 pub dimensions: Option<usize>,
959 #[serde(default)]
961 pub metadata_bytes: Option<usize>,
962}
963
964#[derive(Debug, Deserialize)]
970pub struct ExportRequest {
971 #[serde(default = "default_export_top_k")]
973 pub top_k: usize,
974 #[serde(skip_serializing_if = "Option::is_none")]
976 pub cursor: Option<String>,
977 #[serde(default = "default_true")]
979 pub include_vectors: bool,
980 #[serde(default = "default_true")]
982 pub include_metadata: bool,
983}
984
985fn default_export_top_k() -> usize {
986 1000
987}
988
989impl Default for ExportRequest {
990 fn default() -> Self {
991 Self {
992 top_k: 1000,
993 cursor: None,
994 include_vectors: true,
995 include_metadata: true,
996 }
997 }
998}
999
1000#[derive(Debug, Clone, Serialize, Deserialize)]
1002pub struct ExportedVector {
1003 pub id: String,
1005 #[serde(skip_serializing_if = "Option::is_none")]
1007 pub values: Option<Vec<f32>>,
1008 #[serde(skip_serializing_if = "Option::is_none")]
1010 pub metadata: Option<serde_json::Value>,
1011 #[serde(skip_serializing_if = "Option::is_none")]
1013 pub ttl_seconds: Option<u64>,
1014}
1015
1016impl From<&Vector> for ExportedVector {
1017 fn from(v: &Vector) -> Self {
1018 Self {
1019 id: v.id.clone(),
1020 values: Some(v.values.clone()),
1021 metadata: v.metadata.clone(),
1022 ttl_seconds: v.ttl_seconds,
1023 }
1024 }
1025}
1026
1027#[derive(Debug, Serialize)]
1029pub struct ExportResponse {
1030 pub vectors: Vec<ExportedVector>,
1032 #[serde(skip_serializing_if = "Option::is_none")]
1034 pub next_cursor: Option<String>,
1035 pub total_count: usize,
1037 pub returned_count: usize,
1039}
1040
1041#[derive(Debug, Clone, Serialize, Deserialize)]
1048#[serde(untagged)]
1049pub enum RankBy {
1050 VectorSearch {
1053 field: String,
1054 method: VectorSearchMethod,
1055 query_vector: Vec<f32>,
1056 },
1057 FullTextSearch {
1059 field: String,
1060 method: String, query: String,
1062 },
1063 AttributeOrder {
1065 field: String,
1066 direction: SortDirection,
1067 },
1068 Sum(Vec<RankBy>),
1070 Max(Vec<RankBy>),
1072 Product { weight: f32, ranking: Box<RankBy> },
1074}
1075
1076#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
1078pub enum VectorSearchMethod {
1079 #[default]
1081 ANN,
1082 #[serde(rename = "kNN")]
1084 KNN,
1085}
1086
1087#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
1089#[serde(rename_all = "lowercase")]
1090#[derive(Default)]
1091pub enum SortDirection {
1092 Asc,
1093 #[default]
1094 Desc,
1095}
1096
1097#[derive(Debug, Deserialize)]
1099pub struct UnifiedQueryRequest {
1100 pub rank_by: RankByInput,
1102 #[serde(default = "default_top_k")]
1104 pub top_k: usize,
1105 #[serde(default)]
1107 pub filter: Option<FilterExpression>,
1108 #[serde(default = "default_true")]
1110 pub include_metadata: bool,
1111 #[serde(default)]
1113 pub include_vectors: bool,
1114 #[serde(default)]
1116 pub distance_metric: DistanceMetric,
1117}
1118
1119#[derive(Debug, Clone, Serialize, Deserialize)]
1127#[serde(from = "serde_json::Value")]
1128pub struct RankByInput(pub RankBy);
1129
1130impl From<serde_json::Value> for RankByInput {
1131 fn from(value: serde_json::Value) -> Self {
1132 RankByInput(parse_rank_by(&value).unwrap_or_else(|| {
1133 RankBy::AttributeOrder {
1135 field: "id".to_string(),
1136 direction: SortDirection::Asc,
1137 }
1138 }))
1139 }
1140}
1141
1142fn parse_rank_by(value: &serde_json::Value) -> Option<RankBy> {
1144 let arr = value.as_array()?;
1145 if arr.is_empty() {
1146 return None;
1147 }
1148
1149 let first = arr.first()?.as_str()?;
1150
1151 match first {
1152 "Sum" => {
1154 let rankings = arr.get(1)?.as_array()?;
1155 let parsed: Option<Vec<RankBy>> = rankings.iter().map(parse_rank_by).collect();
1156 Some(RankBy::Sum(parsed?))
1157 }
1158 "Max" => {
1159 let rankings = arr.get(1)?.as_array()?;
1160 let parsed: Option<Vec<RankBy>> = rankings.iter().map(parse_rank_by).collect();
1161 Some(RankBy::Max(parsed?))
1162 }
1163 "Product" => {
1164 let weight = arr.get(1)?.as_f64()? as f32;
1165 let ranking = parse_rank_by(arr.get(2)?)?;
1166 Some(RankBy::Product {
1167 weight,
1168 ranking: Box::new(ranking),
1169 })
1170 }
1171 "ANN" => {
1173 let query_vector = parse_vector_array(arr.get(1)?)?;
1174 Some(RankBy::VectorSearch {
1175 field: "vector".to_string(),
1176 method: VectorSearchMethod::ANN,
1177 query_vector,
1178 })
1179 }
1180 "kNN" => {
1181 let query_vector = parse_vector_array(arr.get(1)?)?;
1182 Some(RankBy::VectorSearch {
1183 field: "vector".to_string(),
1184 method: VectorSearchMethod::KNN,
1185 query_vector,
1186 })
1187 }
1188 field => {
1190 let second = arr.get(1)?;
1191
1192 if let Some(method_str) = second.as_str() {
1194 match method_str {
1195 "ANN" => {
1196 let query_vector = parse_vector_array(arr.get(2)?)?;
1197 Some(RankBy::VectorSearch {
1198 field: field.to_string(),
1199 method: VectorSearchMethod::ANN,
1200 query_vector,
1201 })
1202 }
1203 "kNN" => {
1204 let query_vector = parse_vector_array(arr.get(2)?)?;
1205 Some(RankBy::VectorSearch {
1206 field: field.to_string(),
1207 method: VectorSearchMethod::KNN,
1208 query_vector,
1209 })
1210 }
1211 "BM25" => {
1212 let query = arr.get(2)?.as_str()?;
1213 Some(RankBy::FullTextSearch {
1214 field: field.to_string(),
1215 method: "BM25".to_string(),
1216 query: query.to_string(),
1217 })
1218 }
1219 "asc" => Some(RankBy::AttributeOrder {
1220 field: field.to_string(),
1221 direction: SortDirection::Asc,
1222 }),
1223 "desc" => Some(RankBy::AttributeOrder {
1224 field: field.to_string(),
1225 direction: SortDirection::Desc,
1226 }),
1227 _ => None,
1228 }
1229 } else {
1230 None
1231 }
1232 }
1233 }
1234}
1235
1236fn parse_vector_array(value: &serde_json::Value) -> Option<Vec<f32>> {
1238 let arr = value.as_array()?;
1239 arr.iter().map(|v| v.as_f64().map(|n| n as f32)).collect()
1240}
1241
1242#[derive(Debug, Serialize, Deserialize)]
1244pub struct UnifiedQueryResponse {
1245 pub results: Vec<UnifiedSearchResult>,
1247 #[serde(skip_serializing_if = "Option::is_none")]
1249 pub next_cursor: Option<String>,
1250}
1251
1252#[derive(Debug, Serialize, Deserialize)]
1254pub struct UnifiedSearchResult {
1255 pub id: String,
1257 #[serde(rename = "$dist", skip_serializing_if = "Option::is_none")]
1260 pub dist: Option<f32>,
1261 #[serde(skip_serializing_if = "Option::is_none")]
1263 pub metadata: Option<serde_json::Value>,
1264 #[serde(skip_serializing_if = "Option::is_none")]
1266 pub vector: Option<Vec<f32>>,
1267}
1268
1269#[derive(Debug, Clone, Serialize, Deserialize)]
1275pub enum AggregateFunction {
1276 Count,
1278 Sum { field: String },
1280 Avg { field: String },
1282 Min { field: String },
1284 Max { field: String },
1286}
1287
1288#[derive(Debug, Clone, Serialize, Deserialize)]
1290#[serde(from = "serde_json::Value")]
1291pub struct AggregateFunctionInput(pub AggregateFunction);
1292
1293impl From<serde_json::Value> for AggregateFunctionInput {
1294 fn from(value: serde_json::Value) -> Self {
1295 parse_aggregate_function(&value)
1296 .map(AggregateFunctionInput)
1297 .unwrap_or_else(|| {
1298 AggregateFunctionInput(AggregateFunction::Count)
1300 })
1301 }
1302}
1303
1304fn parse_aggregate_function(value: &serde_json::Value) -> Option<AggregateFunction> {
1306 let arr = value.as_array()?;
1307 if arr.is_empty() {
1308 return None;
1309 }
1310
1311 let func_name = arr.first()?.as_str()?;
1312
1313 match func_name {
1314 "Count" => Some(AggregateFunction::Count),
1315 "Sum" => {
1316 let field = arr.get(1)?.as_str()?;
1317 Some(AggregateFunction::Sum {
1318 field: field.to_string(),
1319 })
1320 }
1321 "Avg" => {
1322 let field = arr.get(1)?.as_str()?;
1323 Some(AggregateFunction::Avg {
1324 field: field.to_string(),
1325 })
1326 }
1327 "Min" => {
1328 let field = arr.get(1)?.as_str()?;
1329 Some(AggregateFunction::Min {
1330 field: field.to_string(),
1331 })
1332 }
1333 "Max" => {
1334 let field = arr.get(1)?.as_str()?;
1335 Some(AggregateFunction::Max {
1336 field: field.to_string(),
1337 })
1338 }
1339 _ => None,
1340 }
1341}
1342
1343#[derive(Debug, Deserialize)]
1345pub struct AggregationRequest {
1346 pub aggregate_by: std::collections::HashMap<String, AggregateFunctionInput>,
1349 #[serde(default)]
1352 pub group_by: Vec<String>,
1353 #[serde(default)]
1355 pub filter: Option<FilterExpression>,
1356 #[serde(default = "default_agg_limit")]
1358 pub limit: usize,
1359}
1360
1361fn default_agg_limit() -> usize {
1362 100
1363}
1364
1365#[derive(Debug, Serialize, Deserialize)]
1367pub struct AggregationResponse {
1368 #[serde(skip_serializing_if = "Option::is_none")]
1370 pub aggregations: Option<std::collections::HashMap<String, serde_json::Value>>,
1371 #[serde(skip_serializing_if = "Option::is_none")]
1373 pub aggregation_groups: Option<Vec<AggregationGroup>>,
1374}
1375
1376#[derive(Debug, Serialize, Deserialize)]
1378pub struct AggregationGroup {
1379 #[serde(flatten)]
1381 pub group_key: std::collections::HashMap<String, serde_json::Value>,
1382 #[serde(flatten)]
1384 pub aggregations: std::collections::HashMap<String, serde_json::Value>,
1385}
1386
1387#[derive(Debug, Clone, Serialize, Deserialize)]
1393pub struct TextDocument {
1394 pub id: VectorId,
1396 pub text: String,
1398 #[serde(skip_serializing_if = "Option::is_none")]
1400 pub metadata: Option<serde_json::Value>,
1401 #[serde(skip_serializing_if = "Option::is_none")]
1403 pub ttl_seconds: Option<u64>,
1404}
1405
1406#[derive(Debug, Deserialize)]
1408pub struct TextUpsertRequest {
1409 pub documents: Vec<TextDocument>,
1411 #[serde(default)]
1413 pub model: Option<EmbeddingModelType>,
1414}
1415
1416#[derive(Debug, Serialize, Deserialize)]
1418pub struct TextUpsertResponse {
1419 pub upserted_count: usize,
1421 pub tokens_processed: usize,
1423 pub model: EmbeddingModelType,
1425 pub embedding_time_ms: u64,
1427}
1428
1429#[derive(Debug, Deserialize)]
1431pub struct TextQueryRequest {
1432 pub text: String,
1434 #[serde(default = "default_top_k")]
1436 pub top_k: usize,
1437 #[serde(default)]
1439 pub filter: Option<FilterExpression>,
1440 #[serde(default)]
1442 pub include_vectors: bool,
1443 #[serde(default = "default_true")]
1445 pub include_text: bool,
1446 #[serde(default)]
1448 pub model: Option<EmbeddingModelType>,
1449}
1450
1451#[derive(Debug, Serialize, Deserialize)]
1453pub struct TextQueryResponse {
1454 pub results: Vec<TextSearchResult>,
1456 pub model: EmbeddingModelType,
1458 pub embedding_time_ms: u64,
1460 pub search_time_ms: u64,
1462}
1463
1464#[derive(Debug, Serialize, Deserialize)]
1466pub struct TextSearchResult {
1467 pub id: VectorId,
1469 pub score: f32,
1471 #[serde(skip_serializing_if = "Option::is_none")]
1473 pub text: Option<String>,
1474 #[serde(skip_serializing_if = "Option::is_none")]
1476 pub metadata: Option<serde_json::Value>,
1477 #[serde(skip_serializing_if = "Option::is_none")]
1479 pub vector: Option<Vec<f32>>,
1480}
1481
1482#[derive(Debug, Deserialize)]
1484pub struct BatchTextQueryRequest {
1485 pub queries: Vec<String>,
1487 #[serde(default = "default_top_k")]
1489 pub top_k: usize,
1490 #[serde(default)]
1492 pub filter: Option<FilterExpression>,
1493 #[serde(default)]
1495 pub include_vectors: bool,
1496 #[serde(default)]
1498 pub model: Option<EmbeddingModelType>,
1499}
1500
1501#[derive(Debug, Serialize, Deserialize)]
1503pub struct BatchTextQueryResponse {
1504 pub results: Vec<Vec<TextSearchResult>>,
1506 pub model: EmbeddingModelType,
1508 pub embedding_time_ms: u64,
1510 pub search_time_ms: u64,
1512}
1513
1514#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1522pub enum EmbeddingModelType {
1523 #[default]
1525 #[serde(rename = "bge-large")]
1526 BgeLarge,
1527 #[serde(rename = "minilm")]
1529 MiniLM,
1530 #[serde(rename = "bge-small")]
1532 BgeSmall,
1533 #[serde(rename = "e5-small")]
1535 E5Small,
1536}
1537
1538impl EmbeddingModelType {
1539 pub fn dimension(&self) -> usize {
1541 match self {
1542 EmbeddingModelType::BgeLarge => 1024,
1543 EmbeddingModelType::MiniLM => 384,
1544 EmbeddingModelType::BgeSmall => 384,
1545 EmbeddingModelType::E5Small => 384,
1546 }
1547 }
1548}
1549
1550impl std::fmt::Display for EmbeddingModelType {
1551 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1552 match self {
1553 EmbeddingModelType::BgeLarge => write!(f, "bge-large"),
1554 EmbeddingModelType::MiniLM => write!(f, "minilm"),
1555 EmbeddingModelType::BgeSmall => write!(f, "bge-small"),
1556 EmbeddingModelType::E5Small => write!(f, "e5-small"),
1557 }
1558 }
1559}
1560
1561#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1567#[serde(rename_all = "snake_case")]
1568#[derive(Default)]
1569pub enum MemoryType {
1570 #[default]
1572 Episodic,
1573 Semantic,
1575 Procedural,
1577 Working,
1579}
1580
1581impl std::fmt::Display for MemoryType {
1582 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1583 match self {
1584 MemoryType::Episodic => write!(f, "episodic"),
1585 MemoryType::Semantic => write!(f, "semantic"),
1586 MemoryType::Procedural => write!(f, "procedural"),
1587 MemoryType::Working => write!(f, "working"),
1588 }
1589 }
1590}
1591
1592#[derive(Debug, Clone, Serialize, Deserialize)]
1594pub struct Memory {
1595 pub id: String,
1596 #[serde(default)]
1597 pub memory_type: MemoryType,
1598 pub content: String,
1599 pub agent_id: String,
1600 #[serde(skip_serializing_if = "Option::is_none")]
1601 pub session_id: Option<String>,
1602 #[serde(default = "default_importance")]
1603 pub importance: f32,
1604 #[serde(default)]
1605 pub tags: Vec<String>,
1606 #[serde(skip_serializing_if = "Option::is_none")]
1607 pub metadata: Option<serde_json::Value>,
1608 pub created_at: u64,
1609 pub last_accessed_at: u64,
1610 #[serde(default)]
1611 pub access_count: u32,
1612 #[serde(skip_serializing_if = "Option::is_none")]
1613 pub ttl_seconds: Option<u64>,
1614 #[serde(skip_serializing_if = "Option::is_none")]
1615 pub expires_at: Option<u64>,
1616}
1617
1618fn default_importance() -> f32 {
1619 0.5
1620}
1621
1622impl Memory {
1623 pub fn new(id: String, content: String, agent_id: String, memory_type: MemoryType) -> Self {
1625 let now = std::time::SystemTime::now()
1626 .duration_since(std::time::UNIX_EPOCH)
1627 .unwrap_or_default()
1628 .as_secs();
1629 Self {
1630 id,
1631 memory_type,
1632 content,
1633 agent_id,
1634 session_id: None,
1635 importance: 0.5,
1636 tags: Vec::new(),
1637 metadata: None,
1638 created_at: now,
1639 last_accessed_at: now,
1640 access_count: 0,
1641 ttl_seconds: None,
1642 expires_at: None,
1643 }
1644 }
1645
1646 pub fn is_expired(&self) -> bool {
1648 if let Some(expires_at) = self.expires_at {
1649 let now = std::time::SystemTime::now()
1650 .duration_since(std::time::UNIX_EPOCH)
1651 .unwrap_or_default()
1652 .as_secs();
1653 now >= expires_at
1654 } else {
1655 false
1656 }
1657 }
1658
1659 pub fn to_vector_metadata(&self) -> serde_json::Value {
1661 let mut meta = serde_json::Map::new();
1662 meta.insert("_dakera_type".to_string(), serde_json::json!("memory"));
1663 meta.insert(
1664 "memory_type".to_string(),
1665 serde_json::json!(self.memory_type),
1666 );
1667 meta.insert("content".to_string(), serde_json::json!(self.content));
1668 meta.insert("agent_id".to_string(), serde_json::json!(self.agent_id));
1669 if let Some(ref sid) = self.session_id {
1670 meta.insert("session_id".to_string(), serde_json::json!(sid));
1671 }
1672 meta.insert("importance".to_string(), serde_json::json!(self.importance));
1673 meta.insert("tags".to_string(), serde_json::json!(self.tags));
1674 meta.insert("created_at".to_string(), serde_json::json!(self.created_at));
1675 meta.insert(
1676 "last_accessed_at".to_string(),
1677 serde_json::json!(self.last_accessed_at),
1678 );
1679 meta.insert(
1680 "access_count".to_string(),
1681 serde_json::json!(self.access_count),
1682 );
1683 if let Some(ref ttl) = self.ttl_seconds {
1684 meta.insert("ttl_seconds".to_string(), serde_json::json!(ttl));
1685 }
1686 if let Some(ref expires) = self.expires_at {
1687 meta.insert("expires_at".to_string(), serde_json::json!(expires));
1688 }
1689 if let Some(ref user_meta) = self.metadata {
1690 meta.insert("user_metadata".to_string(), user_meta.clone());
1691 }
1692 serde_json::Value::Object(meta)
1693 }
1694
1695 pub fn to_vector(&self, embedding: Vec<f32>) -> Vector {
1697 let mut v = Vector {
1698 id: self.id.clone(),
1699 values: embedding,
1700 metadata: Some(self.to_vector_metadata()),
1701 ttl_seconds: self.ttl_seconds,
1702 expires_at: self.expires_at,
1703 };
1704 v.apply_ttl();
1705 v
1706 }
1707
1708 pub fn from_vector(vector: &Vector) -> Option<Self> {
1710 let meta = vector.metadata.as_ref()?.as_object()?;
1711 let entry_type = meta.get("_dakera_type")?.as_str()?;
1712 if entry_type != "memory" {
1713 return None;
1714 }
1715
1716 Some(Memory {
1717 id: vector.id.clone(),
1718 memory_type: serde_json::from_value(meta.get("memory_type")?.clone())
1719 .unwrap_or_default(),
1720 content: meta.get("content")?.as_str()?.to_string(),
1721 agent_id: meta.get("agent_id")?.as_str()?.to_string(),
1722 session_id: meta
1723 .get("session_id")
1724 .and_then(|v| v.as_str())
1725 .map(String::from),
1726 importance: meta
1727 .get("importance")
1728 .and_then(|v| v.as_f64())
1729 .unwrap_or(0.5) as f32,
1730 tags: meta
1731 .get("tags")
1732 .and_then(|v| serde_json::from_value(v.clone()).ok())
1733 .unwrap_or_default(),
1734 metadata: meta.get("user_metadata").cloned(),
1735 created_at: meta.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0),
1736 last_accessed_at: meta
1737 .get("last_accessed_at")
1738 .and_then(|v| v.as_u64())
1739 .unwrap_or(0),
1740 access_count: meta
1741 .get("access_count")
1742 .and_then(|v| v.as_u64())
1743 .unwrap_or(0) as u32,
1744 ttl_seconds: vector.ttl_seconds,
1745 expires_at: vector.expires_at,
1746 })
1747 }
1748}
1749
1750#[derive(Debug, Clone, Serialize, Deserialize)]
1752pub struct Session {
1753 pub id: String,
1754 pub agent_id: String,
1755 pub started_at: u64,
1756 #[serde(skip_serializing_if = "Option::is_none")]
1757 pub ended_at: Option<u64>,
1758 #[serde(skip_serializing_if = "Option::is_none")]
1759 pub summary: Option<String>,
1760 #[serde(skip_serializing_if = "Option::is_none")]
1761 pub metadata: Option<serde_json::Value>,
1762 #[serde(default)]
1764 pub memory_count: usize,
1765}
1766
1767impl Session {
1768 pub fn new(id: String, agent_id: String) -> Self {
1769 let now = std::time::SystemTime::now()
1770 .duration_since(std::time::UNIX_EPOCH)
1771 .unwrap_or_default()
1772 .as_secs();
1773 Self {
1774 id,
1775 agent_id,
1776 started_at: now,
1777 ended_at: None,
1778 summary: None,
1779 metadata: None,
1780 memory_count: 0,
1781 }
1782 }
1783
1784 pub fn to_vector_metadata(&self) -> serde_json::Value {
1786 let mut meta = serde_json::Map::new();
1787 meta.insert("_dakera_type".to_string(), serde_json::json!("session"));
1788 meta.insert("agent_id".to_string(), serde_json::json!(self.agent_id));
1789 meta.insert("started_at".to_string(), serde_json::json!(self.started_at));
1790 if let Some(ref ended) = self.ended_at {
1791 meta.insert("ended_at".to_string(), serde_json::json!(ended));
1792 }
1793 if let Some(ref summary) = self.summary {
1794 meta.insert("summary".to_string(), serde_json::json!(summary));
1795 }
1796 if let Some(ref user_meta) = self.metadata {
1797 meta.insert("user_metadata".to_string(), user_meta.clone());
1798 }
1799 meta.insert(
1800 "memory_count".to_string(),
1801 serde_json::json!(self.memory_count),
1802 );
1803 serde_json::Value::Object(meta)
1804 }
1805
1806 pub fn to_vector(&self, embedding: Vec<f32>) -> Vector {
1808 Vector {
1809 id: self.id.clone(),
1810 values: embedding,
1811 metadata: Some(self.to_vector_metadata()),
1812 ttl_seconds: None,
1813 expires_at: None,
1814 }
1815 }
1816
1817 pub fn from_vector(vector: &Vector) -> Option<Self> {
1819 let meta = vector.metadata.as_ref()?.as_object()?;
1820 let entry_type = meta.get("_dakera_type")?.as_str()?;
1821 if entry_type != "session" {
1822 return None;
1823 }
1824
1825 Some(Session {
1826 id: vector.id.clone(),
1827 agent_id: meta.get("agent_id")?.as_str()?.to_string(),
1828 started_at: meta.get("started_at").and_then(|v| v.as_u64()).unwrap_or(0),
1829 ended_at: meta.get("ended_at").and_then(|v| v.as_u64()),
1830 summary: meta
1831 .get("summary")
1832 .and_then(|v| v.as_str())
1833 .map(String::from),
1834 metadata: meta.get("user_metadata").cloned(),
1835 memory_count: meta
1836 .get("memory_count")
1837 .and_then(|v| v.as_u64())
1838 .unwrap_or(0) as usize,
1839 })
1840 }
1841}
1842
1843#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1845#[serde(rename_all = "snake_case")]
1846#[derive(Default)]
1847pub enum DecayStrategy {
1848 #[default]
1849 Exponential,
1850 Linear,
1851 StepFunction,
1852 PowerLaw,
1854 Logarithmic,
1856 Flat,
1858}
1859
1860#[derive(Debug, Clone, Serialize, Deserialize)]
1862pub struct DecayConfig {
1863 #[serde(default)]
1864 pub strategy: DecayStrategy,
1865 #[serde(default = "default_half_life_hours")]
1866 pub half_life_hours: f64,
1867 #[serde(default = "default_min_importance")]
1868 pub min_importance: f32,
1869}
1870
1871fn default_half_life_hours() -> f64 {
1872 168.0 }
1874
1875fn default_min_importance() -> f32 {
1876 0.01
1877}
1878
1879impl Default for DecayConfig {
1880 fn default() -> Self {
1881 Self {
1882 strategy: DecayStrategy::default(),
1883 half_life_hours: default_half_life_hours(),
1884 min_importance: default_min_importance(),
1885 }
1886 }
1887}
1888
1889#[derive(Debug, Deserialize)]
1895pub struct StoreMemoryRequest {
1896 pub content: String,
1897 pub agent_id: String,
1898 #[serde(default)]
1899 pub memory_type: MemoryType,
1900 #[serde(skip_serializing_if = "Option::is_none")]
1901 pub session_id: Option<String>,
1902 #[serde(default = "default_importance")]
1903 pub importance: f32,
1904 #[serde(default)]
1905 pub tags: Vec<String>,
1906 #[serde(skip_serializing_if = "Option::is_none")]
1907 pub metadata: Option<serde_json::Value>,
1908 #[serde(skip_serializing_if = "Option::is_none")]
1909 pub ttl_seconds: Option<u64>,
1910 #[serde(skip_serializing_if = "Option::is_none")]
1915 pub expires_at: Option<u64>,
1916 #[serde(skip_serializing_if = "Option::is_none")]
1918 pub id: Option<String>,
1919}
1920
1921#[derive(Debug, Serialize)]
1923pub struct StoreMemoryResponse {
1924 pub memory: Memory,
1925 pub embedding_time_ms: u64,
1926}
1927
1928#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
1932#[serde(rename_all = "lowercase")]
1933pub enum RoutingMode {
1934 #[default]
1936 Auto,
1937 Vector,
1939 Bm25,
1941 Hybrid,
1943}
1944
1945#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
1950#[serde(rename_all = "lowercase")]
1951pub enum FusionStrategy {
1952 Rrf,
1957 #[default]
1960 MinMax,
1961}
1962
1963#[derive(Debug, Deserialize)]
1965pub struct RecallRequest {
1966 pub query: String,
1967 pub agent_id: String,
1968 #[serde(default = "default_top_k")]
1969 pub top_k: usize,
1970 #[serde(default)]
1971 pub memory_type: Option<MemoryType>,
1972 #[serde(default)]
1973 pub session_id: Option<String>,
1974 #[serde(default)]
1975 pub tags: Option<Vec<String>>,
1976 #[serde(default)]
1977 pub min_importance: Option<f32>,
1978 #[serde(default = "default_true")]
1980 pub importance_weighted: bool,
1981 #[serde(default)]
1983 pub include_associated: bool,
1984 #[serde(default)]
1986 pub associated_memories_cap: Option<usize>,
1987 #[serde(default)]
1989 pub since: Option<String>,
1990 #[serde(default)]
1992 pub until: Option<String>,
1993 #[serde(default)]
1996 pub associated_memories_depth: Option<u8>,
1997 #[serde(default)]
2000 pub associated_memories_min_weight: Option<f32>,
2001 #[serde(default)]
2005 pub routing: RoutingMode,
2006 #[serde(default = "default_true")]
2010 pub rerank: bool,
2011 #[serde(default)]
2014 pub fusion: FusionStrategy,
2015 #[serde(default)]
2019 pub vector_weight: Option<f32>,
2020 #[serde(default)]
2027 pub iterations: Option<u8>,
2028 #[serde(default = "default_true")]
2032 pub neighborhood: bool,
2033}
2034
2035#[derive(Debug, Serialize, Deserialize)]
2037pub struct RecallResult {
2038 pub memory: Memory,
2039 pub score: f32,
2040 #[serde(skip_serializing_if = "Option::is_none")]
2042 pub weighted_score: Option<f32>,
2043 #[serde(skip_serializing_if = "Option::is_none")]
2045 pub smart_score: Option<f32>,
2046 #[serde(skip_serializing_if = "Option::is_none")]
2049 pub depth: Option<u8>,
2050}
2051
2052#[derive(Debug, Serialize)]
2054pub struct RecallResponse {
2055 pub memories: Vec<RecallResult>,
2056 pub query_embedding_time_ms: u64,
2057 pub search_time_ms: u64,
2058 #[serde(skip_serializing_if = "Option::is_none")]
2061 pub associated_memories: Option<Vec<RecallResult>>,
2062}
2063
2064#[derive(Debug, Deserialize)]
2066pub struct ForgetRequest {
2067 pub agent_id: String,
2068 #[serde(default)]
2069 pub memory_ids: Option<Vec<String>>,
2070 #[serde(default)]
2071 pub memory_type: Option<MemoryType>,
2072 #[serde(default)]
2073 pub session_id: Option<String>,
2074 #[serde(default)]
2075 pub tags: Option<Vec<String>>,
2076 #[serde(default)]
2078 pub below_importance: Option<f32>,
2079}
2080
2081#[derive(Debug, Serialize)]
2083pub struct ForgetResponse {
2084 pub deleted_count: usize,
2085}
2086
2087#[derive(Debug, Deserialize)]
2089pub struct UpdateMemoryRequest {
2090 #[serde(default)]
2091 pub content: Option<String>,
2092 #[serde(default)]
2093 pub importance: Option<f32>,
2094 #[serde(default)]
2095 pub tags: Option<Vec<String>>,
2096 #[serde(default)]
2097 pub metadata: Option<serde_json::Value>,
2098 #[serde(default)]
2099 pub memory_type: Option<MemoryType>,
2100}
2101
2102#[derive(Debug, Deserialize)]
2104pub struct UpdateImportanceRequest {
2105 pub memory_id: String,
2106 pub importance: f32,
2107 pub agent_id: String,
2108}
2109
2110#[derive(Debug, Deserialize)]
2112pub struct ConsolidateRequest {
2113 pub agent_id: String,
2114 #[serde(default)]
2116 pub memory_ids: Option<Vec<String>>,
2117 #[serde(default = "default_consolidation_threshold")]
2119 pub threshold: f32,
2120 #[serde(default)]
2122 pub target_type: Option<MemoryType>,
2123}
2124
2125fn default_consolidation_threshold() -> f32 {
2126 0.85
2127}
2128
2129#[derive(Debug, Serialize)]
2131pub struct ConsolidateResponse {
2132 pub consolidated_memory: Memory,
2133 pub source_memory_ids: Vec<String>,
2134 pub memories_removed: usize,
2135}
2136
2137#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2139#[serde(rename_all = "lowercase")]
2140pub enum FeedbackSignal {
2141 Upvote,
2143 Downvote,
2145 Flag,
2147 Positive,
2149 Negative,
2151}
2152
2153#[derive(Debug, Clone, Serialize, Deserialize)]
2155pub struct FeedbackHistoryEntry {
2156 pub signal: FeedbackSignal,
2157 pub timestamp: u64,
2158 pub old_importance: f32,
2159 pub new_importance: f32,
2160}
2161
2162#[derive(Debug, Deserialize)]
2164pub struct FeedbackRequest {
2165 pub agent_id: String,
2166 pub memory_id: String,
2167 pub signal: FeedbackSignal,
2168}
2169
2170#[derive(Debug, Deserialize)]
2172pub struct MemoryFeedbackRequest {
2173 pub agent_id: String,
2174 pub signal: FeedbackSignal,
2175}
2176
2177#[derive(Debug, Serialize)]
2179pub struct FeedbackResponse {
2180 pub memory_id: String,
2181 pub new_importance: f32,
2182 pub signal: FeedbackSignal,
2183}
2184
2185#[derive(Debug, Serialize)]
2187pub struct FeedbackHistoryResponse {
2188 pub memory_id: String,
2189 pub entries: Vec<FeedbackHistoryEntry>,
2190}
2191
2192#[derive(Debug, Serialize)]
2194pub struct AgentFeedbackSummary {
2195 pub agent_id: String,
2196 pub upvotes: u64,
2197 pub downvotes: u64,
2198 pub flags: u64,
2199 pub total_feedback: u64,
2200 pub health_score: f32,
2202}
2203
2204#[derive(Debug, Deserialize)]
2206pub struct MemoryImportancePatchRequest {
2207 pub agent_id: String,
2208 pub importance: f32,
2209}
2210
2211#[derive(Debug, Deserialize)]
2213pub struct FeedbackHealthQuery {
2214 pub agent_id: String,
2215}
2216
2217#[derive(Debug, Serialize)]
2219pub struct FeedbackHealthResponse {
2220 pub agent_id: String,
2221 pub health_score: f32,
2223 pub memory_count: usize,
2224 pub avg_importance: f32,
2225}
2226
2227#[derive(Debug, Deserialize)]
2229pub struct SearchMemoriesRequest {
2230 pub agent_id: String,
2231 #[serde(default)]
2232 pub query: Option<String>,
2233 #[serde(default)]
2234 pub memory_type: Option<MemoryType>,
2235 #[serde(default)]
2236 pub session_id: Option<String>,
2237 #[serde(default)]
2238 pub tags: Option<Vec<String>>,
2239 #[serde(default)]
2240 pub min_importance: Option<f32>,
2241 #[serde(default)]
2242 pub max_importance: Option<f32>,
2243 #[serde(default)]
2244 pub created_after: Option<u64>,
2245 #[serde(default)]
2246 pub created_before: Option<u64>,
2247 #[serde(default = "default_top_k")]
2248 pub top_k: usize,
2249 #[serde(default)]
2250 pub sort_by: Option<MemorySortField>,
2251 #[serde(default)]
2253 pub routing: RoutingMode,
2254 #[serde(default)]
2257 pub rerank: bool,
2258}
2259
2260#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
2262#[serde(rename_all = "snake_case")]
2263pub enum MemorySortField {
2264 CreatedAt,
2265 LastAccessedAt,
2266 Importance,
2267 AccessCount,
2268}
2269
2270#[derive(Debug, Serialize)]
2272pub struct SearchMemoriesResponse {
2273 pub memories: Vec<RecallResult>,
2274 pub total_count: usize,
2275}
2276
2277#[derive(Debug, Deserialize)]
2283pub struct SessionStartRequest {
2284 pub agent_id: String,
2285 #[serde(skip_serializing_if = "Option::is_none")]
2286 pub metadata: Option<serde_json::Value>,
2287 #[serde(skip_serializing_if = "Option::is_none")]
2289 pub id: Option<String>,
2290}
2291
2292#[derive(Debug, Serialize)]
2294pub struct SessionStartResponse {
2295 pub session: Session,
2296}
2297
2298#[derive(Debug, Deserialize)]
2300pub struct SessionEndRequest {
2301 #[serde(default)]
2302 pub summary: Option<String>,
2303 #[serde(default)]
2305 pub auto_summarize: bool,
2306}
2307
2308#[derive(Debug, Serialize)]
2310pub struct SessionEndResponse {
2311 pub session: Session,
2312 pub memory_count: usize,
2313}
2314
2315#[derive(Debug, Serialize)]
2317pub struct ListSessionsResponse {
2318 pub sessions: Vec<Session>,
2319 pub total: usize,
2320}
2321
2322#[derive(Debug, Serialize)]
2324pub struct SessionMemoriesResponse {
2325 pub session: Session,
2326 pub memories: Vec<Memory>,
2327 #[serde(skip_serializing_if = "Option::is_none")]
2329 pub total: Option<usize>,
2330}
2331
2332#[derive(Debug, Serialize, Deserialize, Clone)]
2338pub struct AgentSummary {
2339 pub agent_id: String,
2340 pub memory_count: usize,
2341 pub session_count: usize,
2342 pub active_sessions: usize,
2343}
2344
2345#[derive(Debug, Serialize)]
2347pub struct AgentStats {
2348 pub agent_id: String,
2349 pub total_memories: usize,
2350 pub memories_by_type: std::collections::HashMap<String, usize>,
2351 pub total_sessions: usize,
2352 pub active_sessions: usize,
2353 pub avg_importance: f32,
2354 pub oldest_memory_at: Option<u64>,
2355 pub newest_memory_at: Option<u64>,
2356}
2357
2358#[derive(Debug, Serialize)]
2364pub struct WakeUpResponse {
2365 pub agent_id: String,
2366 pub memories: Vec<Memory>,
2368 pub total_available: usize,
2370}
2371
2372#[derive(Debug, Deserialize)]
2374pub struct KnowledgeGraphRequest {
2375 pub agent_id: String,
2376 pub memory_id: String,
2377 #[serde(default = "default_graph_depth")]
2378 pub depth: usize,
2379 #[serde(default = "default_graph_min_similarity")]
2380 pub min_similarity: f32,
2381}
2382
2383fn default_graph_depth() -> usize {
2384 2
2385}
2386
2387fn default_graph_min_similarity() -> f32 {
2388 0.7
2389}
2390
2391#[derive(Debug, Serialize)]
2393pub struct KnowledgeGraphNode {
2394 pub memory: Memory,
2395 pub similarity: f32,
2396 pub related: Vec<KnowledgeGraphEdge>,
2397}
2398
2399#[derive(Debug, Serialize)]
2401pub struct KnowledgeGraphEdge {
2402 pub memory_id: String,
2403 pub similarity: f32,
2404 pub shared_tags: Vec<String>,
2405}
2406
2407#[derive(Debug, Serialize)]
2409pub struct KnowledgeGraphResponse {
2410 pub root: KnowledgeGraphNode,
2411 pub total_nodes: usize,
2412}
2413
2414fn default_full_graph_max_nodes() -> usize {
2419 200
2420}
2421
2422fn default_full_graph_min_similarity() -> f32 {
2423 0.50
2424}
2425
2426fn default_full_graph_cluster_threshold() -> f32 {
2427 0.60
2428}
2429
2430fn default_full_graph_max_edges_per_node() -> usize {
2431 8
2432}
2433
2434#[derive(Debug, Deserialize)]
2436pub struct FullKnowledgeGraphRequest {
2437 pub agent_id: String,
2438 #[serde(default = "default_full_graph_max_nodes")]
2439 pub max_nodes: usize,
2440 #[serde(default = "default_full_graph_min_similarity")]
2441 pub min_similarity: f32,
2442 #[serde(default = "default_full_graph_cluster_threshold")]
2443 pub cluster_threshold: f32,
2444 #[serde(default = "default_full_graph_max_edges_per_node")]
2445 pub max_edges_per_node: usize,
2446}
2447
2448#[derive(Debug, Serialize)]
2450pub struct FullGraphNode {
2451 pub id: String,
2452 pub content: String,
2453 pub memory_type: String,
2454 pub importance: f32,
2455 pub tags: Vec<String>,
2456 pub created_at: Option<String>,
2457 pub cluster_id: usize,
2458 pub centrality: f32,
2459}
2460
2461#[derive(Debug, Serialize)]
2463pub struct FullGraphEdge {
2464 pub source: String,
2465 pub target: String,
2466 pub similarity: f32,
2467 pub shared_tags: Vec<String>,
2468}
2469
2470#[derive(Debug, Serialize)]
2472pub struct GraphCluster {
2473 pub id: usize,
2474 pub node_count: usize,
2475 pub top_tags: Vec<String>,
2476 pub avg_importance: f32,
2477}
2478
2479#[derive(Debug, Serialize)]
2481pub struct GraphStats {
2482 pub total_memories: usize,
2483 pub included_memories: usize,
2484 pub total_edges: usize,
2485 pub cluster_count: usize,
2486 pub density: f32,
2487 pub hub_memory_id: Option<String>,
2488}
2489
2490#[derive(Debug, Serialize)]
2492pub struct FullKnowledgeGraphResponse {
2493 pub nodes: Vec<FullGraphNode>,
2494 pub edges: Vec<FullGraphEdge>,
2495 pub clusters: Vec<GraphCluster>,
2496 pub stats: GraphStats,
2497}
2498
2499#[derive(Debug, Deserialize)]
2501pub struct SummarizeRequest {
2502 pub agent_id: String,
2503 pub memory_ids: Vec<String>,
2504 #[serde(default)]
2505 pub target_type: Option<MemoryType>,
2506}
2507
2508#[derive(Debug, Serialize)]
2510pub struct SummarizeResponse {
2511 pub summary_memory: Memory,
2512 pub source_count: usize,
2513}
2514
2515#[derive(Debug, Deserialize)]
2517pub struct DeduplicateRequest {
2518 pub agent_id: String,
2519 #[serde(default = "default_dedup_threshold")]
2520 pub threshold: f32,
2521 #[serde(default)]
2522 pub memory_type: Option<MemoryType>,
2523 #[serde(default)]
2525 pub dry_run: bool,
2526}
2527
2528fn default_dedup_threshold() -> f32 {
2529 0.92
2530}
2531
2532#[derive(Debug, Serialize)]
2534pub struct DuplicateGroup {
2535 pub canonical_id: String,
2536 pub duplicate_ids: Vec<String>,
2537 pub avg_similarity: f32,
2538}
2539
2540#[derive(Debug, Serialize)]
2542pub struct DeduplicateResponse {
2543 pub groups: Vec<DuplicateGroup>,
2544 pub duplicates_found: usize,
2545 pub duplicates_merged: usize,
2546}
2547
2548fn default_cross_agent_min_similarity() -> f32 {
2553 0.3
2554}
2555
2556fn default_cross_agent_max_nodes_per_agent() -> usize {
2557 50
2558}
2559
2560fn default_cross_agent_max_cross_edges() -> usize {
2561 200
2562}
2563
2564#[derive(Debug, Deserialize)]
2566pub struct CrossAgentNetworkRequest {
2567 #[serde(default)]
2569 pub agent_ids: Option<Vec<String>>,
2570 #[serde(default = "default_cross_agent_min_similarity")]
2572 pub min_similarity: f32,
2573 #[serde(default = "default_cross_agent_max_nodes_per_agent")]
2575 pub max_nodes_per_agent: usize,
2576 #[serde(default)]
2578 pub min_importance: f32,
2579 #[serde(default = "default_cross_agent_max_cross_edges")]
2581 pub max_cross_edges: usize,
2582}
2583
2584#[derive(Debug, Serialize)]
2586pub struct AgentNetworkInfo {
2587 pub agent_id: String,
2588 pub memory_count: usize,
2589 pub avg_importance: f32,
2590}
2591
2592#[derive(Debug, Serialize)]
2594pub struct AgentNetworkNode {
2595 pub id: String,
2596 pub agent_id: String,
2597 pub content: String,
2598 pub importance: f32,
2599 pub tags: Vec<String>,
2600 pub memory_type: String,
2601 pub created_at: u64,
2602}
2603
2604#[derive(Debug, Serialize)]
2606pub struct AgentNetworkEdge {
2607 pub source: String,
2608 pub target: String,
2609 pub source_agent: String,
2610 pub target_agent: String,
2611 pub similarity: f32,
2612}
2613
2614#[derive(Debug, Serialize)]
2616pub struct AgentNetworkStats {
2617 pub total_agents: usize,
2618 pub total_nodes: usize,
2619 pub total_cross_edges: usize,
2620 pub density: f32,
2621}
2622
2623#[derive(Debug, Serialize)]
2625pub struct CrossAgentNetworkResponse {
2626 pub node_count: usize,
2627 pub agents: Vec<AgentNetworkInfo>,
2628 pub nodes: Vec<AgentNetworkNode>,
2629 pub edges: Vec<AgentNetworkEdge>,
2630 pub stats: AgentNetworkStats,
2631}
2632
2633#[derive(Debug, Deserialize, Default)]
2641pub struct BatchMemoryFilter {
2642 #[serde(default)]
2644 pub tags: Option<Vec<String>>,
2645 #[serde(default)]
2647 pub min_importance: Option<f32>,
2648 #[serde(default)]
2650 pub max_importance: Option<f32>,
2651 #[serde(default)]
2653 pub created_after: Option<u64>,
2654 #[serde(default)]
2656 pub created_before: Option<u64>,
2657 #[serde(default)]
2659 pub memory_type: Option<MemoryType>,
2660 #[serde(default)]
2662 pub session_id: Option<String>,
2663}
2664
2665impl BatchMemoryFilter {
2666 pub fn has_any(&self) -> bool {
2668 self.tags.is_some()
2669 || self.min_importance.is_some()
2670 || self.max_importance.is_some()
2671 || self.created_after.is_some()
2672 || self.created_before.is_some()
2673 || self.memory_type.is_some()
2674 || self.session_id.is_some()
2675 }
2676
2677 pub fn matches(&self, memory: &Memory) -> bool {
2679 if let Some(ref tags) = self.tags {
2680 if !tags.is_empty() && !tags.iter().all(|t| memory.tags.contains(t)) {
2681 return false;
2682 }
2683 }
2684 if let Some(min) = self.min_importance {
2685 if memory.importance < min {
2686 return false;
2687 }
2688 }
2689 if let Some(max) = self.max_importance {
2690 if memory.importance > max {
2691 return false;
2692 }
2693 }
2694 if let Some(after) = self.created_after {
2695 if memory.created_at < after {
2696 return false;
2697 }
2698 }
2699 if let Some(before) = self.created_before {
2700 if memory.created_at > before {
2701 return false;
2702 }
2703 }
2704 if let Some(ref mt) = self.memory_type {
2705 if memory.memory_type != *mt {
2706 return false;
2707 }
2708 }
2709 if let Some(ref sid) = self.session_id {
2710 if memory.session_id.as_ref() != Some(sid) {
2711 return false;
2712 }
2713 }
2714 true
2715 }
2716}
2717
2718#[derive(Debug, Deserialize)]
2720pub struct BatchRecallRequest {
2721 pub agent_id: String,
2723 #[serde(default)]
2725 pub filter: BatchMemoryFilter,
2726 #[serde(default = "default_batch_limit")]
2728 pub limit: usize,
2729}
2730
2731fn default_batch_limit() -> usize {
2732 100
2733}
2734
2735#[derive(Debug, Serialize)]
2737pub struct BatchRecallResponse {
2738 pub memories: Vec<Memory>,
2739 pub total: usize,
2740 pub filtered: usize,
2741}
2742
2743#[derive(Debug, Deserialize)]
2745pub struct BatchForgetRequest {
2746 pub agent_id: String,
2748 pub filter: BatchMemoryFilter,
2750}
2751
2752#[derive(Debug, Serialize)]
2754pub struct BatchForgetResponse {
2755 pub deleted_count: usize,
2756}
2757
2758#[derive(Debug, Deserialize)]
2765pub struct NamespaceEntityConfigRequest {
2766 pub extract_entities: bool,
2768 #[serde(default)]
2771 pub entity_types: Vec<String>,
2772}
2773
2774#[derive(Debug, Serialize, Deserialize)]
2776pub struct NamespaceEntityConfigResponse {
2777 pub namespace: String,
2778 pub extract_entities: bool,
2779 pub entity_types: Vec<String>,
2780}
2781
2782#[derive(Debug, Deserialize)]
2785pub struct ExtractEntitiesRequest {
2786 pub content: String,
2788 #[serde(default)]
2791 pub entity_types: Vec<String>,
2792}
2793
2794#[derive(Debug, Clone, Serialize, Deserialize)]
2796pub struct EntityResult {
2797 pub entity_type: String,
2798 pub value: String,
2799 pub score: f32,
2800 pub start: usize,
2801 pub end: usize,
2802 pub tag: String,
2804}
2805
2806#[derive(Debug, Serialize)]
2808pub struct ExtractEntitiesResponse {
2809 pub entities: Vec<EntityResult>,
2810 pub count: usize,
2811}
2812
2813#[derive(Debug, Deserialize)]
2819pub struct GraphTraverseQuery {
2820 #[serde(default = "default_ce5_graph_depth")]
2822 pub depth: u32,
2823}
2824
2825fn default_ce5_graph_depth() -> u32 {
2826 3
2827}
2828
2829#[derive(Debug, Deserialize)]
2831pub struct GraphPathQuery {
2832 pub to: String,
2834}
2835
2836#[derive(Debug, Deserialize)]
2838pub struct MemoryLinkRequest {
2839 pub target_id: String,
2841 #[serde(skip_serializing_if = "Option::is_none")]
2843 pub label: Option<String>,
2844 pub agent_id: String,
2846}
2847
2848#[derive(Debug, Serialize)]
2850pub struct GraphTraverseResponse {
2851 pub root_id: String,
2852 pub depth: u32,
2853 pub node_count: usize,
2854 pub nodes: Vec<GraphNodeResponse>,
2855}
2856
2857#[derive(Debug, Serialize)]
2859pub struct GraphNodeResponse {
2860 pub memory_id: String,
2861 pub depth: u32,
2862 pub edges: Vec<GraphEdgeResponse>,
2863}
2864
2865#[derive(Debug, Serialize)]
2867pub struct GraphEdgeResponse {
2868 pub from_id: String,
2869 pub to_id: String,
2870 pub edge_type: String,
2871 pub weight: f32,
2872 pub created_at: u64,
2873}
2874
2875#[derive(Debug, Serialize)]
2877pub struct GraphPathResponse {
2878 pub from_id: String,
2879 pub to_id: String,
2880 pub path: Vec<String>,
2882 pub hop_count: usize,
2883}
2884
2885#[derive(Debug, Serialize)]
2887pub struct MemoryLinkResponse {
2888 pub from_id: String,
2889 pub to_id: String,
2890 pub edge_type: String,
2891}
2892
2893#[derive(Debug, Serialize)]
2895pub struct GraphExportResponse {
2896 pub agent_id: String,
2897 pub namespace: String,
2898 pub node_count: usize,
2899 pub edge_count: usize,
2900 pub edges: Vec<GraphEdgeResponse>,
2901}
2902
2903#[derive(Debug, Deserialize)]
2909pub struct KgQueryParams {
2910 pub agent_id: String,
2912 #[serde(default)]
2914 pub root_id: Option<String>,
2915 #[serde(default)]
2917 pub edge_type: Option<String>,
2918 #[serde(default)]
2920 pub min_weight: Option<f32>,
2921 #[serde(default = "default_kg_depth")]
2923 pub max_depth: u32,
2924 #[serde(default = "default_kg_limit")]
2926 pub limit: usize,
2927}
2928
2929fn default_kg_depth() -> u32 {
2930 3
2931}
2932
2933fn default_kg_limit() -> usize {
2934 100
2935}
2936
2937#[derive(Debug, Serialize)]
2939pub struct KgQueryResponse {
2940 pub agent_id: String,
2941 pub node_count: usize,
2942 pub edge_count: usize,
2943 pub edges: Vec<GraphEdgeResponse>,
2944}
2945
2946#[derive(Debug, Deserialize)]
2948pub struct KgPathParams {
2949 pub agent_id: String,
2951 pub from: String,
2953 pub to: String,
2955}
2956
2957#[derive(Debug, Serialize)]
2959pub struct KgPathResponse {
2960 pub agent_id: String,
2961 pub from_id: String,
2962 pub to_id: String,
2963 pub hop_count: usize,
2964 pub path: Vec<String>,
2965}
2966
2967#[derive(Debug, Deserialize)]
2969pub struct KgExportParams {
2970 pub agent_id: String,
2972 #[serde(default = "default_kg_format")]
2974 pub format: String,
2975}
2976
2977fn default_kg_format() -> String {
2978 "json".to_string()
2979}
2980
2981#[derive(Debug, Serialize)]
2983pub struct KgExportJsonResponse {
2984 pub agent_id: String,
2985 pub format: String,
2986 pub node_count: usize,
2987 pub edge_count: usize,
2988 pub edges: Vec<GraphEdgeResponse>,
2989}
2990
2991fn default_working_ttl() -> Option<u64> {
2996 Some(14_400) }
2998fn default_episodic_ttl() -> Option<u64> {
2999 Some(2_592_000) }
3001fn default_semantic_ttl() -> Option<u64> {
3002 Some(31_536_000) }
3004fn default_procedural_ttl() -> Option<u64> {
3005 Some(63_072_000) }
3007fn default_working_decay() -> DecayStrategy {
3008 DecayStrategy::Exponential
3009}
3010fn default_episodic_decay() -> DecayStrategy {
3011 DecayStrategy::PowerLaw
3012}
3013fn default_semantic_decay() -> DecayStrategy {
3014 DecayStrategy::Logarithmic
3015}
3016fn default_procedural_decay() -> DecayStrategy {
3017 DecayStrategy::Flat
3018}
3019fn default_sr_factor() -> f64 {
3020 1.0
3021}
3022fn default_sr_base_interval() -> u64 {
3023 86_400 }
3025fn default_consolidation_enabled() -> bool {
3026 false
3027}
3028fn default_policy_consolidation_threshold() -> f32 {
3029 0.92
3030}
3031fn default_consolidation_interval_hours() -> u32 {
3032 24
3033}
3034fn default_store_dedup_threshold() -> f32 {
3035 0.95
3036}
3037
3038#[derive(Debug, Clone, Serialize, Deserialize)]
3043pub struct MemoryPolicy {
3044 #[serde(
3047 default = "default_working_ttl",
3048 skip_serializing_if = "Option::is_none"
3049 )]
3050 pub working_ttl_seconds: Option<u64>,
3051 #[serde(
3053 default = "default_episodic_ttl",
3054 skip_serializing_if = "Option::is_none"
3055 )]
3056 pub episodic_ttl_seconds: Option<u64>,
3057 #[serde(
3059 default = "default_semantic_ttl",
3060 skip_serializing_if = "Option::is_none"
3061 )]
3062 pub semantic_ttl_seconds: Option<u64>,
3063 #[serde(
3065 default = "default_procedural_ttl",
3066 skip_serializing_if = "Option::is_none"
3067 )]
3068 pub procedural_ttl_seconds: Option<u64>,
3069
3070 #[serde(default = "default_working_decay")]
3073 pub working_decay: DecayStrategy,
3074 #[serde(default = "default_episodic_decay")]
3076 pub episodic_decay: DecayStrategy,
3077 #[serde(default = "default_semantic_decay")]
3079 pub semantic_decay: DecayStrategy,
3080 #[serde(default = "default_procedural_decay")]
3082 pub procedural_decay: DecayStrategy,
3083
3084 #[serde(default = "default_sr_factor")]
3089 pub spaced_repetition_factor: f64,
3090 #[serde(default = "default_sr_base_interval")]
3092 pub spaced_repetition_base_interval_seconds: u64,
3093
3094 #[serde(default = "default_consolidation_enabled")]
3097 pub consolidation_enabled: bool,
3098 #[serde(default = "default_policy_consolidation_threshold")]
3100 pub consolidation_threshold: f32,
3101 #[serde(default = "default_consolidation_interval_hours")]
3103 pub consolidation_interval_hours: u32,
3104 #[serde(default)]
3106 pub consolidated_count: u64,
3107
3108 #[serde(default)]
3112 pub rate_limit_enabled: bool,
3113 #[serde(default, skip_serializing_if = "Option::is_none")]
3116 pub rate_limit_stores_per_minute: Option<u32>,
3117 #[serde(default, skip_serializing_if = "Option::is_none")]
3120 pub rate_limit_recalls_per_minute: Option<u32>,
3121
3122 #[serde(default)]
3130 pub dedup_on_store: bool,
3131 #[serde(default = "default_store_dedup_threshold")]
3133 pub dedup_threshold: f32,
3134}
3135
3136impl Default for MemoryPolicy {
3137 fn default() -> Self {
3138 Self {
3139 working_ttl_seconds: default_working_ttl(),
3140 episodic_ttl_seconds: default_episodic_ttl(),
3141 semantic_ttl_seconds: default_semantic_ttl(),
3142 procedural_ttl_seconds: default_procedural_ttl(),
3143 working_decay: default_working_decay(),
3144 episodic_decay: default_episodic_decay(),
3145 semantic_decay: default_semantic_decay(),
3146 procedural_decay: default_procedural_decay(),
3147 spaced_repetition_factor: default_sr_factor(),
3148 spaced_repetition_base_interval_seconds: default_sr_base_interval(),
3149 consolidation_enabled: default_consolidation_enabled(),
3150 consolidation_threshold: default_policy_consolidation_threshold(),
3151 consolidation_interval_hours: default_consolidation_interval_hours(),
3152 consolidated_count: 0,
3153 rate_limit_enabled: false,
3154 rate_limit_stores_per_minute: None,
3155 rate_limit_recalls_per_minute: None,
3156 dedup_on_store: false,
3157 dedup_threshold: default_store_dedup_threshold(),
3158 }
3159 }
3160}
3161
3162impl MemoryPolicy {
3163 pub fn ttl_for_type(&self, memory_type: &MemoryType) -> Option<u64> {
3165 match memory_type {
3166 MemoryType::Working => self.working_ttl_seconds,
3167 MemoryType::Episodic => self.episodic_ttl_seconds,
3168 MemoryType::Semantic => self.semantic_ttl_seconds,
3169 MemoryType::Procedural => self.procedural_ttl_seconds,
3170 }
3171 }
3172
3173 pub fn decay_for_type(&self, memory_type: &MemoryType) -> DecayStrategy {
3175 match memory_type {
3176 MemoryType::Working => self.working_decay,
3177 MemoryType::Episodic => self.episodic_decay,
3178 MemoryType::Semantic => self.semantic_decay,
3179 MemoryType::Procedural => self.procedural_decay,
3180 }
3181 }
3182
3183 pub fn spaced_repetition_extension(&self, access_count: u32) -> u64 {
3187 if self.spaced_repetition_factor <= 0.0 {
3188 return 0;
3189 }
3190 let ext = access_count as f64
3191 * self.spaced_repetition_factor
3192 * self.spaced_repetition_base_interval_seconds as f64;
3193 ext.round() as u64
3194 }
3195}