dakera_client/events.rs
1//! SSE streaming event types and client support (CE-1).
2//!
3//! ## Usage
4//!
5//! ```rust,no_run
6//! use dakera_client::DakeraClient;
7//! use tokio::sync::mpsc;
8//!
9//! #[tokio::main]
10//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
11//! let client = DakeraClient::new("http://localhost:3000")?;
12//!
13//! let mut rx = client.stream_namespace_events("my-ns").await?;
14//! while let Some(result) = rx.recv().await {
15//! let event = result?;
16//! println!("Event: {:?}", event);
17//! }
18//! Ok(())
19//! }
20//! ```
21
22use serde::{Deserialize, Serialize};
23
24/// Operation status for [`DakeraEvent::OperationProgress`] events.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
26#[serde(rename_all = "snake_case")]
27pub enum OpStatus {
28 Pending,
29 Running,
30 Completed,
31 Failed,
32}
33
34/// Vector mutation operation type for [`DakeraEvent::VectorsMutated`] events.
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36#[serde(rename_all = "snake_case")]
37pub enum VectorMutationOp {
38 Upserted,
39 Deleted,
40}
41
42/// An event received from a Dakera SSE stream.
43///
44/// Mirrors the server-side `DakeraEvent` enum. Events are delivered over
45/// `GET /v1/namespaces/:ns/events` (namespace-scoped, Read scope) or
46/// `GET /ops/events` (global, Admin scope).
47///
48/// Use [`DakeraClient::stream_namespace_events`] or
49/// [`DakeraClient::stream_global_events`] to subscribe.
50#[derive(Debug, Clone, Serialize, Deserialize)]
51#[serde(tag = "type", rename_all = "snake_case")]
52pub enum DakeraEvent {
53 /// A new namespace was created.
54 NamespaceCreated { namespace: String, dimension: usize },
55
56 /// A namespace was deleted.
57 NamespaceDeleted { namespace: String },
58
59 /// Progress update for a long-running operation (progress: 0–100).
60 OperationProgress {
61 operation_id: String,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 namespace: Option<String>,
64 op_type: String,
65 /// Progress percentage 0–100.
66 progress: u8,
67 status: OpStatus,
68 #[serde(skip_serializing_if = "Option::is_none")]
69 message: Option<String>,
70 /// Unix milliseconds.
71 updated_at: u64,
72 },
73
74 /// A background job changed status.
75 JobProgress {
76 job_id: String,
77 job_type: String,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 namespace: Option<String>,
80 progress: u8,
81 status: String,
82 },
83
84 /// Vectors were upserted or deleted in bulk (threshold: >100 vectors).
85 VectorsMutated {
86 namespace: String,
87 op: VectorMutationOp,
88 count: usize,
89 },
90
91 /// Subscriber fell too far behind — some events were dropped.
92 /// Reconnect to resume the stream.
93 StreamLagged { dropped: u64, hint: String },
94}
95
96impl DakeraEvent {
97 /// Returns the SSE `event` type string for this event variant.
98 pub fn event_type(&self) -> &'static str {
99 match self {
100 DakeraEvent::NamespaceCreated { .. } => "namespace_created",
101 DakeraEvent::NamespaceDeleted { .. } => "namespace_deleted",
102 DakeraEvent::OperationProgress { .. } => "operation_progress",
103 DakeraEvent::JobProgress { .. } => "job_progress",
104 DakeraEvent::VectorsMutated { .. } => "vectors_mutated",
105 DakeraEvent::StreamLagged { .. } => "stream_lagged",
106 }
107 }
108}