1use std::collections::HashMap;
7
8use serde::{Deserialize, Serialize};
9
10use crate::error::Result;
11use crate::DakeraClient;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct OpsStats {
20 pub version: String,
21 pub total_vectors: u64,
22 pub namespace_count: u64,
23 pub uptime_seconds: u64,
24 pub timestamp: u64,
25 pub state: String,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct ClusterStatus {
31 pub cluster_id: String,
32 pub state: String,
33 pub node_count: u32,
34 pub total_vectors: u64,
35 pub namespace_count: u64,
36 pub version: String,
37 pub timestamp: u64,
38 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub redis_healthy: Option<bool>,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct NodeInfo {
46 pub node_id: String,
47 pub address: String,
48 pub role: String,
49 pub status: String,
50 pub version: String,
51 pub uptime_seconds: u64,
52 pub vector_count: u64,
53 pub memory_bytes: u64,
54 #[serde(default)]
55 pub cpu_percent: f32,
56 #[serde(default)]
57 pub memory_percent: f32,
58 pub last_heartbeat: u64,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct NodeListResponse {
64 pub nodes: Vec<NodeInfo>,
65 pub total: u32,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct IndexStats {
75 pub index_type: String,
76 pub is_built: bool,
77 pub size_bytes: u64,
78 pub indexed_vectors: u64,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub last_rebuild: Option<u64>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct NamespaceAdminInfo {
86 pub name: String,
87 pub vector_count: u64,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub dimension: Option<usize>,
90 pub index_type: String,
91 pub storage_bytes: u64,
92 pub document_count: u64,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub created_at: Option<u64>,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub updated_at: Option<u64>,
97 pub index_stats: IndexStats,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct NamespaceListResponse {
103 pub namespaces: Vec<NamespaceAdminInfo>,
104 pub total: u64,
105 pub total_vectors: u64,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct OptimizeRequest {
111 #[serde(default)]
112 pub force: bool,
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub target_index_type: Option<String>,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct OptimizeResponse {
120 pub success: bool,
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub job_id: Option<String>,
123 pub message: String,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct IndexStatsResponse {
133 pub namespaces: HashMap<String, IndexStats>,
134 pub total_indexed_vectors: u64,
135 pub total_size_bytes: u64,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct RebuildIndexRequest {
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub namespace: Option<String>,
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub index_type: Option<String>,
145 #[serde(default)]
146 pub force: bool,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct RebuildIndexResponse {
152 pub success: bool,
153 pub job_id: String,
154 pub message: String,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct CacheStats {
164 pub enabled: bool,
165 pub cache_type: String,
166 pub entries: u64,
167 pub size_bytes: u64,
168 pub hits: u64,
169 pub misses: u64,
170 pub hit_rate: f64,
171 pub evictions: u64,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct ClearCacheRequest {
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub namespace: Option<String>,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct ClearCacheResponse {
184 pub success: bool,
185 pub entries_cleared: u64,
186 pub message: String,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct RuntimeConfig {
196 #[serde(skip_serializing_if = "Option::is_none")]
197 pub max_vectors_per_namespace: Option<u64>,
198 pub default_index_type: String,
199 pub cache_enabled: bool,
200 pub cache_max_size_bytes: u64,
201 pub rate_limit_enabled: bool,
202 pub rate_limit_rps: u32,
203 pub query_timeout_ms: u64,
204 #[serde(default = "default_true")]
206 pub autopilot_enabled: bool,
207 #[serde(default = "default_dedup_threshold")]
209 pub autopilot_dedup_threshold: f32,
210 #[serde(default = "default_dedup_interval")]
212 pub autopilot_dedup_interval_hours: u64,
213 #[serde(default = "default_consolidation_interval")]
215 pub autopilot_consolidation_interval_hours: u64,
216}
217
218fn default_true() -> bool {
219 true
220}
221fn default_dedup_threshold() -> f32 {
222 0.93
223}
224fn default_dedup_interval() -> u64 {
225 6
226}
227fn default_consolidation_interval() -> u64 {
228 12
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct UpdateConfigResponse {
234 pub success: bool,
235 pub config: RuntimeConfig,
236 pub message: String,
237 #[serde(default, skip_serializing_if = "Vec::is_empty")]
238 pub warnings: Vec<String>,
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
247pub struct QuotaConfig {
248 #[serde(skip_serializing_if = "Option::is_none")]
249 pub max_vectors: Option<u64>,
250 #[serde(skip_serializing_if = "Option::is_none")]
251 pub max_storage_bytes: Option<u64>,
252 #[serde(skip_serializing_if = "Option::is_none")]
253 pub max_queries_per_minute: Option<u64>,
254 #[serde(skip_serializing_if = "Option::is_none")]
255 pub max_writes_per_minute: Option<u64>,
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize)]
260pub struct QuotaUsage {
261 #[serde(default)]
262 pub current_vectors: u64,
263 #[serde(default)]
264 pub current_storage_bytes: u64,
265 #[serde(default)]
266 pub queries_this_minute: u64,
267 #[serde(default)]
268 pub writes_this_minute: u64,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
273pub struct QuotaStatus {
274 pub namespace: String,
275 pub config: QuotaConfig,
276 pub usage: QuotaUsage,
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct QuotaListResponse {
282 pub quotas: Vec<QuotaStatus>,
283 pub total: u64,
284 #[serde(skip_serializing_if = "Option::is_none")]
285 pub default_config: Option<QuotaConfig>,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize)]
294pub struct SlowQueryEntry {
295 pub id: String,
296 pub timestamp: u64,
297 pub namespace: String,
298 pub query_type: String,
299 pub duration_ms: f64,
300 #[serde(default)]
301 pub parameters: Option<serde_json::Value>,
302 #[serde(default)]
303 pub results_count: u64,
304 #[serde(default)]
305 pub vectors_scanned: u64,
306}
307
308#[derive(Debug, Clone, Serialize, Deserialize)]
310pub struct SlowQueryListResponse {
311 pub queries: Vec<SlowQueryEntry>,
312 pub total: u64,
313 pub threshold_ms: f64,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct BackupInfo {
323 pub backup_id: String,
324 pub name: String,
325 pub backup_type: String,
326 pub status: String,
327 pub namespaces: Vec<String>,
328 pub vector_count: u64,
329 pub size_bytes: u64,
330 pub created_at: u64,
331 #[serde(skip_serializing_if = "Option::is_none")]
332 pub completed_at: Option<u64>,
333 #[serde(skip_serializing_if = "Option::is_none")]
334 pub duration_seconds: Option<u64>,
335 #[serde(skip_serializing_if = "Option::is_none")]
336 pub storage_path: Option<String>,
337 #[serde(skip_serializing_if = "Option::is_none")]
338 pub error: Option<String>,
339 pub encrypted: bool,
340 #[serde(skip_serializing_if = "Option::is_none")]
341 pub compression: Option<String>,
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize)]
346pub struct BackupListResponse {
347 pub backups: Vec<BackupInfo>,
348 pub total: u64,
349}
350
351#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct CreateBackupRequest {
354 pub name: String,
355 #[serde(skip_serializing_if = "Option::is_none")]
356 pub backup_type: Option<String>,
357 #[serde(skip_serializing_if = "Option::is_none")]
358 pub namespaces: Option<Vec<String>>,
359 #[serde(skip_serializing_if = "Option::is_none")]
360 pub encrypt: Option<bool>,
361 #[serde(skip_serializing_if = "Option::is_none")]
362 pub compression: Option<String>,
363}
364
365#[derive(Debug, Clone, Serialize, Deserialize)]
367pub struct CreateBackupResponse {
368 pub backup: BackupInfo,
369 #[serde(skip_serializing_if = "Option::is_none")]
370 pub estimated_completion: Option<u64>,
371}
372
373#[derive(Debug, Clone, Serialize, Deserialize)]
375pub struct RestoreBackupRequest {
376 pub backup_id: String,
377 #[serde(skip_serializing_if = "Option::is_none")]
378 pub target_namespaces: Option<Vec<String>>,
379 #[serde(skip_serializing_if = "Option::is_none")]
380 pub overwrite: Option<bool>,
381 #[serde(skip_serializing_if = "Option::is_none")]
382 pub point_in_time: Option<u64>,
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize)]
387pub struct RestoreBackupResponse {
388 pub restore_id: String,
389 pub status: String,
390 pub backup_id: String,
391 pub namespaces: Vec<String>,
392 pub started_at: u64,
393 #[serde(skip_serializing_if = "Option::is_none")]
394 pub estimated_completion: Option<u64>,
395 #[serde(skip_serializing_if = "Option::is_none")]
396 pub progress_percent: Option<u8>,
397 #[serde(skip_serializing_if = "Option::is_none")]
398 pub vectors_restored: Option<u64>,
399 #[serde(skip_serializing_if = "Option::is_none")]
400 pub completed_at: Option<u64>,
401 #[serde(skip_serializing_if = "Option::is_none")]
402 pub duration_seconds: Option<u64>,
403 #[serde(skip_serializing_if = "Option::is_none")]
404 pub error: Option<String>,
405}
406
407#[derive(Debug, Clone, Serialize, Deserialize)]
413pub struct AutoPilotConfig {
414 pub enabled: bool,
415 pub dedup_threshold: f32,
416 pub dedup_interval_hours: u64,
417 pub consolidation_interval_hours: u64,
418}
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
422pub struct DedupResultSnapshot {
423 pub namespaces_processed: usize,
424 pub memories_scanned: usize,
425 pub duplicates_removed: usize,
426}
427
428#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct ConsolidationResultSnapshot {
431 pub namespaces_processed: usize,
432 pub memories_scanned: usize,
433 pub clusters_merged: usize,
434 pub memories_consolidated: usize,
435}
436
437#[derive(Debug, Clone, Serialize, Deserialize)]
439pub struct AutoPilotStatusResponse {
440 pub config: AutoPilotConfig,
441 #[serde(skip_serializing_if = "Option::is_none")]
442 pub last_dedup_at: Option<u64>,
443 #[serde(skip_serializing_if = "Option::is_none")]
444 pub last_consolidation_at: Option<u64>,
445 #[serde(skip_serializing_if = "Option::is_none")]
446 pub last_dedup: Option<DedupResultSnapshot>,
447 #[serde(skip_serializing_if = "Option::is_none")]
448 pub last_consolidation: Option<ConsolidationResultSnapshot>,
449 pub total_dedup_removed: u64,
450 pub total_consolidated: u64,
451}
452
453#[derive(Debug, Clone, Serialize, Deserialize, Default)]
455pub struct AutoPilotConfigRequest {
456 #[serde(skip_serializing_if = "Option::is_none")]
457 pub enabled: Option<bool>,
458 #[serde(skip_serializing_if = "Option::is_none")]
459 pub dedup_threshold: Option<f32>,
460 #[serde(skip_serializing_if = "Option::is_none")]
461 pub dedup_interval_hours: Option<u64>,
462 #[serde(skip_serializing_if = "Option::is_none")]
463 pub consolidation_interval_hours: Option<u64>,
464}
465
466#[derive(Debug, Clone, Serialize, Deserialize)]
468pub struct AutoPilotConfigResponse {
469 pub success: bool,
470 pub config: AutoPilotConfig,
471 pub message: String,
472}
473
474#[derive(Debug, Clone, Serialize, Deserialize)]
476#[serde(rename_all = "lowercase")]
477pub enum AutoPilotTriggerAction {
478 Dedup,
479 Consolidate,
480 All,
481}
482
483#[derive(Debug, Clone, Serialize, Deserialize)]
485pub struct AutoPilotTriggerRequest {
486 pub action: AutoPilotTriggerAction,
487}
488
489#[derive(Debug, Clone, Serialize, Deserialize)]
491pub struct AutoPilotDedupResult {
492 pub namespaces_processed: usize,
493 pub memories_scanned: usize,
494 pub duplicates_removed: usize,
495}
496
497#[derive(Debug, Clone, Serialize, Deserialize)]
499pub struct AutoPilotConsolidationResult {
500 pub namespaces_processed: usize,
501 pub memories_scanned: usize,
502 pub clusters_merged: usize,
503 pub memories_consolidated: usize,
504}
505
506#[derive(Debug, Clone, Serialize, Deserialize)]
508pub struct AutoPilotTriggerResponse {
509 pub success: bool,
510 pub action: AutoPilotTriggerAction,
511 #[serde(skip_serializing_if = "Option::is_none")]
512 pub dedup: Option<AutoPilotDedupResult>,
513 #[serde(skip_serializing_if = "Option::is_none")]
514 pub consolidation: Option<AutoPilotConsolidationResult>,
515 pub message: String,
516}
517
518#[derive(Debug, Clone, Serialize, Deserialize)]
524pub struct DecayConfigResponse {
525 pub strategy: String,
527 pub half_life_hours: f64,
529 pub min_importance: f32,
531}
532
533#[derive(Debug, Clone, Serialize, Deserialize, Default)]
535pub struct DecayConfigUpdateRequest {
536 #[serde(skip_serializing_if = "Option::is_none")]
538 pub strategy: Option<String>,
539 #[serde(skip_serializing_if = "Option::is_none")]
541 pub half_life_hours: Option<f64>,
542 #[serde(skip_serializing_if = "Option::is_none")]
544 pub min_importance: Option<f32>,
545}
546
547#[derive(Debug, Clone, Serialize, Deserialize)]
549pub struct DecayConfigUpdateResponse {
550 pub success: bool,
551 pub config: DecayConfigResponse,
552 pub message: String,
553}
554
555#[derive(Debug, Clone, Serialize, Deserialize)]
557pub struct LastDecayCycleStats {
558 pub namespaces_processed: usize,
559 pub memories_processed: usize,
560 pub memories_decayed: usize,
561 pub memories_deleted: usize,
562}
563
564#[derive(Debug, Clone, Serialize, Deserialize)]
566pub struct DecayStatsResponse {
567 pub total_decayed: u64,
569 pub total_deleted: u64,
571 #[serde(skip_serializing_if = "Option::is_none")]
573 pub last_run_at: Option<u64>,
574 pub cycles_run: u64,
576 #[serde(skip_serializing_if = "Option::is_none")]
578 pub last_cycle: Option<LastDecayCycleStats>,
579}
580
581#[derive(Debug, Clone, Serialize, Deserialize)]
587pub struct TtlCleanupRequest {
588 #[serde(skip_serializing_if = "Option::is_none")]
589 pub namespace: Option<String>,
590}
591
592#[derive(Debug, Clone, Serialize, Deserialize)]
594pub struct TtlCleanupResponse {
595 pub success: bool,
596 pub vectors_removed: u64,
597 pub namespaces_cleaned: Vec<String>,
598 pub message: String,
599}
600
601#[derive(Debug, Clone, Serialize, Deserialize)]
603pub struct TtlStats {
604 pub namespace: String,
605 pub vectors_with_ttl: u64,
606 pub expiring_within_hour: u64,
607 pub expiring_within_day: u64,
608 pub expired_pending_cleanup: u64,
609}
610
611#[derive(Debug, Clone, Serialize, Deserialize)]
613pub struct TtlStatsResponse {
614 pub namespaces: Vec<TtlStats>,
615 pub total_with_ttl: u64,
616 pub total_expired: u64,
617}
618
619impl DakeraClient {
624 pub async fn ops_stats(&self) -> Result<OpsStats> {
632 let url = format!("{}/v1/ops/stats", self.base_url);
633 let response = self.client.get(&url).send().await?;
634 self.handle_response(response).await
635 }
636
637 pub async fn ops_metrics(&self) -> Result<String> {
642 let url = format!("{}/v1/ops/metrics", self.base_url);
643 let response = self.client.get(&url).send().await?;
644 self.handle_text_response(response).await
645 }
646
647 pub async fn cluster_status(&self) -> Result<ClusterStatus> {
649 let url = format!("{}/v1/admin/cluster/status", self.base_url);
650 let response = self.client.get(&url).send().await?;
651 self.handle_response(response).await
652 }
653
654 pub async fn cluster_nodes(&self) -> Result<NodeListResponse> {
656 let url = format!("{}/v1/admin/cluster/nodes", self.base_url);
657 let response = self.client.get(&url).send().await?;
658 self.handle_response(response).await
659 }
660
661 pub async fn list_namespaces_admin(&self) -> Result<NamespaceListResponse> {
667 let url = format!("{}/v1/admin/namespaces", self.base_url);
668 let response = self.client.get(&url).send().await?;
669 self.handle_response(response).await
670 }
671
672 pub async fn delete_namespace_admin(&self, namespace: &str) -> Result<serde_json::Value> {
674 let url = format!("{}/v1/admin/namespaces/{}", self.base_url, namespace);
675 let response = self.client.delete(&url).send().await?;
676 self.handle_response(response).await
677 }
678
679 pub async fn optimize_namespace(
681 &self,
682 namespace: &str,
683 request: OptimizeRequest,
684 ) -> Result<OptimizeResponse> {
685 let url = format!(
686 "{}/v1/admin/namespaces/{}/optimize",
687 self.base_url, namespace
688 );
689 let response = self.client.post(&url).json(&request).send().await?;
690 self.handle_response(response).await
691 }
692
693 pub async fn index_stats(&self) -> Result<IndexStatsResponse> {
699 let url = format!("{}/v1/admin/indexes/stats", self.base_url);
700 let response = self.client.get(&url).send().await?;
701 self.handle_response(response).await
702 }
703
704 pub async fn rebuild_indexes(
706 &self,
707 request: RebuildIndexRequest,
708 ) -> Result<RebuildIndexResponse> {
709 let url = format!("{}/v1/admin/indexes/rebuild", self.base_url);
710 let response = self.client.post(&url).json(&request).send().await?;
711 self.handle_response(response).await
712 }
713
714 pub async fn cache_stats(&self) -> Result<CacheStats> {
720 let url = format!("{}/v1/admin/cache/stats", self.base_url);
721 let response = self.client.get(&url).send().await?;
722 self.handle_response(response).await
723 }
724
725 pub async fn cache_clear(&self, namespace: Option<&str>) -> Result<ClearCacheResponse> {
727 let url = format!("{}/v1/admin/cache/clear", self.base_url);
728 let request = ClearCacheRequest {
729 namespace: namespace.map(|s| s.to_string()),
730 };
731 let response = self.client.post(&url).json(&request).send().await?;
732 self.handle_response(response).await
733 }
734
735 pub async fn get_config(&self) -> Result<RuntimeConfig> {
741 let url = format!("{}/v1/admin/config", self.base_url);
742 let response = self.client.get(&url).send().await?;
743 self.handle_response(response).await
744 }
745
746 pub async fn update_config(
748 &self,
749 updates: HashMap<String, serde_json::Value>,
750 ) -> Result<UpdateConfigResponse> {
751 let url = format!("{}/v1/admin/config", self.base_url);
752 let response = self.client.put(&url).json(&updates).send().await?;
753 self.handle_response(response).await
754 }
755
756 pub async fn get_quotas(&self) -> Result<QuotaListResponse> {
762 let url = format!("{}/v1/admin/quotas", self.base_url);
763 let response = self.client.get(&url).send().await?;
764 self.handle_response(response).await
765 }
766
767 pub async fn get_quota(&self, namespace: &str) -> Result<QuotaStatus> {
769 let url = format!("{}/v1/admin/quotas/{}", self.base_url, namespace);
770 let response = self.client.get(&url).send().await?;
771 self.handle_response(response).await
772 }
773
774 pub async fn set_quota(
776 &self,
777 namespace: &str,
778 config: QuotaConfig,
779 ) -> Result<serde_json::Value> {
780 let url = format!("{}/v1/admin/quotas/{}", self.base_url, namespace);
781 let request = serde_json::json!({ "config": config });
782 let response = self.client.put(&url).json(&request).send().await?;
783 self.handle_response(response).await
784 }
785
786 pub async fn delete_quota(&self, namespace: &str) -> Result<serde_json::Value> {
788 let url = format!("{}/v1/admin/quotas/{}", self.base_url, namespace);
789 let response = self.client.delete(&url).send().await?;
790 self.handle_response(response).await
791 }
792
793 pub async fn update_quotas(&self, config: Option<QuotaConfig>) -> Result<serde_json::Value> {
795 let url = format!("{}/v1/admin/quotas/default", self.base_url);
796 let request = serde_json::json!({ "config": config });
797 let response = self.client.put(&url).json(&request).send().await?;
798 self.handle_response(response).await
799 }
800
801 pub async fn slow_queries(
807 &self,
808 limit: Option<usize>,
809 namespace: Option<&str>,
810 query_type: Option<&str>,
811 ) -> Result<SlowQueryListResponse> {
812 let mut url = format!("{}/v1/admin/slow-queries", self.base_url);
813 let mut params = Vec::new();
814 if let Some(l) = limit {
815 params.push(format!("limit={}", l));
816 }
817 if let Some(ns) = namespace {
818 params.push(format!("namespace={}", ns));
819 }
820 if let Some(qt) = query_type {
821 params.push(format!("query_type={}", qt));
822 }
823 if !params.is_empty() {
824 url.push('?');
825 url.push_str(¶ms.join("&"));
826 }
827 let response = self.client.get(&url).send().await?;
828 self.handle_response(response).await
829 }
830
831 pub async fn slow_query_summary(&self) -> Result<serde_json::Value> {
833 let url = format!("{}/v1/admin/slow-queries/summary", self.base_url);
834 let response = self.client.get(&url).send().await?;
835 self.handle_response(response).await
836 }
837
838 pub async fn clear_slow_queries(&self) -> Result<serde_json::Value> {
840 let url = format!("{}/v1/admin/slow-queries", self.base_url);
841 let response = self.client.delete(&url).send().await?;
842 self.handle_response(response).await
843 }
844
845 pub async fn create_backup(
851 &self,
852 request: CreateBackupRequest,
853 ) -> Result<CreateBackupResponse> {
854 let url = format!("{}/v1/admin/backups", self.base_url);
855 let response = self.client.post(&url).json(&request).send().await?;
856 self.handle_response(response).await
857 }
858
859 pub async fn list_backups(&self) -> Result<BackupListResponse> {
861 let url = format!("{}/v1/admin/backups", self.base_url);
862 let response = self.client.get(&url).send().await?;
863 self.handle_response(response).await
864 }
865
866 pub async fn get_backup(&self, backup_id: &str) -> Result<BackupInfo> {
868 let url = format!("{}/v1/admin/backups/{}", self.base_url, backup_id);
869 let response = self.client.get(&url).send().await?;
870 self.handle_response(response).await
871 }
872
873 pub async fn restore_backup(
875 &self,
876 request: RestoreBackupRequest,
877 ) -> Result<RestoreBackupResponse> {
878 let url = format!("{}/v1/admin/backups/restore", self.base_url);
879 let response = self.client.post(&url).json(&request).send().await?;
880 self.handle_response(response).await
881 }
882
883 pub async fn delete_backup(&self, backup_id: &str) -> Result<serde_json::Value> {
885 let url = format!("{}/v1/admin/backups/{}", self.base_url, backup_id);
886 let response = self.client.delete(&url).send().await?;
887 self.handle_response(response).await
888 }
889
890 pub async fn configure_ttl(
896 &self,
897 namespace: &str,
898 ttl_seconds: u64,
899 strategy: Option<&str>,
900 ) -> Result<serde_json::Value> {
901 let url = format!("{}/v1/admin/namespaces/{}/ttl", self.base_url, namespace);
902 let mut body = serde_json::json!({ "ttl_seconds": ttl_seconds });
903 if let Some(s) = strategy {
904 body["strategy"] = serde_json::Value::String(s.to_string());
905 }
906 let response = self.client.post(&url).json(&body).send().await?;
907 self.handle_response(response).await
908 }
909
910 pub async fn ttl_cleanup(&self, namespace: Option<&str>) -> Result<TtlCleanupResponse> {
912 let url = format!("{}/v1/admin/ttl/cleanup", self.base_url);
913 let request = TtlCleanupRequest {
914 namespace: namespace.map(|s| s.to_string()),
915 };
916 let response = self.client.post(&url).json(&request).send().await?;
917 self.handle_response(response).await
918 }
919
920 pub async fn ttl_stats(&self) -> Result<TtlStatsResponse> {
922 let url = format!("{}/v1/admin/ttl/stats", self.base_url);
923 let response = self.client.get(&url).send().await?;
924 self.handle_response(response).await
925 }
926
927 pub async fn autopilot_status(&self) -> Result<AutoPilotStatusResponse> {
933 let url = format!("{}/v1/admin/autopilot/status", self.base_url);
934 let response = self.client.get(&url).send().await?;
935 self.handle_response(response).await
936 }
937
938 pub async fn autopilot_update_config(
942 &self,
943 request: AutoPilotConfigRequest,
944 ) -> Result<AutoPilotConfigResponse> {
945 let url = format!("{}/v1/admin/autopilot/config", self.base_url);
946 let response = self.client.put(&url).json(&request).send().await?;
947 self.handle_response(response).await
948 }
949
950 pub async fn autopilot_trigger(
955 &self,
956 action: AutoPilotTriggerAction,
957 ) -> Result<AutoPilotTriggerResponse> {
958 let url = format!("{}/v1/admin/autopilot/trigger", self.base_url);
959 let request = AutoPilotTriggerRequest { action };
960 let response = self.client.post(&url).json(&request).send().await?;
961 self.handle_response(response).await
962 }
963
964 pub async fn decay_config(&self) -> Result<DecayConfigResponse> {
973 let url = format!("{}/v1/admin/decay/config", self.base_url);
974 let response = self.client.get(&url).send().await?;
975 self.handle_response(response).await
976 }
977
978 pub async fn decay_update_config(
984 &self,
985 request: DecayConfigUpdateRequest,
986 ) -> Result<DecayConfigUpdateResponse> {
987 let url = format!("{}/v1/admin/decay/config", self.base_url);
988 let response = self.client.put(&url).json(&request).send().await?;
989 self.handle_response(response).await
990 }
991
992 pub async fn decay_stats(&self) -> Result<DecayStatsResponse> {
997 let url = format!("{}/v1/admin/decay/stats", self.base_url);
998 let response = self.client.get(&url).send().await?;
999 self.handle_response(response).await
1000 }
1001
1002 pub async fn get_kpis(&self) -> Result<KpiSnapshot> {
1012 let url = format!("{}/kpis", self.base_url);
1013 let response = self.client.get(&url).send().await?;
1014 self.handle_response(response).await
1015 }
1016
1017 pub async fn admin_fulltext_reindex(
1029 &self,
1030 namespace: Option<&str>,
1031 ) -> Result<FulltextReindexResponse> {
1032 let url = format!("{}/v1/admin/fulltext/reindex", self.base_url);
1033 let body = serde_json::json!({ "namespace": namespace });
1034 let response = self.client.post(&url).json(&body).send().await?;
1035 self.handle_response(response).await
1036 }
1037
1038 pub async fn admin_cluster_replication(&self) -> Result<crate::types::ReplicationStatus> {
1044 let url = format!("{}/v1/admin/cluster/replication", self.base_url);
1045 let response = self.client.get(&url).send().await?;
1046 self.handle_response(response).await
1047 }
1048
1049 pub async fn admin_list_shards(&self) -> Result<crate::types::ShardListResponse> {
1051 let url = format!("{}/v1/admin/cluster/shards", self.base_url);
1052 let response = self.client.get(&url).send().await?;
1053 self.handle_response(response).await
1054 }
1055
1056 pub async fn admin_rebalance_shards(
1058 &self,
1059 request: crate::types::ShardRebalanceRequest,
1060 ) -> Result<crate::types::ShardRebalanceResponse> {
1061 let url = format!("{}/v1/admin/cluster/shards/rebalance", self.base_url);
1062 let response = self.client.post(&url).json(&request).send().await?;
1063 self.handle_response(response).await
1064 }
1065
1066 pub async fn admin_maintenance_status(&self) -> Result<crate::types::MaintenanceStatus> {
1068 let url = format!("{}/v1/admin/cluster/maintenance", self.base_url);
1069 let response = self.client.get(&url).send().await?;
1070 self.handle_response(response).await
1071 }
1072
1073 pub async fn admin_enable_maintenance(
1075 &self,
1076 request: crate::types::EnableMaintenanceRequest,
1077 ) -> Result<crate::types::MaintenanceStatus> {
1078 let url = format!("{}/v1/admin/cluster/maintenance/enable", self.base_url);
1079 let response = self.client.post(&url).json(&request).send().await?;
1080 self.handle_response(response).await
1081 }
1082
1083 pub async fn admin_disable_maintenance(
1085 &self,
1086 request: crate::types::DisableMaintenanceRequest,
1087 ) -> Result<crate::types::MaintenanceStatus> {
1088 let url = format!("{}/v1/admin/cluster/maintenance/disable", self.base_url);
1089 let response = self.client.post(&url).json(&request).send().await?;
1090 self.handle_response(response).await
1091 }
1092
1093 pub async fn admin_list_quotas(&self) -> Result<crate::types::QuotaListResponse> {
1099 let url = format!("{}/v1/admin/quotas", self.base_url);
1100 let response = self.client.get(&url).send().await?;
1101 self.handle_response(response).await
1102 }
1103
1104 pub async fn admin_get_default_quota(&self) -> Result<crate::types::DefaultQuotaResponse> {
1106 let url = format!("{}/v1/admin/quotas/default", self.base_url);
1107 let response = self.client.get(&url).send().await?;
1108 self.handle_response(response).await
1109 }
1110
1111 pub async fn admin_set_default_quota(
1113 &self,
1114 request: crate::types::SetDefaultQuotaRequest,
1115 ) -> Result<crate::types::SetQuotaResponse> {
1116 let url = format!("{}/v1/admin/quotas/default", self.base_url);
1117 let response = self.client.put(&url).json(&request).send().await?;
1118 self.handle_response(response).await
1119 }
1120
1121 pub async fn admin_get_quota(&self, namespace: &str) -> Result<crate::types::QuotaStatus> {
1123 let url = format!("{}/v1/admin/quotas/{}", self.base_url, namespace);
1124 let response = self.client.get(&url).send().await?;
1125 self.handle_response(response).await
1126 }
1127
1128 pub async fn admin_set_quota(
1130 &self,
1131 namespace: &str,
1132 request: crate::types::SetQuotaRequest,
1133 ) -> Result<crate::types::SetQuotaResponse> {
1134 let url = format!("{}/v1/admin/quotas/{}", self.base_url, namespace);
1135 let response = self.client.put(&url).json(&request).send().await?;
1136 self.handle_response(response).await
1137 }
1138
1139 pub async fn admin_delete_quota(&self, namespace: &str) -> Result<serde_json::Value> {
1141 let url = format!("{}/v1/admin/quotas/{}", self.base_url, namespace);
1142 let response = self.client.delete(&url).send().await?;
1143 self.handle_response(response).await
1144 }
1145
1146 pub async fn admin_check_quota(
1148 &self,
1149 namespace: &str,
1150 request: crate::types::QuotaCheckRequest,
1151 ) -> Result<crate::types::QuotaCheckResult> {
1152 let url = format!("{}/v1/admin/quotas/{}/check", self.base_url, namespace);
1153 let response = self.client.post(&url).json(&request).send().await?;
1154 self.handle_response(response).await
1155 }
1156
1157 pub async fn admin_list_slow_queries(
1163 &self,
1164 namespace: Option<&str>,
1165 query_type: Option<&str>,
1166 limit: Option<u32>,
1167 ) -> Result<Vec<serde_json::Value>> {
1168 let mut url = format!("{}/v1/admin/slow-queries", self.base_url);
1169 let mut params = Vec::new();
1170 if let Some(ns) = namespace {
1171 params.push(format!("namespace={}", ns));
1172 }
1173 if let Some(qt) = query_type {
1174 params.push(format!("query_type={}", qt));
1175 }
1176 if let Some(l) = limit {
1177 params.push(format!("limit={}", l));
1178 }
1179 if !params.is_empty() {
1180 url.push('?');
1181 url.push_str(¶ms.join("&"));
1182 }
1183 let response = self.client.get(&url).send().await?;
1184 self.handle_response(response).await
1185 }
1186
1187 pub async fn admin_slow_query_summary(&self) -> Result<serde_json::Value> {
1189 let url = format!("{}/v1/admin/slow-queries/summary", self.base_url);
1190 let response = self.client.get(&url).send().await?;
1191 self.handle_response(response).await
1192 }
1193
1194 pub async fn admin_clear_slow_queries(
1196 &self,
1197 namespace: Option<&str>,
1198 ) -> Result<serde_json::Value> {
1199 let mut url = format!("{}/v1/admin/slow-queries", self.base_url);
1200 if let Some(ns) = namespace {
1201 url.push_str(&format!("?namespace={}", ns));
1202 }
1203 let response = self.client.delete(&url).send().await?;
1204 self.handle_response(response).await
1205 }
1206
1207 pub async fn admin_update_slow_query_config(
1209 &self,
1210 config: serde_json::Value,
1211 ) -> Result<serde_json::Value> {
1212 let url = format!("{}/v1/admin/slow-queries/config", self.base_url);
1213 let response = self.client.patch(&url).json(&config).send().await?;
1214 self.handle_response(response).await
1215 }
1216
1217 pub async fn admin_list_backups(&self) -> Result<crate::types::BackupListResponse> {
1223 let url = format!("{}/v1/admin/backups", self.base_url);
1224 let response = self.client.get(&url).send().await?;
1225 self.handle_response(response).await
1226 }
1227
1228 pub async fn admin_create_backup(
1230 &self,
1231 request: crate::types::CreateBackupRequest,
1232 ) -> Result<crate::types::CreateBackupResponse> {
1233 let url = format!("{}/v1/admin/backups", self.base_url);
1234 let response = self.client.post(&url).json(&request).send().await?;
1235 self.handle_response(response).await
1236 }
1237
1238 pub async fn admin_get_backup(&self, backup_id: &str) -> Result<crate::types::AdminBackupInfo> {
1240 let url = format!("{}/v1/admin/backups/{}", self.base_url, backup_id);
1241 let response = self.client.get(&url).send().await?;
1242 self.handle_response(response).await
1243 }
1244
1245 pub async fn admin_delete_backup(&self, backup_id: &str) -> Result<serde_json::Value> {
1247 let url = format!("{}/v1/admin/backups/{}", self.base_url, backup_id);
1248 let response = self.client.delete(&url).send().await?;
1249 self.handle_response(response).await
1250 }
1251
1252 pub async fn admin_get_backup_schedule(&self) -> Result<crate::types::BackupSchedule> {
1254 let url = format!("{}/v1/admin/backups/schedule", self.base_url);
1255 let response = self.client.get(&url).send().await?;
1256 self.handle_response(response).await
1257 }
1258
1259 pub async fn admin_update_backup_schedule(
1261 &self,
1262 request: crate::types::UpdateBackupScheduleRequest,
1263 ) -> Result<crate::types::BackupSchedule> {
1264 let url = format!("{}/v1/admin/backups/schedule", self.base_url);
1265 let response = self.client.post(&url).json(&request).send().await?;
1266 self.handle_response(response).await
1267 }
1268
1269 pub async fn admin_restore_backup(
1271 &self,
1272 request: crate::types::RestoreBackupRequest,
1273 ) -> Result<crate::types::RestoreBackupResponse> {
1274 let url = format!("{}/v1/admin/backups/restore", self.base_url);
1275 let response = self.client.post(&url).json(&request).send().await?;
1276 self.handle_response(response).await
1277 }
1278
1279 pub async fn admin_get_restore_status(
1281 &self,
1282 restore_id: &str,
1283 ) -> Result<crate::types::RestoreBackupResponse> {
1284 let url = format!("{}/v1/admin/backups/restore/{}", self.base_url, restore_id);
1285 let response = self.client.get(&url).send().await?;
1286 self.handle_response(response).await
1287 }
1288
1289 pub async fn ops_diagnostics(&self) -> Result<serde_json::Value> {
1295 let url = format!("{}/ops/diagnostics", self.base_url);
1296 let response = self.client.get(&url).send().await?;
1297 self.handle_response(response).await
1298 }
1299
1300 pub async fn ops_list_jobs(&self) -> Result<Vec<crate::types::JobInfo>> {
1302 let url = format!("{}/ops/jobs", self.base_url);
1303 let response = self.client.get(&url).send().await?;
1304 self.handle_response(response).await
1305 }
1306
1307 pub async fn ops_get_job(&self, job_id: &str) -> Result<crate::types::JobInfo> {
1309 let url = format!("{}/ops/jobs/{}", self.base_url, job_id);
1310 let response = self.client.get(&url).send().await?;
1311 self.handle_response(response).await
1312 }
1313
1314 pub async fn ops_compact(
1316 &self,
1317 request: crate::types::CompactionRequest,
1318 ) -> Result<crate::types::CompactionResponse> {
1319 let url = format!("{}/ops/compact", self.base_url);
1320 let response = self.client.post(&url).json(&request).send().await?;
1321 self.handle_response(response).await
1322 }
1323
1324 pub async fn ops_shutdown(&self) -> Result<serde_json::Value> {
1326 let url = format!("{}/ops/shutdown", self.base_url);
1327 let response = self.client.post(&url).send().await?;
1328 self.handle_response(response).await
1329 }
1330
1331 pub async fn download_backup(&self, backup_id: &str) -> Result<Vec<u8>> {
1337 let url = format!("{}/v1/admin/backups/{}/download", self.base_url, backup_id);
1338 let response = self.client.get(&url).send().await?;
1339 if !response.status().is_success() {
1340 let status = response.status();
1341 let body = response.text().await.unwrap_or_default();
1342 return Err(crate::error::ClientError::Server {
1343 status: status.as_u16(),
1344 message: body,
1345 code: None,
1346 });
1347 }
1348 Ok(response.bytes().await?.to_vec())
1349 }
1350
1351 pub async fn upload_backup(&self, data: Vec<u8>) -> Result<crate::types::CreateBackupResponse> {
1353 let url = format!("{}/v1/admin/backups/upload", self.base_url);
1354 let response = self
1355 .client
1356 .post(&url)
1357 .header("Content-Type", "application/gzip")
1358 .body(data)
1359 .send()
1360 .await?;
1361 self.handle_response(response).await
1362 }
1363
1364 pub async fn storage_tier_overview(&self) -> Result<crate::types::StorageTierOverview> {
1370 let url = format!("{}/v1/admin/storage/tiers", self.base_url);
1371 let response = self.client.get(&url).send().await?;
1372 self.handle_response(response).await
1373 }
1374
1375 pub async fn background_activity(&self) -> Result<serde_json::Value> {
1381 let url = format!("{}/v1/admin/background-activity", self.base_url);
1382 let response = self.client.get(&url).send().await?;
1383 self.handle_response(response).await
1384 }
1385
1386 pub async fn memory_type_stats(&self) -> Result<crate::types::MemoryTypeStatsResponse> {
1392 let url = format!("{}/v1/admin/memory-type-stats", self.base_url);
1393 let response = self.client.get(&url).send().await?;
1394 self.handle_response(response).await
1395 }
1396
1397 pub async fn migrate_namespace_dimensions(
1403 &self,
1404 request: crate::types::MigrateNamespaceDimensionsRequest,
1405 ) -> Result<crate::types::MigrateDimensionsResponse> {
1406 let url = format!("{}/v1/admin/namespaces/migrate-dimensions", self.base_url);
1407 let response = self.client.post(&url).json(&request).send().await?;
1408 self.handle_response(response).await
1409 }
1410
1411 pub async fn drain_reembed(
1426 &self,
1427 request: crate::types::DrainReembedRequest,
1428 ) -> Result<crate::types::DrainReembedResponse> {
1429 let url = format!("{}/v1/admin/reembed/drain", self.base_url);
1430 let response = self.client.post(&url).json(&request).send().await?;
1431 self.handle_response(response).await
1432 }
1433
1434 pub async fn admin_reembed_static_count(&self) -> Result<crate::types::StaticCountResponse> {
1443 let url = format!("{}/v1/admin/reembed/static-count", self.base_url);
1444 let response = self.client.get(&url).send().await?;
1445 self.handle_response(response).await
1446 }
1447}
1448
1449#[derive(Debug, Clone, Serialize, Deserialize)]
1460pub struct KpiSnapshot {
1461 pub recall_latency_p50_ms: f64,
1463 pub recall_latency_p99_ms: f64,
1465 pub store_latency_p50_ms: f64,
1467 pub api_error_rate_5xx_pct: f64,
1469 pub active_agents_count: u64,
1471 pub session_count_week: u64,
1473 pub cross_agent_network_node_count: u64,
1475 pub memory_retention_7d_pct: f64,
1477}
1478
1479#[derive(Debug, Clone, Serialize, Deserialize)]
1485pub struct FulltextReindexNamespaceResult {
1486 pub namespace: String,
1488 pub vectors_scanned: usize,
1490 pub newly_indexed: usize,
1492 pub already_indexed: usize,
1494 pub parse_failures: usize,
1496}
1497
1498#[derive(Debug, Clone, Serialize, Deserialize)]
1502pub struct FulltextReindexResponse {
1503 pub namespaces_processed: usize,
1505 pub total_indexed: usize,
1507 pub total_skipped: usize,
1509 pub details: Vec<FulltextReindexNamespaceResult>,
1511}