1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone)]
12pub struct RetryConfig {
13 pub max_retries: u32,
15 pub base_delay: std::time::Duration,
17 pub max_delay: std::time::Duration,
19 pub jitter: bool,
21}
22
23impl Default for RetryConfig {
24 fn default() -> Self {
25 Self {
26 max_retries: 3,
27 base_delay: std::time::Duration::from_millis(100),
28 max_delay: std::time::Duration::from_secs(60),
29 jitter: true,
30 }
31 }
32}
33
34#[derive(Debug, Clone, Default)]
43pub struct RateLimitHeaders {
44 pub limit: Option<u64>,
46 pub remaining: Option<u64>,
48 pub reset: Option<u64>,
50 pub quota_used: Option<u64>,
52 pub quota_limit: Option<u64>,
54}
55
56impl RateLimitHeaders {
57 pub fn from_response(response: &reqwest::Response) -> Self {
59 let headers = response.headers();
60 fn parse(h: &reqwest::header::HeaderMap, name: &str) -> Option<u64> {
61 h.get(name)
62 .and_then(|v| v.to_str().ok())
63 .and_then(|s| s.parse().ok())
64 }
65 Self {
66 limit: parse(headers, "X-RateLimit-Limit"),
67 remaining: parse(headers, "X-RateLimit-Remaining"),
68 reset: parse(headers, "X-RateLimit-Reset"),
69 quota_used: parse(headers, "X-Quota-Used"),
70 quota_limit: parse(headers, "X-Quota-Limit"),
71 }
72 }
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct HealthResponse {
82 pub healthy: bool,
84 pub version: Option<String>,
86 pub uptime_seconds: Option<u64>,
88 pub build_sha: Option<String>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct ReadinessResponse {
95 pub ready: bool,
97 pub components: Option<HashMap<String, bool>>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct NamespaceInfo {
108 #[serde(alias = "namespace")]
110 pub name: String,
111 #[serde(default)]
113 pub vector_count: u64,
114 #[serde(alias = "dimension")]
116 pub dimensions: Option<u32>,
117 pub index_type: Option<String>,
119 #[serde(default)]
121 pub created: Option<bool>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct ListNamespacesResponse {
127 pub namespaces: Vec<String>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct Vector {
138 pub id: String,
140 pub values: Vec<f32>,
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub metadata: Option<HashMap<String, serde_json::Value>>,
145}
146
147impl Vector {
148 pub fn new(id: impl Into<String>, values: Vec<f32>) -> Self {
150 Self {
151 id: id.into(),
152 values,
153 metadata: None,
154 }
155 }
156
157 pub fn with_metadata(
159 id: impl Into<String>,
160 values: Vec<f32>,
161 metadata: HashMap<String, serde_json::Value>,
162 ) -> Self {
163 Self {
164 id: id.into(),
165 values,
166 metadata: Some(metadata),
167 }
168 }
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct UpsertRequest {
174 pub vectors: Vec<Vector>,
176}
177
178impl UpsertRequest {
179 pub fn single(vector: Vector) -> Self {
181 Self {
182 vectors: vec![vector],
183 }
184 }
185
186 pub fn batch(vectors: Vec<Vector>) -> Self {
188 Self { vectors }
189 }
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct UpsertResponse {
195 pub upserted_count: u64,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct ColumnUpsertRequest {
217 pub ids: Vec<String>,
219 pub vectors: Vec<Vec<f32>>,
221 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
224 pub attributes: HashMap<String, Vec<serde_json::Value>>,
225 #[serde(skip_serializing_if = "Option::is_none")]
227 pub ttl_seconds: Option<u64>,
228 #[serde(skip_serializing_if = "Option::is_none")]
230 pub dimension: Option<usize>,
231}
232
233impl ColumnUpsertRequest {
234 pub fn new(ids: Vec<String>, vectors: Vec<Vec<f32>>) -> Self {
236 Self {
237 ids,
238 vectors,
239 attributes: HashMap::new(),
240 ttl_seconds: None,
241 dimension: None,
242 }
243 }
244
245 pub fn with_attribute(
247 mut self,
248 name: impl Into<String>,
249 values: Vec<serde_json::Value>,
250 ) -> Self {
251 self.attributes.insert(name.into(), values);
252 self
253 }
254
255 pub fn with_ttl(mut self, seconds: u64) -> Self {
257 self.ttl_seconds = Some(seconds);
258 self
259 }
260
261 pub fn with_dimension(mut self, dim: usize) -> Self {
263 self.dimension = Some(dim);
264 self
265 }
266
267 pub fn len(&self) -> usize {
269 self.ids.len()
270 }
271
272 pub fn is_empty(&self) -> bool {
274 self.ids.is_empty()
275 }
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize)]
280pub struct DeleteRequest {
281 pub ids: Vec<String>,
283}
284
285impl DeleteRequest {
286 pub fn single(id: impl Into<String>) -> Self {
288 Self {
289 ids: vec![id.into()],
290 }
291 }
292
293 pub fn batch(ids: Vec<String>) -> Self {
295 Self { ids }
296 }
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct DeleteResponse {
302 pub deleted_count: u64,
304}
305
306#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
312#[serde(rename_all = "snake_case")]
313pub enum ReadConsistency {
314 Strong,
316 #[default]
318 Eventual,
319 #[serde(rename = "bounded_staleness")]
321 BoundedStaleness,
322}
323
324#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
326pub struct StalenessConfig {
327 #[serde(default = "default_max_staleness_ms")]
329 pub max_staleness_ms: u64,
330}
331
332fn default_max_staleness_ms() -> u64 {
333 5000 }
335
336impl StalenessConfig {
337 pub fn new(max_staleness_ms: u64) -> Self {
339 Self { max_staleness_ms }
340 }
341}
342
343#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
345#[serde(rename_all = "snake_case")]
346pub enum DistanceMetric {
347 #[default]
349 Cosine,
350 Euclidean,
352 DotProduct,
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct QueryRequest {
359 pub vector: Vec<f32>,
361 pub top_k: u32,
363 #[serde(default)]
365 pub distance_metric: DistanceMetric,
366 #[serde(skip_serializing_if = "Option::is_none")]
368 pub filter: Option<serde_json::Value>,
369 #[serde(default = "default_true")]
371 pub include_metadata: bool,
372 #[serde(default)]
374 pub include_vectors: bool,
375 #[serde(default)]
377 pub consistency: ReadConsistency,
378 #[serde(skip_serializing_if = "Option::is_none")]
380 pub staleness_config: Option<StalenessConfig>,
381}
382
383fn default_true() -> bool {
384 true
385}
386
387impl QueryRequest {
388 pub fn new(vector: Vec<f32>, top_k: u32) -> Self {
390 Self {
391 vector,
392 top_k,
393 distance_metric: DistanceMetric::default(),
394 filter: None,
395 include_metadata: true,
396 include_vectors: false,
397 consistency: ReadConsistency::default(),
398 staleness_config: None,
399 }
400 }
401
402 pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
404 self.filter = Some(filter);
405 self
406 }
407
408 pub fn include_metadata(mut self, include: bool) -> Self {
410 self.include_metadata = include;
411 self
412 }
413
414 pub fn include_vectors(mut self, include: bool) -> Self {
416 self.include_vectors = include;
417 self
418 }
419
420 pub fn with_distance_metric(mut self, metric: DistanceMetric) -> Self {
422 self.distance_metric = metric;
423 self
424 }
425
426 pub fn with_consistency(mut self, consistency: ReadConsistency) -> Self {
428 self.consistency = consistency;
429 self
430 }
431
432 pub fn with_bounded_staleness(mut self, max_staleness_ms: u64) -> Self {
434 self.consistency = ReadConsistency::BoundedStaleness;
435 self.staleness_config = Some(StalenessConfig::new(max_staleness_ms));
436 self
437 }
438
439 pub fn with_strong_consistency(mut self) -> Self {
441 self.consistency = ReadConsistency::Strong;
442 self
443 }
444}
445
446#[derive(Debug, Clone, Serialize, Deserialize)]
448pub struct Match {
449 pub id: String,
451 pub score: f32,
453 #[serde(skip_serializing_if = "Option::is_none")]
455 pub metadata: Option<HashMap<String, serde_json::Value>>,
456}
457
458#[derive(Debug, Clone, Serialize, Deserialize)]
460pub struct QueryResponse {
461 #[serde(alias = "matches")]
463 pub results: Vec<Match>,
464}
465
466#[derive(Debug, Clone, Serialize, Deserialize)]
472pub struct Document {
473 pub id: String,
475 pub text: String,
477 #[serde(skip_serializing_if = "Option::is_none")]
479 pub metadata: Option<HashMap<String, serde_json::Value>>,
480}
481
482impl Document {
483 pub fn new(id: impl Into<String>, text: impl Into<String>) -> Self {
485 Self {
486 id: id.into(),
487 text: text.into(),
488 metadata: None,
489 }
490 }
491
492 pub fn with_metadata(
494 id: impl Into<String>,
495 text: impl Into<String>,
496 metadata: HashMap<String, serde_json::Value>,
497 ) -> Self {
498 Self {
499 id: id.into(),
500 text: text.into(),
501 metadata: Some(metadata),
502 }
503 }
504}
505
506#[derive(Debug, Clone, Serialize, Deserialize)]
508pub struct IndexDocumentsRequest {
509 pub documents: Vec<Document>,
511}
512
513#[derive(Debug, Clone, Serialize, Deserialize)]
515pub struct IndexDocumentsResponse {
516 pub indexed_count: u64,
518}
519
520#[derive(Debug, Clone, Serialize, Deserialize)]
522pub struct FullTextSearchRequest {
523 pub query: String,
525 pub top_k: u32,
527 #[serde(skip_serializing_if = "Option::is_none")]
529 pub filter: Option<serde_json::Value>,
530}
531
532impl FullTextSearchRequest {
533 pub fn new(query: impl Into<String>, top_k: u32) -> Self {
535 Self {
536 query: query.into(),
537 top_k,
538 filter: None,
539 }
540 }
541
542 pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
544 self.filter = Some(filter);
545 self
546 }
547}
548
549#[derive(Debug, Clone, Serialize, Deserialize)]
551pub struct FullTextMatch {
552 pub id: String,
554 pub score: f32,
556 #[serde(skip_serializing_if = "Option::is_none")]
558 pub text: Option<String>,
559 #[serde(skip_serializing_if = "Option::is_none")]
561 pub metadata: Option<HashMap<String, serde_json::Value>>,
562}
563
564#[derive(Debug, Clone, Serialize, Deserialize)]
566pub struct FullTextSearchResponse {
567 #[serde(alias = "matches")]
569 pub results: Vec<FullTextMatch>,
570}
571
572#[derive(Debug, Clone, Serialize, Deserialize)]
574pub struct FullTextStats {
575 pub document_count: u64,
577 pub term_count: u64,
579}
580
581#[derive(Debug, Clone, Serialize, Deserialize)]
590pub struct HybridSearchRequest {
591 #[serde(skip_serializing_if = "Option::is_none")]
593 pub vector: Option<Vec<f32>>,
594 pub text: String,
596 pub top_k: u32,
598 #[serde(default = "default_vector_weight")]
600 pub vector_weight: f32,
601 #[serde(skip_serializing_if = "Option::is_none")]
603 pub filter: Option<serde_json::Value>,
604}
605
606fn default_vector_weight() -> f32 {
607 0.5
608}
609
610impl HybridSearchRequest {
611 pub fn new(vector: Vec<f32>, text: impl Into<String>, top_k: u32) -> Self {
613 Self {
614 vector: Some(vector),
615 text: text.into(),
616 top_k,
617 vector_weight: 0.5,
618 filter: None,
619 }
620 }
621
622 pub fn text_only(text: impl Into<String>, top_k: u32) -> Self {
624 Self {
625 vector: None,
626 text: text.into(),
627 top_k,
628 vector_weight: 0.5,
629 filter: None,
630 }
631 }
632
633 pub fn with_vector_weight(mut self, weight: f32) -> Self {
635 self.vector_weight = weight.clamp(0.0, 1.0);
636 self
637 }
638
639 pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
641 self.filter = Some(filter);
642 self
643 }
644}
645
646#[derive(Debug, Clone, Serialize, Deserialize)]
648pub struct HybridSearchResponse {
649 #[serde(alias = "matches")]
651 pub results: Vec<Match>,
652}
653
654#[derive(Debug, Clone, Serialize, Deserialize)]
660pub struct SystemDiagnostics {
661 pub system: SystemInfo,
663 pub resources: ResourceUsage,
665 pub components: ComponentHealth,
667 pub active_jobs: u64,
669}
670
671#[derive(Debug, Clone, Serialize, Deserialize)]
673pub struct SystemInfo {
674 pub version: String,
676 pub rust_version: String,
678 pub uptime_seconds: u64,
680 pub pid: u32,
682}
683
684#[derive(Debug, Clone, Serialize, Deserialize)]
686pub struct ResourceUsage {
687 pub memory_bytes: u64,
689 pub thread_count: u64,
691 pub open_fds: u64,
693 pub cpu_percent: Option<f64>,
695}
696
697#[derive(Debug, Clone, Serialize, Deserialize)]
699pub struct ComponentHealth {
700 pub storage: HealthStatus,
702 pub search_engine: HealthStatus,
704 pub cache: HealthStatus,
706 pub grpc: HealthStatus,
708}
709
710#[derive(Debug, Clone, Serialize, Deserialize)]
712pub struct HealthStatus {
713 pub healthy: bool,
715 pub message: String,
717 pub last_check: u64,
719}
720
721#[derive(Debug, Clone, Serialize, Deserialize)]
723pub struct JobInfo {
724 pub id: String,
726 pub job_type: String,
728 pub status: String,
730 pub created_at: u64,
732 #[serde(skip_serializing_if = "Option::is_none")]
734 pub started_at: Option<u64>,
735 #[serde(skip_serializing_if = "Option::is_none")]
737 pub completed_at: Option<u64>,
738 pub progress: u8,
740 #[serde(skip_serializing_if = "Option::is_none")]
742 pub message: Option<String>,
743 #[serde(default)]
745 pub metadata: std::collections::HashMap<String, String>,
746}
747
748#[derive(Debug, Clone, Serialize, Deserialize)]
750pub struct CompactionRequest {
751 #[serde(skip_serializing_if = "Option::is_none")]
753 pub namespace: Option<String>,
754 #[serde(default)]
756 pub force: bool,
757}
758
759#[derive(Debug, Clone, Serialize, Deserialize)]
761pub struct CompactionResponse {
762 pub job_id: String,
764 pub message: String,
766}
767
768#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
774#[serde(rename_all = "snake_case")]
775pub enum WarmingPriority {
776 Critical,
778 High,
780 #[default]
782 Normal,
783 Low,
785 Background,
787}
788
789#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
791#[serde(rename_all = "snake_case")]
792pub enum WarmingTargetTier {
793 L1,
795 #[default]
797 L2,
798 Both,
800}
801
802#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
804#[serde(rename_all = "snake_case")]
805pub enum AccessPatternHint {
806 #[default]
808 Random,
809 Sequential,
811 Temporal,
813 Spatial,
815}
816
817#[derive(Debug, Clone, Serialize, Deserialize)]
819pub struct WarmCacheRequest {
820 pub namespace: String,
822 #[serde(skip_serializing_if = "Option::is_none")]
824 pub vector_ids: Option<Vec<String>>,
825 #[serde(default)]
827 pub priority: WarmingPriority,
828 #[serde(default)]
830 pub target_tier: WarmingTargetTier,
831 #[serde(default)]
833 pub background: bool,
834 #[serde(skip_serializing_if = "Option::is_none")]
836 pub ttl_hint_seconds: Option<u64>,
837 #[serde(default)]
839 pub access_pattern: AccessPatternHint,
840 #[serde(skip_serializing_if = "Option::is_none")]
842 pub max_vectors: Option<usize>,
843}
844
845impl WarmCacheRequest {
846 pub fn new(namespace: impl Into<String>) -> Self {
848 Self {
849 namespace: namespace.into(),
850 vector_ids: None,
851 priority: WarmingPriority::default(),
852 target_tier: WarmingTargetTier::default(),
853 background: false,
854 ttl_hint_seconds: None,
855 access_pattern: AccessPatternHint::default(),
856 max_vectors: None,
857 }
858 }
859
860 pub fn with_vector_ids(mut self, ids: Vec<String>) -> Self {
862 self.vector_ids = Some(ids);
863 self
864 }
865
866 pub fn with_priority(mut self, priority: WarmingPriority) -> Self {
868 self.priority = priority;
869 self
870 }
871
872 pub fn with_target_tier(mut self, tier: WarmingTargetTier) -> Self {
874 self.target_tier = tier;
875 self
876 }
877
878 pub fn in_background(mut self) -> Self {
880 self.background = true;
881 self
882 }
883
884 pub fn with_ttl(mut self, seconds: u64) -> Self {
886 self.ttl_hint_seconds = Some(seconds);
887 self
888 }
889
890 pub fn with_access_pattern(mut self, pattern: AccessPatternHint) -> Self {
892 self.access_pattern = pattern;
893 self
894 }
895
896 pub fn with_max_vectors(mut self, max: usize) -> Self {
898 self.max_vectors = Some(max);
899 self
900 }
901}
902
903#[derive(Debug, Clone, Serialize, Deserialize)]
905pub struct WarmCacheResponse {
906 pub success: bool,
908 pub entries_warmed: u64,
910 pub entries_skipped: u64,
912 #[serde(skip_serializing_if = "Option::is_none")]
914 pub job_id: Option<String>,
915 pub message: String,
917 #[serde(skip_serializing_if = "Option::is_none")]
919 pub estimated_completion: Option<String>,
920 pub target_tier: WarmingTargetTier,
922 pub priority: WarmingPriority,
924 #[serde(skip_serializing_if = "Option::is_none")]
926 pub bytes_warmed: Option<u64>,
927}
928
929#[derive(Debug, Clone, Serialize, Deserialize)]
935pub struct ExportRequest {
936 #[serde(default = "default_export_top_k")]
938 pub top_k: usize,
939 #[serde(skip_serializing_if = "Option::is_none")]
941 pub cursor: Option<String>,
942 #[serde(default = "default_true")]
944 pub include_vectors: bool,
945 #[serde(default = "default_true")]
947 pub include_metadata: bool,
948}
949
950fn default_export_top_k() -> usize {
951 1000
952}
953
954impl Default for ExportRequest {
955 fn default() -> Self {
956 Self {
957 top_k: 1000,
958 cursor: None,
959 include_vectors: true,
960 include_metadata: true,
961 }
962 }
963}
964
965impl ExportRequest {
966 pub fn new() -> Self {
968 Self::default()
969 }
970
971 pub fn with_top_k(mut self, top_k: usize) -> Self {
973 self.top_k = top_k;
974 self
975 }
976
977 pub fn with_cursor(mut self, cursor: impl Into<String>) -> Self {
979 self.cursor = Some(cursor.into());
980 self
981 }
982
983 pub fn include_vectors(mut self, include: bool) -> Self {
985 self.include_vectors = include;
986 self
987 }
988
989 pub fn include_metadata(mut self, include: bool) -> Self {
991 self.include_metadata = include;
992 self
993 }
994}
995
996#[derive(Debug, Clone, Serialize, Deserialize)]
998pub struct ExportedVector {
999 pub id: String,
1001 #[serde(skip_serializing_if = "Option::is_none")]
1003 pub values: Option<Vec<f32>>,
1004 #[serde(skip_serializing_if = "Option::is_none")]
1006 pub metadata: Option<serde_json::Value>,
1007 #[serde(skip_serializing_if = "Option::is_none")]
1009 pub ttl_seconds: Option<u64>,
1010}
1011
1012#[derive(Debug, Clone, Serialize, Deserialize)]
1014pub struct ExportResponse {
1015 pub vectors: Vec<ExportedVector>,
1017 #[serde(skip_serializing_if = "Option::is_none")]
1019 pub next_cursor: Option<String>,
1020 pub total_count: usize,
1022 pub returned_count: usize,
1024}
1025
1026#[derive(Debug, Clone, Serialize, Deserialize)]
1032pub struct BatchQueryItem {
1033 #[serde(skip_serializing_if = "Option::is_none")]
1035 pub id: Option<String>,
1036 pub vector: Vec<f32>,
1038 #[serde(default = "default_batch_top_k")]
1040 pub top_k: u32,
1041 #[serde(skip_serializing_if = "Option::is_none")]
1043 pub filter: Option<serde_json::Value>,
1044 #[serde(default)]
1046 pub include_metadata: bool,
1047 #[serde(default)]
1049 pub consistency: ReadConsistency,
1050 #[serde(skip_serializing_if = "Option::is_none")]
1052 pub staleness_config: Option<StalenessConfig>,
1053}
1054
1055fn default_batch_top_k() -> u32 {
1056 10
1057}
1058
1059impl BatchQueryItem {
1060 pub fn new(vector: Vec<f32>, top_k: u32) -> Self {
1062 Self {
1063 id: None,
1064 vector,
1065 top_k,
1066 filter: None,
1067 include_metadata: true,
1068 consistency: ReadConsistency::default(),
1069 staleness_config: None,
1070 }
1071 }
1072
1073 pub fn with_id(mut self, id: impl Into<String>) -> Self {
1075 self.id = Some(id.into());
1076 self
1077 }
1078
1079 pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
1081 self.filter = Some(filter);
1082 self
1083 }
1084
1085 pub fn include_metadata(mut self, include: bool) -> Self {
1087 self.include_metadata = include;
1088 self
1089 }
1090
1091 pub fn with_consistency(mut self, consistency: ReadConsistency) -> Self {
1093 self.consistency = consistency;
1094 self
1095 }
1096
1097 pub fn with_bounded_staleness(mut self, max_staleness_ms: u64) -> Self {
1099 self.consistency = ReadConsistency::BoundedStaleness;
1100 self.staleness_config = Some(StalenessConfig::new(max_staleness_ms));
1101 self
1102 }
1103}
1104
1105#[derive(Debug, Clone, Serialize, Deserialize)]
1107pub struct BatchQueryRequest {
1108 pub queries: Vec<BatchQueryItem>,
1110}
1111
1112impl BatchQueryRequest {
1113 pub fn new(queries: Vec<BatchQueryItem>) -> Self {
1115 Self { queries }
1116 }
1117
1118 pub fn single(query: BatchQueryItem) -> Self {
1120 Self {
1121 queries: vec![query],
1122 }
1123 }
1124}
1125
1126#[derive(Debug, Clone, Serialize, Deserialize)]
1128pub struct BatchQueryResult {
1129 #[serde(skip_serializing_if = "Option::is_none")]
1131 pub id: Option<String>,
1132 pub results: Vec<Match>,
1134 pub latency_ms: f64,
1136 #[serde(skip_serializing_if = "Option::is_none")]
1138 pub error: Option<String>,
1139}
1140
1141#[derive(Debug, Clone, Serialize, Deserialize)]
1143pub struct BatchQueryResponse {
1144 pub results: Vec<BatchQueryResult>,
1146 pub total_latency_ms: f64,
1148 pub query_count: usize,
1150}
1151
1152#[derive(Debug, Clone, Serialize, Deserialize)]
1158pub struct MultiVectorSearchRequest {
1159 pub positive_vectors: Vec<Vec<f32>>,
1161 #[serde(skip_serializing_if = "Option::is_none")]
1163 pub positive_weights: Option<Vec<f32>>,
1164 #[serde(skip_serializing_if = "Option::is_none")]
1166 pub negative_vectors: Option<Vec<Vec<f32>>>,
1167 #[serde(skip_serializing_if = "Option::is_none")]
1169 pub negative_weights: Option<Vec<f32>>,
1170 #[serde(default = "default_multi_vector_top_k")]
1172 pub top_k: u32,
1173 #[serde(default)]
1175 pub distance_metric: DistanceMetric,
1176 #[serde(skip_serializing_if = "Option::is_none")]
1178 pub score_threshold: Option<f32>,
1179 #[serde(default)]
1181 pub enable_mmr: bool,
1182 #[serde(default = "default_mmr_lambda")]
1184 pub mmr_lambda: f32,
1185 #[serde(default = "default_true")]
1187 pub include_metadata: bool,
1188 #[serde(default)]
1190 pub include_vectors: bool,
1191 #[serde(skip_serializing_if = "Option::is_none")]
1193 pub filter: Option<serde_json::Value>,
1194 #[serde(default)]
1196 pub consistency: ReadConsistency,
1197 #[serde(skip_serializing_if = "Option::is_none")]
1199 pub staleness_config: Option<StalenessConfig>,
1200}
1201
1202fn default_multi_vector_top_k() -> u32 {
1203 10
1204}
1205
1206fn default_mmr_lambda() -> f32 {
1207 0.5
1208}
1209
1210impl MultiVectorSearchRequest {
1211 pub fn new(positive_vectors: Vec<Vec<f32>>) -> Self {
1213 Self {
1214 positive_vectors,
1215 positive_weights: None,
1216 negative_vectors: None,
1217 negative_weights: None,
1218 top_k: 10,
1219 distance_metric: DistanceMetric::default(),
1220 score_threshold: None,
1221 enable_mmr: false,
1222 mmr_lambda: 0.5,
1223 include_metadata: true,
1224 include_vectors: false,
1225 filter: None,
1226 consistency: ReadConsistency::default(),
1227 staleness_config: None,
1228 }
1229 }
1230
1231 pub fn with_top_k(mut self, top_k: u32) -> Self {
1233 self.top_k = top_k;
1234 self
1235 }
1236
1237 pub fn with_positive_weights(mut self, weights: Vec<f32>) -> Self {
1239 self.positive_weights = Some(weights);
1240 self
1241 }
1242
1243 pub fn with_negative_vectors(mut self, vectors: Vec<Vec<f32>>) -> Self {
1245 self.negative_vectors = Some(vectors);
1246 self
1247 }
1248
1249 pub fn with_negative_weights(mut self, weights: Vec<f32>) -> Self {
1251 self.negative_weights = Some(weights);
1252 self
1253 }
1254
1255 pub fn with_distance_metric(mut self, metric: DistanceMetric) -> Self {
1257 self.distance_metric = metric;
1258 self
1259 }
1260
1261 pub fn with_score_threshold(mut self, threshold: f32) -> Self {
1263 self.score_threshold = Some(threshold);
1264 self
1265 }
1266
1267 pub fn with_mmr(mut self, lambda: f32) -> Self {
1269 self.enable_mmr = true;
1270 self.mmr_lambda = lambda.clamp(0.0, 1.0);
1271 self
1272 }
1273
1274 pub fn include_metadata(mut self, include: bool) -> Self {
1276 self.include_metadata = include;
1277 self
1278 }
1279
1280 pub fn include_vectors(mut self, include: bool) -> Self {
1282 self.include_vectors = include;
1283 self
1284 }
1285
1286 pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
1288 self.filter = Some(filter);
1289 self
1290 }
1291
1292 pub fn with_consistency(mut self, consistency: ReadConsistency) -> Self {
1294 self.consistency = consistency;
1295 self
1296 }
1297}
1298
1299#[derive(Debug, Clone, Serialize, Deserialize)]
1301pub struct MultiVectorSearchResult {
1302 pub id: String,
1304 pub score: f32,
1306 #[serde(skip_serializing_if = "Option::is_none")]
1308 pub mmr_score: Option<f32>,
1309 #[serde(skip_serializing_if = "Option::is_none")]
1311 pub original_rank: Option<usize>,
1312 #[serde(skip_serializing_if = "Option::is_none")]
1314 pub metadata: Option<HashMap<String, serde_json::Value>>,
1315 #[serde(skip_serializing_if = "Option::is_none")]
1317 pub vector: Option<Vec<f32>>,
1318}
1319
1320#[derive(Debug, Clone, Serialize, Deserialize)]
1322pub struct MultiVectorSearchResponse {
1323 pub results: Vec<MultiVectorSearchResult>,
1325 #[serde(skip_serializing_if = "Option::is_none")]
1327 pub computed_query_vector: Option<Vec<f32>>,
1328}
1329
1330#[derive(Debug, Clone, Serialize, Deserialize)]
1336#[serde(untagged)]
1337pub enum AggregateFunction {
1338 Count,
1340 Sum { field: String },
1342 Avg { field: String },
1344 Min { field: String },
1346 Max { field: String },
1348}
1349
1350#[derive(Debug, Clone, Serialize, Deserialize)]
1352pub struct AggregationRequest {
1353 pub aggregate_by: HashMap<String, serde_json::Value>,
1356 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1359 pub group_by: Vec<String>,
1360 #[serde(skip_serializing_if = "Option::is_none")]
1362 pub filter: Option<serde_json::Value>,
1363 #[serde(default = "default_agg_limit")]
1365 pub limit: usize,
1366}
1367
1368fn default_agg_limit() -> usize {
1369 100
1370}
1371
1372impl AggregationRequest {
1373 pub fn new() -> Self {
1375 Self {
1376 aggregate_by: HashMap::new(),
1377 group_by: Vec::new(),
1378 filter: None,
1379 limit: 100,
1380 }
1381 }
1382
1383 pub fn with_count(mut self, name: impl Into<String>) -> Self {
1385 self.aggregate_by
1386 .insert(name.into(), serde_json::json!(["Count"]));
1387 self
1388 }
1389
1390 pub fn with_sum(mut self, name: impl Into<String>, field: impl Into<String>) -> Self {
1392 self.aggregate_by
1393 .insert(name.into(), serde_json::json!(["Sum", field.into()]));
1394 self
1395 }
1396
1397 pub fn with_avg(mut self, name: impl Into<String>, field: impl Into<String>) -> Self {
1399 self.aggregate_by
1400 .insert(name.into(), serde_json::json!(["Avg", field.into()]));
1401 self
1402 }
1403
1404 pub fn with_min(mut self, name: impl Into<String>, field: impl Into<String>) -> Self {
1406 self.aggregate_by
1407 .insert(name.into(), serde_json::json!(["Min", field.into()]));
1408 self
1409 }
1410
1411 pub fn with_max(mut self, name: impl Into<String>, field: impl Into<String>) -> Self {
1413 self.aggregate_by
1414 .insert(name.into(), serde_json::json!(["Max", field.into()]));
1415 self
1416 }
1417
1418 pub fn group_by(mut self, fields: Vec<String>) -> Self {
1420 self.group_by = fields;
1421 self
1422 }
1423
1424 pub fn with_group_by(mut self, field: impl Into<String>) -> Self {
1426 self.group_by.push(field.into());
1427 self
1428 }
1429
1430 pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
1432 self.filter = Some(filter);
1433 self
1434 }
1435
1436 pub fn with_limit(mut self, limit: usize) -> Self {
1438 self.limit = limit;
1439 self
1440 }
1441}
1442
1443impl Default for AggregationRequest {
1444 fn default() -> Self {
1445 Self::new()
1446 }
1447}
1448
1449#[derive(Debug, Clone, Serialize, Deserialize)]
1451pub struct AggregationResponse {
1452 #[serde(skip_serializing_if = "Option::is_none")]
1454 pub aggregations: Option<HashMap<String, serde_json::Value>>,
1455 #[serde(skip_serializing_if = "Option::is_none")]
1457 pub aggregation_groups: Option<Vec<AggregationGroup>>,
1458}
1459
1460#[derive(Debug, Clone, Serialize, Deserialize)]
1462pub struct AggregationGroup {
1463 #[serde(flatten)]
1465 pub group_key: HashMap<String, serde_json::Value>,
1466 #[serde(flatten)]
1468 pub aggregations: HashMap<String, serde_json::Value>,
1469}
1470
1471#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
1477pub enum VectorSearchMethod {
1478 #[default]
1480 ANN,
1481 #[serde(rename = "kNN")]
1483 KNN,
1484}
1485
1486#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
1488#[serde(rename_all = "lowercase")]
1489pub enum SortDirection {
1490 Asc,
1492 #[default]
1494 Desc,
1495}
1496
1497#[derive(Debug, Clone, Serialize, Deserialize)]
1500#[serde(untagged)]
1501pub enum RankBy {
1502 VectorSearch {
1504 field: String,
1505 method: VectorSearchMethod,
1506 query_vector: Vec<f32>,
1507 },
1508 FullTextSearch {
1510 field: String,
1511 method: String, query: String,
1513 },
1514 AttributeOrder {
1516 field: String,
1517 direction: SortDirection,
1518 },
1519 Sum(Vec<RankBy>),
1521 Max(Vec<RankBy>),
1523 Product { weight: f32, ranking: Box<RankBy> },
1525}
1526
1527impl RankBy {
1528 pub fn vector_ann(field: impl Into<String>, query_vector: Vec<f32>) -> Self {
1530 RankBy::VectorSearch {
1531 field: field.into(),
1532 method: VectorSearchMethod::ANN,
1533 query_vector,
1534 }
1535 }
1536
1537 pub fn ann(query_vector: Vec<f32>) -> Self {
1539 Self::vector_ann("vector", query_vector)
1540 }
1541
1542 pub fn vector_knn(field: impl Into<String>, query_vector: Vec<f32>) -> Self {
1544 RankBy::VectorSearch {
1545 field: field.into(),
1546 method: VectorSearchMethod::KNN,
1547 query_vector,
1548 }
1549 }
1550
1551 pub fn knn(query_vector: Vec<f32>) -> Self {
1553 Self::vector_knn("vector", query_vector)
1554 }
1555
1556 pub fn bm25(field: impl Into<String>, query: impl Into<String>) -> Self {
1558 RankBy::FullTextSearch {
1559 field: field.into(),
1560 method: "BM25".to_string(),
1561 query: query.into(),
1562 }
1563 }
1564
1565 pub fn asc(field: impl Into<String>) -> Self {
1567 RankBy::AttributeOrder {
1568 field: field.into(),
1569 direction: SortDirection::Asc,
1570 }
1571 }
1572
1573 pub fn desc(field: impl Into<String>) -> Self {
1575 RankBy::AttributeOrder {
1576 field: field.into(),
1577 direction: SortDirection::Desc,
1578 }
1579 }
1580
1581 pub fn sum(rankings: Vec<RankBy>) -> Self {
1583 RankBy::Sum(rankings)
1584 }
1585
1586 pub fn max(rankings: Vec<RankBy>) -> Self {
1588 RankBy::Max(rankings)
1589 }
1590
1591 pub fn product(weight: f32, ranking: RankBy) -> Self {
1593 RankBy::Product {
1594 weight,
1595 ranking: Box::new(ranking),
1596 }
1597 }
1598}
1599
1600#[derive(Debug, Clone, Serialize, Deserialize)]
1618pub struct UnifiedQueryRequest {
1619 pub rank_by: serde_json::Value,
1621 #[serde(default = "default_unified_top_k")]
1623 pub top_k: usize,
1624 #[serde(skip_serializing_if = "Option::is_none")]
1626 pub filter: Option<serde_json::Value>,
1627 #[serde(default = "default_true")]
1629 pub include_metadata: bool,
1630 #[serde(default)]
1632 pub include_vectors: bool,
1633 #[serde(default)]
1635 pub distance_metric: DistanceMetric,
1636}
1637
1638fn default_unified_top_k() -> usize {
1639 10
1640}
1641
1642impl UnifiedQueryRequest {
1643 pub fn vector_search(query_vector: Vec<f32>, top_k: usize) -> Self {
1645 Self {
1646 rank_by: serde_json::json!(["ANN", query_vector]),
1647 top_k,
1648 filter: None,
1649 include_metadata: true,
1650 include_vectors: false,
1651 distance_metric: DistanceMetric::default(),
1652 }
1653 }
1654
1655 pub fn vector_knn_search(query_vector: Vec<f32>, top_k: usize) -> Self {
1657 Self {
1658 rank_by: serde_json::json!(["kNN", query_vector]),
1659 top_k,
1660 filter: None,
1661 include_metadata: true,
1662 include_vectors: false,
1663 distance_metric: DistanceMetric::default(),
1664 }
1665 }
1666
1667 pub fn fulltext_search(
1669 field: impl Into<String>,
1670 query: impl Into<String>,
1671 top_k: usize,
1672 ) -> Self {
1673 Self {
1674 rank_by: serde_json::json!([field.into(), "BM25", query.into()]),
1675 top_k,
1676 filter: None,
1677 include_metadata: true,
1678 include_vectors: false,
1679 distance_metric: DistanceMetric::default(),
1680 }
1681 }
1682
1683 pub fn attribute_order(
1685 field: impl Into<String>,
1686 direction: SortDirection,
1687 top_k: usize,
1688 ) -> Self {
1689 let dir = match direction {
1690 SortDirection::Asc => "asc",
1691 SortDirection::Desc => "desc",
1692 };
1693 Self {
1694 rank_by: serde_json::json!([field.into(), dir]),
1695 top_k,
1696 filter: None,
1697 include_metadata: true,
1698 include_vectors: false,
1699 distance_metric: DistanceMetric::default(),
1700 }
1701 }
1702
1703 pub fn with_rank_by(rank_by: serde_json::Value, top_k: usize) -> Self {
1705 Self {
1706 rank_by,
1707 top_k,
1708 filter: None,
1709 include_metadata: true,
1710 include_vectors: false,
1711 distance_metric: DistanceMetric::default(),
1712 }
1713 }
1714
1715 pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
1717 self.filter = Some(filter);
1718 self
1719 }
1720
1721 pub fn include_metadata(mut self, include: bool) -> Self {
1723 self.include_metadata = include;
1724 self
1725 }
1726
1727 pub fn include_vectors(mut self, include: bool) -> Self {
1729 self.include_vectors = include;
1730 self
1731 }
1732
1733 pub fn with_distance_metric(mut self, metric: DistanceMetric) -> Self {
1735 self.distance_metric = metric;
1736 self
1737 }
1738
1739 pub fn with_top_k(mut self, top_k: usize) -> Self {
1741 self.top_k = top_k;
1742 self
1743 }
1744}
1745
1746#[derive(Debug, Clone, Serialize, Deserialize)]
1748pub struct UnifiedSearchResult {
1749 pub id: String,
1751 #[serde(rename = "$dist", skip_serializing_if = "Option::is_none")]
1754 pub dist: Option<f32>,
1755 #[serde(skip_serializing_if = "Option::is_none")]
1757 pub metadata: Option<serde_json::Value>,
1758 #[serde(skip_serializing_if = "Option::is_none")]
1760 pub vector: Option<Vec<f32>>,
1761}
1762
1763#[derive(Debug, Clone, Serialize, Deserialize)]
1765pub struct UnifiedQueryResponse {
1766 pub results: Vec<UnifiedSearchResult>,
1768 #[serde(skip_serializing_if = "Option::is_none")]
1770 pub next_cursor: Option<String>,
1771}
1772
1773fn default_explain_top_k() -> usize {
1778 10
1779}
1780
1781#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1783#[serde(rename_all = "snake_case")]
1784#[derive(Default)]
1785pub enum ExplainQueryType {
1786 #[default]
1788 VectorSearch,
1789 FullTextSearch,
1791 HybridSearch,
1793 MultiVector,
1795 BatchQuery,
1797}
1798
1799#[derive(Debug, Clone, Serialize, Deserialize)]
1801pub struct QueryExplainRequest {
1802 #[serde(default)]
1804 pub query_type: ExplainQueryType,
1805 #[serde(skip_serializing_if = "Option::is_none")]
1807 pub vector: Option<Vec<f32>>,
1808 #[serde(default = "default_explain_top_k")]
1810 pub top_k: usize,
1811 #[serde(skip_serializing_if = "Option::is_none")]
1813 pub filter: Option<serde_json::Value>,
1814 #[serde(skip_serializing_if = "Option::is_none")]
1816 pub text_query: Option<String>,
1817 #[serde(default = "default_distance_metric")]
1819 pub distance_metric: String,
1820 #[serde(default)]
1822 pub execute: bool,
1823 #[serde(default)]
1825 pub verbose: bool,
1826}
1827
1828fn default_distance_metric() -> String {
1829 "cosine".to_string()
1830}
1831
1832impl QueryExplainRequest {
1833 pub fn vector_search(vector: Vec<f32>, top_k: usize) -> Self {
1835 Self {
1836 query_type: ExplainQueryType::VectorSearch,
1837 vector: Some(vector),
1838 top_k,
1839 filter: None,
1840 text_query: None,
1841 distance_metric: "cosine".to_string(),
1842 execute: false,
1843 verbose: false,
1844 }
1845 }
1846
1847 pub fn fulltext_search(text_query: impl Into<String>, top_k: usize) -> Self {
1849 Self {
1850 query_type: ExplainQueryType::FullTextSearch,
1851 vector: None,
1852 top_k,
1853 filter: None,
1854 text_query: Some(text_query.into()),
1855 distance_metric: "bm25".to_string(),
1856 execute: false,
1857 verbose: false,
1858 }
1859 }
1860
1861 pub fn hybrid_search(vector: Vec<f32>, text_query: impl Into<String>, top_k: usize) -> Self {
1863 Self {
1864 query_type: ExplainQueryType::HybridSearch,
1865 vector: Some(vector),
1866 top_k,
1867 filter: None,
1868 text_query: Some(text_query.into()),
1869 distance_metric: "hybrid".to_string(),
1870 execute: false,
1871 verbose: false,
1872 }
1873 }
1874
1875 pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
1877 self.filter = Some(filter);
1878 self
1879 }
1880
1881 pub fn with_distance_metric(mut self, metric: impl Into<String>) -> Self {
1883 self.distance_metric = metric.into();
1884 self
1885 }
1886
1887 pub fn with_execution(mut self) -> Self {
1889 self.execute = true;
1890 self
1891 }
1892
1893 pub fn with_verbose(mut self) -> Self {
1895 self.verbose = true;
1896 self
1897 }
1898}
1899
1900#[derive(Debug, Clone, Serialize, Deserialize)]
1902pub struct ExecutionStage {
1903 pub name: String,
1905 pub description: String,
1907 pub order: u32,
1909 pub estimated_input: u64,
1911 pub estimated_output: u64,
1913 pub estimated_cost: f64,
1915 #[serde(default)]
1917 pub details: HashMap<String, serde_json::Value>,
1918}
1919
1920#[derive(Debug, Clone, Serialize, Deserialize)]
1922pub struct CostEstimate {
1923 pub total_cost: f64,
1925 pub estimated_time_ms: u64,
1927 pub estimated_memory_bytes: u64,
1929 pub estimated_io_ops: u64,
1931 #[serde(default)]
1933 pub cost_breakdown: HashMap<String, f64>,
1934 pub confidence: f64,
1936}
1937
1938#[derive(Debug, Clone, Serialize, Deserialize)]
1940pub struct ActualStats {
1941 pub execution_time_ms: u64,
1943 pub results_returned: usize,
1945 pub vectors_scanned: u64,
1947 pub vectors_after_filter: u64,
1949 pub index_lookups: u64,
1951 pub cache_hits: u64,
1953 pub cache_misses: u64,
1955 pub memory_used_bytes: u64,
1957}
1958
1959#[derive(Debug, Clone, Serialize, Deserialize)]
1961pub struct Recommendation {
1962 pub recommendation_type: String,
1964 pub priority: String,
1966 pub description: String,
1968 pub expected_improvement: String,
1970 pub implementation: String,
1972}
1973
1974#[derive(Debug, Clone, Serialize, Deserialize)]
1976pub struct IndexSelection {
1977 pub index_type: String,
1979 pub selection_reason: String,
1981 #[serde(default)]
1983 pub alternatives_considered: Vec<IndexAlternative>,
1984 #[serde(default)]
1986 pub index_config: HashMap<String, serde_json::Value>,
1987 pub index_stats: IndexStatistics,
1989}
1990
1991#[derive(Debug, Clone, Serialize, Deserialize)]
1993pub struct IndexAlternative {
1994 pub index_type: String,
1996 pub rejection_reason: String,
1998 pub estimated_cost: f64,
2000}
2001
2002#[derive(Debug, Clone, Serialize, Deserialize)]
2004pub struct IndexStatistics {
2005 pub vector_count: u64,
2007 pub dimension: usize,
2009 pub memory_bytes: u64,
2011 #[serde(skip_serializing_if = "Option::is_none")]
2013 pub build_time_ms: Option<u64>,
2014 #[serde(skip_serializing_if = "Option::is_none")]
2016 pub last_updated: Option<u64>,
2017}
2018
2019#[derive(Debug, Clone, Serialize, Deserialize)]
2021pub struct QueryParams {
2022 pub top_k: usize,
2024 pub has_filter: bool,
2026 pub filter_complexity: String,
2028 #[serde(skip_serializing_if = "Option::is_none")]
2030 pub vector_dimension: Option<usize>,
2031 pub distance_metric: String,
2033 #[serde(skip_serializing_if = "Option::is_none")]
2035 pub text_query_length: Option<usize>,
2036}
2037
2038#[derive(Debug, Clone, Serialize, Deserialize)]
2040pub struct QueryExplainResponse {
2041 pub query_type: ExplainQueryType,
2043 pub namespace: String,
2045 pub index_selection: IndexSelection,
2047 pub stages: Vec<ExecutionStage>,
2049 pub cost_estimate: CostEstimate,
2051 #[serde(skip_serializing_if = "Option::is_none")]
2053 pub actual_stats: Option<ActualStats>,
2054 #[serde(default)]
2056 pub recommendations: Vec<Recommendation>,
2057 pub summary: String,
2059 pub query_params: QueryParams,
2061}
2062
2063#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
2069#[serde(rename_all = "kebab-case")]
2070pub enum EmbeddingModel {
2071 #[default]
2073 BgeLarge,
2074 Minilm,
2076 BgeSmall,
2078 E5Small,
2080}
2081
2082#[derive(Debug, Clone, Serialize, Deserialize)]
2084pub struct TextDocument {
2085 pub id: String,
2087 pub text: String,
2089 #[serde(skip_serializing_if = "Option::is_none")]
2091 pub metadata: Option<HashMap<String, serde_json::Value>>,
2092 #[serde(skip_serializing_if = "Option::is_none")]
2094 pub ttl_seconds: Option<u64>,
2095}
2096
2097impl TextDocument {
2098 pub fn new(id: impl Into<String>, text: impl Into<String>) -> Self {
2100 Self {
2101 id: id.into(),
2102 text: text.into(),
2103 metadata: None,
2104 ttl_seconds: None,
2105 }
2106 }
2107
2108 pub fn with_metadata(mut self, metadata: HashMap<String, serde_json::Value>) -> Self {
2110 self.metadata = Some(metadata);
2111 self
2112 }
2113
2114 pub fn with_ttl(mut self, ttl_seconds: u64) -> Self {
2116 self.ttl_seconds = Some(ttl_seconds);
2117 self
2118 }
2119}
2120
2121#[derive(Debug, Clone, Serialize, Deserialize)]
2123pub struct UpsertTextRequest {
2124 pub documents: Vec<TextDocument>,
2126 #[serde(skip_serializing_if = "Option::is_none")]
2128 pub model: Option<EmbeddingModel>,
2129}
2130
2131impl UpsertTextRequest {
2132 pub fn new(documents: Vec<TextDocument>) -> Self {
2134 Self {
2135 documents,
2136 model: None,
2137 }
2138 }
2139
2140 pub fn with_model(mut self, model: EmbeddingModel) -> Self {
2142 self.model = Some(model);
2143 self
2144 }
2145}
2146
2147#[derive(Debug, Clone, Serialize, Deserialize)]
2149pub struct TextUpsertResponse {
2150 pub upserted_count: u64,
2152 pub tokens_processed: u64,
2154 pub model: EmbeddingModel,
2156 pub embedding_time_ms: u64,
2158}
2159
2160#[derive(Debug, Clone, Serialize, Deserialize)]
2162pub struct TextSearchResult {
2163 pub id: String,
2165 pub score: f32,
2167 #[serde(skip_serializing_if = "Option::is_none")]
2169 pub text: Option<String>,
2170 #[serde(skip_serializing_if = "Option::is_none")]
2172 pub metadata: Option<HashMap<String, serde_json::Value>>,
2173 #[serde(skip_serializing_if = "Option::is_none")]
2175 pub vector: Option<Vec<f32>>,
2176}
2177
2178#[derive(Debug, Clone, Serialize, Deserialize)]
2180pub struct QueryTextRequest {
2181 pub text: String,
2183 pub top_k: u32,
2185 #[serde(skip_serializing_if = "Option::is_none")]
2187 pub filter: Option<serde_json::Value>,
2188 pub include_text: bool,
2190 pub include_vectors: bool,
2192 #[serde(skip_serializing_if = "Option::is_none")]
2194 pub model: Option<EmbeddingModel>,
2195}
2196
2197impl QueryTextRequest {
2198 pub fn new(text: impl Into<String>, top_k: u32) -> Self {
2200 Self {
2201 text: text.into(),
2202 top_k,
2203 filter: None,
2204 include_text: true,
2205 include_vectors: false,
2206 model: None,
2207 }
2208 }
2209
2210 pub fn with_filter(mut self, filter: serde_json::Value) -> Self {
2212 self.filter = Some(filter);
2213 self
2214 }
2215
2216 pub fn include_text(mut self, include: bool) -> Self {
2218 self.include_text = include;
2219 self
2220 }
2221
2222 pub fn include_vectors(mut self, include: bool) -> Self {
2224 self.include_vectors = include;
2225 self
2226 }
2227
2228 pub fn with_model(mut self, model: EmbeddingModel) -> Self {
2230 self.model = Some(model);
2231 self
2232 }
2233}
2234
2235#[derive(Debug, Clone, Serialize, Deserialize)]
2237pub struct TextQueryResponse {
2238 pub results: Vec<TextSearchResult>,
2240 pub model: EmbeddingModel,
2242 pub embedding_time_ms: u64,
2244 pub search_time_ms: u64,
2246}
2247
2248#[derive(Debug, Clone, Serialize, Deserialize)]
2250pub struct BatchQueryTextRequest {
2251 pub queries: Vec<String>,
2253 pub top_k: u32,
2255 #[serde(skip_serializing_if = "Option::is_none")]
2257 pub filter: Option<serde_json::Value>,
2258 pub include_vectors: bool,
2260 #[serde(skip_serializing_if = "Option::is_none")]
2262 pub model: Option<EmbeddingModel>,
2263}
2264
2265impl BatchQueryTextRequest {
2266 pub fn new(queries: Vec<String>, top_k: u32) -> Self {
2268 Self {
2269 queries,
2270 top_k,
2271 filter: None,
2272 include_vectors: false,
2273 model: None,
2274 }
2275 }
2276}
2277
2278#[derive(Debug, Clone, Serialize, Deserialize)]
2280pub struct BatchQueryTextResponse {
2281 pub results: Vec<Vec<TextSearchResult>>,
2283 pub model: EmbeddingModel,
2285 pub embedding_time_ms: u64,
2287 pub search_time_ms: u64,
2289}
2290
2291#[derive(Debug, Clone, Serialize, Deserialize)]
2297pub struct FetchRequest {
2298 pub ids: Vec<String>,
2300 pub include_values: bool,
2302 pub include_metadata: bool,
2304}
2305
2306impl FetchRequest {
2307 pub fn new(ids: Vec<String>) -> Self {
2309 Self {
2310 ids,
2311 include_values: true,
2312 include_metadata: true,
2313 }
2314 }
2315}
2316
2317#[derive(Debug, Clone, Serialize, Deserialize)]
2319pub struct FetchResponse {
2320 pub vectors: Vec<Vector>,
2322}
2323
2324#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2330pub struct CreateNamespaceRequest {
2331 #[serde(rename = "dimension", skip_serializing_if = "Option::is_none")]
2333 pub dimensions: Option<u32>,
2334 #[serde(skip_serializing_if = "Option::is_none")]
2336 pub index_type: Option<String>,
2337 #[serde(skip_serializing_if = "Option::is_none")]
2339 pub metadata: Option<HashMap<String, serde_json::Value>>,
2340}
2341
2342impl CreateNamespaceRequest {
2343 pub fn new() -> Self {
2345 Self::default()
2346 }
2347
2348 pub fn with_dimensions(mut self, dimensions: u32) -> Self {
2350 self.dimensions = Some(dimensions);
2351 self
2352 }
2353
2354 pub fn with_index_type(mut self, index_type: impl Into<String>) -> Self {
2356 self.index_type = Some(index_type.into());
2357 self
2358 }
2359}
2360
2361#[derive(Debug, Clone, Serialize, Deserialize)]
2366pub struct ConfigureNamespaceRequest {
2367 pub dimension: usize,
2369 #[serde(skip_serializing_if = "Option::is_none")]
2371 pub distance: Option<DistanceMetric>,
2372}
2373
2374impl ConfigureNamespaceRequest {
2375 pub fn new(dimension: usize) -> Self {
2377 Self {
2378 dimension,
2379 distance: None,
2380 }
2381 }
2382
2383 pub fn with_distance(mut self, distance: DistanceMetric) -> Self {
2385 self.distance = Some(distance);
2386 self
2387 }
2388}
2389
2390#[derive(Debug, Clone, Serialize, Deserialize)]
2392pub struct ConfigureNamespaceResponse {
2393 pub namespace: String,
2395 pub dimension: usize,
2397 pub distance: DistanceMetric,
2399 pub created: bool,
2401}
2402
2403#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
2409#[serde(rename_all = "snake_case")]
2410pub enum EdgeType {
2411 RelatedTo,
2413 SharesEntity,
2415 Precedes,
2417 #[default]
2419 LinkedBy,
2420}
2421
2422#[derive(Debug, Clone, Serialize, Deserialize)]
2424pub struct GraphEdge {
2425 pub id: String,
2427 pub source_id: String,
2429 pub target_id: String,
2431 pub edge_type: EdgeType,
2433 pub weight: f64,
2435 pub created_at: i64,
2437}
2438
2439#[derive(Debug, Clone, Serialize, Deserialize)]
2441pub struct GraphNode {
2442 pub memory_id: String,
2444 pub content_preview: String,
2446 pub importance: f64,
2448 pub depth: u32,
2450}
2451
2452#[derive(Debug, Clone, Serialize, Deserialize)]
2454pub struct MemoryGraph {
2455 pub root_id: String,
2457 pub depth: u32,
2459 pub nodes: Vec<GraphNode>,
2461 pub edges: Vec<GraphEdge>,
2463}
2464
2465#[derive(Debug, Clone, Serialize, Deserialize)]
2467pub struct GraphPath {
2468 pub source_id: String,
2470 pub target_id: String,
2472 pub path: Vec<String>,
2474 pub hops: i32,
2476 pub edges: Vec<GraphEdge>,
2478}
2479
2480#[derive(Debug, Clone, Serialize, Deserialize)]
2482pub struct GraphLinkRequest {
2483 pub target_id: String,
2485 pub edge_type: EdgeType,
2487}
2488
2489#[derive(Debug, Clone, Serialize, Deserialize)]
2491pub struct GraphLinkResponse {
2492 pub edge: GraphEdge,
2494}
2495
2496#[derive(Debug, Clone, Serialize, Deserialize)]
2498pub struct GraphExport {
2499 pub agent_id: String,
2501 pub format: String,
2503 pub data: String,
2505 pub node_count: u64,
2507 pub edge_count: u64,
2509}
2510
2511#[derive(Debug, Clone, Default)]
2513pub struct GraphOptions {
2514 pub depth: Option<u32>,
2516 pub types: Option<Vec<EdgeType>>,
2518}
2519
2520impl GraphOptions {
2521 pub fn new() -> Self {
2523 Self::default()
2524 }
2525
2526 pub fn depth(mut self, depth: u32) -> Self {
2528 self.depth = Some(depth);
2529 self
2530 }
2531
2532 pub fn types(mut self, types: Vec<EdgeType>) -> Self {
2534 self.types = Some(types);
2535 self
2536 }
2537}
2538
2539#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2545pub struct NamespaceNerConfig {
2546 pub extract_entities: bool,
2547 #[serde(skip_serializing_if = "Option::is_none")]
2548 pub entity_types: Option<Vec<String>>,
2549}
2550
2551#[derive(Debug, Clone, Serialize, Deserialize)]
2553pub struct ExtractedEntity {
2554 pub entity_type: String,
2555 pub value: String,
2556 pub score: f64,
2557}
2558
2559#[derive(Debug, Clone, Serialize, Deserialize)]
2561pub struct EntityExtractionResponse {
2562 pub entities: Vec<ExtractedEntity>,
2563}
2564
2565#[derive(Debug, Clone, Serialize, Deserialize)]
2567pub struct MemoryEntitiesResponse {
2568 pub memory_id: String,
2569 pub entities: Vec<ExtractedEntity>,
2570}
2571
2572#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2584#[serde(rename_all = "lowercase")]
2585pub enum FeedbackSignal {
2586 Upvote,
2587 Downvote,
2588 Flag,
2589 Positive,
2590 Negative,
2591}
2592
2593#[derive(Debug, Clone, Serialize, Deserialize)]
2595pub struct FeedbackHistoryEntry {
2596 pub signal: FeedbackSignal,
2597 pub timestamp: u64,
2599 pub old_importance: f32,
2600 pub new_importance: f32,
2601}
2602
2603#[derive(Debug, Clone, Serialize, Deserialize)]
2605pub struct MemoryFeedbackBody {
2606 pub agent_id: String,
2607 pub signal: FeedbackSignal,
2608}
2609
2610#[derive(Debug, Clone, Serialize, Deserialize)]
2612pub struct MemoryImportancePatch {
2613 pub agent_id: String,
2614 pub importance: f32,
2615}
2616
2617#[derive(Debug, Clone, Serialize, Deserialize)]
2619pub struct FeedbackResponse {
2620 pub memory_id: String,
2621 pub new_importance: f32,
2623 pub signal: FeedbackSignal,
2624}
2625
2626#[derive(Debug, Clone, Serialize, Deserialize)]
2628pub struct FeedbackHistoryResponse {
2629 pub memory_id: String,
2630 pub entries: Vec<FeedbackHistoryEntry>,
2632}
2633
2634#[derive(Debug, Clone, Serialize, Deserialize)]
2636pub struct AgentFeedbackSummary {
2637 pub agent_id: String,
2638 pub upvotes: u64,
2639 pub downvotes: u64,
2640 pub flags: u64,
2641 pub total_feedback: u64,
2642 pub health_score: f32,
2644}
2645
2646#[derive(Debug, Clone, Serialize, Deserialize)]
2648pub struct FeedbackHealthResponse {
2649 pub agent_id: String,
2650 pub health_score: f32,
2652 pub memory_count: usize,
2653 pub avg_importance: f32,
2654}
2655
2656#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2662#[serde(rename_all = "snake_case")]
2663pub enum TifClassification {
2664 SurfaceContradiction,
2666 AskClarification,
2668 ConfidentReuse,
2670 VerifyBeforeUse,
2672}
2673
2674impl TifClassification {
2675 pub fn as_str(&self) -> &'static str {
2677 match self {
2678 Self::SurfaceContradiction => "surface_contradiction",
2679 Self::AskClarification => "ask_clarification",
2680 Self::ConfidentReuse => "confident_reuse",
2681 Self::VerifyBeforeUse => "verify_before_use",
2682 }
2683 }
2684}
2685
2686#[derive(Debug, Clone, Serialize, Deserialize)]
2691pub struct TifScore {
2692 pub truth: f64,
2694 pub indeterminacy: f64,
2696 pub falsity: f64,
2698 pub feedback_count: u64,
2700 pub classification: TifClassification,
2702}
2703
2704fn classify_tif(truth: f64, indeterminacy: f64, falsity: f64) -> TifClassification {
2705 if falsity >= 0.5 {
2706 TifClassification::SurfaceContradiction
2707 } else if indeterminacy >= 0.5 {
2708 TifClassification::AskClarification
2709 } else if truth >= 0.7 {
2710 TifClassification::ConfidentReuse
2711 } else {
2712 TifClassification::VerifyBeforeUse
2713 }
2714}
2715
2716impl TifScore {
2717 pub fn from_feedback_history(history: &FeedbackHistoryResponse) -> Self {
2726 let mut upvotes: u64 = 0;
2727 let mut downvotes: u64 = 0;
2728 let mut flags: u64 = 0;
2729 for entry in &history.entries {
2730 match entry.signal {
2731 FeedbackSignal::Upvote | FeedbackSignal::Positive => upvotes += 1,
2732 FeedbackSignal::Downvote | FeedbackSignal::Negative => downvotes += 1,
2733 FeedbackSignal::Flag => flags += 1,
2734 }
2735 }
2736 let total = upvotes + downvotes + flags;
2737 if total == 0 {
2738 return Self {
2739 truth: 0.0,
2740 indeterminacy: 1.0,
2741 falsity: 0.0,
2742 feedback_count: 0,
2743 classification: TifClassification::AskClarification,
2744 };
2745 }
2746 let total_f = total as f64;
2747 let base_indeterminacy = if total < 3 {
2748 (3 - total) as f64 * 0.25
2749 } else {
2750 0.0
2751 };
2752 let mut truth = upvotes as f64 / total_f;
2753 let mut falsity = downvotes as f64 / total_f;
2754 let mut indeterminacy = flags as f64 / total_f + base_indeterminacy;
2755 let sum = truth + falsity + indeterminacy;
2756 truth /= sum;
2757 falsity /= sum;
2758 indeterminacy /= sum;
2759 Self {
2760 truth,
2761 indeterminacy,
2762 falsity,
2763 feedback_count: total,
2764 classification: classify_tif(truth, indeterminacy, falsity),
2765 }
2766 }
2767
2768 pub fn from_metadata(data: &serde_json::Value) -> Option<Self> {
2772 let truth = data["truth"].as_f64()?;
2773 let indeterminacy = data["indeterminacy"].as_f64()?;
2774 let falsity = data["falsity"].as_f64()?;
2775 let feedback_count = data["feedback_count"].as_u64().unwrap_or(0);
2776 Some(Self {
2777 truth,
2778 indeterminacy,
2779 falsity,
2780 feedback_count,
2781 classification: classify_tif(truth, indeterminacy, falsity),
2782 })
2783 }
2784}
2785
2786#[cfg(test)]
2787mod tif_tests {
2788 use super::*;
2789
2790 fn make_history(signals: &[&str]) -> FeedbackHistoryResponse {
2791 FeedbackHistoryResponse {
2792 memory_id: "test-mem".to_string(),
2793 entries: signals
2794 .iter()
2795 .map(|s| {
2796 let signal = match *s {
2797 "upvote" => FeedbackSignal::Upvote,
2798 "downvote" => FeedbackSignal::Downvote,
2799 "flag" => FeedbackSignal::Flag,
2800 "positive" => FeedbackSignal::Positive,
2801 "negative" => FeedbackSignal::Negative,
2802 other => panic!("unknown signal: {other}"),
2803 };
2804 FeedbackHistoryEntry {
2805 signal,
2806 timestamp: 0,
2807 old_importance: 0.5,
2808 new_importance: 0.5,
2809 }
2810 })
2811 .collect(),
2812 }
2813 }
2814
2815 #[test]
2816 fn no_feedback_max_indeterminacy() {
2817 let score = TifScore::from_feedback_history(&make_history(&[]));
2818 assert_eq!(score.truth, 0.0);
2819 assert_eq!(score.indeterminacy, 1.0);
2820 assert_eq!(score.falsity, 0.0);
2821 assert_eq!(score.feedback_count, 0);
2822 assert_eq!(score.classification, TifClassification::AskClarification);
2823 }
2824
2825 #[test]
2826 fn all_upvotes() {
2827 let score = TifScore::from_feedback_history(&make_history(&["upvote", "upvote", "upvote"]));
2828 assert!((score.truth - 1.0).abs() < 1e-9);
2829 assert_eq!(score.feedback_count, 3);
2830 assert_eq!(score.classification, TifClassification::ConfidentReuse);
2831 }
2832
2833 #[test]
2834 fn all_downvotes() {
2835 let score = TifScore::from_feedback_history(&make_history(&["downvote", "downvote"]));
2836 assert!((score.falsity - 0.8).abs() < 1e-9);
2837 assert!((score.indeterminacy - 0.2).abs() < 1e-9);
2838 assert_eq!(
2839 score.classification,
2840 TifClassification::SurfaceContradiction
2841 );
2842 }
2843
2844 #[test]
2845 fn all_flags() {
2846 let score = TifScore::from_feedback_history(&make_history(&["flag", "flag"]));
2847 assert!((score.indeterminacy - 1.0).abs() < 1e-9);
2848 assert_eq!(score.classification, TifClassification::AskClarification);
2849 }
2850
2851 #[test]
2852 fn mixed_signals() {
2853 let score = TifScore::from_feedback_history(&make_history(&[
2854 "upvote", "upvote", "upvote", "upvote", "downvote", "downvote", "flag", "flag", "flag",
2855 "flag",
2856 ]));
2857 assert!((score.truth - 0.4).abs() < 1e-9);
2858 assert!((score.falsity - 0.2).abs() < 1e-9);
2859 assert!((score.indeterminacy - 0.4).abs() < 1e-9);
2860 assert_eq!(score.feedback_count, 10);
2861 }
2862
2863 #[test]
2864 fn positive_alias() {
2865 let score =
2866 TifScore::from_feedback_history(&make_history(&["positive", "positive", "downvote"]));
2867 assert!((score.truth - 2.0 / 3.0).abs() < 1e-9);
2868 assert!((score.falsity - 1.0 / 3.0).abs() < 1e-9);
2869 }
2870
2871 #[test]
2872 fn negative_alias() {
2873 let score =
2874 TifScore::from_feedback_history(&make_history(&["upvote", "negative", "negative"]));
2875 assert!((score.falsity - 2.0 / 3.0).abs() < 1e-9);
2876 }
2877
2878 #[test]
2879 fn proportions_sum_to_one() {
2880 let score = TifScore::from_feedback_history(&make_history(&["upvote", "downvote", "flag"]));
2881 assert!((score.truth + score.indeterminacy + score.falsity - 1.0).abs() < 1e-9);
2882 }
2883
2884 #[test]
2885 fn classification_surface_contradiction() {
2886 let score = TifScore::from_feedback_history(&make_history(&[
2887 "downvote", "downvote", "downvote", "upvote", "upvote",
2888 ]));
2889 assert_eq!(
2890 score.classification,
2891 TifClassification::SurfaceContradiction
2892 );
2893 }
2894
2895 #[test]
2896 fn classification_verify_before_use() {
2897 let score = TifScore::from_feedback_history(&make_history(&[
2899 "upvote", "upvote", "downvote", "downvote", "flag", "flag", "flag",
2900 ]));
2901 assert_eq!(score.classification, TifClassification::VerifyBeforeUse);
2902 }
2903
2904 #[test]
2905 fn falsity_priority_over_indeterminacy() {
2906 let score = TifScore::from_feedback_history(&make_history(&[
2908 "downvote", "downvote", "downvote", "flag", "flag", "flag",
2909 ]));
2910 assert_eq!(
2911 score.classification,
2912 TifClassification::SurfaceContradiction
2913 );
2914 }
2915
2916 #[test]
2917 fn from_metadata_round_trip() {
2918 use serde_json::json;
2919 let data =
2920 json!({ "truth": 0.75, "indeterminacy": 0.15, "falsity": 0.10, "feedback_count": 20 });
2921 let score = TifScore::from_metadata(&data).unwrap();
2922 assert!((score.truth - 0.75).abs() < 1e-9);
2923 assert_eq!(score.feedback_count, 20);
2924 assert_eq!(score.classification, TifClassification::ConfidentReuse);
2925 }
2926
2927 #[test]
2928 fn from_metadata_missing_feedback_count() {
2929 use serde_json::json;
2930 let data = json!({ "truth": 0.8, "indeterminacy": 0.1, "falsity": 0.1 });
2931 let score = TifScore::from_metadata(&data).unwrap();
2932 assert_eq!(score.feedback_count, 0);
2933 }
2934
2935 #[test]
2936 fn classification_as_str() {
2937 assert_eq!(
2938 TifClassification::SurfaceContradiction.as_str(),
2939 "surface_contradiction"
2940 );
2941 assert_eq!(
2942 TifClassification::AskClarification.as_str(),
2943 "ask_clarification"
2944 );
2945 assert_eq!(
2946 TifClassification::ConfidentReuse.as_str(),
2947 "confident_reuse"
2948 );
2949 assert_eq!(
2950 TifClassification::VerifyBeforeUse.as_str(),
2951 "verify_before_use"
2952 );
2953 }
2954
2955 #[test]
2958 fn thin_evidence_single_upvote_not_confident() {
2959 let score = TifScore::from_feedback_history(&make_history(&["upvote"]));
2960 assert!((score.truth + score.indeterminacy + score.falsity - 1.0).abs() < 1e-9);
2961 assert!(score.indeterminacy > 0.0);
2962 assert!(score.truth < 0.70);
2963 assert_eq!(score.classification, TifClassification::VerifyBeforeUse);
2964 }
2965
2966 #[test]
2967 fn thin_evidence_two_upvotes_confident() {
2968 let score = TifScore::from_feedback_history(&make_history(&["upvote", "upvote"]));
2969 assert!((score.truth - 0.8).abs() < 1e-9);
2970 assert!((score.indeterminacy - 0.2).abs() < 1e-9);
2971 assert_eq!(score.classification, TifClassification::ConfidentReuse);
2972 }
2973
2974 #[test]
2975 fn thin_evidence_three_upvotes_no_base() {
2976 let score = TifScore::from_feedback_history(&make_history(&["upvote", "upvote", "upvote"]));
2977 assert!((score.truth - 1.0).abs() < 1e-9);
2978 assert!((score.indeterminacy - 0.0).abs() < 1e-9);
2979 assert_eq!(score.classification, TifClassification::ConfidentReuse);
2980 }
2981
2982 #[test]
2985 fn golden_no_feedback() {
2986 let s = TifScore::from_feedback_history(&make_history(&[]));
2987 assert!((s.truth - 0.0).abs() < 1e-9);
2988 assert!((s.indeterminacy - 1.0).abs() < 1e-9);
2989 assert!((s.falsity - 0.0).abs() < 1e-9);
2990 assert_eq!(s.classification, TifClassification::AskClarification);
2991 }
2992
2993 #[test]
2994 fn golden_one_upvote() {
2995 let s = TifScore::from_feedback_history(&make_history(&["upvote"]));
2996 assert!((s.truth - 2.0 / 3.0).abs() < 1e-4);
2997 assert!((s.indeterminacy - 1.0 / 3.0).abs() < 1e-4);
2998 assert!((s.falsity - 0.0).abs() < 1e-9);
2999 assert_eq!(s.classification, TifClassification::VerifyBeforeUse);
3000 }
3001
3002 #[test]
3003 fn golden_two_upvotes() {
3004 let s = TifScore::from_feedback_history(&make_history(&["upvote", "upvote"]));
3005 assert!((s.truth - 0.8).abs() < 1e-9);
3006 assert!((s.indeterminacy - 0.2).abs() < 1e-9);
3007 assert!((s.falsity - 0.0).abs() < 1e-9);
3008 assert_eq!(s.classification, TifClassification::ConfidentReuse);
3009 }
3010
3011 #[test]
3012 fn golden_three_upvotes() {
3013 let s = TifScore::from_feedback_history(&make_history(&["upvote", "upvote", "upvote"]));
3014 assert!((s.truth - 1.0).abs() < 1e-9);
3015 assert!((s.indeterminacy - 0.0).abs() < 1e-9);
3016 assert!((s.falsity - 0.0).abs() < 1e-9);
3017 assert_eq!(s.classification, TifClassification::ConfidentReuse);
3018 }
3019
3020 #[test]
3021 fn golden_two_downvotes() {
3022 let s = TifScore::from_feedback_history(&make_history(&["downvote", "downvote"]));
3023 assert!((s.truth - 0.0).abs() < 1e-9);
3024 assert!((s.indeterminacy - 0.2).abs() < 1e-9);
3025 assert!((s.falsity - 0.8).abs() < 1e-9);
3026 assert_eq!(s.classification, TifClassification::SurfaceContradiction);
3027 }
3028
3029 #[test]
3030 fn golden_two_flags() {
3031 let s = TifScore::from_feedback_history(&make_history(&["flag", "flag"]));
3032 assert!((s.truth - 0.0).abs() < 1e-9);
3033 assert!((s.indeterminacy - 1.0).abs() < 1e-9);
3034 assert!((s.falsity - 0.0).abs() < 1e-9);
3035 assert_eq!(s.classification, TifClassification::AskClarification);
3036 }
3037
3038 #[test]
3039 fn golden_8up_1down_1flag() {
3040 let s = TifScore::from_feedback_history(&make_history(&[
3041 "upvote", "upvote", "upvote", "upvote", "upvote", "upvote", "upvote", "upvote",
3042 "downvote", "flag",
3043 ]));
3044 assert!((s.truth - 0.8).abs() < 1e-9);
3045 assert!((s.indeterminacy - 0.1).abs() < 1e-9);
3046 assert!((s.falsity - 0.1).abs() < 1e-9);
3047 assert_eq!(s.classification, TifClassification::ConfidentReuse);
3048 }
3049
3050 #[test]
3051 fn golden_3down_3flag() {
3052 let s = TifScore::from_feedback_history(&make_history(&[
3053 "downvote", "downvote", "downvote", "flag", "flag", "flag",
3054 ]));
3055 assert!((s.truth - 0.0).abs() < 1e-9);
3056 assert!((s.indeterminacy - 0.5).abs() < 1e-9);
3057 assert!((s.falsity - 0.5).abs() < 1e-9);
3058 assert_eq!(s.classification, TifClassification::SurfaceContradiction);
3059 }
3060}
3061
3062#[derive(Debug, Clone, Serialize, Deserialize)]
3068pub struct OdeEntity {
3069 pub text: String,
3071 pub label: String,
3073 pub start: usize,
3075 pub end: usize,
3077 pub score: f32,
3079}
3080
3081#[derive(Debug, Clone, Serialize, Deserialize)]
3083pub struct ExtractEntitiesRequest {
3084 pub content: String,
3086 pub agent_id: String,
3088 #[serde(skip_serializing_if = "Option::is_none")]
3090 pub memory_id: Option<String>,
3091 #[serde(skip_serializing_if = "Option::is_none")]
3094 pub entity_types: Option<Vec<String>>,
3095}
3096
3097#[derive(Debug, Clone, Serialize, Deserialize)]
3099pub struct ExtractEntitiesResponse {
3100 pub entities: Vec<OdeEntity>,
3102 pub model: String,
3104 pub processing_time_ms: u64,
3106}
3107
3108#[derive(Debug, Clone, Serialize, Deserialize)]
3114pub struct KgQueryResponse {
3115 pub agent_id: String,
3117 pub node_count: usize,
3119 pub edge_count: usize,
3121 pub edges: Vec<GraphEdge>,
3123}
3124
3125#[derive(Debug, Clone, Serialize, Deserialize)]
3127pub struct KgPathResponse {
3128 pub agent_id: String,
3130 pub from_id: String,
3132 pub to_id: String,
3134 pub hop_count: usize,
3136 pub path: Vec<String>,
3138}
3139
3140#[derive(Debug, Clone, Serialize, Deserialize)]
3142pub struct KgExportResponse {
3143 pub agent_id: String,
3145 pub format: String,
3147 pub node_count: usize,
3149 pub edge_count: usize,
3151 pub edges: Vec<GraphEdge>,
3153}
3154
3155#[derive(Debug, Clone, Serialize, Deserialize)]
3167pub struct MemoryPolicy {
3168 #[serde(skip_serializing_if = "Option::is_none")]
3171 pub working_ttl_seconds: Option<u64>,
3172 #[serde(skip_serializing_if = "Option::is_none")]
3174 pub episodic_ttl_seconds: Option<u64>,
3175 #[serde(skip_serializing_if = "Option::is_none")]
3177 pub semantic_ttl_seconds: Option<u64>,
3178 #[serde(skip_serializing_if = "Option::is_none")]
3180 pub procedural_ttl_seconds: Option<u64>,
3181
3182 #[serde(skip_serializing_if = "Option::is_none")]
3185 pub working_decay: Option<String>,
3186 #[serde(skip_serializing_if = "Option::is_none")]
3188 pub episodic_decay: Option<String>,
3189 #[serde(skip_serializing_if = "Option::is_none")]
3191 pub semantic_decay: Option<String>,
3192 #[serde(skip_serializing_if = "Option::is_none")]
3194 pub procedural_decay: Option<String>,
3195
3196 #[serde(skip_serializing_if = "Option::is_none")]
3200 pub spaced_repetition_factor: Option<f64>,
3201 #[serde(skip_serializing_if = "Option::is_none")]
3203 pub spaced_repetition_base_interval_seconds: Option<u64>,
3204
3205 #[serde(skip_serializing_if = "Option::is_none")]
3210 pub consolidation_enabled: Option<bool>,
3211 #[serde(skip_serializing_if = "Option::is_none")]
3214 pub consolidation_threshold: Option<f32>,
3215 #[serde(skip_serializing_if = "Option::is_none")]
3217 pub consolidation_interval_hours: Option<u32>,
3218 #[serde(skip_serializing_if = "Option::is_none")]
3223 pub consolidated_count: Option<u64>,
3224
3225 #[serde(skip_serializing_if = "Option::is_none")]
3228 pub rate_limit_enabled: Option<bool>,
3229 #[serde(skip_serializing_if = "Option::is_none")]
3231 pub rate_limit_stores_per_minute: Option<u32>,
3232 #[serde(skip_serializing_if = "Option::is_none")]
3234 pub rate_limit_recalls_per_minute: Option<u32>,
3235
3236 #[serde(skip_serializing_if = "Option::is_none")]
3243 pub dedup_on_store: Option<bool>,
3244 #[serde(skip_serializing_if = "Option::is_none")]
3249 pub dedup_threshold: Option<f32>,
3250}
3251
3252impl Default for MemoryPolicy {
3253 fn default() -> Self {
3254 Self {
3255 working_ttl_seconds: Some(14_400),
3256 episodic_ttl_seconds: Some(2_592_000),
3257 semantic_ttl_seconds: Some(31_536_000),
3258 procedural_ttl_seconds: Some(63_072_000),
3259 working_decay: Some("exponential".to_string()),
3260 episodic_decay: Some("power_law".to_string()),
3261 semantic_decay: Some("logarithmic".to_string()),
3262 procedural_decay: Some("flat".to_string()),
3263 spaced_repetition_factor: Some(1.0),
3264 spaced_repetition_base_interval_seconds: Some(86_400),
3265 consolidation_enabled: Some(false),
3266 consolidation_threshold: Some(0.92),
3267 consolidation_interval_hours: Some(24),
3268 consolidated_count: Some(0),
3269 rate_limit_enabled: Some(false),
3270 rate_limit_stores_per_minute: None,
3271 rate_limit_recalls_per_minute: None,
3272 dedup_on_store: Some(false),
3273 dedup_threshold: Some(0.92),
3274 }
3275 }
3276}
3277
3278#[derive(Debug, Clone, Serialize, Deserialize)]
3284pub struct BulkUpdateRequest {
3285 pub filter: serde_json::Value,
3286 pub update: serde_json::Value,
3287}
3288
3289#[derive(Debug, Clone, Serialize, Deserialize)]
3291pub struct BulkUpdateResponse {
3292 pub updated: u64,
3293 pub failed: u64,
3294 pub errors: Vec<String>,
3295}
3296
3297#[derive(Debug, Clone, Serialize, Deserialize)]
3299pub struct BulkDeleteRequest {
3300 pub filter: serde_json::Value,
3301}
3302
3303#[derive(Debug, Clone, Serialize, Deserialize)]
3305pub struct BulkDeleteResponse {
3306 pub deleted: u64,
3307 pub failed: u64,
3308 pub errors: Vec<String>,
3309}
3310
3311#[derive(Debug, Clone, Serialize, Deserialize)]
3313pub struct CountVectorsRequest {
3314 #[serde(skip_serializing_if = "Option::is_none")]
3315 pub filter: Option<serde_json::Value>,
3316}
3317
3318#[derive(Debug, Clone, Serialize, Deserialize)]
3320pub struct CountVectorsResponse {
3321 pub count: u64,
3322 pub namespace: String,
3323}
3324
3325#[derive(Debug, Clone, Serialize, Deserialize)]
3327pub struct AgentConsolidateResponse {
3328 pub agent_id: String,
3329 pub memories_scanned: u64,
3330 pub clusters_found: u64,
3331 pub memories_deprecated: u64,
3332 pub anchor_ids: Vec<String>,
3333 pub deprecated_ids: Vec<String>,
3334 #[serde(skip_serializing_if = "Option::is_none")]
3335 pub skipped: Option<bool>,
3336 #[serde(skip_serializing_if = "Option::is_none")]
3337 pub reason: Option<String>,
3338}
3339
3340#[derive(Debug, Clone, Serialize, Deserialize)]
3342pub struct AgentConsolidationLogEntry {
3343 pub timestamp: u64,
3344 pub clusters_found: u64,
3345 pub memories_deprecated: u64,
3346 pub anchor_ids: Vec<String>,
3347 pub deprecated_ids: Vec<String>,
3348}
3349
3350#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3352pub struct ConsolidationConfigPatch {
3353 #[serde(skip_serializing_if = "Option::is_none")]
3354 pub enabled: Option<bool>,
3355 #[serde(skip_serializing_if = "Option::is_none")]
3356 pub epsilon: Option<f64>,
3357 #[serde(skip_serializing_if = "Option::is_none")]
3358 pub min_samples: Option<u32>,
3359 #[serde(skip_serializing_if = "Option::is_none")]
3360 pub soft_deprecation_days: Option<u32>,
3361}
3362
3363#[derive(Debug, Clone, Serialize, Deserialize)]
3365pub struct AgentConsolidationConfig {
3366 pub enabled: bool,
3367 pub epsilon: f64,
3368 pub min_samples: u32,
3369 pub soft_deprecation_days: u32,
3370}
3371
3372#[derive(Debug, Clone, Serialize, Deserialize)]
3374pub struct NamespaceEntityConfig {
3375 pub namespace: String,
3376 pub extract_entities: bool,
3377 pub entity_types: Vec<String>,
3378}
3379
3380#[derive(Debug, Clone, Serialize, Deserialize)]
3382pub struct NamespaceExtractorConfig {
3383 pub provider: String,
3384 #[serde(skip_serializing_if = "Option::is_none")]
3385 pub model: Option<String>,
3386 #[serde(skip_serializing_if = "Option::is_none")]
3387 pub base_url: Option<String>,
3388}
3389
3390#[derive(Debug, Clone, Serialize, Deserialize)]
3396pub struct NodeReplicationLag {
3397 pub node_id: String,
3398 pub lag_ms: u64,
3399 pub status: String,
3400}
3401
3402#[derive(Debug, Clone, Serialize, Deserialize)]
3404pub struct ReplicationStatus {
3405 pub replication_factor: u32,
3406 pub healthy_replicas: u32,
3407 pub total_nodes: u32,
3408 #[serde(default)]
3409 pub replication_lag: Vec<NodeReplicationLag>,
3410}
3411
3412#[derive(Debug, Clone, Serialize, Deserialize)]
3414pub struct ShardInfo {
3415 pub shard_id: String,
3416 pub namespace: String,
3417 pub primary_node: String,
3418 #[serde(default)]
3419 pub replica_nodes: Vec<String>,
3420 pub state: String,
3421 pub vector_count: u64,
3422 pub size_bytes: u64,
3423}
3424
3425#[derive(Debug, Clone, Serialize, Deserialize)]
3427pub struct ShardListResponse {
3428 pub shards: Vec<ShardInfo>,
3429 pub total: u32,
3430}
3431
3432#[derive(Debug, Clone, Serialize, Deserialize, Default)]
3434pub struct ShardRebalanceRequest {
3435 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3436 pub shard_ids: Vec<String>,
3437 #[serde(default)]
3438 pub dry_run: bool,
3439}
3440
3441#[derive(Debug, Clone, Serialize, Deserialize)]
3443pub struct ShardMove {
3444 pub shard_id: String,
3445 pub from_node: String,
3446 pub to_node: String,
3447}
3448
3449#[derive(Debug, Clone, Serialize, Deserialize)]
3451pub struct ShardRebalanceResponse {
3452 pub initiated: bool,
3453 pub operation_id: String,
3454 pub shards_affected: u32,
3455 #[serde(skip_serializing_if = "Option::is_none")]
3456 pub estimated_seconds: Option<u64>,
3457 #[serde(default)]
3458 pub planned_moves: Vec<ShardMove>,
3459}
3460
3461#[derive(Debug, Clone, Serialize, Deserialize)]
3463pub struct MaintenanceStatus {
3464 pub enabled: bool,
3465 #[serde(skip_serializing_if = "Option::is_none")]
3466 pub reason: Option<String>,
3467 #[serde(skip_serializing_if = "Option::is_none")]
3468 pub enabled_at: Option<u64>,
3469 #[serde(skip_serializing_if = "Option::is_none")]
3470 pub scheduled_end: Option<u64>,
3471 #[serde(default)]
3472 pub nodes_in_maintenance: Vec<String>,
3473 pub rejecting_requests: bool,
3474}
3475
3476#[derive(Debug, Clone, Serialize, Deserialize)]
3478pub struct EnableMaintenanceRequest {
3479 pub reason: String,
3480 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3481 pub node_ids: Vec<String>,
3482 #[serde(default)]
3483 pub reject_requests: bool,
3484 #[serde(skip_serializing_if = "Option::is_none")]
3485 pub duration_minutes: Option<u32>,
3486}
3487
3488#[derive(Debug, Clone, Serialize, Deserialize, Default)]
3490pub struct DisableMaintenanceRequest {
3491 #[serde(skip_serializing_if = "Option::is_none")]
3492 pub force: Option<bool>,
3493}
3494
3495#[derive(Debug, Clone, Serialize, Deserialize, Default)]
3497pub struct QuotaConfig {
3498 #[serde(skip_serializing_if = "Option::is_none")]
3499 pub max_vectors: Option<u64>,
3500 #[serde(skip_serializing_if = "Option::is_none")]
3501 pub max_storage_bytes: Option<u64>,
3502 #[serde(skip_serializing_if = "Option::is_none")]
3503 pub max_dimensions: Option<usize>,
3504 #[serde(skip_serializing_if = "Option::is_none")]
3505 pub max_metadata_bytes: Option<usize>,
3506 #[serde(default)]
3507 pub enforcement: String,
3508}
3509
3510#[derive(Debug, Clone, Serialize, Deserialize, Default)]
3512pub struct QuotaUsage {
3513 pub vector_count: u64,
3514 pub storage_bytes: u64,
3515 #[serde(skip_serializing_if = "Option::is_none")]
3516 pub avg_dimensions: Option<usize>,
3517 #[serde(skip_serializing_if = "Option::is_none")]
3518 pub avg_metadata_bytes: Option<usize>,
3519 pub last_updated: u64,
3520}
3521
3522#[derive(Debug, Clone, Serialize, Deserialize)]
3524pub struct QuotaStatus {
3525 pub namespace: String,
3526 pub config: QuotaConfig,
3527 pub usage: QuotaUsage,
3528 #[serde(skip_serializing_if = "Option::is_none")]
3529 pub vector_usage_percent: Option<f32>,
3530 #[serde(skip_serializing_if = "Option::is_none")]
3531 pub storage_usage_percent: Option<f32>,
3532 pub is_exceeded: bool,
3533 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3534 pub exceeded_quotas: Vec<String>,
3535}
3536
3537#[derive(Debug, Clone, Serialize, Deserialize)]
3539pub struct QuotaListResponse {
3540 pub quotas: Vec<QuotaStatus>,
3541 pub total: u64,
3542 #[serde(skip_serializing_if = "Option::is_none")]
3543 pub default_config: Option<QuotaConfig>,
3544}
3545
3546#[derive(Debug, Clone, Serialize, Deserialize)]
3548pub struct DefaultQuotaResponse {
3549 pub config: Option<QuotaConfig>,
3550}
3551
3552#[derive(Debug, Clone, Serialize, Deserialize, Default)]
3554pub struct SetDefaultQuotaRequest {
3555 pub config: Option<QuotaConfig>,
3556}
3557
3558#[derive(Debug, Clone, Serialize, Deserialize)]
3560pub struct SetQuotaRequest {
3561 pub config: QuotaConfig,
3562}
3563
3564#[derive(Debug, Clone, Serialize, Deserialize)]
3566pub struct SetQuotaResponse {
3567 pub success: bool,
3568 pub namespace: String,
3569 pub config: QuotaConfig,
3570 pub message: String,
3571}
3572
3573#[derive(Debug, Clone, Serialize, Deserialize)]
3575pub struct QuotaCheckRequest {
3576 pub vector_ids: Vec<String>,
3577 #[serde(skip_serializing_if = "Option::is_none")]
3578 pub dimensions: Option<usize>,
3579 #[serde(skip_serializing_if = "Option::is_none")]
3580 pub metadata_bytes: Option<usize>,
3581}
3582
3583#[derive(Debug, Clone, Serialize, Deserialize)]
3585pub struct QuotaCheckResult {
3586 pub allowed: bool,
3587 #[serde(skip_serializing_if = "Option::is_none")]
3588 pub reason: Option<String>,
3589 pub usage: QuotaUsage,
3590 #[serde(skip_serializing_if = "Option::is_none")]
3591 pub exceeded_quota: Option<String>,
3592}
3593
3594#[derive(Debug, Clone, Serialize, Deserialize)]
3596pub struct AdminBackupInfo {
3597 pub backup_id: String,
3598 pub name: String,
3599 pub backup_type: String,
3600 pub status: String,
3601 #[serde(default)]
3602 pub namespaces: Vec<String>,
3603 pub vector_count: u64,
3604 pub size_bytes: u64,
3605 pub created_at: u64,
3606 #[serde(skip_serializing_if = "Option::is_none")]
3607 pub completed_at: Option<u64>,
3608 #[serde(skip_serializing_if = "Option::is_none")]
3609 pub duration_seconds: Option<u64>,
3610 #[serde(skip_serializing_if = "Option::is_none")]
3611 pub storage_path: Option<String>,
3612 #[serde(skip_serializing_if = "Option::is_none")]
3613 pub error: Option<String>,
3614 pub encrypted: bool,
3615 #[serde(skip_serializing_if = "Option::is_none")]
3616 pub compression: Option<String>,
3617}
3618
3619#[derive(Debug, Clone, Serialize, Deserialize)]
3621pub struct BackupListResponse {
3622 pub backups: Vec<AdminBackupInfo>,
3623 pub total: u64,
3624}
3625
3626#[derive(Debug, Clone, Serialize, Deserialize)]
3628pub struct CreateBackupRequest {
3629 pub name: String,
3630 #[serde(skip_serializing_if = "Option::is_none")]
3631 pub backup_type: Option<String>,
3632 #[serde(skip_serializing_if = "Option::is_none")]
3633 pub namespaces: Option<Vec<String>>,
3634 #[serde(skip_serializing_if = "Option::is_none")]
3635 pub encrypt: Option<bool>,
3636 #[serde(skip_serializing_if = "Option::is_none")]
3637 pub compression: Option<String>,
3638}
3639
3640#[derive(Debug, Clone, Serialize, Deserialize)]
3642pub struct CreateBackupResponse {
3643 pub backup: AdminBackupInfo,
3644 #[serde(skip_serializing_if = "Option::is_none")]
3645 pub estimated_completion: Option<u64>,
3646}
3647
3648#[derive(Debug, Clone, Serialize, Deserialize)]
3650pub struct RestoreBackupRequest {
3651 pub backup_id: String,
3652 #[serde(skip_serializing_if = "Option::is_none")]
3653 pub target_namespaces: Option<Vec<String>>,
3654 #[serde(skip_serializing_if = "Option::is_none")]
3655 pub overwrite: Option<bool>,
3656 #[serde(skip_serializing_if = "Option::is_none")]
3657 pub point_in_time: Option<u64>,
3658}
3659
3660#[derive(Debug, Clone, Serialize, Deserialize)]
3662pub struct RestoreBackupResponse {
3663 pub restore_id: String,
3664 pub status: String,
3665 pub backup_id: String,
3666 #[serde(default)]
3667 pub namespaces: Vec<String>,
3668 pub started_at: u64,
3669 #[serde(skip_serializing_if = "Option::is_none")]
3670 pub estimated_completion: Option<u64>,
3671 #[serde(skip_serializing_if = "Option::is_none")]
3672 pub progress_percent: Option<u8>,
3673 #[serde(skip_serializing_if = "Option::is_none")]
3674 pub vectors_restored: Option<u64>,
3675 #[serde(skip_serializing_if = "Option::is_none")]
3676 pub completed_at: Option<u64>,
3677 #[serde(skip_serializing_if = "Option::is_none")]
3678 pub duration_seconds: Option<u64>,
3679 #[serde(skip_serializing_if = "Option::is_none")]
3680 pub error: Option<String>,
3681}
3682
3683#[derive(Debug, Clone, Serialize, Deserialize)]
3685pub struct BackupSchedule {
3686 pub enabled: bool,
3687 #[serde(skip_serializing_if = "Option::is_none")]
3688 pub cron: Option<String>,
3689 pub backup_type: String,
3690 pub retention_days: u32,
3691 pub max_backups: u32,
3692 #[serde(default)]
3693 pub namespaces: Vec<String>,
3694 pub encrypt: bool,
3695 #[serde(skip_serializing_if = "Option::is_none")]
3696 pub compression: Option<String>,
3697 #[serde(skip_serializing_if = "Option::is_none")]
3698 pub last_backup_at: Option<u64>,
3699 #[serde(skip_serializing_if = "Option::is_none")]
3700 pub next_backup_at: Option<u64>,
3701}
3702
3703#[derive(Debug, Clone, Serialize, Deserialize, Default)]
3705pub struct UpdateBackupScheduleRequest {
3706 #[serde(skip_serializing_if = "Option::is_none")]
3707 pub enabled: Option<bool>,
3708 #[serde(skip_serializing_if = "Option::is_none")]
3709 pub cron: Option<String>,
3710 #[serde(skip_serializing_if = "Option::is_none")]
3711 pub backup_type: Option<String>,
3712 #[serde(skip_serializing_if = "Option::is_none")]
3713 pub retention_days: Option<u32>,
3714 #[serde(skip_serializing_if = "Option::is_none")]
3715 pub max_backups: Option<u32>,
3716 #[serde(skip_serializing_if = "Option::is_none")]
3717 pub namespaces: Option<Vec<String>>,
3718 #[serde(skip_serializing_if = "Option::is_none")]
3719 pub encrypt: Option<bool>,
3720 #[serde(skip_serializing_if = "Option::is_none")]
3721 pub compression: Option<String>,
3722}
3723
3724fn default_route_top_k() -> usize {
3729 3
3730}
3731
3732fn default_route_min_similarity() -> f32 {
3733 0.3
3734}
3735
3736#[derive(Debug, Clone, Serialize, Deserialize)]
3738pub struct RouteRequest {
3739 pub query: String,
3741 #[serde(default = "default_route_top_k")]
3743 pub top_k: usize,
3744 #[serde(default = "default_route_min_similarity")]
3746 pub min_similarity: f32,
3747 #[serde(skip_serializing_if = "Option::is_none")]
3749 pub model: Option<String>,
3750}
3751
3752#[derive(Debug, Clone, Serialize, Deserialize)]
3754pub struct RouteMatch {
3755 pub namespace: String,
3757 pub similarity: f64,
3759 #[serde(skip_serializing_if = "Option::is_none")]
3761 pub description: Option<String>,
3762}
3763
3764#[derive(Debug, Clone, Serialize, Deserialize)]
3766pub struct RouteResponse {
3767 pub routes: Vec<RouteMatch>,
3769 pub model: String,
3771 pub embedding_time_ms: u64,
3773}
3774
3775#[derive(Debug, Clone, Serialize, Deserialize)]
3781pub struct ImportJobStatus {
3782 pub job_id: String,
3784 pub status: String,
3786 pub format: String,
3788 pub total: usize,
3790 pub imported: usize,
3792 pub skipped: usize,
3794 #[serde(default)]
3796 pub errors: Vec<String>,
3797 pub started_at: u64,
3799 #[serde(skip_serializing_if = "Option::is_none")]
3801 pub finished_at: Option<u64>,
3802}
3803
3804#[derive(Debug, Clone, Serialize, Deserialize)]
3810pub struct TierInfo {
3811 pub name: String,
3813 pub tier_type: String,
3815 pub technology: String,
3817 pub description: String,
3819 pub target_latency: String,
3821 #[serde(skip_serializing_if = "Option::is_none")]
3823 pub capacity: Option<String>,
3824 pub status: String,
3826 pub current_count: u64,
3828 pub hit_count: u64,
3830 pub hit_rate: f64,
3832}
3833
3834#[derive(Debug, Clone, Serialize, Deserialize)]
3836pub struct TierConfig {
3837 pub hot_tier_capacity: usize,
3839 pub hot_to_warm_threshold_secs: u64,
3841 pub warm_to_cold_threshold_secs: u64,
3843 pub auto_tier_enabled: bool,
3845 pub tier_check_interval_secs: u64,
3847}
3848
3849#[derive(Debug, Clone, Serialize, Deserialize)]
3851pub struct TierActivity {
3852 pub promotions: u64,
3854 pub demotions: u64,
3856 pub cache_hit_rate: f64,
3858 pub storage_backend: String,
3860 pub promotions_to_hot: u64,
3862 pub demotions_to_warm: u64,
3864 pub demotions_to_cold: u64,
3866}
3867
3868#[derive(Debug, Clone, Serialize, Deserialize)]
3870pub struct StorageTierOverview {
3871 pub tiers_enabled: bool,
3873 pub architecture: Vec<TierInfo>,
3875 pub config: TierConfig,
3877 pub activity: TierActivity,
3879}
3880
3881#[derive(Debug, Clone, Serialize, Deserialize)]
3887pub struct MemoryTypeStatsResponse {
3888 pub total: u64,
3890 pub working: u64,
3892 pub episodic: u64,
3894 pub semantic: u64,
3896 pub procedural: u64,
3898 pub agent_namespaces: u64,
3900}
3901
3902fn default_target_dimension() -> usize {
3907 1024
3908}
3909
3910#[derive(Debug, Clone, Serialize, Deserialize)]
3912pub struct MigrateNamespaceDimensionsRequest {
3913 #[serde(default)]
3915 pub namespaces: Vec<String>,
3916 #[serde(default = "default_target_dimension")]
3918 pub target_dimension: usize,
3919}
3920
3921#[derive(Debug, Clone, Serialize, Deserialize)]
3923pub struct NamespaceMigrationResult {
3924 pub namespace: String,
3926 pub original_dimension: usize,
3928 pub vectors_migrated: usize,
3930 pub vectors_skipped: usize,
3932 pub status: String,
3934 #[serde(skip_serializing_if = "Option::is_none")]
3936 pub error: Option<String>,
3937}
3938
3939#[derive(Debug, Clone, Serialize, Deserialize)]
3941pub struct MigrateDimensionsResponse {
3942 pub migrated: usize,
3944 pub failed: usize,
3946 pub already_current: usize,
3948 pub results: Vec<NamespaceMigrationResult>,
3950}
3951
3952#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3954pub struct DrainReembedRequest {
3955 #[serde(skip_serializing_if = "Option::is_none")]
3957 pub timeout_secs: Option<u64>,
3958 #[serde(skip_serializing_if = "Option::is_none")]
3960 pub batch_size: Option<usize>,
3961 #[serde(skip_serializing_if = "Option::is_none")]
3963 pub min_importance: Option<f32>,
3964}
3965
3966#[derive(Debug, Clone, Serialize, Deserialize)]
3971pub struct DrainReembedResponse {
3972 pub processed: usize,
3974 pub remaining: usize,
3976 pub elapsed_ms: u128,
3978 pub cycles: usize,
3980 pub timed_out: bool,
3982}