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}
718
719#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
721#[serde(untagged)]
722pub enum FilterExpression {
723 And {
725 #[serde(rename = "$and")]
726 conditions: Vec<FilterExpression>,
727 },
728 Or {
730 #[serde(rename = "$or")]
731 conditions: Vec<FilterExpression>,
732 },
733 Field {
735 #[serde(flatten)]
736 field: std::collections::HashMap<String, FilterCondition>,
737 },
738}
739
740#[derive(Debug, Clone, Serialize, Deserialize, Default)]
746pub struct QuotaConfig {
747 #[serde(skip_serializing_if = "Option::is_none")]
749 pub max_vectors: Option<u64>,
750 #[serde(skip_serializing_if = "Option::is_none")]
752 pub max_storage_bytes: Option<u64>,
753 #[serde(skip_serializing_if = "Option::is_none")]
755 pub max_dimensions: Option<usize>,
756 #[serde(skip_serializing_if = "Option::is_none")]
758 pub max_metadata_bytes: Option<usize>,
759 #[serde(default)]
761 pub enforcement: QuotaEnforcement,
762}
763
764#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
766#[serde(rename_all = "snake_case")]
767pub enum QuotaEnforcement {
768 None,
770 Soft,
772 #[default]
774 Hard,
775}
776
777#[derive(Debug, Clone, Serialize, Deserialize, Default)]
779pub struct QuotaUsage {
780 pub vector_count: u64,
782 pub storage_bytes: u64,
784 pub avg_dimensions: Option<usize>,
786 pub avg_metadata_bytes: Option<usize>,
788 pub last_updated: u64,
790}
791
792impl QuotaUsage {
793 pub fn new(vector_count: u64, storage_bytes: u64) -> Self {
795 let now = std::time::SystemTime::now()
796 .duration_since(std::time::UNIX_EPOCH)
797 .unwrap_or_default()
798 .as_secs();
799 Self {
800 vector_count,
801 storage_bytes,
802 avg_dimensions: None,
803 avg_metadata_bytes: None,
804 last_updated: now,
805 }
806 }
807
808 pub fn touch(&mut self) {
810 self.last_updated = std::time::SystemTime::now()
811 .duration_since(std::time::UNIX_EPOCH)
812 .unwrap_or_default()
813 .as_secs();
814 }
815}
816
817#[derive(Debug, Clone, Serialize, Deserialize)]
819pub struct QuotaStatus {
820 pub namespace: String,
822 pub config: QuotaConfig,
824 pub usage: QuotaUsage,
826 #[serde(skip_serializing_if = "Option::is_none")]
828 pub vector_usage_percent: Option<f32>,
829 #[serde(skip_serializing_if = "Option::is_none")]
831 pub storage_usage_percent: Option<f32>,
832 pub is_exceeded: bool,
834 #[serde(skip_serializing_if = "Vec::is_empty")]
836 pub exceeded_quotas: Vec<String>,
837}
838
839impl QuotaStatus {
840 pub fn new(namespace: String, config: QuotaConfig, usage: QuotaUsage) -> Self {
842 let vector_usage_percent = config
843 .max_vectors
844 .map(|max| (usage.vector_count as f32 / max as f32) * 100.0);
845
846 let storage_usage_percent = config
847 .max_storage_bytes
848 .map(|max| (usage.storage_bytes as f32 / max as f32) * 100.0);
849
850 let mut exceeded_quotas = Vec::new();
851
852 if let Some(max) = config.max_vectors {
853 if usage.vector_count > max {
854 exceeded_quotas.push("max_vectors".to_string());
855 }
856 }
857
858 if let Some(max) = config.max_storage_bytes {
859 if usage.storage_bytes > max {
860 exceeded_quotas.push("max_storage_bytes".to_string());
861 }
862 }
863
864 let is_exceeded = !exceeded_quotas.is_empty();
865
866 Self {
867 namespace,
868 config,
869 usage,
870 vector_usage_percent,
871 storage_usage_percent,
872 is_exceeded,
873 exceeded_quotas,
874 }
875 }
876}
877
878#[derive(Debug, Deserialize)]
880pub struct SetQuotaRequest {
881 pub config: QuotaConfig,
883}
884
885#[derive(Debug, Serialize)]
887pub struct SetQuotaResponse {
888 pub success: bool,
890 pub namespace: String,
892 pub config: QuotaConfig,
894 pub message: String,
896}
897
898#[derive(Debug, Clone, Serialize)]
900pub struct QuotaCheckResult {
901 pub allowed: bool,
903 #[serde(skip_serializing_if = "Option::is_none")]
905 pub reason: Option<String>,
906 pub usage: QuotaUsage,
908 #[serde(skip_serializing_if = "Option::is_none")]
910 pub exceeded_quota: Option<String>,
911}
912
913#[derive(Debug, Serialize)]
915pub struct QuotaListResponse {
916 pub quotas: Vec<QuotaStatus>,
918 pub total: u64,
920 #[serde(skip_serializing_if = "Option::is_none")]
922 pub default_config: Option<QuotaConfig>,
923}
924
925#[derive(Debug, Serialize)]
927pub struct DefaultQuotaResponse {
928 pub config: Option<QuotaConfig>,
930}
931
932#[derive(Debug, Deserialize)]
934pub struct SetDefaultQuotaRequest {
935 pub config: Option<QuotaConfig>,
937}
938
939#[derive(Debug, Deserialize)]
941pub struct QuotaCheckRequest {
942 pub vector_ids: Vec<String>,
944 #[serde(default)]
946 pub dimensions: Option<usize>,
947 #[serde(default)]
949 pub metadata_bytes: Option<usize>,
950}
951
952#[derive(Debug, Deserialize)]
958pub struct ExportRequest {
959 #[serde(default = "default_export_top_k")]
961 pub top_k: usize,
962 #[serde(skip_serializing_if = "Option::is_none")]
964 pub cursor: Option<String>,
965 #[serde(default = "default_true")]
967 pub include_vectors: bool,
968 #[serde(default = "default_true")]
970 pub include_metadata: bool,
971}
972
973fn default_export_top_k() -> usize {
974 1000
975}
976
977impl Default for ExportRequest {
978 fn default() -> Self {
979 Self {
980 top_k: 1000,
981 cursor: None,
982 include_vectors: true,
983 include_metadata: true,
984 }
985 }
986}
987
988#[derive(Debug, Clone, Serialize, Deserialize)]
990pub struct ExportedVector {
991 pub id: String,
993 #[serde(skip_serializing_if = "Option::is_none")]
995 pub values: Option<Vec<f32>>,
996 #[serde(skip_serializing_if = "Option::is_none")]
998 pub metadata: Option<serde_json::Value>,
999 #[serde(skip_serializing_if = "Option::is_none")]
1001 pub ttl_seconds: Option<u64>,
1002}
1003
1004impl From<&Vector> for ExportedVector {
1005 fn from(v: &Vector) -> Self {
1006 Self {
1007 id: v.id.clone(),
1008 values: Some(v.values.clone()),
1009 metadata: v.metadata.clone(),
1010 ttl_seconds: v.ttl_seconds,
1011 }
1012 }
1013}
1014
1015#[derive(Debug, Serialize)]
1017pub struct ExportResponse {
1018 pub vectors: Vec<ExportedVector>,
1020 #[serde(skip_serializing_if = "Option::is_none")]
1022 pub next_cursor: Option<String>,
1023 pub total_count: usize,
1025 pub returned_count: usize,
1027}
1028
1029#[derive(Debug, Clone, Serialize, Deserialize)]
1036#[serde(untagged)]
1037pub enum RankBy {
1038 VectorSearch {
1041 field: String,
1042 method: VectorSearchMethod,
1043 query_vector: Vec<f32>,
1044 },
1045 FullTextSearch {
1047 field: String,
1048 method: String, query: String,
1050 },
1051 AttributeOrder {
1053 field: String,
1054 direction: SortDirection,
1055 },
1056 Sum(Vec<RankBy>),
1058 Max(Vec<RankBy>),
1060 Product { weight: f32, ranking: Box<RankBy> },
1062}
1063
1064#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
1066pub enum VectorSearchMethod {
1067 #[default]
1069 ANN,
1070 #[serde(rename = "kNN")]
1072 KNN,
1073}
1074
1075#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
1077#[serde(rename_all = "lowercase")]
1078#[derive(Default)]
1079pub enum SortDirection {
1080 Asc,
1081 #[default]
1082 Desc,
1083}
1084
1085#[derive(Debug, Deserialize)]
1087pub struct UnifiedQueryRequest {
1088 pub rank_by: RankByInput,
1090 #[serde(default = "default_top_k")]
1092 pub top_k: usize,
1093 #[serde(default)]
1095 pub filter: Option<FilterExpression>,
1096 #[serde(default = "default_true")]
1098 pub include_metadata: bool,
1099 #[serde(default)]
1101 pub include_vectors: bool,
1102 #[serde(default)]
1104 pub distance_metric: DistanceMetric,
1105}
1106
1107#[derive(Debug, Clone, Serialize, Deserialize)]
1115#[serde(from = "serde_json::Value")]
1116pub struct RankByInput(pub RankBy);
1117
1118impl From<serde_json::Value> for RankByInput {
1119 fn from(value: serde_json::Value) -> Self {
1120 RankByInput(parse_rank_by(&value).unwrap_or_else(|| {
1121 RankBy::AttributeOrder {
1123 field: "id".to_string(),
1124 direction: SortDirection::Asc,
1125 }
1126 }))
1127 }
1128}
1129
1130fn parse_rank_by(value: &serde_json::Value) -> Option<RankBy> {
1132 let arr = value.as_array()?;
1133 if arr.is_empty() {
1134 return None;
1135 }
1136
1137 let first = arr.first()?.as_str()?;
1138
1139 match first {
1140 "Sum" => {
1142 let rankings = arr.get(1)?.as_array()?;
1143 let parsed: Option<Vec<RankBy>> = rankings.iter().map(parse_rank_by).collect();
1144 Some(RankBy::Sum(parsed?))
1145 }
1146 "Max" => {
1147 let rankings = arr.get(1)?.as_array()?;
1148 let parsed: Option<Vec<RankBy>> = rankings.iter().map(parse_rank_by).collect();
1149 Some(RankBy::Max(parsed?))
1150 }
1151 "Product" => {
1152 let weight = arr.get(1)?.as_f64()? as f32;
1153 let ranking = parse_rank_by(arr.get(2)?)?;
1154 Some(RankBy::Product {
1155 weight,
1156 ranking: Box::new(ranking),
1157 })
1158 }
1159 "ANN" => {
1161 let query_vector = parse_vector_array(arr.get(1)?)?;
1162 Some(RankBy::VectorSearch {
1163 field: "vector".to_string(),
1164 method: VectorSearchMethod::ANN,
1165 query_vector,
1166 })
1167 }
1168 "kNN" => {
1169 let query_vector = parse_vector_array(arr.get(1)?)?;
1170 Some(RankBy::VectorSearch {
1171 field: "vector".to_string(),
1172 method: VectorSearchMethod::KNN,
1173 query_vector,
1174 })
1175 }
1176 field => {
1178 let second = arr.get(1)?;
1179
1180 if let Some(method_str) = second.as_str() {
1182 match method_str {
1183 "ANN" => {
1184 let query_vector = parse_vector_array(arr.get(2)?)?;
1185 Some(RankBy::VectorSearch {
1186 field: field.to_string(),
1187 method: VectorSearchMethod::ANN,
1188 query_vector,
1189 })
1190 }
1191 "kNN" => {
1192 let query_vector = parse_vector_array(arr.get(2)?)?;
1193 Some(RankBy::VectorSearch {
1194 field: field.to_string(),
1195 method: VectorSearchMethod::KNN,
1196 query_vector,
1197 })
1198 }
1199 "BM25" => {
1200 let query = arr.get(2)?.as_str()?;
1201 Some(RankBy::FullTextSearch {
1202 field: field.to_string(),
1203 method: "BM25".to_string(),
1204 query: query.to_string(),
1205 })
1206 }
1207 "asc" => Some(RankBy::AttributeOrder {
1208 field: field.to_string(),
1209 direction: SortDirection::Asc,
1210 }),
1211 "desc" => Some(RankBy::AttributeOrder {
1212 field: field.to_string(),
1213 direction: SortDirection::Desc,
1214 }),
1215 _ => None,
1216 }
1217 } else {
1218 None
1219 }
1220 }
1221 }
1222}
1223
1224fn parse_vector_array(value: &serde_json::Value) -> Option<Vec<f32>> {
1226 let arr = value.as_array()?;
1227 arr.iter().map(|v| v.as_f64().map(|n| n as f32)).collect()
1228}
1229
1230#[derive(Debug, Serialize, Deserialize)]
1232pub struct UnifiedQueryResponse {
1233 pub results: Vec<UnifiedSearchResult>,
1235 #[serde(skip_serializing_if = "Option::is_none")]
1237 pub next_cursor: Option<String>,
1238}
1239
1240#[derive(Debug, Serialize, Deserialize)]
1242pub struct UnifiedSearchResult {
1243 pub id: String,
1245 #[serde(rename = "$dist", skip_serializing_if = "Option::is_none")]
1248 pub dist: Option<f32>,
1249 #[serde(skip_serializing_if = "Option::is_none")]
1251 pub metadata: Option<serde_json::Value>,
1252 #[serde(skip_serializing_if = "Option::is_none")]
1254 pub vector: Option<Vec<f32>>,
1255}
1256
1257#[derive(Debug, Clone, Serialize, Deserialize)]
1263pub enum AggregateFunction {
1264 Count,
1266 Sum { field: String },
1268 Avg { field: String },
1270 Min { field: String },
1272 Max { field: String },
1274}
1275
1276#[derive(Debug, Clone, Serialize, Deserialize)]
1278#[serde(from = "serde_json::Value")]
1279pub struct AggregateFunctionInput(pub AggregateFunction);
1280
1281impl From<serde_json::Value> for AggregateFunctionInput {
1282 fn from(value: serde_json::Value) -> Self {
1283 parse_aggregate_function(&value)
1284 .map(AggregateFunctionInput)
1285 .unwrap_or_else(|| {
1286 AggregateFunctionInput(AggregateFunction::Count)
1288 })
1289 }
1290}
1291
1292fn parse_aggregate_function(value: &serde_json::Value) -> Option<AggregateFunction> {
1294 let arr = value.as_array()?;
1295 if arr.is_empty() {
1296 return None;
1297 }
1298
1299 let func_name = arr.first()?.as_str()?;
1300
1301 match func_name {
1302 "Count" => Some(AggregateFunction::Count),
1303 "Sum" => {
1304 let field = arr.get(1)?.as_str()?;
1305 Some(AggregateFunction::Sum {
1306 field: field.to_string(),
1307 })
1308 }
1309 "Avg" => {
1310 let field = arr.get(1)?.as_str()?;
1311 Some(AggregateFunction::Avg {
1312 field: field.to_string(),
1313 })
1314 }
1315 "Min" => {
1316 let field = arr.get(1)?.as_str()?;
1317 Some(AggregateFunction::Min {
1318 field: field.to_string(),
1319 })
1320 }
1321 "Max" => {
1322 let field = arr.get(1)?.as_str()?;
1323 Some(AggregateFunction::Max {
1324 field: field.to_string(),
1325 })
1326 }
1327 _ => None,
1328 }
1329}
1330
1331#[derive(Debug, Deserialize)]
1333pub struct AggregationRequest {
1334 pub aggregate_by: std::collections::HashMap<String, AggregateFunctionInput>,
1337 #[serde(default)]
1340 pub group_by: Vec<String>,
1341 #[serde(default)]
1343 pub filter: Option<FilterExpression>,
1344 #[serde(default = "default_agg_limit")]
1346 pub limit: usize,
1347}
1348
1349fn default_agg_limit() -> usize {
1350 100
1351}
1352
1353#[derive(Debug, Serialize, Deserialize)]
1355pub struct AggregationResponse {
1356 #[serde(skip_serializing_if = "Option::is_none")]
1358 pub aggregations: Option<std::collections::HashMap<String, serde_json::Value>>,
1359 #[serde(skip_serializing_if = "Option::is_none")]
1361 pub aggregation_groups: Option<Vec<AggregationGroup>>,
1362}
1363
1364#[derive(Debug, Serialize, Deserialize)]
1366pub struct AggregationGroup {
1367 #[serde(flatten)]
1369 pub group_key: std::collections::HashMap<String, serde_json::Value>,
1370 #[serde(flatten)]
1372 pub aggregations: std::collections::HashMap<String, serde_json::Value>,
1373}
1374
1375#[derive(Debug, Clone, Serialize, Deserialize)]
1381pub struct TextDocument {
1382 pub id: VectorId,
1384 pub text: String,
1386 #[serde(skip_serializing_if = "Option::is_none")]
1388 pub metadata: Option<serde_json::Value>,
1389 #[serde(skip_serializing_if = "Option::is_none")]
1391 pub ttl_seconds: Option<u64>,
1392}
1393
1394#[derive(Debug, Deserialize)]
1396pub struct TextUpsertRequest {
1397 pub documents: Vec<TextDocument>,
1399 #[serde(default)]
1401 pub model: Option<EmbeddingModelType>,
1402}
1403
1404#[derive(Debug, Serialize, Deserialize)]
1406pub struct TextUpsertResponse {
1407 pub upserted_count: usize,
1409 pub tokens_processed: usize,
1411 pub model: EmbeddingModelType,
1413 pub embedding_time_ms: u64,
1415}
1416
1417#[derive(Debug, Deserialize)]
1419pub struct TextQueryRequest {
1420 pub text: String,
1422 #[serde(default = "default_top_k")]
1424 pub top_k: usize,
1425 #[serde(default)]
1427 pub filter: Option<FilterExpression>,
1428 #[serde(default)]
1430 pub include_vectors: bool,
1431 #[serde(default = "default_true")]
1433 pub include_text: bool,
1434 #[serde(default)]
1436 pub model: Option<EmbeddingModelType>,
1437}
1438
1439#[derive(Debug, Serialize, Deserialize)]
1441pub struct TextQueryResponse {
1442 pub results: Vec<TextSearchResult>,
1444 pub model: EmbeddingModelType,
1446 pub embedding_time_ms: u64,
1448 pub search_time_ms: u64,
1450}
1451
1452#[derive(Debug, Serialize, Deserialize)]
1454pub struct TextSearchResult {
1455 pub id: VectorId,
1457 pub score: f32,
1459 #[serde(skip_serializing_if = "Option::is_none")]
1461 pub text: Option<String>,
1462 #[serde(skip_serializing_if = "Option::is_none")]
1464 pub metadata: Option<serde_json::Value>,
1465 #[serde(skip_serializing_if = "Option::is_none")]
1467 pub vector: Option<Vec<f32>>,
1468}
1469
1470#[derive(Debug, Deserialize)]
1472pub struct BatchTextQueryRequest {
1473 pub queries: Vec<String>,
1475 #[serde(default = "default_top_k")]
1477 pub top_k: usize,
1478 #[serde(default)]
1480 pub filter: Option<FilterExpression>,
1481 #[serde(default)]
1483 pub include_vectors: bool,
1484 #[serde(default)]
1486 pub model: Option<EmbeddingModelType>,
1487}
1488
1489#[derive(Debug, Serialize, Deserialize)]
1491pub struct BatchTextQueryResponse {
1492 pub results: Vec<Vec<TextSearchResult>>,
1494 pub model: EmbeddingModelType,
1496 pub embedding_time_ms: u64,
1498 pub search_time_ms: u64,
1500}
1501
1502#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1510pub enum EmbeddingModelType {
1511 #[default]
1513 #[serde(rename = "bge-large")]
1514 BgeLarge,
1515 #[serde(rename = "minilm")]
1517 MiniLM,
1518 #[serde(rename = "bge-small")]
1520 BgeSmall,
1521 #[serde(rename = "e5-small")]
1523 E5Small,
1524}
1525
1526impl EmbeddingModelType {
1527 pub fn dimension(&self) -> usize {
1529 match self {
1530 EmbeddingModelType::BgeLarge => 1024,
1531 EmbeddingModelType::MiniLM => 384,
1532 EmbeddingModelType::BgeSmall => 384,
1533 EmbeddingModelType::E5Small => 384,
1534 }
1535 }
1536}
1537
1538impl std::fmt::Display for EmbeddingModelType {
1539 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1540 match self {
1541 EmbeddingModelType::BgeLarge => write!(f, "bge-large"),
1542 EmbeddingModelType::MiniLM => write!(f, "minilm"),
1543 EmbeddingModelType::BgeSmall => write!(f, "bge-small"),
1544 EmbeddingModelType::E5Small => write!(f, "e5-small"),
1545 }
1546 }
1547}
1548
1549#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1555#[serde(rename_all = "snake_case")]
1556#[derive(Default)]
1557pub enum MemoryType {
1558 #[default]
1560 Episodic,
1561 Semantic,
1563 Procedural,
1565 Working,
1567}
1568
1569impl std::fmt::Display for MemoryType {
1570 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1571 match self {
1572 MemoryType::Episodic => write!(f, "episodic"),
1573 MemoryType::Semantic => write!(f, "semantic"),
1574 MemoryType::Procedural => write!(f, "procedural"),
1575 MemoryType::Working => write!(f, "working"),
1576 }
1577 }
1578}
1579
1580#[derive(Debug, Clone, Serialize, Deserialize)]
1582pub struct Memory {
1583 pub id: String,
1584 #[serde(default)]
1585 pub memory_type: MemoryType,
1586 pub content: String,
1587 pub agent_id: String,
1588 #[serde(skip_serializing_if = "Option::is_none")]
1589 pub session_id: Option<String>,
1590 #[serde(default = "default_importance")]
1591 pub importance: f32,
1592 #[serde(default)]
1593 pub tags: Vec<String>,
1594 #[serde(skip_serializing_if = "Option::is_none")]
1595 pub metadata: Option<serde_json::Value>,
1596 pub created_at: u64,
1597 pub last_accessed_at: u64,
1598 #[serde(default)]
1599 pub access_count: u32,
1600 #[serde(skip_serializing_if = "Option::is_none")]
1601 pub ttl_seconds: Option<u64>,
1602 #[serde(skip_serializing_if = "Option::is_none")]
1603 pub expires_at: Option<u64>,
1604}
1605
1606fn default_importance() -> f32 {
1607 0.5
1608}
1609
1610impl Memory {
1611 pub fn new(id: String, content: String, agent_id: String, memory_type: MemoryType) -> Self {
1613 let now = std::time::SystemTime::now()
1614 .duration_since(std::time::UNIX_EPOCH)
1615 .unwrap_or_default()
1616 .as_secs();
1617 Self {
1618 id,
1619 memory_type,
1620 content,
1621 agent_id,
1622 session_id: None,
1623 importance: 0.5,
1624 tags: Vec::new(),
1625 metadata: None,
1626 created_at: now,
1627 last_accessed_at: now,
1628 access_count: 0,
1629 ttl_seconds: None,
1630 expires_at: None,
1631 }
1632 }
1633
1634 pub fn is_expired(&self) -> bool {
1636 if let Some(expires_at) = self.expires_at {
1637 let now = std::time::SystemTime::now()
1638 .duration_since(std::time::UNIX_EPOCH)
1639 .unwrap_or_default()
1640 .as_secs();
1641 now >= expires_at
1642 } else {
1643 false
1644 }
1645 }
1646
1647 pub fn to_vector_metadata(&self) -> serde_json::Value {
1649 let mut meta = serde_json::Map::new();
1650 meta.insert("_dakera_type".to_string(), serde_json::json!("memory"));
1651 meta.insert(
1652 "memory_type".to_string(),
1653 serde_json::json!(self.memory_type),
1654 );
1655 meta.insert("content".to_string(), serde_json::json!(self.content));
1656 meta.insert("agent_id".to_string(), serde_json::json!(self.agent_id));
1657 if let Some(ref sid) = self.session_id {
1658 meta.insert("session_id".to_string(), serde_json::json!(sid));
1659 }
1660 meta.insert("importance".to_string(), serde_json::json!(self.importance));
1661 meta.insert("tags".to_string(), serde_json::json!(self.tags));
1662 meta.insert("created_at".to_string(), serde_json::json!(self.created_at));
1663 meta.insert(
1664 "last_accessed_at".to_string(),
1665 serde_json::json!(self.last_accessed_at),
1666 );
1667 meta.insert(
1668 "access_count".to_string(),
1669 serde_json::json!(self.access_count),
1670 );
1671 if let Some(ref ttl) = self.ttl_seconds {
1672 meta.insert("ttl_seconds".to_string(), serde_json::json!(ttl));
1673 }
1674 if let Some(ref expires) = self.expires_at {
1675 meta.insert("expires_at".to_string(), serde_json::json!(expires));
1676 }
1677 if let Some(ref user_meta) = self.metadata {
1678 meta.insert("user_metadata".to_string(), user_meta.clone());
1679 }
1680 serde_json::Value::Object(meta)
1681 }
1682
1683 pub fn to_vector(&self, embedding: Vec<f32>) -> Vector {
1685 let mut v = Vector {
1686 id: self.id.clone(),
1687 values: embedding,
1688 metadata: Some(self.to_vector_metadata()),
1689 ttl_seconds: self.ttl_seconds,
1690 expires_at: self.expires_at,
1691 };
1692 v.apply_ttl();
1693 v
1694 }
1695
1696 pub fn from_vector(vector: &Vector) -> Option<Self> {
1698 let meta = vector.metadata.as_ref()?.as_object()?;
1699 let entry_type = meta.get("_dakera_type")?.as_str()?;
1700 if entry_type != "memory" {
1701 return None;
1702 }
1703
1704 Some(Memory {
1705 id: vector.id.clone(),
1706 memory_type: serde_json::from_value(meta.get("memory_type")?.clone())
1707 .unwrap_or_default(),
1708 content: meta.get("content")?.as_str()?.to_string(),
1709 agent_id: meta.get("agent_id")?.as_str()?.to_string(),
1710 session_id: meta
1711 .get("session_id")
1712 .and_then(|v| v.as_str())
1713 .map(String::from),
1714 importance: meta
1715 .get("importance")
1716 .and_then(|v| v.as_f64())
1717 .unwrap_or(0.5) as f32,
1718 tags: meta
1719 .get("tags")
1720 .and_then(|v| serde_json::from_value(v.clone()).ok())
1721 .unwrap_or_default(),
1722 metadata: meta.get("user_metadata").cloned(),
1723 created_at: meta.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0),
1724 last_accessed_at: meta
1725 .get("last_accessed_at")
1726 .and_then(|v| v.as_u64())
1727 .unwrap_or(0),
1728 access_count: meta
1729 .get("access_count")
1730 .and_then(|v| v.as_u64())
1731 .unwrap_or(0) as u32,
1732 ttl_seconds: vector.ttl_seconds,
1733 expires_at: vector.expires_at,
1734 })
1735 }
1736}
1737
1738#[derive(Debug, Clone, Serialize, Deserialize)]
1740pub struct Session {
1741 pub id: String,
1742 pub agent_id: String,
1743 pub started_at: u64,
1744 #[serde(skip_serializing_if = "Option::is_none")]
1745 pub ended_at: Option<u64>,
1746 #[serde(skip_serializing_if = "Option::is_none")]
1747 pub summary: Option<String>,
1748 #[serde(skip_serializing_if = "Option::is_none")]
1749 pub metadata: Option<serde_json::Value>,
1750 #[serde(default)]
1752 pub memory_count: usize,
1753}
1754
1755impl Session {
1756 pub fn new(id: String, agent_id: String) -> Self {
1757 let now = std::time::SystemTime::now()
1758 .duration_since(std::time::UNIX_EPOCH)
1759 .unwrap_or_default()
1760 .as_secs();
1761 Self {
1762 id,
1763 agent_id,
1764 started_at: now,
1765 ended_at: None,
1766 summary: None,
1767 metadata: None,
1768 memory_count: 0,
1769 }
1770 }
1771
1772 pub fn to_vector_metadata(&self) -> serde_json::Value {
1774 let mut meta = serde_json::Map::new();
1775 meta.insert("_dakera_type".to_string(), serde_json::json!("session"));
1776 meta.insert("agent_id".to_string(), serde_json::json!(self.agent_id));
1777 meta.insert("started_at".to_string(), serde_json::json!(self.started_at));
1778 if let Some(ref ended) = self.ended_at {
1779 meta.insert("ended_at".to_string(), serde_json::json!(ended));
1780 }
1781 if let Some(ref summary) = self.summary {
1782 meta.insert("summary".to_string(), serde_json::json!(summary));
1783 }
1784 if let Some(ref user_meta) = self.metadata {
1785 meta.insert("user_metadata".to_string(), user_meta.clone());
1786 }
1787 meta.insert(
1788 "memory_count".to_string(),
1789 serde_json::json!(self.memory_count),
1790 );
1791 serde_json::Value::Object(meta)
1792 }
1793
1794 pub fn to_vector(&self, embedding: Vec<f32>) -> Vector {
1796 Vector {
1797 id: self.id.clone(),
1798 values: embedding,
1799 metadata: Some(self.to_vector_metadata()),
1800 ttl_seconds: None,
1801 expires_at: None,
1802 }
1803 }
1804
1805 pub fn from_vector(vector: &Vector) -> Option<Self> {
1807 let meta = vector.metadata.as_ref()?.as_object()?;
1808 let entry_type = meta.get("_dakera_type")?.as_str()?;
1809 if entry_type != "session" {
1810 return None;
1811 }
1812
1813 Some(Session {
1814 id: vector.id.clone(),
1815 agent_id: meta.get("agent_id")?.as_str()?.to_string(),
1816 started_at: meta.get("started_at").and_then(|v| v.as_u64()).unwrap_or(0),
1817 ended_at: meta.get("ended_at").and_then(|v| v.as_u64()),
1818 summary: meta
1819 .get("summary")
1820 .and_then(|v| v.as_str())
1821 .map(String::from),
1822 metadata: meta.get("user_metadata").cloned(),
1823 memory_count: meta
1824 .get("memory_count")
1825 .and_then(|v| v.as_u64())
1826 .unwrap_or(0) as usize,
1827 })
1828 }
1829}
1830
1831#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1833#[serde(rename_all = "snake_case")]
1834#[derive(Default)]
1835pub enum DecayStrategy {
1836 #[default]
1837 Exponential,
1838 Linear,
1839 StepFunction,
1840 PowerLaw,
1842 Logarithmic,
1844 Flat,
1846}
1847
1848#[derive(Debug, Clone, Serialize, Deserialize)]
1850pub struct DecayConfig {
1851 #[serde(default)]
1852 pub strategy: DecayStrategy,
1853 #[serde(default = "default_half_life_hours")]
1854 pub half_life_hours: f64,
1855 #[serde(default = "default_min_importance")]
1856 pub min_importance: f32,
1857}
1858
1859fn default_half_life_hours() -> f64 {
1860 168.0 }
1862
1863fn default_min_importance() -> f32 {
1864 0.01
1865}
1866
1867impl Default for DecayConfig {
1868 fn default() -> Self {
1869 Self {
1870 strategy: DecayStrategy::default(),
1871 half_life_hours: default_half_life_hours(),
1872 min_importance: default_min_importance(),
1873 }
1874 }
1875}
1876
1877#[derive(Debug, Deserialize)]
1883pub struct StoreMemoryRequest {
1884 pub content: String,
1885 pub agent_id: String,
1886 #[serde(default)]
1887 pub memory_type: MemoryType,
1888 #[serde(skip_serializing_if = "Option::is_none")]
1889 pub session_id: Option<String>,
1890 #[serde(default = "default_importance")]
1891 pub importance: f32,
1892 #[serde(default)]
1893 pub tags: Vec<String>,
1894 #[serde(skip_serializing_if = "Option::is_none")]
1895 pub metadata: Option<serde_json::Value>,
1896 #[serde(skip_serializing_if = "Option::is_none")]
1897 pub ttl_seconds: Option<u64>,
1898 #[serde(skip_serializing_if = "Option::is_none")]
1903 pub expires_at: Option<u64>,
1904 #[serde(skip_serializing_if = "Option::is_none")]
1906 pub id: Option<String>,
1907}
1908
1909#[derive(Debug, Serialize)]
1911pub struct StoreMemoryResponse {
1912 pub memory: Memory,
1913 pub embedding_time_ms: u64,
1914}
1915
1916#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
1920#[serde(rename_all = "lowercase")]
1921pub enum RoutingMode {
1922 #[default]
1924 Auto,
1925 Vector,
1927 Bm25,
1929 Hybrid,
1931}
1932
1933#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
1938#[serde(rename_all = "lowercase")]
1939pub enum FusionStrategy {
1940 Rrf,
1945 #[default]
1948 MinMax,
1949}
1950
1951#[derive(Debug, Deserialize)]
1953pub struct RecallRequest {
1954 pub query: String,
1955 pub agent_id: String,
1956 #[serde(default = "default_top_k")]
1957 pub top_k: usize,
1958 #[serde(default)]
1959 pub memory_type: Option<MemoryType>,
1960 #[serde(default)]
1961 pub session_id: Option<String>,
1962 #[serde(default)]
1963 pub tags: Option<Vec<String>>,
1964 #[serde(default)]
1965 pub min_importance: Option<f32>,
1966 #[serde(default = "default_true")]
1968 pub importance_weighted: bool,
1969 #[serde(default)]
1971 pub include_associated: bool,
1972 #[serde(default)]
1974 pub associated_memories_cap: Option<usize>,
1975 #[serde(default)]
1977 pub since: Option<String>,
1978 #[serde(default)]
1980 pub until: Option<String>,
1981 #[serde(default)]
1984 pub associated_memories_depth: Option<u8>,
1985 #[serde(default)]
1988 pub associated_memories_min_weight: Option<f32>,
1989 #[serde(default)]
1993 pub routing: RoutingMode,
1994 #[serde(default = "default_true")]
1998 pub rerank: bool,
1999 #[serde(default)]
2002 pub fusion: FusionStrategy,
2003 #[serde(default)]
2007 pub vector_weight: Option<f32>,
2008 #[serde(default)]
2014 pub iterations: Option<u8>,
2015 #[serde(default = "default_true")]
2019 pub neighborhood: bool,
2020}
2021
2022#[derive(Debug, Serialize, Deserialize)]
2024pub struct RecallResult {
2025 pub memory: Memory,
2026 pub score: f32,
2027 #[serde(skip_serializing_if = "Option::is_none")]
2029 pub weighted_score: Option<f32>,
2030 #[serde(skip_serializing_if = "Option::is_none")]
2032 pub smart_score: Option<f32>,
2033 #[serde(skip_serializing_if = "Option::is_none")]
2036 pub depth: Option<u8>,
2037}
2038
2039#[derive(Debug, Serialize)]
2041pub struct RecallResponse {
2042 pub memories: Vec<RecallResult>,
2043 pub query_embedding_time_ms: u64,
2044 pub search_time_ms: u64,
2045 #[serde(skip_serializing_if = "Option::is_none")]
2048 pub associated_memories: Option<Vec<RecallResult>>,
2049}
2050
2051#[derive(Debug, Deserialize)]
2053pub struct ForgetRequest {
2054 pub agent_id: String,
2055 #[serde(default)]
2056 pub memory_ids: Option<Vec<String>>,
2057 #[serde(default)]
2058 pub memory_type: Option<MemoryType>,
2059 #[serde(default)]
2060 pub session_id: Option<String>,
2061 #[serde(default)]
2062 pub tags: Option<Vec<String>>,
2063 #[serde(default)]
2065 pub below_importance: Option<f32>,
2066}
2067
2068#[derive(Debug, Serialize)]
2070pub struct ForgetResponse {
2071 pub deleted_count: usize,
2072}
2073
2074#[derive(Debug, Deserialize)]
2076pub struct UpdateMemoryRequest {
2077 #[serde(default)]
2078 pub content: Option<String>,
2079 #[serde(default)]
2080 pub importance: Option<f32>,
2081 #[serde(default)]
2082 pub tags: Option<Vec<String>>,
2083 #[serde(default)]
2084 pub metadata: Option<serde_json::Value>,
2085 #[serde(default)]
2086 pub memory_type: Option<MemoryType>,
2087}
2088
2089#[derive(Debug, Deserialize)]
2091pub struct UpdateImportanceRequest {
2092 pub memory_id: String,
2093 pub importance: f32,
2094 pub agent_id: String,
2095}
2096
2097#[derive(Debug, Deserialize)]
2099pub struct ConsolidateRequest {
2100 pub agent_id: String,
2101 #[serde(default)]
2103 pub memory_ids: Option<Vec<String>>,
2104 #[serde(default = "default_consolidation_threshold")]
2106 pub threshold: f32,
2107 #[serde(default)]
2109 pub target_type: Option<MemoryType>,
2110}
2111
2112fn default_consolidation_threshold() -> f32 {
2113 0.85
2114}
2115
2116#[derive(Debug, Serialize)]
2118pub struct ConsolidateResponse {
2119 pub consolidated_memory: Memory,
2120 pub source_memory_ids: Vec<String>,
2121 pub memories_removed: usize,
2122}
2123
2124#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2126#[serde(rename_all = "lowercase")]
2127pub enum FeedbackSignal {
2128 Upvote,
2130 Downvote,
2132 Flag,
2134 Positive,
2136 Negative,
2138}
2139
2140#[derive(Debug, Clone, Serialize, Deserialize)]
2142pub struct FeedbackHistoryEntry {
2143 pub signal: FeedbackSignal,
2144 pub timestamp: u64,
2145 pub old_importance: f32,
2146 pub new_importance: f32,
2147}
2148
2149#[derive(Debug, Deserialize)]
2151pub struct FeedbackRequest {
2152 pub agent_id: String,
2153 pub memory_id: String,
2154 pub signal: FeedbackSignal,
2155}
2156
2157#[derive(Debug, Deserialize)]
2159pub struct MemoryFeedbackRequest {
2160 pub agent_id: String,
2161 pub signal: FeedbackSignal,
2162}
2163
2164#[derive(Debug, Serialize)]
2166pub struct FeedbackResponse {
2167 pub memory_id: String,
2168 pub new_importance: f32,
2169 pub signal: FeedbackSignal,
2170}
2171
2172#[derive(Debug, Serialize)]
2174pub struct FeedbackHistoryResponse {
2175 pub memory_id: String,
2176 pub entries: Vec<FeedbackHistoryEntry>,
2177}
2178
2179#[derive(Debug, Serialize)]
2181pub struct AgentFeedbackSummary {
2182 pub agent_id: String,
2183 pub upvotes: u64,
2184 pub downvotes: u64,
2185 pub flags: u64,
2186 pub total_feedback: u64,
2187 pub health_score: f32,
2189}
2190
2191#[derive(Debug, Deserialize)]
2193pub struct MemoryImportancePatchRequest {
2194 pub agent_id: String,
2195 pub importance: f32,
2196}
2197
2198#[derive(Debug, Deserialize)]
2200pub struct FeedbackHealthQuery {
2201 pub agent_id: String,
2202}
2203
2204#[derive(Debug, Serialize)]
2206pub struct FeedbackHealthResponse {
2207 pub agent_id: String,
2208 pub health_score: f32,
2210 pub memory_count: usize,
2211 pub avg_importance: f32,
2212}
2213
2214#[derive(Debug, Deserialize)]
2216pub struct SearchMemoriesRequest {
2217 pub agent_id: String,
2218 #[serde(default)]
2219 pub query: Option<String>,
2220 #[serde(default)]
2221 pub memory_type: Option<MemoryType>,
2222 #[serde(default)]
2223 pub session_id: Option<String>,
2224 #[serde(default)]
2225 pub tags: Option<Vec<String>>,
2226 #[serde(default)]
2227 pub min_importance: Option<f32>,
2228 #[serde(default)]
2229 pub max_importance: Option<f32>,
2230 #[serde(default)]
2231 pub created_after: Option<u64>,
2232 #[serde(default)]
2233 pub created_before: Option<u64>,
2234 #[serde(default = "default_top_k")]
2235 pub top_k: usize,
2236 #[serde(default)]
2237 pub sort_by: Option<MemorySortField>,
2238 #[serde(default)]
2240 pub routing: RoutingMode,
2241 #[serde(default)]
2244 pub rerank: bool,
2245}
2246
2247#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
2249#[serde(rename_all = "snake_case")]
2250pub enum MemorySortField {
2251 CreatedAt,
2252 LastAccessedAt,
2253 Importance,
2254 AccessCount,
2255}
2256
2257#[derive(Debug, Serialize)]
2259pub struct SearchMemoriesResponse {
2260 pub memories: Vec<RecallResult>,
2261 pub total_count: usize,
2262}
2263
2264#[derive(Debug, Deserialize)]
2270pub struct SessionStartRequest {
2271 pub agent_id: String,
2272 #[serde(skip_serializing_if = "Option::is_none")]
2273 pub metadata: Option<serde_json::Value>,
2274 #[serde(skip_serializing_if = "Option::is_none")]
2276 pub id: Option<String>,
2277}
2278
2279#[derive(Debug, Serialize)]
2281pub struct SessionStartResponse {
2282 pub session: Session,
2283}
2284
2285#[derive(Debug, Deserialize)]
2287pub struct SessionEndRequest {
2288 #[serde(default)]
2289 pub summary: Option<String>,
2290 #[serde(default)]
2292 pub auto_summarize: bool,
2293}
2294
2295#[derive(Debug, Serialize)]
2297pub struct SessionEndResponse {
2298 pub session: Session,
2299 pub memory_count: usize,
2300}
2301
2302#[derive(Debug, Serialize)]
2304pub struct ListSessionsResponse {
2305 pub sessions: Vec<Session>,
2306 pub total: usize,
2307}
2308
2309#[derive(Debug, Serialize)]
2311pub struct SessionMemoriesResponse {
2312 pub session: Session,
2313 pub memories: Vec<Memory>,
2314 #[serde(skip_serializing_if = "Option::is_none")]
2316 pub total: Option<usize>,
2317}
2318
2319#[derive(Debug, Serialize, Deserialize, Clone)]
2325pub struct AgentSummary {
2326 pub agent_id: String,
2327 pub memory_count: usize,
2328 pub session_count: usize,
2329 pub active_sessions: usize,
2330}
2331
2332#[derive(Debug, Serialize)]
2334pub struct AgentStats {
2335 pub agent_id: String,
2336 pub total_memories: usize,
2337 pub memories_by_type: std::collections::HashMap<String, usize>,
2338 pub total_sessions: usize,
2339 pub active_sessions: usize,
2340 pub avg_importance: f32,
2341 pub oldest_memory_at: Option<u64>,
2342 pub newest_memory_at: Option<u64>,
2343}
2344
2345#[derive(Debug, Serialize)]
2351pub struct WakeUpResponse {
2352 pub agent_id: String,
2353 pub memories: Vec<Memory>,
2355 pub total_available: usize,
2357}
2358
2359#[derive(Debug, Deserialize)]
2361pub struct KnowledgeGraphRequest {
2362 pub agent_id: String,
2363 pub memory_id: String,
2364 #[serde(default = "default_graph_depth")]
2365 pub depth: usize,
2366 #[serde(default = "default_graph_min_similarity")]
2367 pub min_similarity: f32,
2368}
2369
2370fn default_graph_depth() -> usize {
2371 2
2372}
2373
2374fn default_graph_min_similarity() -> f32 {
2375 0.7
2376}
2377
2378#[derive(Debug, Serialize)]
2380pub struct KnowledgeGraphNode {
2381 pub memory: Memory,
2382 pub similarity: f32,
2383 pub related: Vec<KnowledgeGraphEdge>,
2384}
2385
2386#[derive(Debug, Serialize)]
2388pub struct KnowledgeGraphEdge {
2389 pub memory_id: String,
2390 pub similarity: f32,
2391 pub shared_tags: Vec<String>,
2392}
2393
2394#[derive(Debug, Serialize)]
2396pub struct KnowledgeGraphResponse {
2397 pub root: KnowledgeGraphNode,
2398 pub total_nodes: usize,
2399}
2400
2401fn default_full_graph_max_nodes() -> usize {
2406 200
2407}
2408
2409fn default_full_graph_min_similarity() -> f32 {
2410 0.50
2411}
2412
2413fn default_full_graph_cluster_threshold() -> f32 {
2414 0.60
2415}
2416
2417fn default_full_graph_max_edges_per_node() -> usize {
2418 8
2419}
2420
2421#[derive(Debug, Deserialize)]
2423pub struct FullKnowledgeGraphRequest {
2424 pub agent_id: String,
2425 #[serde(default = "default_full_graph_max_nodes")]
2426 pub max_nodes: usize,
2427 #[serde(default = "default_full_graph_min_similarity")]
2428 pub min_similarity: f32,
2429 #[serde(default = "default_full_graph_cluster_threshold")]
2430 pub cluster_threshold: f32,
2431 #[serde(default = "default_full_graph_max_edges_per_node")]
2432 pub max_edges_per_node: usize,
2433}
2434
2435#[derive(Debug, Serialize)]
2437pub struct FullGraphNode {
2438 pub id: String,
2439 pub content: String,
2440 pub memory_type: String,
2441 pub importance: f32,
2442 pub tags: Vec<String>,
2443 pub created_at: Option<String>,
2444 pub cluster_id: usize,
2445 pub centrality: f32,
2446}
2447
2448#[derive(Debug, Serialize)]
2450pub struct FullGraphEdge {
2451 pub source: String,
2452 pub target: String,
2453 pub similarity: f32,
2454 pub shared_tags: Vec<String>,
2455}
2456
2457#[derive(Debug, Serialize)]
2459pub struct GraphCluster {
2460 pub id: usize,
2461 pub node_count: usize,
2462 pub top_tags: Vec<String>,
2463 pub avg_importance: f32,
2464}
2465
2466#[derive(Debug, Serialize)]
2468pub struct GraphStats {
2469 pub total_memories: usize,
2470 pub included_memories: usize,
2471 pub total_edges: usize,
2472 pub cluster_count: usize,
2473 pub density: f32,
2474 pub hub_memory_id: Option<String>,
2475}
2476
2477#[derive(Debug, Serialize)]
2479pub struct FullKnowledgeGraphResponse {
2480 pub nodes: Vec<FullGraphNode>,
2481 pub edges: Vec<FullGraphEdge>,
2482 pub clusters: Vec<GraphCluster>,
2483 pub stats: GraphStats,
2484}
2485
2486#[derive(Debug, Deserialize)]
2488pub struct SummarizeRequest {
2489 pub agent_id: String,
2490 pub memory_ids: Vec<String>,
2491 #[serde(default)]
2492 pub target_type: Option<MemoryType>,
2493}
2494
2495#[derive(Debug, Serialize)]
2497pub struct SummarizeResponse {
2498 pub summary_memory: Memory,
2499 pub source_count: usize,
2500}
2501
2502#[derive(Debug, Deserialize)]
2504pub struct DeduplicateRequest {
2505 pub agent_id: String,
2506 #[serde(default = "default_dedup_threshold")]
2507 pub threshold: f32,
2508 #[serde(default)]
2509 pub memory_type: Option<MemoryType>,
2510 #[serde(default)]
2512 pub dry_run: bool,
2513}
2514
2515fn default_dedup_threshold() -> f32 {
2516 0.92
2517}
2518
2519#[derive(Debug, Serialize)]
2521pub struct DuplicateGroup {
2522 pub canonical_id: String,
2523 pub duplicate_ids: Vec<String>,
2524 pub avg_similarity: f32,
2525}
2526
2527#[derive(Debug, Serialize)]
2529pub struct DeduplicateResponse {
2530 pub groups: Vec<DuplicateGroup>,
2531 pub duplicates_found: usize,
2532 pub duplicates_merged: usize,
2533}
2534
2535fn default_cross_agent_min_similarity() -> f32 {
2540 0.3
2541}
2542
2543fn default_cross_agent_max_nodes_per_agent() -> usize {
2544 50
2545}
2546
2547fn default_cross_agent_max_cross_edges() -> usize {
2548 200
2549}
2550
2551#[derive(Debug, Deserialize)]
2553pub struct CrossAgentNetworkRequest {
2554 #[serde(default)]
2556 pub agent_ids: Option<Vec<String>>,
2557 #[serde(default = "default_cross_agent_min_similarity")]
2559 pub min_similarity: f32,
2560 #[serde(default = "default_cross_agent_max_nodes_per_agent")]
2562 pub max_nodes_per_agent: usize,
2563 #[serde(default)]
2565 pub min_importance: f32,
2566 #[serde(default = "default_cross_agent_max_cross_edges")]
2568 pub max_cross_edges: usize,
2569}
2570
2571#[derive(Debug, Serialize)]
2573pub struct AgentNetworkInfo {
2574 pub agent_id: String,
2575 pub memory_count: usize,
2576 pub avg_importance: f32,
2577}
2578
2579#[derive(Debug, Serialize)]
2581pub struct AgentNetworkNode {
2582 pub id: String,
2583 pub agent_id: String,
2584 pub content: String,
2585 pub importance: f32,
2586 pub tags: Vec<String>,
2587 pub memory_type: String,
2588 pub created_at: u64,
2589}
2590
2591#[derive(Debug, Serialize)]
2593pub struct AgentNetworkEdge {
2594 pub source: String,
2595 pub target: String,
2596 pub source_agent: String,
2597 pub target_agent: String,
2598 pub similarity: f32,
2599}
2600
2601#[derive(Debug, Serialize)]
2603pub struct AgentNetworkStats {
2604 pub total_agents: usize,
2605 pub total_nodes: usize,
2606 pub total_cross_edges: usize,
2607 pub density: f32,
2608}
2609
2610#[derive(Debug, Serialize)]
2612pub struct CrossAgentNetworkResponse {
2613 pub node_count: usize,
2614 pub agents: Vec<AgentNetworkInfo>,
2615 pub nodes: Vec<AgentNetworkNode>,
2616 pub edges: Vec<AgentNetworkEdge>,
2617 pub stats: AgentNetworkStats,
2618}
2619
2620#[derive(Debug, Deserialize, Default)]
2628pub struct BatchMemoryFilter {
2629 #[serde(default)]
2631 pub tags: Option<Vec<String>>,
2632 #[serde(default)]
2634 pub min_importance: Option<f32>,
2635 #[serde(default)]
2637 pub max_importance: Option<f32>,
2638 #[serde(default)]
2640 pub created_after: Option<u64>,
2641 #[serde(default)]
2643 pub created_before: Option<u64>,
2644 #[serde(default)]
2646 pub memory_type: Option<MemoryType>,
2647 #[serde(default)]
2649 pub session_id: Option<String>,
2650}
2651
2652impl BatchMemoryFilter {
2653 pub fn has_any(&self) -> bool {
2655 self.tags.is_some()
2656 || self.min_importance.is_some()
2657 || self.max_importance.is_some()
2658 || self.created_after.is_some()
2659 || self.created_before.is_some()
2660 || self.memory_type.is_some()
2661 || self.session_id.is_some()
2662 }
2663
2664 pub fn matches(&self, memory: &Memory) -> bool {
2666 if let Some(ref tags) = self.tags {
2667 if !tags.is_empty() && !tags.iter().all(|t| memory.tags.contains(t)) {
2668 return false;
2669 }
2670 }
2671 if let Some(min) = self.min_importance {
2672 if memory.importance < min {
2673 return false;
2674 }
2675 }
2676 if let Some(max) = self.max_importance {
2677 if memory.importance > max {
2678 return false;
2679 }
2680 }
2681 if let Some(after) = self.created_after {
2682 if memory.created_at < after {
2683 return false;
2684 }
2685 }
2686 if let Some(before) = self.created_before {
2687 if memory.created_at > before {
2688 return false;
2689 }
2690 }
2691 if let Some(ref mt) = self.memory_type {
2692 if memory.memory_type != *mt {
2693 return false;
2694 }
2695 }
2696 if let Some(ref sid) = self.session_id {
2697 if memory.session_id.as_ref() != Some(sid) {
2698 return false;
2699 }
2700 }
2701 true
2702 }
2703}
2704
2705#[derive(Debug, Deserialize)]
2707pub struct BatchRecallRequest {
2708 pub agent_id: String,
2710 #[serde(default)]
2712 pub filter: BatchMemoryFilter,
2713 #[serde(default = "default_batch_limit")]
2715 pub limit: usize,
2716}
2717
2718fn default_batch_limit() -> usize {
2719 100
2720}
2721
2722#[derive(Debug, Serialize)]
2724pub struct BatchRecallResponse {
2725 pub memories: Vec<Memory>,
2726 pub total: usize,
2727 pub filtered: usize,
2728}
2729
2730#[derive(Debug, Deserialize)]
2732pub struct BatchForgetRequest {
2733 pub agent_id: String,
2735 pub filter: BatchMemoryFilter,
2737}
2738
2739#[derive(Debug, Serialize)]
2741pub struct BatchForgetResponse {
2742 pub deleted_count: usize,
2743}
2744
2745#[derive(Debug, Deserialize)]
2752pub struct NamespaceEntityConfigRequest {
2753 pub extract_entities: bool,
2755 #[serde(default)]
2758 pub entity_types: Vec<String>,
2759}
2760
2761#[derive(Debug, Serialize, Deserialize)]
2763pub struct NamespaceEntityConfigResponse {
2764 pub namespace: String,
2765 pub extract_entities: bool,
2766 pub entity_types: Vec<String>,
2767}
2768
2769#[derive(Debug, Deserialize)]
2772pub struct ExtractEntitiesRequest {
2773 pub content: String,
2775 #[serde(default)]
2778 pub entity_types: Vec<String>,
2779}
2780
2781#[derive(Debug, Clone, Serialize, Deserialize)]
2783pub struct EntityResult {
2784 pub entity_type: String,
2785 pub value: String,
2786 pub score: f32,
2787 pub start: usize,
2788 pub end: usize,
2789 pub tag: String,
2791}
2792
2793#[derive(Debug, Serialize)]
2795pub struct ExtractEntitiesResponse {
2796 pub entities: Vec<EntityResult>,
2797 pub count: usize,
2798}
2799
2800#[derive(Debug, Deserialize)]
2806pub struct GraphTraverseQuery {
2807 #[serde(default = "default_ce5_graph_depth")]
2809 pub depth: u32,
2810}
2811
2812fn default_ce5_graph_depth() -> u32 {
2813 3
2814}
2815
2816#[derive(Debug, Deserialize)]
2818pub struct GraphPathQuery {
2819 pub to: String,
2821}
2822
2823#[derive(Debug, Deserialize)]
2825pub struct MemoryLinkRequest {
2826 pub target_id: String,
2828 #[serde(skip_serializing_if = "Option::is_none")]
2830 pub label: Option<String>,
2831 pub agent_id: String,
2833}
2834
2835#[derive(Debug, Serialize)]
2837pub struct GraphTraverseResponse {
2838 pub root_id: String,
2839 pub depth: u32,
2840 pub node_count: usize,
2841 pub nodes: Vec<GraphNodeResponse>,
2842}
2843
2844#[derive(Debug, Serialize)]
2846pub struct GraphNodeResponse {
2847 pub memory_id: String,
2848 pub depth: u32,
2849 pub edges: Vec<GraphEdgeResponse>,
2850}
2851
2852#[derive(Debug, Serialize)]
2854pub struct GraphEdgeResponse {
2855 pub from_id: String,
2856 pub to_id: String,
2857 pub edge_type: String,
2858 pub weight: f32,
2859 pub created_at: u64,
2860}
2861
2862#[derive(Debug, Serialize)]
2864pub struct GraphPathResponse {
2865 pub from_id: String,
2866 pub to_id: String,
2867 pub path: Vec<String>,
2869 pub hop_count: usize,
2870}
2871
2872#[derive(Debug, Serialize)]
2874pub struct MemoryLinkResponse {
2875 pub from_id: String,
2876 pub to_id: String,
2877 pub edge_type: String,
2878}
2879
2880#[derive(Debug, Serialize)]
2882pub struct GraphExportResponse {
2883 pub agent_id: String,
2884 pub namespace: String,
2885 pub node_count: usize,
2886 pub edge_count: usize,
2887 pub edges: Vec<GraphEdgeResponse>,
2888}
2889
2890#[derive(Debug, Deserialize)]
2896pub struct KgQueryParams {
2897 pub agent_id: String,
2899 #[serde(default)]
2901 pub root_id: Option<String>,
2902 #[serde(default)]
2904 pub edge_type: Option<String>,
2905 #[serde(default)]
2907 pub min_weight: Option<f32>,
2908 #[serde(default = "default_kg_depth")]
2910 pub max_depth: u32,
2911 #[serde(default = "default_kg_limit")]
2913 pub limit: usize,
2914}
2915
2916fn default_kg_depth() -> u32 {
2917 3
2918}
2919
2920fn default_kg_limit() -> usize {
2921 100
2922}
2923
2924#[derive(Debug, Serialize)]
2926pub struct KgQueryResponse {
2927 pub agent_id: String,
2928 pub node_count: usize,
2929 pub edge_count: usize,
2930 pub edges: Vec<GraphEdgeResponse>,
2931}
2932
2933#[derive(Debug, Deserialize)]
2935pub struct KgPathParams {
2936 pub agent_id: String,
2938 pub from: String,
2940 pub to: String,
2942}
2943
2944#[derive(Debug, Serialize)]
2946pub struct KgPathResponse {
2947 pub agent_id: String,
2948 pub from_id: String,
2949 pub to_id: String,
2950 pub hop_count: usize,
2951 pub path: Vec<String>,
2952}
2953
2954#[derive(Debug, Deserialize)]
2956pub struct KgExportParams {
2957 pub agent_id: String,
2959 #[serde(default = "default_kg_format")]
2961 pub format: String,
2962}
2963
2964fn default_kg_format() -> String {
2965 "json".to_string()
2966}
2967
2968#[derive(Debug, Serialize)]
2970pub struct KgExportJsonResponse {
2971 pub agent_id: String,
2972 pub format: String,
2973 pub node_count: usize,
2974 pub edge_count: usize,
2975 pub edges: Vec<GraphEdgeResponse>,
2976}
2977
2978fn default_working_ttl() -> Option<u64> {
2983 Some(14_400) }
2985fn default_episodic_ttl() -> Option<u64> {
2986 Some(2_592_000) }
2988fn default_semantic_ttl() -> Option<u64> {
2989 Some(31_536_000) }
2991fn default_procedural_ttl() -> Option<u64> {
2992 Some(63_072_000) }
2994fn default_working_decay() -> DecayStrategy {
2995 DecayStrategy::Exponential
2996}
2997fn default_episodic_decay() -> DecayStrategy {
2998 DecayStrategy::PowerLaw
2999}
3000fn default_semantic_decay() -> DecayStrategy {
3001 DecayStrategy::Logarithmic
3002}
3003fn default_procedural_decay() -> DecayStrategy {
3004 DecayStrategy::Flat
3005}
3006fn default_sr_factor() -> f64 {
3007 1.0
3008}
3009fn default_sr_base_interval() -> u64 {
3010 86_400 }
3012fn default_consolidation_enabled() -> bool {
3013 false
3014}
3015fn default_policy_consolidation_threshold() -> f32 {
3016 0.92
3017}
3018fn default_consolidation_interval_hours() -> u32 {
3019 24
3020}
3021fn default_store_dedup_threshold() -> f32 {
3022 0.95
3023}
3024
3025#[derive(Debug, Clone, Serialize, Deserialize)]
3030pub struct MemoryPolicy {
3031 #[serde(
3034 default = "default_working_ttl",
3035 skip_serializing_if = "Option::is_none"
3036 )]
3037 pub working_ttl_seconds: Option<u64>,
3038 #[serde(
3040 default = "default_episodic_ttl",
3041 skip_serializing_if = "Option::is_none"
3042 )]
3043 pub episodic_ttl_seconds: Option<u64>,
3044 #[serde(
3046 default = "default_semantic_ttl",
3047 skip_serializing_if = "Option::is_none"
3048 )]
3049 pub semantic_ttl_seconds: Option<u64>,
3050 #[serde(
3052 default = "default_procedural_ttl",
3053 skip_serializing_if = "Option::is_none"
3054 )]
3055 pub procedural_ttl_seconds: Option<u64>,
3056
3057 #[serde(default = "default_working_decay")]
3060 pub working_decay: DecayStrategy,
3061 #[serde(default = "default_episodic_decay")]
3063 pub episodic_decay: DecayStrategy,
3064 #[serde(default = "default_semantic_decay")]
3066 pub semantic_decay: DecayStrategy,
3067 #[serde(default = "default_procedural_decay")]
3069 pub procedural_decay: DecayStrategy,
3070
3071 #[serde(default = "default_sr_factor")]
3076 pub spaced_repetition_factor: f64,
3077 #[serde(default = "default_sr_base_interval")]
3079 pub spaced_repetition_base_interval_seconds: u64,
3080
3081 #[serde(default = "default_consolidation_enabled")]
3084 pub consolidation_enabled: bool,
3085 #[serde(default = "default_policy_consolidation_threshold")]
3087 pub consolidation_threshold: f32,
3088 #[serde(default = "default_consolidation_interval_hours")]
3090 pub consolidation_interval_hours: u32,
3091 #[serde(default)]
3093 pub consolidated_count: u64,
3094
3095 #[serde(default)]
3099 pub rate_limit_enabled: bool,
3100 #[serde(default, skip_serializing_if = "Option::is_none")]
3103 pub rate_limit_stores_per_minute: Option<u32>,
3104 #[serde(default, skip_serializing_if = "Option::is_none")]
3107 pub rate_limit_recalls_per_minute: Option<u32>,
3108
3109 #[serde(default)]
3117 pub dedup_on_store: bool,
3118 #[serde(default = "default_store_dedup_threshold")]
3120 pub dedup_threshold: f32,
3121}
3122
3123impl Default for MemoryPolicy {
3124 fn default() -> Self {
3125 Self {
3126 working_ttl_seconds: default_working_ttl(),
3127 episodic_ttl_seconds: default_episodic_ttl(),
3128 semantic_ttl_seconds: default_semantic_ttl(),
3129 procedural_ttl_seconds: default_procedural_ttl(),
3130 working_decay: default_working_decay(),
3131 episodic_decay: default_episodic_decay(),
3132 semantic_decay: default_semantic_decay(),
3133 procedural_decay: default_procedural_decay(),
3134 spaced_repetition_factor: default_sr_factor(),
3135 spaced_repetition_base_interval_seconds: default_sr_base_interval(),
3136 consolidation_enabled: default_consolidation_enabled(),
3137 consolidation_threshold: default_policy_consolidation_threshold(),
3138 consolidation_interval_hours: default_consolidation_interval_hours(),
3139 consolidated_count: 0,
3140 rate_limit_enabled: false,
3141 rate_limit_stores_per_minute: None,
3142 rate_limit_recalls_per_minute: None,
3143 dedup_on_store: false,
3144 dedup_threshold: default_store_dedup_threshold(),
3145 }
3146 }
3147}
3148
3149impl MemoryPolicy {
3150 pub fn ttl_for_type(&self, memory_type: &MemoryType) -> Option<u64> {
3152 match memory_type {
3153 MemoryType::Working => self.working_ttl_seconds,
3154 MemoryType::Episodic => self.episodic_ttl_seconds,
3155 MemoryType::Semantic => self.semantic_ttl_seconds,
3156 MemoryType::Procedural => self.procedural_ttl_seconds,
3157 }
3158 }
3159
3160 pub fn decay_for_type(&self, memory_type: &MemoryType) -> DecayStrategy {
3162 match memory_type {
3163 MemoryType::Working => self.working_decay,
3164 MemoryType::Episodic => self.episodic_decay,
3165 MemoryType::Semantic => self.semantic_decay,
3166 MemoryType::Procedural => self.procedural_decay,
3167 }
3168 }
3169
3170 pub fn spaced_repetition_extension(&self, access_count: u32) -> u64 {
3174 if self.spaced_repetition_factor <= 0.0 {
3175 return 0;
3176 }
3177 let ext = access_count as f64
3178 * self.spaced_repetition_factor
3179 * self.spaced_repetition_base_interval_seconds as f64;
3180 ext.round() as u64
3181 }
3182}