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 pub vector: Vec<f32>,
568 pub text: String,
570 #[serde(default = "default_top_k")]
572 pub top_k: usize,
573 #[serde(default = "default_vector_weight")]
576 pub vector_weight: f32,
577 #[serde(default)]
579 pub distance_metric: DistanceMetric,
580 #[serde(default = "default_true")]
582 pub include_metadata: bool,
583 #[serde(default)]
585 pub include_vectors: bool,
586 #[serde(default)]
588 pub filter: Option<FilterExpression>,
589}
590
591fn default_vector_weight() -> f32 {
592 0.5 }
594
595#[derive(Debug, Serialize, Deserialize)]
597pub struct HybridSearchResult {
598 pub id: String,
599 pub score: f32,
601 pub vector_score: f32,
603 pub text_score: f32,
605 #[serde(skip_serializing_if = "Option::is_none")]
606 pub metadata: Option<serde_json::Value>,
607 #[serde(skip_serializing_if = "Option::is_none")]
608 pub vector: Option<Vec<f32>>,
609}
610
611#[derive(Debug, Serialize, Deserialize)]
613pub struct HybridSearchResponse {
614 pub results: Vec<HybridSearchResult>,
615 #[serde(default)]
617 pub search_time_ms: u64,
618}
619
620#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
626#[serde(untagged)]
627pub enum FilterValue {
628 String(String),
629 Number(f64),
630 Integer(i64),
631 Boolean(bool),
632 StringArray(Vec<String>),
633 NumberArray(Vec<f64>),
634}
635
636impl FilterValue {
637 pub fn as_f64(&self) -> Option<f64> {
639 match self {
640 FilterValue::Number(n) => Some(*n),
641 FilterValue::Integer(i) => Some(*i as f64),
642 _ => None,
643 }
644 }
645
646 pub fn as_str(&self) -> Option<&str> {
648 match self {
649 FilterValue::String(s) => Some(s.as_str()),
650 _ => None,
651 }
652 }
653
654 pub fn as_bool(&self) -> Option<bool> {
656 match self {
657 FilterValue::Boolean(b) => Some(*b),
658 _ => None,
659 }
660 }
661}
662
663#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
665#[serde(rename_all = "snake_case")]
666pub enum FilterCondition {
667 #[serde(rename = "$eq")]
669 Eq(FilterValue),
670 #[serde(rename = "$ne")]
672 Ne(FilterValue),
673 #[serde(rename = "$gt")]
675 Gt(FilterValue),
676 #[serde(rename = "$gte")]
678 Gte(FilterValue),
679 #[serde(rename = "$lt")]
681 Lt(FilterValue),
682 #[serde(rename = "$lte")]
684 Lte(FilterValue),
685 #[serde(rename = "$in")]
687 In(Vec<FilterValue>),
688 #[serde(rename = "$nin")]
690 NotIn(Vec<FilterValue>),
691 #[serde(rename = "$exists")]
693 Exists(bool),
694 #[serde(rename = "$contains")]
699 Contains(String),
700 #[serde(rename = "$icontains")]
702 IContains(String),
703 #[serde(rename = "$startsWith")]
705 StartsWith(String),
706 #[serde(rename = "$endsWith")]
708 EndsWith(String),
709 #[serde(rename = "$glob")]
711 Glob(String),
712 #[serde(rename = "$regex")]
714 Regex(String),
715}
716
717#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
719#[serde(untagged)]
720pub enum FilterExpression {
721 And {
723 #[serde(rename = "$and")]
724 conditions: Vec<FilterExpression>,
725 },
726 Or {
728 #[serde(rename = "$or")]
729 conditions: Vec<FilterExpression>,
730 },
731 Field {
733 #[serde(flatten)]
734 field: std::collections::HashMap<String, FilterCondition>,
735 },
736}
737
738#[derive(Debug, Clone, Serialize, Deserialize, Default)]
744pub struct QuotaConfig {
745 #[serde(skip_serializing_if = "Option::is_none")]
747 pub max_vectors: Option<u64>,
748 #[serde(skip_serializing_if = "Option::is_none")]
750 pub max_storage_bytes: Option<u64>,
751 #[serde(skip_serializing_if = "Option::is_none")]
753 pub max_dimensions: Option<usize>,
754 #[serde(skip_serializing_if = "Option::is_none")]
756 pub max_metadata_bytes: Option<usize>,
757 #[serde(default)]
759 pub enforcement: QuotaEnforcement,
760}
761
762#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
764#[serde(rename_all = "snake_case")]
765pub enum QuotaEnforcement {
766 None,
768 Soft,
770 #[default]
772 Hard,
773}
774
775#[derive(Debug, Clone, Serialize, Deserialize, Default)]
777pub struct QuotaUsage {
778 pub vector_count: u64,
780 pub storage_bytes: u64,
782 pub avg_dimensions: Option<usize>,
784 pub avg_metadata_bytes: Option<usize>,
786 pub last_updated: u64,
788}
789
790impl QuotaUsage {
791 pub fn new(vector_count: u64, storage_bytes: u64) -> Self {
793 let now = std::time::SystemTime::now()
794 .duration_since(std::time::UNIX_EPOCH)
795 .unwrap_or_default()
796 .as_secs();
797 Self {
798 vector_count,
799 storage_bytes,
800 avg_dimensions: None,
801 avg_metadata_bytes: None,
802 last_updated: now,
803 }
804 }
805
806 pub fn touch(&mut self) {
808 self.last_updated = std::time::SystemTime::now()
809 .duration_since(std::time::UNIX_EPOCH)
810 .unwrap_or_default()
811 .as_secs();
812 }
813}
814
815#[derive(Debug, Clone, Serialize, Deserialize)]
817pub struct QuotaStatus {
818 pub namespace: String,
820 pub config: QuotaConfig,
822 pub usage: QuotaUsage,
824 #[serde(skip_serializing_if = "Option::is_none")]
826 pub vector_usage_percent: Option<f32>,
827 #[serde(skip_serializing_if = "Option::is_none")]
829 pub storage_usage_percent: Option<f32>,
830 pub is_exceeded: bool,
832 #[serde(skip_serializing_if = "Vec::is_empty")]
834 pub exceeded_quotas: Vec<String>,
835}
836
837impl QuotaStatus {
838 pub fn new(namespace: String, config: QuotaConfig, usage: QuotaUsage) -> Self {
840 let vector_usage_percent = config
841 .max_vectors
842 .map(|max| (usage.vector_count as f32 / max as f32) * 100.0);
843
844 let storage_usage_percent = config
845 .max_storage_bytes
846 .map(|max| (usage.storage_bytes as f32 / max as f32) * 100.0);
847
848 let mut exceeded_quotas = Vec::new();
849
850 if let Some(max) = config.max_vectors {
851 if usage.vector_count > max {
852 exceeded_quotas.push("max_vectors".to_string());
853 }
854 }
855
856 if let Some(max) = config.max_storage_bytes {
857 if usage.storage_bytes > max {
858 exceeded_quotas.push("max_storage_bytes".to_string());
859 }
860 }
861
862 let is_exceeded = !exceeded_quotas.is_empty();
863
864 Self {
865 namespace,
866 config,
867 usage,
868 vector_usage_percent,
869 storage_usage_percent,
870 is_exceeded,
871 exceeded_quotas,
872 }
873 }
874}
875
876#[derive(Debug, Deserialize)]
878pub struct SetQuotaRequest {
879 pub config: QuotaConfig,
881}
882
883#[derive(Debug, Serialize)]
885pub struct SetQuotaResponse {
886 pub success: bool,
888 pub namespace: String,
890 pub config: QuotaConfig,
892 pub message: String,
894}
895
896#[derive(Debug, Clone, Serialize)]
898pub struct QuotaCheckResult {
899 pub allowed: bool,
901 #[serde(skip_serializing_if = "Option::is_none")]
903 pub reason: Option<String>,
904 pub usage: QuotaUsage,
906 #[serde(skip_serializing_if = "Option::is_none")]
908 pub exceeded_quota: Option<String>,
909}
910
911#[derive(Debug, Serialize)]
913pub struct QuotaListResponse {
914 pub quotas: Vec<QuotaStatus>,
916 pub total: u64,
918 #[serde(skip_serializing_if = "Option::is_none")]
920 pub default_config: Option<QuotaConfig>,
921}
922
923#[derive(Debug, Serialize)]
925pub struct DefaultQuotaResponse {
926 pub config: Option<QuotaConfig>,
928}
929
930#[derive(Debug, Deserialize)]
932pub struct SetDefaultQuotaRequest {
933 pub config: Option<QuotaConfig>,
935}
936
937#[derive(Debug, Deserialize)]
939pub struct QuotaCheckRequest {
940 pub vector_ids: Vec<String>,
942 #[serde(default)]
944 pub dimensions: Option<usize>,
945 #[serde(default)]
947 pub metadata_bytes: Option<usize>,
948}
949
950#[derive(Debug, Deserialize)]
956pub struct ExportRequest {
957 #[serde(default = "default_export_top_k")]
959 pub top_k: usize,
960 #[serde(skip_serializing_if = "Option::is_none")]
962 pub cursor: Option<String>,
963 #[serde(default = "default_true")]
965 pub include_vectors: bool,
966 #[serde(default = "default_true")]
968 pub include_metadata: bool,
969}
970
971fn default_export_top_k() -> usize {
972 1000
973}
974
975impl Default for ExportRequest {
976 fn default() -> Self {
977 Self {
978 top_k: 1000,
979 cursor: None,
980 include_vectors: true,
981 include_metadata: true,
982 }
983 }
984}
985
986#[derive(Debug, Clone, Serialize, Deserialize)]
988pub struct ExportedVector {
989 pub id: String,
991 #[serde(skip_serializing_if = "Option::is_none")]
993 pub values: Option<Vec<f32>>,
994 #[serde(skip_serializing_if = "Option::is_none")]
996 pub metadata: Option<serde_json::Value>,
997 #[serde(skip_serializing_if = "Option::is_none")]
999 pub ttl_seconds: Option<u64>,
1000}
1001
1002impl From<&Vector> for ExportedVector {
1003 fn from(v: &Vector) -> Self {
1004 Self {
1005 id: v.id.clone(),
1006 values: Some(v.values.clone()),
1007 metadata: v.metadata.clone(),
1008 ttl_seconds: v.ttl_seconds,
1009 }
1010 }
1011}
1012
1013#[derive(Debug, Serialize)]
1015pub struct ExportResponse {
1016 pub vectors: Vec<ExportedVector>,
1018 #[serde(skip_serializing_if = "Option::is_none")]
1020 pub next_cursor: Option<String>,
1021 pub total_count: usize,
1023 pub returned_count: usize,
1025}
1026
1027#[derive(Debug, Clone, Serialize, Deserialize)]
1034#[serde(untagged)]
1035pub enum RankBy {
1036 VectorSearch {
1039 field: String,
1040 method: VectorSearchMethod,
1041 query_vector: Vec<f32>,
1042 },
1043 FullTextSearch {
1045 field: String,
1046 method: String, query: String,
1048 },
1049 AttributeOrder {
1051 field: String,
1052 direction: SortDirection,
1053 },
1054 Sum(Vec<RankBy>),
1056 Max(Vec<RankBy>),
1058 Product { weight: f32, ranking: Box<RankBy> },
1060}
1061
1062#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
1064pub enum VectorSearchMethod {
1065 #[default]
1067 ANN,
1068 #[serde(rename = "kNN")]
1070 KNN,
1071}
1072
1073#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
1075#[serde(rename_all = "lowercase")]
1076#[derive(Default)]
1077pub enum SortDirection {
1078 Asc,
1079 #[default]
1080 Desc,
1081}
1082
1083#[derive(Debug, Deserialize)]
1085pub struct UnifiedQueryRequest {
1086 pub rank_by: RankByInput,
1088 #[serde(default = "default_top_k")]
1090 pub top_k: usize,
1091 #[serde(default)]
1093 pub filter: Option<FilterExpression>,
1094 #[serde(default = "default_true")]
1096 pub include_metadata: bool,
1097 #[serde(default)]
1099 pub include_vectors: bool,
1100 #[serde(default)]
1102 pub distance_metric: DistanceMetric,
1103}
1104
1105#[derive(Debug, Clone, Serialize, Deserialize)]
1113#[serde(from = "serde_json::Value")]
1114pub struct RankByInput(pub RankBy);
1115
1116impl From<serde_json::Value> for RankByInput {
1117 fn from(value: serde_json::Value) -> Self {
1118 RankByInput(parse_rank_by(&value).unwrap_or_else(|| {
1119 RankBy::AttributeOrder {
1121 field: "id".to_string(),
1122 direction: SortDirection::Asc,
1123 }
1124 }))
1125 }
1126}
1127
1128fn parse_rank_by(value: &serde_json::Value) -> Option<RankBy> {
1130 let arr = value.as_array()?;
1131 if arr.is_empty() {
1132 return None;
1133 }
1134
1135 let first = arr.first()?.as_str()?;
1136
1137 match first {
1138 "Sum" => {
1140 let rankings = arr.get(1)?.as_array()?;
1141 let parsed: Option<Vec<RankBy>> = rankings.iter().map(parse_rank_by).collect();
1142 Some(RankBy::Sum(parsed?))
1143 }
1144 "Max" => {
1145 let rankings = arr.get(1)?.as_array()?;
1146 let parsed: Option<Vec<RankBy>> = rankings.iter().map(parse_rank_by).collect();
1147 Some(RankBy::Max(parsed?))
1148 }
1149 "Product" => {
1150 let weight = arr.get(1)?.as_f64()? as f32;
1151 let ranking = parse_rank_by(arr.get(2)?)?;
1152 Some(RankBy::Product {
1153 weight,
1154 ranking: Box::new(ranking),
1155 })
1156 }
1157 "ANN" => {
1159 let query_vector = parse_vector_array(arr.get(1)?)?;
1160 Some(RankBy::VectorSearch {
1161 field: "vector".to_string(),
1162 method: VectorSearchMethod::ANN,
1163 query_vector,
1164 })
1165 }
1166 "kNN" => {
1167 let query_vector = parse_vector_array(arr.get(1)?)?;
1168 Some(RankBy::VectorSearch {
1169 field: "vector".to_string(),
1170 method: VectorSearchMethod::KNN,
1171 query_vector,
1172 })
1173 }
1174 field => {
1176 let second = arr.get(1)?;
1177
1178 if let Some(method_str) = second.as_str() {
1180 match method_str {
1181 "ANN" => {
1182 let query_vector = parse_vector_array(arr.get(2)?)?;
1183 Some(RankBy::VectorSearch {
1184 field: field.to_string(),
1185 method: VectorSearchMethod::ANN,
1186 query_vector,
1187 })
1188 }
1189 "kNN" => {
1190 let query_vector = parse_vector_array(arr.get(2)?)?;
1191 Some(RankBy::VectorSearch {
1192 field: field.to_string(),
1193 method: VectorSearchMethod::KNN,
1194 query_vector,
1195 })
1196 }
1197 "BM25" => {
1198 let query = arr.get(2)?.as_str()?;
1199 Some(RankBy::FullTextSearch {
1200 field: field.to_string(),
1201 method: "BM25".to_string(),
1202 query: query.to_string(),
1203 })
1204 }
1205 "asc" => Some(RankBy::AttributeOrder {
1206 field: field.to_string(),
1207 direction: SortDirection::Asc,
1208 }),
1209 "desc" => Some(RankBy::AttributeOrder {
1210 field: field.to_string(),
1211 direction: SortDirection::Desc,
1212 }),
1213 _ => None,
1214 }
1215 } else {
1216 None
1217 }
1218 }
1219 }
1220}
1221
1222fn parse_vector_array(value: &serde_json::Value) -> Option<Vec<f32>> {
1224 let arr = value.as_array()?;
1225 arr.iter().map(|v| v.as_f64().map(|n| n as f32)).collect()
1226}
1227
1228#[derive(Debug, Serialize, Deserialize)]
1230pub struct UnifiedQueryResponse {
1231 pub results: Vec<UnifiedSearchResult>,
1233 #[serde(skip_serializing_if = "Option::is_none")]
1235 pub next_cursor: Option<String>,
1236}
1237
1238#[derive(Debug, Serialize, Deserialize)]
1240pub struct UnifiedSearchResult {
1241 pub id: String,
1243 #[serde(rename = "$dist", skip_serializing_if = "Option::is_none")]
1246 pub dist: Option<f32>,
1247 #[serde(skip_serializing_if = "Option::is_none")]
1249 pub metadata: Option<serde_json::Value>,
1250 #[serde(skip_serializing_if = "Option::is_none")]
1252 pub vector: Option<Vec<f32>>,
1253}
1254
1255#[derive(Debug, Clone, Serialize, Deserialize)]
1261pub enum AggregateFunction {
1262 Count,
1264 Sum { field: String },
1266 Avg { field: String },
1268 Min { field: String },
1270 Max { field: String },
1272}
1273
1274#[derive(Debug, Clone, Serialize, Deserialize)]
1276#[serde(from = "serde_json::Value")]
1277pub struct AggregateFunctionInput(pub AggregateFunction);
1278
1279impl From<serde_json::Value> for AggregateFunctionInput {
1280 fn from(value: serde_json::Value) -> Self {
1281 parse_aggregate_function(&value)
1282 .map(AggregateFunctionInput)
1283 .unwrap_or_else(|| {
1284 AggregateFunctionInput(AggregateFunction::Count)
1286 })
1287 }
1288}
1289
1290fn parse_aggregate_function(value: &serde_json::Value) -> Option<AggregateFunction> {
1292 let arr = value.as_array()?;
1293 if arr.is_empty() {
1294 return None;
1295 }
1296
1297 let func_name = arr.first()?.as_str()?;
1298
1299 match func_name {
1300 "Count" => Some(AggregateFunction::Count),
1301 "Sum" => {
1302 let field = arr.get(1)?.as_str()?;
1303 Some(AggregateFunction::Sum {
1304 field: field.to_string(),
1305 })
1306 }
1307 "Avg" => {
1308 let field = arr.get(1)?.as_str()?;
1309 Some(AggregateFunction::Avg {
1310 field: field.to_string(),
1311 })
1312 }
1313 "Min" => {
1314 let field = arr.get(1)?.as_str()?;
1315 Some(AggregateFunction::Min {
1316 field: field.to_string(),
1317 })
1318 }
1319 "Max" => {
1320 let field = arr.get(1)?.as_str()?;
1321 Some(AggregateFunction::Max {
1322 field: field.to_string(),
1323 })
1324 }
1325 _ => None,
1326 }
1327}
1328
1329#[derive(Debug, Deserialize)]
1331pub struct AggregationRequest {
1332 pub aggregate_by: std::collections::HashMap<String, AggregateFunctionInput>,
1335 #[serde(default)]
1338 pub group_by: Vec<String>,
1339 #[serde(default)]
1341 pub filter: Option<FilterExpression>,
1342 #[serde(default = "default_agg_limit")]
1344 pub limit: usize,
1345}
1346
1347fn default_agg_limit() -> usize {
1348 100
1349}
1350
1351#[derive(Debug, Serialize, Deserialize)]
1353pub struct AggregationResponse {
1354 #[serde(skip_serializing_if = "Option::is_none")]
1356 pub aggregations: Option<std::collections::HashMap<String, serde_json::Value>>,
1357 #[serde(skip_serializing_if = "Option::is_none")]
1359 pub aggregation_groups: Option<Vec<AggregationGroup>>,
1360}
1361
1362#[derive(Debug, Serialize, Deserialize)]
1364pub struct AggregationGroup {
1365 #[serde(flatten)]
1367 pub group_key: std::collections::HashMap<String, serde_json::Value>,
1368 #[serde(flatten)]
1370 pub aggregations: std::collections::HashMap<String, serde_json::Value>,
1371}
1372
1373#[derive(Debug, Clone, Serialize, Deserialize)]
1379pub struct TextDocument {
1380 pub id: VectorId,
1382 pub text: String,
1384 #[serde(skip_serializing_if = "Option::is_none")]
1386 pub metadata: Option<serde_json::Value>,
1387 #[serde(skip_serializing_if = "Option::is_none")]
1389 pub ttl_seconds: Option<u64>,
1390}
1391
1392#[derive(Debug, Deserialize)]
1394pub struct TextUpsertRequest {
1395 pub documents: Vec<TextDocument>,
1397 #[serde(default)]
1399 pub model: Option<EmbeddingModelType>,
1400}
1401
1402#[derive(Debug, Serialize, Deserialize)]
1404pub struct TextUpsertResponse {
1405 pub upserted_count: usize,
1407 pub tokens_processed: usize,
1409 pub model: EmbeddingModelType,
1411 pub embedding_time_ms: u64,
1413}
1414
1415#[derive(Debug, Deserialize)]
1417pub struct TextQueryRequest {
1418 pub text: String,
1420 #[serde(default = "default_top_k")]
1422 pub top_k: usize,
1423 #[serde(default)]
1425 pub filter: Option<FilterExpression>,
1426 #[serde(default)]
1428 pub include_vectors: bool,
1429 #[serde(default = "default_true")]
1431 pub include_text: bool,
1432 #[serde(default)]
1434 pub model: Option<EmbeddingModelType>,
1435}
1436
1437#[derive(Debug, Serialize, Deserialize)]
1439pub struct TextQueryResponse {
1440 pub results: Vec<TextSearchResult>,
1442 pub model: EmbeddingModelType,
1444 pub embedding_time_ms: u64,
1446 pub search_time_ms: u64,
1448}
1449
1450#[derive(Debug, Serialize, Deserialize)]
1452pub struct TextSearchResult {
1453 pub id: VectorId,
1455 pub score: f32,
1457 #[serde(skip_serializing_if = "Option::is_none")]
1459 pub text: Option<String>,
1460 #[serde(skip_serializing_if = "Option::is_none")]
1462 pub metadata: Option<serde_json::Value>,
1463 #[serde(skip_serializing_if = "Option::is_none")]
1465 pub vector: Option<Vec<f32>>,
1466}
1467
1468#[derive(Debug, Deserialize)]
1470pub struct BatchTextQueryRequest {
1471 pub queries: Vec<String>,
1473 #[serde(default = "default_top_k")]
1475 pub top_k: usize,
1476 #[serde(default)]
1478 pub filter: Option<FilterExpression>,
1479 #[serde(default)]
1481 pub include_vectors: bool,
1482 #[serde(default)]
1484 pub model: Option<EmbeddingModelType>,
1485}
1486
1487#[derive(Debug, Serialize, Deserialize)]
1489pub struct BatchTextQueryResponse {
1490 pub results: Vec<Vec<TextSearchResult>>,
1492 pub model: EmbeddingModelType,
1494 pub embedding_time_ms: u64,
1496 pub search_time_ms: u64,
1498}
1499
1500#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1508pub enum EmbeddingModelType {
1509 #[default]
1511 #[serde(rename = "minilm")]
1512 MiniLM,
1513 #[serde(rename = "bge-small")]
1515 BgeSmall,
1516 #[serde(rename = "e5-small")]
1518 E5Small,
1519}
1520
1521impl EmbeddingModelType {
1522 pub fn dimension(&self) -> usize {
1524 match self {
1525 EmbeddingModelType::MiniLM => 384,
1526 EmbeddingModelType::BgeSmall => 384,
1527 EmbeddingModelType::E5Small => 384,
1528 }
1529 }
1530}
1531
1532impl std::fmt::Display for EmbeddingModelType {
1533 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1534 match self {
1535 EmbeddingModelType::MiniLM => write!(f, "minilm"),
1536 EmbeddingModelType::BgeSmall => write!(f, "bge-small"),
1537 EmbeddingModelType::E5Small => write!(f, "e5-small"),
1538 }
1539 }
1540}
1541
1542#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1548#[serde(rename_all = "snake_case")]
1549#[derive(Default)]
1550pub enum MemoryType {
1551 #[default]
1553 Episodic,
1554 Semantic,
1556 Procedural,
1558 Working,
1560}
1561
1562impl std::fmt::Display for MemoryType {
1563 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1564 match self {
1565 MemoryType::Episodic => write!(f, "episodic"),
1566 MemoryType::Semantic => write!(f, "semantic"),
1567 MemoryType::Procedural => write!(f, "procedural"),
1568 MemoryType::Working => write!(f, "working"),
1569 }
1570 }
1571}
1572
1573#[derive(Debug, Clone, Serialize, Deserialize)]
1575pub struct Memory {
1576 pub id: String,
1577 #[serde(default)]
1578 pub memory_type: MemoryType,
1579 pub content: String,
1580 pub agent_id: String,
1581 #[serde(skip_serializing_if = "Option::is_none")]
1582 pub session_id: Option<String>,
1583 #[serde(default = "default_importance")]
1584 pub importance: f32,
1585 #[serde(default)]
1586 pub tags: Vec<String>,
1587 #[serde(skip_serializing_if = "Option::is_none")]
1588 pub metadata: Option<serde_json::Value>,
1589 pub created_at: u64,
1590 pub last_accessed_at: u64,
1591 #[serde(default)]
1592 pub access_count: u32,
1593 #[serde(skip_serializing_if = "Option::is_none")]
1594 pub ttl_seconds: Option<u64>,
1595 #[serde(skip_serializing_if = "Option::is_none")]
1596 pub expires_at: Option<u64>,
1597}
1598
1599fn default_importance() -> f32 {
1600 0.5
1601}
1602
1603impl Memory {
1604 pub fn new(id: String, content: String, agent_id: String, memory_type: MemoryType) -> Self {
1606 let now = std::time::SystemTime::now()
1607 .duration_since(std::time::UNIX_EPOCH)
1608 .unwrap_or_default()
1609 .as_secs();
1610 Self {
1611 id,
1612 memory_type,
1613 content,
1614 agent_id,
1615 session_id: None,
1616 importance: 0.5,
1617 tags: Vec::new(),
1618 metadata: None,
1619 created_at: now,
1620 last_accessed_at: now,
1621 access_count: 0,
1622 ttl_seconds: None,
1623 expires_at: None,
1624 }
1625 }
1626
1627 pub fn is_expired(&self) -> bool {
1629 if let Some(expires_at) = self.expires_at {
1630 let now = std::time::SystemTime::now()
1631 .duration_since(std::time::UNIX_EPOCH)
1632 .unwrap_or_default()
1633 .as_secs();
1634 now >= expires_at
1635 } else {
1636 false
1637 }
1638 }
1639
1640 pub fn to_vector_metadata(&self) -> serde_json::Value {
1642 let mut meta = serde_json::Map::new();
1643 meta.insert("_dakera_type".to_string(), serde_json::json!("memory"));
1644 meta.insert(
1645 "memory_type".to_string(),
1646 serde_json::json!(self.memory_type),
1647 );
1648 meta.insert("content".to_string(), serde_json::json!(self.content));
1649 meta.insert("agent_id".to_string(), serde_json::json!(self.agent_id));
1650 if let Some(ref sid) = self.session_id {
1651 meta.insert("session_id".to_string(), serde_json::json!(sid));
1652 }
1653 meta.insert("importance".to_string(), serde_json::json!(self.importance));
1654 meta.insert("tags".to_string(), serde_json::json!(self.tags));
1655 meta.insert("created_at".to_string(), serde_json::json!(self.created_at));
1656 meta.insert(
1657 "last_accessed_at".to_string(),
1658 serde_json::json!(self.last_accessed_at),
1659 );
1660 meta.insert(
1661 "access_count".to_string(),
1662 serde_json::json!(self.access_count),
1663 );
1664 if let Some(ref ttl) = self.ttl_seconds {
1665 meta.insert("ttl_seconds".to_string(), serde_json::json!(ttl));
1666 }
1667 if let Some(ref expires) = self.expires_at {
1668 meta.insert("expires_at".to_string(), serde_json::json!(expires));
1669 }
1670 if let Some(ref user_meta) = self.metadata {
1671 meta.insert("user_metadata".to_string(), user_meta.clone());
1672 }
1673 serde_json::Value::Object(meta)
1674 }
1675
1676 pub fn to_vector(&self, embedding: Vec<f32>) -> Vector {
1678 let mut v = Vector {
1679 id: self.id.clone(),
1680 values: embedding,
1681 metadata: Some(self.to_vector_metadata()),
1682 ttl_seconds: self.ttl_seconds,
1683 expires_at: self.expires_at,
1684 };
1685 v.apply_ttl();
1686 v
1687 }
1688
1689 pub fn from_vector(vector: &Vector) -> Option<Self> {
1691 let meta = vector.metadata.as_ref()?.as_object()?;
1692 let entry_type = meta.get("_dakera_type")?.as_str()?;
1693 if entry_type != "memory" {
1694 return None;
1695 }
1696
1697 Some(Memory {
1698 id: vector.id.clone(),
1699 memory_type: serde_json::from_value(meta.get("memory_type")?.clone())
1700 .unwrap_or_default(),
1701 content: meta.get("content")?.as_str()?.to_string(),
1702 agent_id: meta.get("agent_id")?.as_str()?.to_string(),
1703 session_id: meta
1704 .get("session_id")
1705 .and_then(|v| v.as_str())
1706 .map(String::from),
1707 importance: meta
1708 .get("importance")
1709 .and_then(|v| v.as_f64())
1710 .unwrap_or(0.5) as f32,
1711 tags: meta
1712 .get("tags")
1713 .and_then(|v| serde_json::from_value(v.clone()).ok())
1714 .unwrap_or_default(),
1715 metadata: meta.get("user_metadata").cloned(),
1716 created_at: meta.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0),
1717 last_accessed_at: meta
1718 .get("last_accessed_at")
1719 .and_then(|v| v.as_u64())
1720 .unwrap_or(0),
1721 access_count: meta
1722 .get("access_count")
1723 .and_then(|v| v.as_u64())
1724 .unwrap_or(0) as u32,
1725 ttl_seconds: vector.ttl_seconds,
1726 expires_at: vector.expires_at,
1727 })
1728 }
1729}
1730
1731#[derive(Debug, Clone, Serialize, Deserialize)]
1733pub struct Session {
1734 pub id: String,
1735 pub agent_id: String,
1736 pub started_at: u64,
1737 #[serde(skip_serializing_if = "Option::is_none")]
1738 pub ended_at: Option<u64>,
1739 #[serde(skip_serializing_if = "Option::is_none")]
1740 pub summary: Option<String>,
1741 #[serde(skip_serializing_if = "Option::is_none")]
1742 pub metadata: Option<serde_json::Value>,
1743 #[serde(default)]
1745 pub memory_count: usize,
1746}
1747
1748impl Session {
1749 pub fn new(id: String, agent_id: String) -> Self {
1750 let now = std::time::SystemTime::now()
1751 .duration_since(std::time::UNIX_EPOCH)
1752 .unwrap_or_default()
1753 .as_secs();
1754 Self {
1755 id,
1756 agent_id,
1757 started_at: now,
1758 ended_at: None,
1759 summary: None,
1760 metadata: None,
1761 memory_count: 0,
1762 }
1763 }
1764
1765 pub fn to_vector_metadata(&self) -> serde_json::Value {
1767 let mut meta = serde_json::Map::new();
1768 meta.insert("_dakera_type".to_string(), serde_json::json!("session"));
1769 meta.insert("agent_id".to_string(), serde_json::json!(self.agent_id));
1770 meta.insert("started_at".to_string(), serde_json::json!(self.started_at));
1771 if let Some(ref ended) = self.ended_at {
1772 meta.insert("ended_at".to_string(), serde_json::json!(ended));
1773 }
1774 if let Some(ref summary) = self.summary {
1775 meta.insert("summary".to_string(), serde_json::json!(summary));
1776 }
1777 if let Some(ref user_meta) = self.metadata {
1778 meta.insert("user_metadata".to_string(), user_meta.clone());
1779 }
1780 meta.insert(
1781 "memory_count".to_string(),
1782 serde_json::json!(self.memory_count),
1783 );
1784 serde_json::Value::Object(meta)
1785 }
1786
1787 pub fn to_vector(&self, embedding: Vec<f32>) -> Vector {
1789 Vector {
1790 id: self.id.clone(),
1791 values: embedding,
1792 metadata: Some(self.to_vector_metadata()),
1793 ttl_seconds: None,
1794 expires_at: None,
1795 }
1796 }
1797
1798 pub fn from_vector(vector: &Vector) -> Option<Self> {
1800 let meta = vector.metadata.as_ref()?.as_object()?;
1801 let entry_type = meta.get("_dakera_type")?.as_str()?;
1802 if entry_type != "session" {
1803 return None;
1804 }
1805
1806 Some(Session {
1807 id: vector.id.clone(),
1808 agent_id: meta.get("agent_id")?.as_str()?.to_string(),
1809 started_at: meta.get("started_at").and_then(|v| v.as_u64()).unwrap_or(0),
1810 ended_at: meta.get("ended_at").and_then(|v| v.as_u64()),
1811 summary: meta
1812 .get("summary")
1813 .and_then(|v| v.as_str())
1814 .map(String::from),
1815 metadata: meta.get("user_metadata").cloned(),
1816 memory_count: meta
1817 .get("memory_count")
1818 .and_then(|v| v.as_u64())
1819 .unwrap_or(0) as usize,
1820 })
1821 }
1822}
1823
1824#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1826#[serde(rename_all = "snake_case")]
1827#[derive(Default)]
1828pub enum DecayStrategy {
1829 #[default]
1830 Exponential,
1831 Linear,
1832 StepFunction,
1833}
1834
1835#[derive(Debug, Clone, Serialize, Deserialize)]
1837pub struct DecayConfig {
1838 #[serde(default)]
1839 pub strategy: DecayStrategy,
1840 #[serde(default = "default_half_life_hours")]
1841 pub half_life_hours: f64,
1842 #[serde(default = "default_min_importance")]
1843 pub min_importance: f32,
1844}
1845
1846fn default_half_life_hours() -> f64 {
1847 168.0 }
1849
1850fn default_min_importance() -> f32 {
1851 0.01
1852}
1853
1854impl Default for DecayConfig {
1855 fn default() -> Self {
1856 Self {
1857 strategy: DecayStrategy::default(),
1858 half_life_hours: default_half_life_hours(),
1859 min_importance: default_min_importance(),
1860 }
1861 }
1862}
1863
1864#[derive(Debug, Deserialize)]
1870pub struct StoreMemoryRequest {
1871 pub content: String,
1872 pub agent_id: String,
1873 #[serde(default)]
1874 pub memory_type: MemoryType,
1875 #[serde(skip_serializing_if = "Option::is_none")]
1876 pub session_id: Option<String>,
1877 #[serde(default = "default_importance")]
1878 pub importance: f32,
1879 #[serde(default)]
1880 pub tags: Vec<String>,
1881 #[serde(skip_serializing_if = "Option::is_none")]
1882 pub metadata: Option<serde_json::Value>,
1883 #[serde(skip_serializing_if = "Option::is_none")]
1884 pub ttl_seconds: Option<u64>,
1885 #[serde(skip_serializing_if = "Option::is_none")]
1890 pub expires_at: Option<u64>,
1891 #[serde(skip_serializing_if = "Option::is_none")]
1893 pub id: Option<String>,
1894}
1895
1896#[derive(Debug, Serialize)]
1898pub struct StoreMemoryResponse {
1899 pub memory: Memory,
1900 pub embedding_time_ms: u64,
1901}
1902
1903#[derive(Debug, Deserialize)]
1905pub struct RecallRequest {
1906 pub query: String,
1907 pub agent_id: String,
1908 #[serde(default = "default_top_k")]
1909 pub top_k: usize,
1910 #[serde(default)]
1911 pub memory_type: Option<MemoryType>,
1912 #[serde(default)]
1913 pub session_id: Option<String>,
1914 #[serde(default)]
1915 pub tags: Option<Vec<String>>,
1916 #[serde(default)]
1917 pub min_importance: Option<f32>,
1918 #[serde(default = "default_true")]
1920 pub importance_weighted: bool,
1921}
1922
1923#[derive(Debug, Serialize, Deserialize)]
1925pub struct RecallResult {
1926 pub memory: Memory,
1927 pub score: f32,
1928 #[serde(skip_serializing_if = "Option::is_none")]
1930 pub weighted_score: Option<f32>,
1931 #[serde(skip_serializing_if = "Option::is_none")]
1933 pub smart_score: Option<f32>,
1934}
1935
1936#[derive(Debug, Serialize)]
1938pub struct RecallResponse {
1939 pub memories: Vec<RecallResult>,
1940 pub query_embedding_time_ms: u64,
1941 pub search_time_ms: u64,
1942}
1943
1944#[derive(Debug, Deserialize)]
1946pub struct ForgetRequest {
1947 pub agent_id: String,
1948 #[serde(default)]
1949 pub memory_ids: Option<Vec<String>>,
1950 #[serde(default)]
1951 pub memory_type: Option<MemoryType>,
1952 #[serde(default)]
1953 pub session_id: Option<String>,
1954 #[serde(default)]
1955 pub tags: Option<Vec<String>>,
1956 #[serde(default)]
1958 pub below_importance: Option<f32>,
1959}
1960
1961#[derive(Debug, Serialize)]
1963pub struct ForgetResponse {
1964 pub deleted_count: usize,
1965}
1966
1967#[derive(Debug, Deserialize)]
1969pub struct UpdateMemoryRequest {
1970 #[serde(default)]
1971 pub content: Option<String>,
1972 #[serde(default)]
1973 pub importance: Option<f32>,
1974 #[serde(default)]
1975 pub tags: Option<Vec<String>>,
1976 #[serde(default)]
1977 pub metadata: Option<serde_json::Value>,
1978 #[serde(default)]
1979 pub memory_type: Option<MemoryType>,
1980}
1981
1982#[derive(Debug, Deserialize)]
1984pub struct UpdateImportanceRequest {
1985 pub memory_id: String,
1986 pub importance: f32,
1987 pub agent_id: String,
1988}
1989
1990#[derive(Debug, Deserialize)]
1992pub struct ConsolidateRequest {
1993 pub agent_id: String,
1994 #[serde(default)]
1996 pub memory_ids: Option<Vec<String>>,
1997 #[serde(default = "default_consolidation_threshold")]
1999 pub threshold: f32,
2000 #[serde(default)]
2002 pub target_type: Option<MemoryType>,
2003}
2004
2005fn default_consolidation_threshold() -> f32 {
2006 0.85
2007}
2008
2009#[derive(Debug, Serialize)]
2011pub struct ConsolidateResponse {
2012 pub consolidated_memory: Memory,
2013 pub source_memory_ids: Vec<String>,
2014 pub memories_removed: usize,
2015}
2016
2017#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
2019#[serde(rename_all = "lowercase")]
2020pub enum FeedbackSignal {
2021 Positive,
2022 Negative,
2023}
2024
2025#[derive(Debug, Deserialize)]
2027pub struct FeedbackRequest {
2028 pub agent_id: String,
2029 pub memory_id: String,
2030 pub signal: FeedbackSignal,
2031}
2032
2033#[derive(Debug, Serialize)]
2035pub struct FeedbackResponse {
2036 pub memory_id: String,
2037 pub new_importance: f32,
2038 pub signal: FeedbackSignal,
2039}
2040
2041#[derive(Debug, Deserialize)]
2043pub struct SearchMemoriesRequest {
2044 pub agent_id: String,
2045 #[serde(default)]
2046 pub query: Option<String>,
2047 #[serde(default)]
2048 pub memory_type: Option<MemoryType>,
2049 #[serde(default)]
2050 pub session_id: Option<String>,
2051 #[serde(default)]
2052 pub tags: Option<Vec<String>>,
2053 #[serde(default)]
2054 pub min_importance: Option<f32>,
2055 #[serde(default)]
2056 pub max_importance: Option<f32>,
2057 #[serde(default)]
2058 pub created_after: Option<u64>,
2059 #[serde(default)]
2060 pub created_before: Option<u64>,
2061 #[serde(default = "default_top_k")]
2062 pub top_k: usize,
2063 #[serde(default)]
2064 pub sort_by: Option<MemorySortField>,
2065}
2066
2067#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
2069#[serde(rename_all = "snake_case")]
2070pub enum MemorySortField {
2071 CreatedAt,
2072 LastAccessedAt,
2073 Importance,
2074 AccessCount,
2075}
2076
2077#[derive(Debug, Serialize)]
2079pub struct SearchMemoriesResponse {
2080 pub memories: Vec<RecallResult>,
2081 pub total_count: usize,
2082}
2083
2084#[derive(Debug, Deserialize)]
2090pub struct SessionStartRequest {
2091 pub agent_id: String,
2092 #[serde(skip_serializing_if = "Option::is_none")]
2093 pub metadata: Option<serde_json::Value>,
2094 #[serde(skip_serializing_if = "Option::is_none")]
2096 pub id: Option<String>,
2097}
2098
2099#[derive(Debug, Serialize)]
2101pub struct SessionStartResponse {
2102 pub session: Session,
2103}
2104
2105#[derive(Debug, Deserialize)]
2107pub struct SessionEndRequest {
2108 #[serde(default)]
2109 pub summary: Option<String>,
2110 #[serde(default)]
2112 pub auto_summarize: bool,
2113}
2114
2115#[derive(Debug, Serialize)]
2117pub struct SessionEndResponse {
2118 pub session: Session,
2119 pub memory_count: usize,
2120}
2121
2122#[derive(Debug, Serialize)]
2124pub struct ListSessionsResponse {
2125 pub sessions: Vec<Session>,
2126 pub total: usize,
2127}
2128
2129#[derive(Debug, Serialize)]
2131pub struct SessionMemoriesResponse {
2132 pub session: Session,
2133 pub memories: Vec<Memory>,
2134 #[serde(skip_serializing_if = "Option::is_none")]
2136 pub total: Option<usize>,
2137}
2138
2139#[derive(Debug, Serialize, Deserialize, Clone)]
2145pub struct AgentSummary {
2146 pub agent_id: String,
2147 pub memory_count: usize,
2148 pub session_count: usize,
2149 pub active_sessions: usize,
2150}
2151
2152#[derive(Debug, Serialize)]
2154pub struct AgentStats {
2155 pub agent_id: String,
2156 pub total_memories: usize,
2157 pub memories_by_type: std::collections::HashMap<String, usize>,
2158 pub total_sessions: usize,
2159 pub active_sessions: usize,
2160 pub avg_importance: f32,
2161 pub oldest_memory_at: Option<u64>,
2162 pub newest_memory_at: Option<u64>,
2163}
2164
2165#[derive(Debug, Deserialize)]
2167pub struct KnowledgeGraphRequest {
2168 pub agent_id: String,
2169 pub memory_id: String,
2170 #[serde(default = "default_graph_depth")]
2171 pub depth: usize,
2172 #[serde(default = "default_graph_min_similarity")]
2173 pub min_similarity: f32,
2174}
2175
2176fn default_graph_depth() -> usize {
2177 2
2178}
2179
2180fn default_graph_min_similarity() -> f32 {
2181 0.7
2182}
2183
2184#[derive(Debug, Serialize)]
2186pub struct KnowledgeGraphNode {
2187 pub memory: Memory,
2188 pub similarity: f32,
2189 pub related: Vec<KnowledgeGraphEdge>,
2190}
2191
2192#[derive(Debug, Serialize)]
2194pub struct KnowledgeGraphEdge {
2195 pub memory_id: String,
2196 pub similarity: f32,
2197 pub shared_tags: Vec<String>,
2198}
2199
2200#[derive(Debug, Serialize)]
2202pub struct KnowledgeGraphResponse {
2203 pub root: KnowledgeGraphNode,
2204 pub total_nodes: usize,
2205}
2206
2207fn default_full_graph_max_nodes() -> usize {
2212 200
2213}
2214
2215fn default_full_graph_min_similarity() -> f32 {
2216 0.50
2217}
2218
2219fn default_full_graph_cluster_threshold() -> f32 {
2220 0.60
2221}
2222
2223fn default_full_graph_max_edges_per_node() -> usize {
2224 8
2225}
2226
2227#[derive(Debug, Deserialize)]
2229pub struct FullKnowledgeGraphRequest {
2230 pub agent_id: String,
2231 #[serde(default = "default_full_graph_max_nodes")]
2232 pub max_nodes: usize,
2233 #[serde(default = "default_full_graph_min_similarity")]
2234 pub min_similarity: f32,
2235 #[serde(default = "default_full_graph_cluster_threshold")]
2236 pub cluster_threshold: f32,
2237 #[serde(default = "default_full_graph_max_edges_per_node")]
2238 pub max_edges_per_node: usize,
2239}
2240
2241#[derive(Debug, Serialize)]
2243pub struct FullGraphNode {
2244 pub id: String,
2245 pub content: String,
2246 pub memory_type: String,
2247 pub importance: f32,
2248 pub tags: Vec<String>,
2249 pub created_at: Option<String>,
2250 pub cluster_id: usize,
2251 pub centrality: f32,
2252}
2253
2254#[derive(Debug, Serialize)]
2256pub struct FullGraphEdge {
2257 pub source: String,
2258 pub target: String,
2259 pub similarity: f32,
2260 pub shared_tags: Vec<String>,
2261}
2262
2263#[derive(Debug, Serialize)]
2265pub struct GraphCluster {
2266 pub id: usize,
2267 pub node_count: usize,
2268 pub top_tags: Vec<String>,
2269 pub avg_importance: f32,
2270}
2271
2272#[derive(Debug, Serialize)]
2274pub struct GraphStats {
2275 pub total_memories: usize,
2276 pub included_memories: usize,
2277 pub total_edges: usize,
2278 pub cluster_count: usize,
2279 pub density: f32,
2280 pub hub_memory_id: Option<String>,
2281}
2282
2283#[derive(Debug, Serialize)]
2285pub struct FullKnowledgeGraphResponse {
2286 pub nodes: Vec<FullGraphNode>,
2287 pub edges: Vec<FullGraphEdge>,
2288 pub clusters: Vec<GraphCluster>,
2289 pub stats: GraphStats,
2290}
2291
2292#[derive(Debug, Deserialize)]
2294pub struct SummarizeRequest {
2295 pub agent_id: String,
2296 pub memory_ids: Vec<String>,
2297 #[serde(default)]
2298 pub target_type: Option<MemoryType>,
2299}
2300
2301#[derive(Debug, Serialize)]
2303pub struct SummarizeResponse {
2304 pub summary_memory: Memory,
2305 pub source_count: usize,
2306}
2307
2308#[derive(Debug, Deserialize)]
2310pub struct DeduplicateRequest {
2311 pub agent_id: String,
2312 #[serde(default = "default_dedup_threshold")]
2313 pub threshold: f32,
2314 #[serde(default)]
2315 pub memory_type: Option<MemoryType>,
2316 #[serde(default)]
2318 pub dry_run: bool,
2319}
2320
2321fn default_dedup_threshold() -> f32 {
2322 0.92
2323}
2324
2325#[derive(Debug, Serialize)]
2327pub struct DuplicateGroup {
2328 pub canonical_id: String,
2329 pub duplicate_ids: Vec<String>,
2330 pub avg_similarity: f32,
2331}
2332
2333#[derive(Debug, Serialize)]
2335pub struct DeduplicateResponse {
2336 pub groups: Vec<DuplicateGroup>,
2337 pub duplicates_found: usize,
2338 pub duplicates_merged: usize,
2339}
2340
2341fn default_cross_agent_min_similarity() -> f32 {
2346 0.3
2347}
2348
2349fn default_cross_agent_max_nodes_per_agent() -> usize {
2350 50
2351}
2352
2353fn default_cross_agent_max_cross_edges() -> usize {
2354 200
2355}
2356
2357#[derive(Debug, Deserialize)]
2359pub struct CrossAgentNetworkRequest {
2360 #[serde(default)]
2362 pub agent_ids: Option<Vec<String>>,
2363 #[serde(default = "default_cross_agent_min_similarity")]
2365 pub min_similarity: f32,
2366 #[serde(default = "default_cross_agent_max_nodes_per_agent")]
2368 pub max_nodes_per_agent: usize,
2369 #[serde(default)]
2371 pub min_importance: f32,
2372 #[serde(default = "default_cross_agent_max_cross_edges")]
2374 pub max_cross_edges: usize,
2375}
2376
2377#[derive(Debug, Serialize)]
2379pub struct AgentNetworkInfo {
2380 pub agent_id: String,
2381 pub memory_count: usize,
2382 pub avg_importance: f32,
2383}
2384
2385#[derive(Debug, Serialize)]
2387pub struct AgentNetworkNode {
2388 pub id: String,
2389 pub agent_id: String,
2390 pub content: String,
2391 pub importance: f32,
2392 pub tags: Vec<String>,
2393 pub memory_type: String,
2394 pub created_at: u64,
2395}
2396
2397#[derive(Debug, Serialize)]
2399pub struct AgentNetworkEdge {
2400 pub source: String,
2401 pub target: String,
2402 pub source_agent: String,
2403 pub target_agent: String,
2404 pub similarity: f32,
2405}
2406
2407#[derive(Debug, Serialize)]
2409pub struct AgentNetworkStats {
2410 pub total_agents: usize,
2411 pub total_nodes: usize,
2412 pub total_cross_edges: usize,
2413 pub density: f32,
2414}
2415
2416#[derive(Debug, Serialize)]
2418pub struct CrossAgentNetworkResponse {
2419 pub node_count: usize,
2420 pub agents: Vec<AgentNetworkInfo>,
2421 pub nodes: Vec<AgentNetworkNode>,
2422 pub edges: Vec<AgentNetworkEdge>,
2423 pub stats: AgentNetworkStats,
2424}
2425
2426#[derive(Debug, Deserialize, Default)]
2434pub struct BatchMemoryFilter {
2435 #[serde(default)]
2437 pub tags: Option<Vec<String>>,
2438 #[serde(default)]
2440 pub min_importance: Option<f32>,
2441 #[serde(default)]
2443 pub max_importance: Option<f32>,
2444 #[serde(default)]
2446 pub created_after: Option<u64>,
2447 #[serde(default)]
2449 pub created_before: Option<u64>,
2450 #[serde(default)]
2452 pub memory_type: Option<MemoryType>,
2453 #[serde(default)]
2455 pub session_id: Option<String>,
2456}
2457
2458impl BatchMemoryFilter {
2459 pub fn has_any(&self) -> bool {
2461 self.tags.is_some()
2462 || self.min_importance.is_some()
2463 || self.max_importance.is_some()
2464 || self.created_after.is_some()
2465 || self.created_before.is_some()
2466 || self.memory_type.is_some()
2467 || self.session_id.is_some()
2468 }
2469
2470 pub fn matches(&self, memory: &Memory) -> bool {
2472 if let Some(ref tags) = self.tags {
2473 if !tags.is_empty() && !tags.iter().all(|t| memory.tags.contains(t)) {
2474 return false;
2475 }
2476 }
2477 if let Some(min) = self.min_importance {
2478 if memory.importance < min {
2479 return false;
2480 }
2481 }
2482 if let Some(max) = self.max_importance {
2483 if memory.importance > max {
2484 return false;
2485 }
2486 }
2487 if let Some(after) = self.created_after {
2488 if memory.created_at < after {
2489 return false;
2490 }
2491 }
2492 if let Some(before) = self.created_before {
2493 if memory.created_at > before {
2494 return false;
2495 }
2496 }
2497 if let Some(ref mt) = self.memory_type {
2498 if memory.memory_type != *mt {
2499 return false;
2500 }
2501 }
2502 if let Some(ref sid) = self.session_id {
2503 if memory.session_id.as_ref() != Some(sid) {
2504 return false;
2505 }
2506 }
2507 true
2508 }
2509}
2510
2511#[derive(Debug, Deserialize)]
2513pub struct BatchRecallRequest {
2514 pub agent_id: String,
2516 #[serde(default)]
2518 pub filter: BatchMemoryFilter,
2519 #[serde(default = "default_batch_limit")]
2521 pub limit: usize,
2522}
2523
2524fn default_batch_limit() -> usize {
2525 100
2526}
2527
2528#[derive(Debug, Serialize)]
2530pub struct BatchRecallResponse {
2531 pub memories: Vec<Memory>,
2532 pub total: usize,
2533 pub filtered: usize,
2534}
2535
2536#[derive(Debug, Deserialize)]
2538pub struct BatchForgetRequest {
2539 pub agent_id: String,
2541 pub filter: BatchMemoryFilter,
2543}
2544
2545#[derive(Debug, Serialize)]
2547pub struct BatchForgetResponse {
2548 pub deleted_count: usize,
2549}