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 ClusterStatus {
20 pub cluster_id: String,
21 pub state: String,
22 pub node_count: u32,
23 pub total_vectors: u64,
24 pub namespace_count: u64,
25 pub version: String,
26 pub timestamp: u64,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct NodeInfo {
32 pub node_id: String,
33 pub address: String,
34 pub role: String,
35 pub status: String,
36 pub version: String,
37 pub uptime_seconds: u64,
38 pub vector_count: u64,
39 pub memory_bytes: u64,
40 #[serde(default)]
41 pub cpu_percent: f32,
42 #[serde(default)]
43 pub memory_percent: f32,
44 pub last_heartbeat: u64,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct NodeListResponse {
50 pub nodes: Vec<NodeInfo>,
51 pub total: u32,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct IndexStats {
61 pub index_type: String,
62 pub is_built: bool,
63 pub size_bytes: u64,
64 pub indexed_vectors: u64,
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub last_rebuild: Option<u64>,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct NamespaceAdminInfo {
72 pub name: String,
73 pub vector_count: u64,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub dimension: Option<usize>,
76 pub index_type: String,
77 pub storage_bytes: u64,
78 pub document_count: u64,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub created_at: Option<u64>,
81 #[serde(skip_serializing_if = "Option::is_none")]
82 pub updated_at: Option<u64>,
83 pub index_stats: IndexStats,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct NamespaceListResponse {
89 pub namespaces: Vec<NamespaceAdminInfo>,
90 pub total: u64,
91 pub total_vectors: u64,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct OptimizeRequest {
97 #[serde(default)]
98 pub force: bool,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub target_index_type: Option<String>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct OptimizeResponse {
106 pub success: bool,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub job_id: Option<String>,
109 pub message: String,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct IndexStatsResponse {
119 pub namespaces: HashMap<String, IndexStats>,
120 pub total_indexed_vectors: u64,
121 pub total_size_bytes: u64,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct RebuildIndexRequest {
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub namespace: Option<String>,
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub index_type: Option<String>,
131 #[serde(default)]
132 pub force: bool,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct RebuildIndexResponse {
138 pub success: bool,
139 pub job_id: String,
140 pub message: String,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct CacheStats {
150 pub enabled: bool,
151 pub cache_type: String,
152 pub entries: u64,
153 pub size_bytes: u64,
154 pub hits: u64,
155 pub misses: u64,
156 pub hit_rate: f64,
157 pub evictions: u64,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct ClearCacheRequest {
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub namespace: Option<String>,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct ClearCacheResponse {
170 pub success: bool,
171 pub entries_cleared: u64,
172 pub message: String,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct RuntimeConfig {
182 #[serde(skip_serializing_if = "Option::is_none")]
183 pub max_vectors_per_namespace: Option<u64>,
184 pub default_index_type: String,
185 pub cache_enabled: bool,
186 pub cache_max_size_bytes: u64,
187 pub rate_limit_enabled: bool,
188 pub rate_limit_rps: u32,
189 pub query_timeout_ms: u64,
190 #[serde(default = "default_true")]
192 pub autopilot_enabled: bool,
193 #[serde(default = "default_dedup_threshold")]
195 pub autopilot_dedup_threshold: f32,
196 #[serde(default = "default_dedup_interval")]
198 pub autopilot_dedup_interval_hours: u64,
199 #[serde(default = "default_consolidation_interval")]
201 pub autopilot_consolidation_interval_hours: u64,
202}
203
204fn default_true() -> bool {
205 true
206}
207fn default_dedup_threshold() -> f32 {
208 0.93
209}
210fn default_dedup_interval() -> u64 {
211 6
212}
213fn default_consolidation_interval() -> u64 {
214 12
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct UpdateConfigResponse {
220 pub success: bool,
221 pub config: RuntimeConfig,
222 pub message: String,
223 #[serde(default, skip_serializing_if = "Vec::is_empty")]
224 pub warnings: Vec<String>,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct QuotaConfig {
234 #[serde(skip_serializing_if = "Option::is_none")]
235 pub max_vectors: Option<u64>,
236 #[serde(skip_serializing_if = "Option::is_none")]
237 pub max_storage_bytes: Option<u64>,
238 #[serde(skip_serializing_if = "Option::is_none")]
239 pub max_queries_per_minute: Option<u64>,
240 #[serde(skip_serializing_if = "Option::is_none")]
241 pub max_writes_per_minute: Option<u64>,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct QuotaUsage {
247 #[serde(default)]
248 pub current_vectors: u64,
249 #[serde(default)]
250 pub current_storage_bytes: u64,
251 #[serde(default)]
252 pub queries_this_minute: u64,
253 #[serde(default)]
254 pub writes_this_minute: u64,
255}
256
257#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct QuotaStatus {
260 pub namespace: String,
261 pub config: QuotaConfig,
262 pub usage: QuotaUsage,
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct QuotaListResponse {
268 pub quotas: Vec<QuotaStatus>,
269 pub total: u64,
270 #[serde(skip_serializing_if = "Option::is_none")]
271 pub default_config: Option<QuotaConfig>,
272}
273
274#[derive(Debug, Clone, Serialize, Deserialize)]
280pub struct SlowQueryEntry {
281 pub id: String,
282 pub timestamp: u64,
283 pub namespace: String,
284 pub query_type: String,
285 pub duration_ms: f64,
286 #[serde(default)]
287 pub parameters: Option<serde_json::Value>,
288 #[serde(default)]
289 pub results_count: u64,
290 #[serde(default)]
291 pub vectors_scanned: u64,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
296pub struct SlowQueryListResponse {
297 pub queries: Vec<SlowQueryEntry>,
298 pub total: u64,
299 pub threshold_ms: f64,
300}
301
302#[derive(Debug, Clone, Serialize, Deserialize)]
308pub struct BackupInfo {
309 pub backup_id: String,
310 pub name: String,
311 pub backup_type: String,
312 pub status: String,
313 pub namespaces: Vec<String>,
314 pub vector_count: u64,
315 pub size_bytes: u64,
316 pub created_at: u64,
317 #[serde(skip_serializing_if = "Option::is_none")]
318 pub completed_at: Option<u64>,
319 #[serde(skip_serializing_if = "Option::is_none")]
320 pub duration_seconds: Option<u64>,
321 #[serde(skip_serializing_if = "Option::is_none")]
322 pub storage_path: Option<String>,
323 #[serde(skip_serializing_if = "Option::is_none")]
324 pub error: Option<String>,
325 pub encrypted: bool,
326 #[serde(skip_serializing_if = "Option::is_none")]
327 pub compression: Option<String>,
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct BackupListResponse {
333 pub backups: Vec<BackupInfo>,
334 pub total: u64,
335}
336
337#[derive(Debug, Clone, Serialize, Deserialize)]
339pub struct CreateBackupRequest {
340 pub name: String,
341 #[serde(skip_serializing_if = "Option::is_none")]
342 pub backup_type: Option<String>,
343 #[serde(skip_serializing_if = "Option::is_none")]
344 pub namespaces: Option<Vec<String>>,
345 #[serde(skip_serializing_if = "Option::is_none")]
346 pub encrypt: Option<bool>,
347 #[serde(skip_serializing_if = "Option::is_none")]
348 pub compression: Option<String>,
349}
350
351#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct CreateBackupResponse {
354 pub backup: BackupInfo,
355 #[serde(skip_serializing_if = "Option::is_none")]
356 pub estimated_completion: Option<u64>,
357}
358
359#[derive(Debug, Clone, Serialize, Deserialize)]
361pub struct RestoreBackupRequest {
362 pub backup_id: String,
363 #[serde(skip_serializing_if = "Option::is_none")]
364 pub target_namespaces: Option<Vec<String>>,
365 #[serde(skip_serializing_if = "Option::is_none")]
366 pub overwrite: Option<bool>,
367 #[serde(skip_serializing_if = "Option::is_none")]
368 pub point_in_time: Option<u64>,
369}
370
371#[derive(Debug, Clone, Serialize, Deserialize)]
373pub struct RestoreBackupResponse {
374 pub restore_id: String,
375 pub status: String,
376 pub backup_id: String,
377 pub namespaces: Vec<String>,
378 pub started_at: u64,
379 #[serde(skip_serializing_if = "Option::is_none")]
380 pub estimated_completion: Option<u64>,
381 #[serde(skip_serializing_if = "Option::is_none")]
382 pub progress_percent: Option<u8>,
383 #[serde(skip_serializing_if = "Option::is_none")]
384 pub vectors_restored: Option<u64>,
385 #[serde(skip_serializing_if = "Option::is_none")]
386 pub completed_at: Option<u64>,
387 #[serde(skip_serializing_if = "Option::is_none")]
388 pub duration_seconds: Option<u64>,
389 #[serde(skip_serializing_if = "Option::is_none")]
390 pub error: Option<String>,
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize)]
399pub struct AutoPilotConfig {
400 pub enabled: bool,
401 pub dedup_threshold: f32,
402 pub dedup_interval_hours: u64,
403 pub consolidation_interval_hours: u64,
404}
405
406#[derive(Debug, Clone, Serialize, Deserialize)]
408pub struct DedupResultSnapshot {
409 pub namespaces_processed: usize,
410 pub memories_scanned: usize,
411 pub duplicates_removed: usize,
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize)]
416pub struct ConsolidationResultSnapshot {
417 pub namespaces_processed: usize,
418 pub memories_scanned: usize,
419 pub clusters_merged: usize,
420 pub memories_consolidated: usize,
421}
422
423#[derive(Debug, Clone, Serialize, Deserialize)]
425pub struct AutoPilotStatusResponse {
426 pub config: AutoPilotConfig,
427 #[serde(skip_serializing_if = "Option::is_none")]
428 pub last_dedup_at: Option<u64>,
429 #[serde(skip_serializing_if = "Option::is_none")]
430 pub last_consolidation_at: Option<u64>,
431 #[serde(skip_serializing_if = "Option::is_none")]
432 pub last_dedup: Option<DedupResultSnapshot>,
433 #[serde(skip_serializing_if = "Option::is_none")]
434 pub last_consolidation: Option<ConsolidationResultSnapshot>,
435 pub total_dedup_removed: u64,
436 pub total_consolidated: u64,
437}
438
439#[derive(Debug, Clone, Serialize, Deserialize, Default)]
441pub struct AutoPilotConfigRequest {
442 #[serde(skip_serializing_if = "Option::is_none")]
443 pub enabled: Option<bool>,
444 #[serde(skip_serializing_if = "Option::is_none")]
445 pub dedup_threshold: Option<f32>,
446 #[serde(skip_serializing_if = "Option::is_none")]
447 pub dedup_interval_hours: Option<u64>,
448 #[serde(skip_serializing_if = "Option::is_none")]
449 pub consolidation_interval_hours: Option<u64>,
450}
451
452#[derive(Debug, Clone, Serialize, Deserialize)]
454pub struct AutoPilotConfigResponse {
455 pub success: bool,
456 pub config: AutoPilotConfig,
457 pub message: String,
458}
459
460#[derive(Debug, Clone, Serialize, Deserialize)]
462#[serde(rename_all = "lowercase")]
463pub enum AutoPilotTriggerAction {
464 Dedup,
465 Consolidate,
466 All,
467}
468
469#[derive(Debug, Clone, Serialize, Deserialize)]
471pub struct AutoPilotTriggerRequest {
472 pub action: AutoPilotTriggerAction,
473}
474
475#[derive(Debug, Clone, Serialize, Deserialize)]
477pub struct AutoPilotDedupResult {
478 pub namespaces_processed: usize,
479 pub memories_scanned: usize,
480 pub duplicates_removed: usize,
481}
482
483#[derive(Debug, Clone, Serialize, Deserialize)]
485pub struct AutoPilotConsolidationResult {
486 pub namespaces_processed: usize,
487 pub memories_scanned: usize,
488 pub clusters_merged: usize,
489 pub memories_consolidated: usize,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize)]
494pub struct AutoPilotTriggerResponse {
495 pub success: bool,
496 pub action: AutoPilotTriggerAction,
497 #[serde(skip_serializing_if = "Option::is_none")]
498 pub dedup: Option<AutoPilotDedupResult>,
499 #[serde(skip_serializing_if = "Option::is_none")]
500 pub consolidation: Option<AutoPilotConsolidationResult>,
501 pub message: String,
502}
503
504#[derive(Debug, Clone, Serialize, Deserialize)]
510pub struct TtlCleanupRequest {
511 #[serde(skip_serializing_if = "Option::is_none")]
512 pub namespace: Option<String>,
513}
514
515#[derive(Debug, Clone, Serialize, Deserialize)]
517pub struct TtlCleanupResponse {
518 pub success: bool,
519 pub vectors_removed: u64,
520 pub namespaces_cleaned: Vec<String>,
521 pub message: String,
522}
523
524#[derive(Debug, Clone, Serialize, Deserialize)]
526pub struct TtlStats {
527 pub namespace: String,
528 pub vectors_with_ttl: u64,
529 pub expiring_within_hour: u64,
530 pub expiring_within_day: u64,
531 pub expired_pending_cleanup: u64,
532}
533
534#[derive(Debug, Clone, Serialize, Deserialize)]
536pub struct TtlStatsResponse {
537 pub namespaces: Vec<TtlStats>,
538 pub total_with_ttl: u64,
539 pub total_expired: u64,
540}
541
542impl DakeraClient {
547 pub async fn cluster_status(&self) -> Result<ClusterStatus> {
553 let url = format!("{}/admin/cluster/status", self.base_url);
554 let response = self.client.get(&url).send().await?;
555 self.handle_response(response).await
556 }
557
558 pub async fn cluster_nodes(&self) -> Result<NodeListResponse> {
560 let url = format!("{}/admin/cluster/nodes", self.base_url);
561 let response = self.client.get(&url).send().await?;
562 self.handle_response(response).await
563 }
564
565 pub async fn list_namespaces_admin(&self) -> Result<NamespaceListResponse> {
571 let url = format!("{}/admin/namespaces", self.base_url);
572 let response = self.client.get(&url).send().await?;
573 self.handle_response(response).await
574 }
575
576 pub async fn delete_namespace_admin(&self, namespace: &str) -> Result<serde_json::Value> {
578 let url = format!("{}/admin/namespaces/{}", self.base_url, namespace);
579 let response = self.client.delete(&url).send().await?;
580 self.handle_response(response).await
581 }
582
583 pub async fn optimize_namespace(
585 &self,
586 namespace: &str,
587 request: OptimizeRequest,
588 ) -> Result<OptimizeResponse> {
589 let url = format!("{}/admin/namespaces/{}/optimize", self.base_url, namespace);
590 let response = self.client.post(&url).json(&request).send().await?;
591 self.handle_response(response).await
592 }
593
594 pub async fn index_stats(&self) -> Result<IndexStatsResponse> {
600 let url = format!("{}/admin/indexes/stats", self.base_url);
601 let response = self.client.get(&url).send().await?;
602 self.handle_response(response).await
603 }
604
605 pub async fn rebuild_indexes(
607 &self,
608 request: RebuildIndexRequest,
609 ) -> Result<RebuildIndexResponse> {
610 let url = format!("{}/admin/indexes/rebuild", self.base_url);
611 let response = self.client.post(&url).json(&request).send().await?;
612 self.handle_response(response).await
613 }
614
615 pub async fn cache_stats(&self) -> Result<CacheStats> {
621 let url = format!("{}/admin/cache/stats", self.base_url);
622 let response = self.client.get(&url).send().await?;
623 self.handle_response(response).await
624 }
625
626 pub async fn cache_clear(&self, namespace: Option<&str>) -> Result<ClearCacheResponse> {
628 let url = format!("{}/admin/cache/clear", self.base_url);
629 let request = ClearCacheRequest {
630 namespace: namespace.map(|s| s.to_string()),
631 };
632 let response = self.client.post(&url).json(&request).send().await?;
633 self.handle_response(response).await
634 }
635
636 pub async fn get_config(&self) -> Result<RuntimeConfig> {
642 let url = format!("{}/admin/config", self.base_url);
643 let response = self.client.get(&url).send().await?;
644 self.handle_response(response).await
645 }
646
647 pub async fn update_config(
649 &self,
650 updates: HashMap<String, serde_json::Value>,
651 ) -> Result<UpdateConfigResponse> {
652 let url = format!("{}/admin/config", self.base_url);
653 let response = self.client.put(&url).json(&updates).send().await?;
654 self.handle_response(response).await
655 }
656
657 pub async fn get_quotas(&self) -> Result<QuotaListResponse> {
663 let url = format!("{}/admin/quotas", self.base_url);
664 let response = self.client.get(&url).send().await?;
665 self.handle_response(response).await
666 }
667
668 pub async fn get_quota(&self, namespace: &str) -> Result<QuotaStatus> {
670 let url = format!("{}/admin/quotas/{}", self.base_url, namespace);
671 let response = self.client.get(&url).send().await?;
672 self.handle_response(response).await
673 }
674
675 pub async fn set_quota(
677 &self,
678 namespace: &str,
679 config: QuotaConfig,
680 ) -> Result<serde_json::Value> {
681 let url = format!("{}/admin/quotas/{}", self.base_url, namespace);
682 let request = serde_json::json!({ "config": config });
683 let response = self.client.put(&url).json(&request).send().await?;
684 self.handle_response(response).await
685 }
686
687 pub async fn delete_quota(&self, namespace: &str) -> Result<serde_json::Value> {
689 let url = format!("{}/admin/quotas/{}", self.base_url, namespace);
690 let response = self.client.delete(&url).send().await?;
691 self.handle_response(response).await
692 }
693
694 pub async fn update_quotas(&self, config: Option<QuotaConfig>) -> Result<serde_json::Value> {
696 let url = format!("{}/admin/quotas/default", self.base_url);
697 let request = serde_json::json!({ "config": config });
698 let response = self.client.put(&url).json(&request).send().await?;
699 self.handle_response(response).await
700 }
701
702 pub async fn slow_queries(
708 &self,
709 limit: Option<usize>,
710 namespace: Option<&str>,
711 query_type: Option<&str>,
712 ) -> Result<SlowQueryListResponse> {
713 let mut url = format!("{}/admin/slow-queries", self.base_url);
714 let mut params = Vec::new();
715 if let Some(l) = limit {
716 params.push(format!("limit={}", l));
717 }
718 if let Some(ns) = namespace {
719 params.push(format!("namespace={}", ns));
720 }
721 if let Some(qt) = query_type {
722 params.push(format!("query_type={}", qt));
723 }
724 if !params.is_empty() {
725 url.push('?');
726 url.push_str(¶ms.join("&"));
727 }
728 let response = self.client.get(&url).send().await?;
729 self.handle_response(response).await
730 }
731
732 pub async fn slow_query_summary(&self) -> Result<serde_json::Value> {
734 let url = format!("{}/admin/slow-queries/summary", self.base_url);
735 let response = self.client.get(&url).send().await?;
736 self.handle_response(response).await
737 }
738
739 pub async fn clear_slow_queries(&self) -> Result<serde_json::Value> {
741 let url = format!("{}/admin/slow-queries", self.base_url);
742 let response = self.client.delete(&url).send().await?;
743 self.handle_response(response).await
744 }
745
746 pub async fn create_backup(
752 &self,
753 request: CreateBackupRequest,
754 ) -> Result<CreateBackupResponse> {
755 let url = format!("{}/admin/backups", self.base_url);
756 let response = self.client.post(&url).json(&request).send().await?;
757 self.handle_response(response).await
758 }
759
760 pub async fn list_backups(&self) -> Result<BackupListResponse> {
762 let url = format!("{}/admin/backups", 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_backup(&self, backup_id: &str) -> Result<BackupInfo> {
769 let url = format!("{}/admin/backups/{}", self.base_url, backup_id);
770 let response = self.client.get(&url).send().await?;
771 self.handle_response(response).await
772 }
773
774 pub async fn restore_backup(
776 &self,
777 request: RestoreBackupRequest,
778 ) -> Result<RestoreBackupResponse> {
779 let url = format!("{}/admin/backups/restore", self.base_url);
780 let response = self.client.post(&url).json(&request).send().await?;
781 self.handle_response(response).await
782 }
783
784 pub async fn delete_backup(&self, backup_id: &str) -> Result<serde_json::Value> {
786 let url = format!("{}/admin/backups/{}", self.base_url, backup_id);
787 let response = self.client.delete(&url).send().await?;
788 self.handle_response(response).await
789 }
790
791 pub async fn ttl_cleanup(&self, namespace: Option<&str>) -> Result<TtlCleanupResponse> {
797 let url = format!("{}/admin/ttl/cleanup", self.base_url);
798 let request = TtlCleanupRequest {
799 namespace: namespace.map(|s| s.to_string()),
800 };
801 let response = self.client.post(&url).json(&request).send().await?;
802 self.handle_response(response).await
803 }
804
805 pub async fn ttl_stats(&self) -> Result<TtlStatsResponse> {
807 let url = format!("{}/admin/ttl/stats", self.base_url);
808 let response = self.client.get(&url).send().await?;
809 self.handle_response(response).await
810 }
811
812 pub async fn autopilot_status(&self) -> Result<AutoPilotStatusResponse> {
818 let url = format!("{}/admin/autopilot/status", self.base_url);
819 let response = self.client.get(&url).send().await?;
820 self.handle_response(response).await
821 }
822
823 pub async fn autopilot_update_config(
827 &self,
828 request: AutoPilotConfigRequest,
829 ) -> Result<AutoPilotConfigResponse> {
830 let url = format!("{}/admin/autopilot/config", self.base_url);
831 let response = self.client.put(&url).json(&request).send().await?;
832 self.handle_response(response).await
833 }
834
835 pub async fn autopilot_trigger(
840 &self,
841 action: AutoPilotTriggerAction,
842 ) -> Result<AutoPilotTriggerResponse> {
843 let url = format!("{}/admin/autopilot/trigger", self.base_url);
844 let request = AutoPilotTriggerRequest { action };
845 let response = self.client.post(&url).json(&request).send().await?;
846 self.handle_response(response).await
847 }
848}