1use std::collections::HashMap;
7
8use serde::{Deserialize, Serialize};
9
10use crate::error::Result;
11use crate::types::{WarmCacheRequest, WarmCacheResponse};
12use crate::DakeraClient;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct OpsStats {
21 pub version: String,
22 pub total_vectors: u64,
23 pub namespace_count: u64,
24 pub uptime_seconds: u64,
25 pub timestamp: u64,
26 pub state: String,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct ClusterStatus {
32 pub cluster_id: String,
33 pub state: String,
34 pub node_count: u32,
35 pub total_vectors: u64,
36 pub namespace_count: u64,
37 pub version: String,
38 pub timestamp: u64,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
41 pub redis_healthy: Option<bool>,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct NodeInfo {
47 pub node_id: String,
48 pub address: String,
49 pub role: String,
50 pub status: String,
51 pub version: String,
52 pub uptime_seconds: u64,
53 pub vector_count: u64,
54 pub memory_bytes: u64,
55 #[serde(default)]
56 pub cpu_percent: f32,
57 #[serde(default)]
58 pub memory_percent: f32,
59 pub last_heartbeat: u64,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct NodeListResponse {
65 pub nodes: Vec<NodeInfo>,
66 pub total: u32,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct IndexStats {
76 pub index_type: String,
77 pub is_built: bool,
78 pub size_bytes: u64,
79 pub indexed_vectors: u64,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub last_rebuild: Option<u64>,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct NamespaceAdminInfo {
87 pub name: String,
88 pub vector_count: u64,
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub dimension: Option<usize>,
91 pub index_type: String,
92 pub storage_bytes: u64,
93 pub document_count: u64,
94 #[serde(skip_serializing_if = "Option::is_none")]
95 pub created_at: Option<u64>,
96 #[serde(skip_serializing_if = "Option::is_none")]
97 pub updated_at: Option<u64>,
98 pub index_stats: IndexStats,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct NamespaceListResponse {
104 pub namespaces: Vec<NamespaceAdminInfo>,
105 pub total: u64,
106 pub total_vectors: u64,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct OptimizeRequest {
112 #[serde(default)]
113 pub force: bool,
114 #[serde(skip_serializing_if = "Option::is_none")]
115 pub target_index_type: Option<String>,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct OptimizeResponse {
121 pub success: bool,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub job_id: Option<String>,
124 pub message: String,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct IndexStatsResponse {
134 pub namespaces: HashMap<String, IndexStats>,
135 pub total_indexed_vectors: u64,
136 pub total_size_bytes: u64,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct RebuildIndexRequest {
142 #[serde(skip_serializing_if = "Option::is_none")]
143 pub namespace: Option<String>,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub index_type: Option<String>,
146 #[serde(default)]
147 pub force: bool,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct RebuildIndexResponse {
153 pub success: bool,
154 pub job_id: String,
155 pub message: String,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct CacheStats {
165 pub enabled: bool,
166 pub cache_type: String,
167 pub entries: u64,
168 pub size_bytes: u64,
169 pub hits: u64,
170 pub misses: u64,
171 pub hit_rate: f64,
172 pub evictions: u64,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct ClearCacheRequest {
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub namespace: Option<String>,
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct ClearCacheResponse {
185 pub success: bool,
186 pub entries_cleared: u64,
187 pub message: String,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct RuntimeConfig {
197 #[serde(skip_serializing_if = "Option::is_none")]
198 pub max_vectors_per_namespace: Option<u64>,
199 pub default_index_type: String,
200 pub cache_enabled: bool,
201 pub cache_max_size_bytes: u64,
202 pub rate_limit_enabled: bool,
203 pub rate_limit_rps: u32,
204 pub query_timeout_ms: u64,
205 #[serde(default = "default_true")]
207 pub autopilot_enabled: bool,
208 #[serde(default = "default_dedup_threshold")]
210 pub autopilot_dedup_threshold: f32,
211 #[serde(default = "default_dedup_interval")]
213 pub autopilot_dedup_interval_hours: u64,
214 #[serde(default = "default_consolidation_interval")]
216 pub autopilot_consolidation_interval_hours: u64,
217}
218
219fn default_true() -> bool {
220 true
221}
222fn default_dedup_threshold() -> f32 {
223 0.93
224}
225fn default_dedup_interval() -> u64 {
226 6
227}
228fn default_consolidation_interval() -> u64 {
229 12
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct UpdateConfigResponse {
235 pub success: bool,
236 pub config: RuntimeConfig,
237 pub message: String,
238 #[serde(default, skip_serializing_if = "Vec::is_empty")]
239 pub warnings: Vec<String>,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct QuotaConfig {
249 #[serde(skip_serializing_if = "Option::is_none")]
250 pub max_vectors: Option<u64>,
251 #[serde(skip_serializing_if = "Option::is_none")]
252 pub max_storage_bytes: Option<u64>,
253 #[serde(skip_serializing_if = "Option::is_none")]
254 pub max_queries_per_minute: Option<u64>,
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub max_writes_per_minute: Option<u64>,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct QuotaUsage {
262 #[serde(default)]
263 pub current_vectors: u64,
264 #[serde(default)]
265 pub current_storage_bytes: u64,
266 #[serde(default)]
267 pub queries_this_minute: u64,
268 #[serde(default)]
269 pub writes_this_minute: u64,
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize)]
274pub struct QuotaStatus {
275 pub namespace: String,
276 pub config: QuotaConfig,
277 pub usage: QuotaUsage,
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize)]
282pub struct QuotaListResponse {
283 pub quotas: Vec<QuotaStatus>,
284 pub total: u64,
285 #[serde(skip_serializing_if = "Option::is_none")]
286 pub default_config: Option<QuotaConfig>,
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct SlowQueryEntry {
296 pub id: String,
297 pub timestamp: u64,
298 pub namespace: String,
299 pub query_type: String,
300 pub duration_ms: f64,
301 #[serde(default)]
302 pub parameters: Option<serde_json::Value>,
303 #[serde(default)]
304 pub results_count: u64,
305 #[serde(default)]
306 pub vectors_scanned: u64,
307}
308
309#[derive(Debug, Clone, Serialize, Deserialize)]
311pub struct SlowQueryListResponse {
312 pub queries: Vec<SlowQueryEntry>,
313 pub total: u64,
314 pub threshold_ms: f64,
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize)]
323pub struct BackupInfo {
324 pub backup_id: String,
325 pub name: String,
326 pub backup_type: String,
327 pub status: String,
328 pub namespaces: Vec<String>,
329 pub vector_count: u64,
330 pub size_bytes: u64,
331 pub created_at: u64,
332 #[serde(skip_serializing_if = "Option::is_none")]
333 pub completed_at: Option<u64>,
334 #[serde(skip_serializing_if = "Option::is_none")]
335 pub duration_seconds: Option<u64>,
336 #[serde(skip_serializing_if = "Option::is_none")]
337 pub storage_path: Option<String>,
338 #[serde(skip_serializing_if = "Option::is_none")]
339 pub error: Option<String>,
340 pub encrypted: bool,
341 #[serde(skip_serializing_if = "Option::is_none")]
342 pub compression: Option<String>,
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct BackupListResponse {
348 pub backups: Vec<BackupInfo>,
349 pub total: u64,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize)]
354pub struct CreateBackupRequest {
355 pub name: String,
356 #[serde(skip_serializing_if = "Option::is_none")]
357 pub backup_type: Option<String>,
358 #[serde(skip_serializing_if = "Option::is_none")]
359 pub namespaces: Option<Vec<String>>,
360 #[serde(skip_serializing_if = "Option::is_none")]
361 pub encrypt: Option<bool>,
362 #[serde(skip_serializing_if = "Option::is_none")]
363 pub compression: Option<String>,
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct CreateBackupResponse {
369 pub backup: BackupInfo,
370 #[serde(skip_serializing_if = "Option::is_none")]
371 pub estimated_completion: Option<u64>,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
376pub struct RestoreBackupRequest {
377 pub backup_id: String,
378 #[serde(skip_serializing_if = "Option::is_none")]
379 pub target_namespaces: Option<Vec<String>>,
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub overwrite: Option<bool>,
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub point_in_time: Option<u64>,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize)]
388pub struct RestoreBackupResponse {
389 pub restore_id: String,
390 pub status: String,
391 pub backup_id: String,
392 pub namespaces: Vec<String>,
393 pub started_at: u64,
394 #[serde(skip_serializing_if = "Option::is_none")]
395 pub estimated_completion: Option<u64>,
396 #[serde(skip_serializing_if = "Option::is_none")]
397 pub progress_percent: Option<u8>,
398 #[serde(skip_serializing_if = "Option::is_none")]
399 pub vectors_restored: Option<u64>,
400 #[serde(skip_serializing_if = "Option::is_none")]
401 pub completed_at: Option<u64>,
402 #[serde(skip_serializing_if = "Option::is_none")]
403 pub duration_seconds: Option<u64>,
404 #[serde(skip_serializing_if = "Option::is_none")]
405 pub error: Option<String>,
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
414pub struct AutoPilotConfig {
415 pub enabled: bool,
416 pub dedup_threshold: f32,
417 pub dedup_interval_hours: u64,
418 pub consolidation_interval_hours: u64,
419}
420
421#[derive(Debug, Clone, Serialize, Deserialize)]
423pub struct DedupResultSnapshot {
424 pub namespaces_processed: usize,
425 pub memories_scanned: usize,
426 pub duplicates_removed: usize,
427}
428
429#[derive(Debug, Clone, Serialize, Deserialize)]
431pub struct ConsolidationResultSnapshot {
432 pub namespaces_processed: usize,
433 pub memories_scanned: usize,
434 pub clusters_merged: usize,
435 pub memories_consolidated: usize,
436}
437
438#[derive(Debug, Clone, Serialize, Deserialize)]
440pub struct AutoPilotStatusResponse {
441 pub config: AutoPilotConfig,
442 #[serde(skip_serializing_if = "Option::is_none")]
443 pub last_dedup_at: Option<u64>,
444 #[serde(skip_serializing_if = "Option::is_none")]
445 pub last_consolidation_at: Option<u64>,
446 #[serde(skip_serializing_if = "Option::is_none")]
447 pub last_dedup: Option<DedupResultSnapshot>,
448 #[serde(skip_serializing_if = "Option::is_none")]
449 pub last_consolidation: Option<ConsolidationResultSnapshot>,
450 pub total_dedup_removed: u64,
451 pub total_consolidated: u64,
452}
453
454#[derive(Debug, Clone, Serialize, Deserialize, Default)]
456pub struct AutoPilotConfigRequest {
457 #[serde(skip_serializing_if = "Option::is_none")]
458 pub enabled: Option<bool>,
459 #[serde(skip_serializing_if = "Option::is_none")]
460 pub dedup_threshold: Option<f32>,
461 #[serde(skip_serializing_if = "Option::is_none")]
462 pub dedup_interval_hours: Option<u64>,
463 #[serde(skip_serializing_if = "Option::is_none")]
464 pub consolidation_interval_hours: Option<u64>,
465}
466
467#[derive(Debug, Clone, Serialize, Deserialize)]
469pub struct AutoPilotConfigResponse {
470 pub success: bool,
471 pub config: AutoPilotConfig,
472 pub message: String,
473}
474
475#[derive(Debug, Clone, Serialize, Deserialize)]
477#[serde(rename_all = "lowercase")]
478pub enum AutoPilotTriggerAction {
479 Dedup,
480 Consolidate,
481 All,
482}
483
484#[derive(Debug, Clone, Serialize, Deserialize)]
486pub struct AutoPilotTriggerRequest {
487 pub action: AutoPilotTriggerAction,
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct AutoPilotDedupResult {
493 pub namespaces_processed: usize,
494 pub memories_scanned: usize,
495 pub duplicates_removed: usize,
496}
497
498#[derive(Debug, Clone, Serialize, Deserialize)]
500pub struct AutoPilotConsolidationResult {
501 pub namespaces_processed: usize,
502 pub memories_scanned: usize,
503 pub clusters_merged: usize,
504 pub memories_consolidated: usize,
505}
506
507#[derive(Debug, Clone, Serialize, Deserialize)]
509pub struct AutoPilotTriggerResponse {
510 pub success: bool,
511 pub action: AutoPilotTriggerAction,
512 #[serde(skip_serializing_if = "Option::is_none")]
513 pub dedup: Option<AutoPilotDedupResult>,
514 #[serde(skip_serializing_if = "Option::is_none")]
515 pub consolidation: Option<AutoPilotConsolidationResult>,
516 pub message: String,
517}
518
519#[derive(Debug, Clone, Serialize, Deserialize)]
525pub struct DecayConfigResponse {
526 pub strategy: String,
528 pub half_life_hours: f64,
530 pub min_importance: f32,
532}
533
534#[derive(Debug, Clone, Serialize, Deserialize, Default)]
536pub struct DecayConfigUpdateRequest {
537 #[serde(skip_serializing_if = "Option::is_none")]
539 pub strategy: Option<String>,
540 #[serde(skip_serializing_if = "Option::is_none")]
542 pub half_life_hours: Option<f64>,
543 #[serde(skip_serializing_if = "Option::is_none")]
545 pub min_importance: Option<f32>,
546}
547
548#[derive(Debug, Clone, Serialize, Deserialize)]
550pub struct DecayConfigUpdateResponse {
551 pub success: bool,
552 pub config: DecayConfigResponse,
553 pub message: String,
554}
555
556#[derive(Debug, Clone, Serialize, Deserialize)]
558pub struct LastDecayCycleStats {
559 pub namespaces_processed: usize,
560 pub memories_processed: usize,
561 pub memories_decayed: usize,
562 pub memories_deleted: usize,
563}
564
565#[derive(Debug, Clone, Serialize, Deserialize)]
567pub struct DecayStatsResponse {
568 pub total_decayed: u64,
570 pub total_deleted: u64,
572 #[serde(skip_serializing_if = "Option::is_none")]
574 pub last_run_at: Option<u64>,
575 pub cycles_run: u64,
577 #[serde(skip_serializing_if = "Option::is_none")]
579 pub last_cycle: Option<LastDecayCycleStats>,
580}
581
582#[derive(Debug, Clone, Serialize, Deserialize)]
588pub struct TtlCleanupRequest {
589 #[serde(skip_serializing_if = "Option::is_none")]
590 pub namespace: Option<String>,
591}
592
593#[derive(Debug, Clone, Serialize, Deserialize)]
595pub struct TtlCleanupResponse {
596 pub success: bool,
597 pub vectors_removed: u64,
598 pub namespaces_cleaned: Vec<String>,
599 pub message: String,
600}
601
602#[derive(Debug, Clone, Serialize, Deserialize)]
604pub struct TtlStats {
605 pub namespace: String,
606 pub vectors_with_ttl: u64,
607 pub expiring_within_hour: u64,
608 pub expiring_within_day: u64,
609 pub expired_pending_cleanup: u64,
610}
611
612#[derive(Debug, Clone, Serialize, Deserialize)]
614pub struct TtlStatsResponse {
615 pub namespaces: Vec<TtlStats>,
616 pub total_with_ttl: u64,
617 pub total_expired: u64,
618}
619
620impl DakeraClient {
625 pub async fn ops_stats(&self) -> Result<OpsStats> {
633 let url = format!("{}/v1/ops/stats", self.base_url);
634 let response = self.client.get(&url).send().await?;
635 self.handle_response(response).await
636 }
637
638 pub async fn ops_metrics(&self) -> Result<String> {
643 let url = format!("{}/v1/ops/metrics", self.base_url);
644 let response = self.client.get(&url).send().await?;
645 self.handle_text_response(response).await
646 }
647
648 pub async fn cluster_status(&self) -> Result<ClusterStatus> {
650 let url = format!("{}/v1/admin/cluster/status", self.base_url);
651 let response = self.client.get(&url).send().await?;
652 self.handle_response(response).await
653 }
654
655 pub async fn cluster_nodes(&self) -> Result<NodeListResponse> {
657 let url = format!("{}/v1/admin/cluster/nodes", self.base_url);
658 let response = self.client.get(&url).send().await?;
659 self.handle_response(response).await
660 }
661
662 pub async fn list_namespaces_admin(&self) -> Result<NamespaceListResponse> {
668 let url = format!("{}/v1/admin/namespaces", self.base_url);
669 let response = self.client.get(&url).send().await?;
670 self.handle_response(response).await
671 }
672
673 pub async fn delete_namespace_admin(&self, namespace: &str) -> Result<serde_json::Value> {
675 let url = format!("{}/v1/admin/namespaces/{}", self.base_url, namespace);
676 let response = self.client.delete(&url).send().await?;
677 self.handle_response(response).await
678 }
679
680 pub async fn optimize_namespace(
682 &self,
683 namespace: &str,
684 request: OptimizeRequest,
685 ) -> Result<OptimizeResponse> {
686 let url = format!(
687 "{}/v1/admin/namespaces/{}/optimize",
688 self.base_url, namespace
689 );
690 let response = self.client.post(&url).json(&request).send().await?;
691 self.handle_response(response).await
692 }
693
694 pub async fn index_stats(&self) -> Result<IndexStatsResponse> {
700 let url = format!("{}/v1/admin/indexes/stats", self.base_url);
701 let response = self.client.get(&url).send().await?;
702 self.handle_response(response).await
703 }
704
705 pub async fn rebuild_indexes(
707 &self,
708 request: RebuildIndexRequest,
709 ) -> Result<RebuildIndexResponse> {
710 let url = format!("{}/v1/admin/indexes/rebuild", self.base_url);
711 let response = self.client.post(&url).json(&request).send().await?;
712 self.handle_response(response).await
713 }
714
715 pub async fn cache_stats(&self) -> Result<CacheStats> {
721 let url = format!("{}/v1/admin/cache/stats", self.base_url);
722 let response = self.client.get(&url).send().await?;
723 self.handle_response(response).await
724 }
725
726 pub async fn cache_clear(&self, namespace: Option<&str>) -> Result<ClearCacheResponse> {
728 let url = format!("{}/v1/admin/cache/clear", self.base_url);
729 let request = ClearCacheRequest {
730 namespace: namespace.map(|s| s.to_string()),
731 };
732 let response = self.client.post(&url).json(&request).send().await?;
733 self.handle_response(response).await
734 }
735
736 pub async fn cache_warm(&self, request: WarmCacheRequest) -> Result<WarmCacheResponse> {
738 let url = format!("{}/v1/admin/cache/warm", self.base_url);
739 let response = self.client.post(&url).json(&request).send().await?;
740 self.handle_response(response).await
741 }
742
743 pub async fn get_config(&self) -> Result<RuntimeConfig> {
749 let url = format!("{}/v1/admin/config", self.base_url);
750 let response = self.client.get(&url).send().await?;
751 self.handle_response(response).await
752 }
753
754 pub async fn update_config(
756 &self,
757 updates: HashMap<String, serde_json::Value>,
758 ) -> Result<UpdateConfigResponse> {
759 let url = format!("{}/v1/admin/config", self.base_url);
760 let response = self.client.put(&url).json(&updates).send().await?;
761 self.handle_response(response).await
762 }
763
764 pub async fn get_quotas(&self) -> Result<QuotaListResponse> {
770 let url = format!("{}/v1/admin/quotas", self.base_url);
771 let response = self.client.get(&url).send().await?;
772 self.handle_response(response).await
773 }
774
775 pub async fn get_quota(&self, namespace: &str) -> Result<QuotaStatus> {
777 let url = format!("{}/v1/admin/quotas/{}", self.base_url, namespace);
778 let response = self.client.get(&url).send().await?;
779 self.handle_response(response).await
780 }
781
782 pub async fn set_quota(
784 &self,
785 namespace: &str,
786 config: QuotaConfig,
787 ) -> Result<serde_json::Value> {
788 let url = format!("{}/v1/admin/quotas/{}", self.base_url, namespace);
789 let request = serde_json::json!({ "config": config });
790 let response = self.client.put(&url).json(&request).send().await?;
791 self.handle_response(response).await
792 }
793
794 pub async fn delete_quota(&self, namespace: &str) -> Result<serde_json::Value> {
796 let url = format!("{}/v1/admin/quotas/{}", self.base_url, namespace);
797 let response = self.client.delete(&url).send().await?;
798 self.handle_response(response).await
799 }
800
801 pub async fn update_quotas(&self, config: Option<QuotaConfig>) -> Result<serde_json::Value> {
803 let url = format!("{}/v1/admin/quotas/default", self.base_url);
804 let request = serde_json::json!({ "config": config });
805 let response = self.client.put(&url).json(&request).send().await?;
806 self.handle_response(response).await
807 }
808
809 pub async fn slow_queries(
815 &self,
816 limit: Option<usize>,
817 namespace: Option<&str>,
818 query_type: Option<&str>,
819 ) -> Result<SlowQueryListResponse> {
820 let mut url = format!("{}/v1/admin/slow-queries", self.base_url);
821 let mut params = Vec::new();
822 if let Some(l) = limit {
823 params.push(format!("limit={}", l));
824 }
825 if let Some(ns) = namespace {
826 params.push(format!("namespace={}", ns));
827 }
828 if let Some(qt) = query_type {
829 params.push(format!("query_type={}", qt));
830 }
831 if !params.is_empty() {
832 url.push('?');
833 url.push_str(¶ms.join("&"));
834 }
835 let response = self.client.get(&url).send().await?;
836 self.handle_response(response).await
837 }
838
839 pub async fn slow_query_summary(&self) -> Result<serde_json::Value> {
841 let url = format!("{}/v1/admin/slow-queries/summary", self.base_url);
842 let response = self.client.get(&url).send().await?;
843 self.handle_response(response).await
844 }
845
846 pub async fn clear_slow_queries(&self) -> Result<serde_json::Value> {
848 let url = format!("{}/v1/admin/slow-queries", self.base_url);
849 let response = self.client.delete(&url).send().await?;
850 self.handle_response(response).await
851 }
852
853 pub async fn create_backup(
859 &self,
860 request: CreateBackupRequest,
861 ) -> Result<CreateBackupResponse> {
862 let url = format!("{}/v1/admin/backups", self.base_url);
863 let response = self.client.post(&url).json(&request).send().await?;
864 self.handle_response(response).await
865 }
866
867 pub async fn list_backups(&self) -> Result<BackupListResponse> {
869 let url = format!("{}/v1/admin/backups", self.base_url);
870 let response = self.client.get(&url).send().await?;
871 self.handle_response(response).await
872 }
873
874 pub async fn get_backup(&self, backup_id: &str) -> Result<BackupInfo> {
876 let url = format!("{}/v1/admin/backups/{}", self.base_url, backup_id);
877 let response = self.client.get(&url).send().await?;
878 self.handle_response(response).await
879 }
880
881 pub async fn restore_backup(
883 &self,
884 request: RestoreBackupRequest,
885 ) -> Result<RestoreBackupResponse> {
886 let url = format!("{}/v1/admin/backups/restore", self.base_url);
887 let response = self.client.post(&url).json(&request).send().await?;
888 self.handle_response(response).await
889 }
890
891 pub async fn delete_backup(&self, backup_id: &str) -> Result<serde_json::Value> {
893 let url = format!("{}/v1/admin/backups/{}", self.base_url, backup_id);
894 let response = self.client.delete(&url).send().await?;
895 self.handle_response(response).await
896 }
897
898 pub async fn configure_ttl(
904 &self,
905 namespace: &str,
906 ttl_seconds: u64,
907 strategy: Option<&str>,
908 ) -> Result<serde_json::Value> {
909 let url = format!("{}/v1/admin/namespaces/{}/ttl", self.base_url, namespace);
910 let mut body = serde_json::json!({ "ttl_seconds": ttl_seconds });
911 if let Some(s) = strategy {
912 body["strategy"] = serde_json::Value::String(s.to_string());
913 }
914 let response = self.client.post(&url).json(&body).send().await?;
915 self.handle_response(response).await
916 }
917
918 pub async fn ttl_cleanup(&self, namespace: Option<&str>) -> Result<TtlCleanupResponse> {
920 let url = format!("{}/v1/admin/ttl/cleanup", self.base_url);
921 let request = TtlCleanupRequest {
922 namespace: namespace.map(|s| s.to_string()),
923 };
924 let response = self.client.post(&url).json(&request).send().await?;
925 self.handle_response(response).await
926 }
927
928 pub async fn ttl_stats(&self) -> Result<TtlStatsResponse> {
930 let url = format!("{}/v1/admin/ttl/stats", self.base_url);
931 let response = self.client.get(&url).send().await?;
932 self.handle_response(response).await
933 }
934
935 pub async fn autopilot_status(&self) -> Result<AutoPilotStatusResponse> {
941 let url = format!("{}/v1/admin/autopilot/status", self.base_url);
942 let response = self.client.get(&url).send().await?;
943 self.handle_response(response).await
944 }
945
946 pub async fn autopilot_update_config(
950 &self,
951 request: AutoPilotConfigRequest,
952 ) -> Result<AutoPilotConfigResponse> {
953 let url = format!("{}/v1/admin/autopilot/config", self.base_url);
954 let response = self.client.put(&url).json(&request).send().await?;
955 self.handle_response(response).await
956 }
957
958 pub async fn autopilot_trigger(
963 &self,
964 action: AutoPilotTriggerAction,
965 ) -> Result<AutoPilotTriggerResponse> {
966 let url = format!("{}/v1/admin/autopilot/trigger", self.base_url);
967 let request = AutoPilotTriggerRequest { action };
968 let response = self.client.post(&url).json(&request).send().await?;
969 self.handle_response(response).await
970 }
971
972 pub async fn decay_config(&self) -> Result<DecayConfigResponse> {
981 let url = format!("{}/v1/admin/decay/config", self.base_url);
982 let response = self.client.get(&url).send().await?;
983 self.handle_response(response).await
984 }
985
986 pub async fn decay_update_config(
992 &self,
993 request: DecayConfigUpdateRequest,
994 ) -> Result<DecayConfigUpdateResponse> {
995 let url = format!("{}/v1/admin/decay/config", self.base_url);
996 let response = self.client.put(&url).json(&request).send().await?;
997 self.handle_response(response).await
998 }
999
1000 pub async fn decay_stats(&self) -> Result<DecayStatsResponse> {
1005 let url = format!("{}/v1/admin/decay/stats", self.base_url);
1006 let response = self.client.get(&url).send().await?;
1007 self.handle_response(response).await
1008 }
1009
1010 pub async fn get_kpis(&self) -> Result<KpiSnapshot> {
1020 let url = format!("{}/kpis", self.base_url);
1021 let response = self.client.get(&url).send().await?;
1022 self.handle_response(response).await
1023 }
1024
1025 pub async fn admin_fulltext_reindex(
1037 &self,
1038 namespace: Option<&str>,
1039 ) -> Result<FulltextReindexResponse> {
1040 let url = format!("{}/v1/admin/fulltext/reindex", self.base_url);
1041 let body = serde_json::json!({ "namespace": namespace });
1042 let response = self.client.post(&url).json(&body).send().await?;
1043 self.handle_response(response).await
1044 }
1045
1046 pub async fn admin_cluster_replication(&self) -> Result<crate::types::ReplicationStatus> {
1052 let url = format!("{}/v1/admin/cluster/replication", self.base_url);
1053 let response = self.client.get(&url).send().await?;
1054 self.handle_response(response).await
1055 }
1056
1057 pub async fn admin_list_shards(&self) -> Result<crate::types::ShardListResponse> {
1059 let url = format!("{}/v1/admin/cluster/shards", self.base_url);
1060 let response = self.client.get(&url).send().await?;
1061 self.handle_response(response).await
1062 }
1063
1064 pub async fn admin_rebalance_shards(
1066 &self,
1067 request: crate::types::ShardRebalanceRequest,
1068 ) -> Result<crate::types::ShardRebalanceResponse> {
1069 let url = format!("{}/v1/admin/cluster/shards/rebalance", self.base_url);
1070 let response = self.client.post(&url).json(&request).send().await?;
1071 self.handle_response(response).await
1072 }
1073
1074 pub async fn admin_maintenance_status(&self) -> Result<crate::types::MaintenanceStatus> {
1076 let url = format!("{}/v1/admin/cluster/maintenance", self.base_url);
1077 let response = self.client.get(&url).send().await?;
1078 self.handle_response(response).await
1079 }
1080
1081 pub async fn admin_enable_maintenance(
1083 &self,
1084 request: crate::types::EnableMaintenanceRequest,
1085 ) -> Result<crate::types::MaintenanceStatus> {
1086 let url = format!("{}/v1/admin/cluster/maintenance/enable", self.base_url);
1087 let response = self.client.post(&url).json(&request).send().await?;
1088 self.handle_response(response).await
1089 }
1090
1091 pub async fn admin_disable_maintenance(
1093 &self,
1094 request: crate::types::DisableMaintenanceRequest,
1095 ) -> Result<crate::types::MaintenanceStatus> {
1096 let url = format!("{}/v1/admin/cluster/maintenance/disable", self.base_url);
1097 let response = self.client.post(&url).json(&request).send().await?;
1098 self.handle_response(response).await
1099 }
1100
1101 pub async fn admin_list_quotas(&self) -> Result<crate::types::QuotaListResponse> {
1107 let url = format!("{}/v1/admin/quotas", self.base_url);
1108 let response = self.client.get(&url).send().await?;
1109 self.handle_response(response).await
1110 }
1111
1112 pub async fn admin_get_default_quota(&self) -> Result<crate::types::DefaultQuotaResponse> {
1114 let url = format!("{}/v1/admin/quotas/default", self.base_url);
1115 let response = self.client.get(&url).send().await?;
1116 self.handle_response(response).await
1117 }
1118
1119 pub async fn admin_set_default_quota(
1121 &self,
1122 request: crate::types::SetDefaultQuotaRequest,
1123 ) -> Result<crate::types::SetQuotaResponse> {
1124 let url = format!("{}/v1/admin/quotas/default", self.base_url);
1125 let response = self.client.put(&url).json(&request).send().await?;
1126 self.handle_response(response).await
1127 }
1128
1129 pub async fn admin_get_quota(&self, namespace: &str) -> Result<crate::types::QuotaStatus> {
1131 let url = format!("{}/v1/admin/quotas/{}", self.base_url, namespace);
1132 let response = self.client.get(&url).send().await?;
1133 self.handle_response(response).await
1134 }
1135
1136 pub async fn admin_set_quota(
1138 &self,
1139 namespace: &str,
1140 request: crate::types::SetQuotaRequest,
1141 ) -> Result<crate::types::SetQuotaResponse> {
1142 let url = format!("{}/v1/admin/quotas/{}", self.base_url, namespace);
1143 let response = self.client.put(&url).json(&request).send().await?;
1144 self.handle_response(response).await
1145 }
1146
1147 pub async fn admin_delete_quota(&self, namespace: &str) -> Result<serde_json::Value> {
1149 let url = format!("{}/v1/admin/quotas/{}", self.base_url, namespace);
1150 let response = self.client.delete(&url).send().await?;
1151 self.handle_response(response).await
1152 }
1153
1154 pub async fn admin_check_quota(
1156 &self,
1157 namespace: &str,
1158 request: crate::types::QuotaCheckRequest,
1159 ) -> Result<crate::types::QuotaCheckResult> {
1160 let url = format!("{}/v1/admin/quotas/{}/check", self.base_url, namespace);
1161 let response = self.client.post(&url).json(&request).send().await?;
1162 self.handle_response(response).await
1163 }
1164
1165 pub async fn admin_list_slow_queries(
1171 &self,
1172 namespace: Option<&str>,
1173 query_type: Option<&str>,
1174 limit: Option<u32>,
1175 ) -> Result<Vec<serde_json::Value>> {
1176 let mut url = format!("{}/v1/admin/slow-queries", self.base_url);
1177 let mut params = Vec::new();
1178 if let Some(ns) = namespace {
1179 params.push(format!("namespace={}", ns));
1180 }
1181 if let Some(qt) = query_type {
1182 params.push(format!("query_type={}", qt));
1183 }
1184 if let Some(l) = limit {
1185 params.push(format!("limit={}", l));
1186 }
1187 if !params.is_empty() {
1188 url.push('?');
1189 url.push_str(¶ms.join("&"));
1190 }
1191 let response = self.client.get(&url).send().await?;
1192 self.handle_response(response).await
1193 }
1194
1195 pub async fn admin_slow_query_summary(&self) -> Result<serde_json::Value> {
1197 let url = format!("{}/v1/admin/slow-queries/summary", self.base_url);
1198 let response = self.client.get(&url).send().await?;
1199 self.handle_response(response).await
1200 }
1201
1202 pub async fn admin_clear_slow_queries(
1204 &self,
1205 namespace: Option<&str>,
1206 ) -> Result<serde_json::Value> {
1207 let mut url = format!("{}/v1/admin/slow-queries", self.base_url);
1208 if let Some(ns) = namespace {
1209 url.push_str(&format!("?namespace={}", ns));
1210 }
1211 let response = self.client.delete(&url).send().await?;
1212 self.handle_response(response).await
1213 }
1214
1215 pub async fn admin_update_slow_query_config(
1217 &self,
1218 config: serde_json::Value,
1219 ) -> Result<serde_json::Value> {
1220 let url = format!("{}/v1/admin/slow-queries/config", self.base_url);
1221 let response = self.client.patch(&url).json(&config).send().await?;
1222 self.handle_response(response).await
1223 }
1224
1225 pub async fn admin_list_backups(&self) -> Result<crate::types::BackupListResponse> {
1231 let url = format!("{}/v1/admin/backups", self.base_url);
1232 let response = self.client.get(&url).send().await?;
1233 self.handle_response(response).await
1234 }
1235
1236 pub async fn admin_create_backup(
1238 &self,
1239 request: crate::types::CreateBackupRequest,
1240 ) -> Result<crate::types::CreateBackupResponse> {
1241 let url = format!("{}/v1/admin/backups", self.base_url);
1242 let response = self.client.post(&url).json(&request).send().await?;
1243 self.handle_response(response).await
1244 }
1245
1246 pub async fn admin_get_backup(&self, backup_id: &str) -> Result<crate::types::AdminBackupInfo> {
1248 let url = format!("{}/v1/admin/backups/{}", self.base_url, backup_id);
1249 let response = self.client.get(&url).send().await?;
1250 self.handle_response(response).await
1251 }
1252
1253 pub async fn admin_delete_backup(&self, backup_id: &str) -> Result<serde_json::Value> {
1255 let url = format!("{}/v1/admin/backups/{}", self.base_url, backup_id);
1256 let response = self.client.delete(&url).send().await?;
1257 self.handle_response(response).await
1258 }
1259
1260 pub async fn admin_get_backup_schedule(&self) -> Result<crate::types::BackupSchedule> {
1262 let url = format!("{}/v1/admin/backups/schedule", self.base_url);
1263 let response = self.client.get(&url).send().await?;
1264 self.handle_response(response).await
1265 }
1266
1267 pub async fn admin_update_backup_schedule(
1269 &self,
1270 request: crate::types::UpdateBackupScheduleRequest,
1271 ) -> Result<crate::types::BackupSchedule> {
1272 let url = format!("{}/v1/admin/backups/schedule", self.base_url);
1273 let response = self.client.post(&url).json(&request).send().await?;
1274 self.handle_response(response).await
1275 }
1276
1277 pub async fn admin_restore_backup(
1279 &self,
1280 request: crate::types::RestoreBackupRequest,
1281 ) -> Result<crate::types::RestoreBackupResponse> {
1282 let url = format!("{}/v1/admin/backups/restore", self.base_url);
1283 let response = self.client.post(&url).json(&request).send().await?;
1284 self.handle_response(response).await
1285 }
1286
1287 pub async fn admin_get_restore_status(
1289 &self,
1290 restore_id: &str,
1291 ) -> Result<crate::types::RestoreBackupResponse> {
1292 let url = format!("{}/v1/admin/backups/restore/{}", self.base_url, restore_id);
1293 let response = self.client.get(&url).send().await?;
1294 self.handle_response(response).await
1295 }
1296
1297 pub async fn ops_diagnostics(&self) -> Result<serde_json::Value> {
1303 let url = format!("{}/ops/diagnostics", self.base_url);
1304 let response = self.client.get(&url).send().await?;
1305 self.handle_response(response).await
1306 }
1307
1308 pub async fn ops_list_jobs(&self) -> Result<Vec<crate::types::JobInfo>> {
1310 let url = format!("{}/ops/jobs", self.base_url);
1311 let response = self.client.get(&url).send().await?;
1312 self.handle_response(response).await
1313 }
1314
1315 pub async fn ops_get_job(&self, job_id: &str) -> Result<crate::types::JobInfo> {
1317 let url = format!("{}/ops/jobs/{}", self.base_url, job_id);
1318 let response = self.client.get(&url).send().await?;
1319 self.handle_response(response).await
1320 }
1321
1322 pub async fn ops_compact(
1324 &self,
1325 request: crate::types::CompactionRequest,
1326 ) -> Result<crate::types::CompactionResponse> {
1327 let url = format!("{}/ops/compact", self.base_url);
1328 let response = self.client.post(&url).json(&request).send().await?;
1329 self.handle_response(response).await
1330 }
1331
1332 pub async fn ops_shutdown(&self) -> Result<serde_json::Value> {
1334 let url = format!("{}/ops/shutdown", self.base_url);
1335 let response = self.client.post(&url).send().await?;
1336 self.handle_response(response).await
1337 }
1338
1339 pub async fn download_backup(&self, backup_id: &str) -> Result<Vec<u8>> {
1345 let url = format!("{}/v1/admin/backups/{}/download", self.base_url, backup_id);
1346 let response = self.client.get(&url).send().await?;
1347 if !response.status().is_success() {
1348 let status = response.status();
1349 let body = response.text().await.unwrap_or_default();
1350 return Err(crate::error::ClientError::Server {
1351 status: status.as_u16(),
1352 message: body,
1353 code: None,
1354 });
1355 }
1356 Ok(response.bytes().await?.to_vec())
1357 }
1358
1359 pub async fn upload_backup(&self, data: Vec<u8>) -> Result<crate::types::CreateBackupResponse> {
1361 let url = format!("{}/v1/admin/backups/upload", self.base_url);
1362 let response = self
1363 .client
1364 .post(&url)
1365 .header("Content-Type", "application/gzip")
1366 .body(data)
1367 .send()
1368 .await?;
1369 self.handle_response(response).await
1370 }
1371
1372 pub async fn storage_tier_overview(&self) -> Result<crate::types::StorageTierOverview> {
1378 let url = format!("{}/v1/admin/storage/tiers", self.base_url);
1379 let response = self.client.get(&url).send().await?;
1380 self.handle_response(response).await
1381 }
1382
1383 pub async fn background_activity(&self) -> Result<serde_json::Value> {
1389 let url = format!("{}/v1/admin/background-activity", self.base_url);
1390 let response = self.client.get(&url).send().await?;
1391 self.handle_response(response).await
1392 }
1393
1394 pub async fn memory_type_stats(&self) -> Result<crate::types::MemoryTypeStatsResponse> {
1400 let url = format!("{}/v1/admin/memory-type-stats", self.base_url);
1401 let response = self.client.get(&url).send().await?;
1402 self.handle_response(response).await
1403 }
1404
1405 pub async fn migrate_namespace_dimensions(
1411 &self,
1412 request: crate::types::MigrateNamespaceDimensionsRequest,
1413 ) -> Result<crate::types::MigrateDimensionsResponse> {
1414 let url = format!("{}/v1/admin/namespaces/migrate-dimensions", self.base_url);
1415 let response = self.client.post(&url).json(&request).send().await?;
1416 self.handle_response(response).await
1417 }
1418
1419 pub async fn drain_reembed(
1434 &self,
1435 request: crate::types::DrainReembedRequest,
1436 ) -> Result<crate::types::DrainReembedResponse> {
1437 let url = format!("{}/v1/admin/reembed/drain", self.base_url);
1438 let response = self.client.post(&url).json(&request).send().await?;
1439 self.handle_response(response).await
1440 }
1441
1442 pub async fn admin_reembed_static_count(&self) -> Result<crate::types::StaticCountResponse> {
1451 let url = format!("{}/v1/admin/reembed/static-count", self.base_url);
1452 let response = self.client.get(&url).send().await?;
1453 self.handle_response(response).await
1454 }
1455}
1456
1457#[derive(Debug, Clone, Serialize, Deserialize)]
1468pub struct KpiSnapshot {
1469 pub recall_latency_p50_ms: f64,
1471 pub recall_latency_p99_ms: f64,
1473 pub store_latency_p50_ms: f64,
1475 pub api_error_rate_5xx_pct: f64,
1477 pub active_agents_count: u64,
1479 pub session_count_week: u64,
1481 pub cross_agent_network_node_count: u64,
1483 pub memory_retention_7d_pct: f64,
1485}
1486
1487#[derive(Debug, Clone, Serialize, Deserialize)]
1493pub struct FulltextReindexNamespaceResult {
1494 pub namespace: String,
1496 pub vectors_scanned: usize,
1498 pub newly_indexed: usize,
1500 pub already_indexed: usize,
1502 pub parse_failures: usize,
1504}
1505
1506#[derive(Debug, Clone, Serialize, Deserialize)]
1510pub struct FulltextReindexResponse {
1511 pub namespaces_processed: usize,
1513 pub total_indexed: usize,
1515 pub total_skipped: usize,
1517 pub details: Vec<FulltextReindexNamespaceResult>,
1519}