vectorizer_sdk/models.rs
1//! Data models for the Vectorizer SDK
2
3use std::collections::HashMap;
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8// Re-export hybrid search models
9pub mod hybrid_search;
10pub use hybrid_search::*;
11
12// Re-export graph models
13pub mod graph;
14pub use graph::*;
15
16// Re-export file upload models
17pub mod file_upload;
18pub use file_upload::*;
19
20// Typed Qdrant-compatible filter builder (phase23).
21// Exposed as `vectorizer_sdk::models::filter` so callers do not need
22// to depend on the server crate directly.
23pub mod filter;
24pub use filter::{
25 QdrantCondition, QdrantFilter, QdrantGeoBoundingBox, QdrantGeoPoint, QdrantGeoRadius,
26 QdrantMatchValue, QdrantRange, QdrantValuesCount,
27};
28
29// ===== TIER-CONTROL REPORTS (phase13) =====
30
31/// Aggregate outcome of a `delete_by_filter` call against
32/// `POST /collections/{name}/vectors/delete_by_filter`.
33///
34/// Server contract: `{scanned, matched, deleted, results}`.
35#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
36pub struct DeleteByFilterReport {
37 /// Total vectors scanned by the filter pass.
38 pub scanned: usize,
39 /// Vectors that matched the filter predicate.
40 pub matched: usize,
41 /// Vectors successfully deleted.
42 pub deleted: usize,
43 /// Per-id outcomes for matched vectors.
44 #[serde(default)]
45 pub results: Vec<serde_json::Value>,
46}
47
48/// Aggregate outcome of a `bulk_update_metadata` call against
49/// `POST /collections/{name}/vectors/bulk_update_metadata`.
50///
51/// Server contract: `{scanned, matched, updated, results}`.
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
53pub struct BulkUpdateReport {
54 /// Total vectors scanned by the filter pass.
55 pub scanned: usize,
56 /// Vectors that matched the filter predicate.
57 pub matched: usize,
58 /// Vectors whose metadata was successfully updated.
59 pub updated: usize,
60 /// Per-id outcomes for matched vectors.
61 #[serde(default)]
62 pub results: Vec<serde_json::Value>,
63}
64
65/// Aggregate outcome of a `copy_vectors` call against
66/// `POST /collections/{src}/vectors/copy`.
67///
68/// Server contract: `{src, dst, requested, copied, failed, results}`.
69/// Per-id status: `ok | missing_in_src | dst_insert_failed`.
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
71pub struct CopyReport {
72 /// Source collection name, echoed by the server.
73 pub src: String,
74 /// Destination collection name, echoed by the server.
75 pub dst: String,
76 /// Total ids the request asked to copy.
77 pub requested: usize,
78 /// Successfully copied ids.
79 pub copied: usize,
80 /// Ids that failed at any step.
81 pub failed: usize,
82 /// Per-id outcomes, in request order.
83 pub results: Vec<VectorOpResult>,
84}
85
86/// Job descriptor returned by `reencode_collection` against
87/// `POST /collections/{name}/reencode`.
88///
89/// Server contract: `{job_id, collection, state, target_encoding, progress}`.
90#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
91pub struct ReencodeJob {
92 /// Opaque job id generated by the server.
93 pub job_id: String,
94 /// Collection being re-encoded.
95 pub collection: String,
96 /// Job state string (e.g. `"completed"`).
97 pub state: String,
98 /// Target encoding requested (e.g. `"sq8"`, `"binary"`, `"fp32"`).
99 pub target_encoding: String,
100 /// Progress fraction in `[0.0, 1.0]`.
101 pub progress: f64,
102}
103
104// ===== TIER-DEMOTION REPORTS (issue #265) =====
105
106/// Per-vector outcome for a `delete_vectors` or `move_to_collection`
107/// call. The `status` string is one of:
108///
109/// - `ok` — vector was deleted (delete) or moved (move) successfully.
110/// - `missing_in_src` — id was not present in the source collection.
111/// - `dst_insert_failed` — destination rejected the insert (move only;
112/// typically a dim/encoding mismatch).
113/// - `src_delete_failed` — destination accepted the insert but the
114/// source delete failed (move only; the vector now exists in BOTH
115/// collections — recoverable on retry).
116/// - `error` — generic per-id failure (delete only).
117#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
118pub struct VectorOpResult {
119 /// Vector id this row refers to. May be missing when the request
120 /// payload contained a non-string entry that the server rejected.
121 #[serde(default)]
122 pub id: Option<String>,
123
124 /// One of `ok | missing_in_src | dst_insert_failed |
125 /// src_delete_failed | error` — see [`VectorOpResult`] doc.
126 pub status: String,
127
128 /// Server-side error message, populated when `status != "ok"`.
129 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub error: Option<String>,
131
132 /// Index of this entry in the request's `ids` array (delete only).
133 #[serde(default, skip_serializing_if = "Option::is_none")]
134 pub index: Option<usize>,
135}
136
137/// Aggregate outcome of a `delete_vectors` call against
138/// `POST /batch_delete`. Mirrors the server contract:
139/// `{collection, count, deleted, failed, results}`.
140#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
141pub struct DeleteReport {
142 /// Source collection name, echoed by the server.
143 pub collection: String,
144 /// Total ids the request asked to delete.
145 #[serde(default)]
146 pub count: usize,
147 /// Successfully deleted ids.
148 pub deleted: usize,
149 /// Ids that failed (missing or backend error).
150 pub failed: usize,
151 /// Per-id outcomes, in request order.
152 pub results: Vec<VectorOpResult>,
153}
154
155/// Aggregate outcome of a `move_to_collection` call against
156/// `POST /collections/{src}/vectors/move` (issue #265).
157///
158/// Server invariant: vectors are inserted into `dst` BEFORE being
159/// deleted from `src`, so a mid-batch failure leaves a recoverable
160/// duplicate (never data loss). Per-id failures populate `results`
161/// without aborting the batch — operators chasing tier-demotion sweeps
162/// want partial progress, not an abort-on-first-error contract.
163#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
164pub struct MoveReport {
165 /// Source collection name, echoed by the server.
166 pub src: String,
167 /// Destination collection name, echoed by the server.
168 pub dst: String,
169 /// Total ids the request asked to move.
170 pub requested: usize,
171 /// Successfully moved ids (insert + delete both succeeded).
172 pub moved: usize,
173 /// Ids that failed at any step.
174 pub failed: usize,
175 /// Per-id outcomes, in request order.
176 pub results: Vec<VectorOpResult>,
177}
178
179// ===== CLIENT-SIDE REPLICATION CONFIGURATION =====
180
181/// Read preference for routing read operations.
182/// Similar to MongoDB's read preferences.
183#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
184#[serde(rename_all = "snake_case")]
185pub enum ReadPreference {
186 /// Route all reads to master
187 Master,
188 /// Route reads to replicas (round-robin)
189 #[default]
190 Replica,
191 /// Route to the node with lowest latency
192 Nearest,
193}
194
195/// Host configuration for master/replica topology.
196#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct HostConfig {
198 /// Master node URL (receives all write operations)
199 pub master: String,
200 /// Replica node URLs (receive read operations based on read_preference)
201 pub replicas: Vec<String>,
202}
203
204/// Options that can be passed to read operations for per-operation override.
205#[derive(Debug, Clone, Default)]
206pub struct ReadOptions {
207 /// Override the default read preference for this operation
208 pub read_preference: Option<ReadPreference>,
209}
210
211/// Vector similarity metrics
212#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
213#[serde(rename_all = "snake_case")]
214pub enum SimilarityMetric {
215 /// Cosine similarity
216 #[default]
217 Cosine,
218 /// Euclidean distance
219 Euclidean,
220 /// Dot product
221 DotProduct,
222}
223
224/// Vector representation
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct Vector {
227 /// Unique identifier for the vector
228 pub id: String,
229 /// Vector data as an array of numbers
230 pub data: Vec<f32>,
231 /// Optional metadata associated with the vector
232 pub metadata: Option<HashMap<String, serde_json::Value>>,
233 /// Optional ECC public key for payload encryption (PEM, base64, or hex format)
234 #[serde(skip_serializing_if = "Option::is_none")]
235 pub public_key: Option<String>,
236}
237
238/// Collection representation
239#[derive(Debug, Clone, Serialize, Deserialize)]
240pub struct Collection {
241 /// Collection name
242 pub name: String,
243 /// Vector dimension
244 pub dimension: usize,
245 /// Similarity metric used for search (API may return as 'metric')
246 #[serde(alias = "similarity_metric")]
247 pub metric: Option<String>,
248 /// Optional description
249 #[serde(default)]
250 pub description: Option<String>,
251 /// Creation timestamp
252 #[serde(default)]
253 pub created_at: Option<String>,
254 /// Last update timestamp
255 #[serde(default)]
256 pub updated_at: Option<String>,
257 /// Vector count
258 #[serde(default)]
259 pub vector_count: usize,
260 /// Document count
261 #[serde(default)]
262 pub document_count: usize,
263 /// Embedding provider
264 #[serde(default)]
265 pub embedding_provider: Option<String>,
266 /// Indexing status
267 #[serde(default)]
268 pub indexing_status: Option<serde_json::Value>,
269 /// Normalization config
270 #[serde(default)]
271 pub normalization: Option<serde_json::Value>,
272 /// Quantization config
273 #[serde(default)]
274 pub quantization: Option<serde_json::Value>,
275 /// Size info
276 #[serde(default)]
277 pub size: Option<serde_json::Value>,
278 /// Per-collection vector-count ring buffer (phase25 §6). At most
279 /// 60 samples, one per minute, sampled lazily on
280 /// `GET /collections/{name}` requests. Empty array on older
281 /// servers or for collections that have never been read.
282 #[serde(default)]
283 pub vector_count_history: Vec<VectorCountSample>,
284}
285
286/// One sample in the per-collection vector-count history (phase25 §6).
287#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
288pub struct VectorCountSample {
289 /// Sample timestamp in unix seconds.
290 pub at: u64,
291 /// Vector count at the time of the sample.
292 pub count: usize,
293}
294
295/// Collection information.
296///
297/// The v3.0.0 REST surface returns `metric` in Rust-Debug form
298/// (e.g. `"Cosine"`), plus new top-level blocks (`size`, `quantization`,
299/// `normalization`, `status`). Every field beyond `name` + `dimension`
300/// carries `#[serde(default)]` so the model tolerates pre-v3 servers
301/// and future additions (request models keep the strict posture; this
302/// is a response-only struct).
303#[derive(Debug, Clone, Serialize, Deserialize)]
304pub struct CollectionInfo {
305 /// Collection name
306 pub name: String,
307 /// Vector dimension
308 pub dimension: usize,
309 /// Similarity metric used for search. The v3 server emits this in
310 /// Rust-Debug form (`"Cosine"` / `"Euclidean"` / `"DotProduct"`);
311 /// callers that compare against `"cosine"` etc. should go through
312 /// `.to_lowercase()`.
313 #[serde(default, alias = "similarity_metric")]
314 pub metric: String,
315 /// Number of vectors in the collection
316 #[serde(default)]
317 pub vector_count: usize,
318 /// Number of documents in the collection
319 #[serde(default)]
320 pub document_count: usize,
321 /// Creation timestamp (RFC3339). Optional — pre-v3 servers may omit.
322 #[serde(default)]
323 pub created_at: String,
324 /// Last update timestamp (RFC3339). Optional — pre-v3 servers may omit.
325 #[serde(default)]
326 pub updated_at: String,
327 /// Indexing status. Absent on the v3 server; some legacy servers send it.
328 #[serde(default)]
329 pub indexing_status: Option<IndexingStatus>,
330 /// Size block emitted by v3 (`{total, total_bytes, index, index_bytes,
331 /// payload, payload_bytes}`).
332 #[serde(default)]
333 pub size: Option<serde_json::Value>,
334 /// Quantization block emitted by v3 (`{enabled, type, bits}`).
335 #[serde(default)]
336 pub quantization: Option<serde_json::Value>,
337 /// Normalization block emitted by v3.
338 #[serde(default)]
339 pub normalization: Option<serde_json::Value>,
340 /// Ready/indexing/error state emitted by v3.
341 #[serde(default)]
342 pub status: Option<String>,
343}
344
345/// Indexing status.
346///
347/// Every field carries `#[serde(default)]` to match the tolerant
348/// posture of the parent [`CollectionInfo`] — the v3 server emits a
349/// subset of this shape (`status`/`progress`/`total_documents`/
350/// `processed_documents` plus some extra keys this struct doesn't
351/// model) and omits `vector_count` and `last_updated`, which used to
352/// make `serde_json::from_str::<CollectionInfo>` fail on a
353/// `Collection → JSON → CollectionInfo` round-trip through
354/// `Collection::indexing_status: Option<serde_json::Value>` that
355/// preserves the server's partial shape.
356#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct IndexingStatus {
358 /// Status
359 #[serde(default)]
360 pub status: String,
361 /// Progress percentage
362 #[serde(default)]
363 pub progress: f32,
364 /// Total documents
365 #[serde(default)]
366 pub total_documents: usize,
367 /// Processed documents
368 #[serde(default)]
369 pub processed_documents: usize,
370 /// Vector count
371 #[serde(default)]
372 pub vector_count: usize,
373 /// Estimated time remaining
374 #[serde(default)]
375 pub estimated_time_remaining: Option<String>,
376 /// Last updated timestamp
377 #[serde(default)]
378 pub last_updated: String,
379}
380
381/// Search result
382#[derive(Debug, Clone, Serialize, Deserialize)]
383pub struct SearchResult {
384 /// Vector ID
385 pub id: String,
386 /// Similarity score
387 pub score: f32,
388 /// Vector content (if available)
389 pub content: Option<String>,
390 /// Optional metadata
391 pub metadata: Option<HashMap<String, serde_json::Value>>,
392}
393
394/// Search response.
395///
396/// `query_time_ms` defaults to `0.0` because the v3.0.x server's text
397/// search handler doesn't emit it — callers that need elapsed timing
398/// should measure client-side. Same tolerance applies to the
399/// additional diagnostic fields the server may add in later versions.
400#[derive(Debug, Clone, Serialize, Deserialize)]
401pub struct SearchResponse {
402 /// Search results.
403 #[serde(default)]
404 pub results: Vec<SearchResult>,
405 /// Query time in milliseconds (server-reported; 0.0 when the
406 /// server omits it).
407 #[serde(default)]
408 pub query_time_ms: f64,
409 /// Echo of the original query string, if the server returned one.
410 #[serde(default)]
411 pub query: Option<String>,
412 /// Echo of the requested result limit, if the server returned one.
413 #[serde(default)]
414 pub limit: Option<usize>,
415 /// Echo of the collection name, if the server returned one.
416 #[serde(default)]
417 pub collection: Option<String>,
418}
419
420/// Embedding request
421#[derive(Debug, Clone, Serialize, Deserialize)]
422pub struct EmbeddingRequest {
423 /// Text to embed
424 pub text: String,
425 /// Optional model to use for embedding
426 pub model: Option<String>,
427 /// Optional parameters for embedding generation
428 pub parameters: Option<EmbeddingParameters>,
429}
430
431/// Embedding parameters
432#[derive(Debug, Clone, Serialize, Deserialize)]
433pub struct EmbeddingParameters {
434 /// Maximum sequence length
435 pub max_length: Option<usize>,
436 /// Whether to normalize the embedding
437 pub normalize: Option<bool>,
438 /// Optional prefix for the text
439 pub prefix: Option<String>,
440}
441
442/// Embedding response
443#[derive(Debug, Clone, Serialize, Deserialize)]
444pub struct EmbeddingResponse {
445 /// Generated embedding vector
446 pub embedding: Vec<f32>,
447 /// Model used for embedding
448 pub model: String,
449 /// Text that was embedded
450 pub text: String,
451 /// Embedding dimension
452 pub dimension: usize,
453 /// Provider used
454 pub provider: String,
455}
456
457/// Health status
458#[derive(Debug, Clone, Serialize, Deserialize)]
459pub struct HealthStatus {
460 /// Service status
461 pub status: String,
462 /// Service version
463 pub version: String,
464 /// Timestamp
465 pub timestamp: String,
466 /// Uptime in seconds
467 pub uptime: Option<u64>,
468 /// Number of collections
469 pub collections: Option<usize>,
470 /// Total number of vectors
471 pub total_vectors: Option<usize>,
472}
473
474/// Collections list response
475#[derive(Debug, Clone, Serialize, Deserialize)]
476pub struct CollectionsResponse {
477 /// List of collections
478 pub collections: Vec<Collection>,
479}
480
481/// Create collection response
482#[derive(Debug, Clone, Serialize, Deserialize)]
483pub struct CreateCollectionResponse {
484 /// Success message
485 pub message: String,
486 /// Collection name
487 pub collection: String,
488}
489
490/// Database statistics
491#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct DatabaseStats {
493 /// Total number of collections
494 pub total_collections: usize,
495 /// Total number of vectors
496 pub total_vectors: usize,
497 /// Total memory estimate in bytes
498 pub total_memory_estimate_bytes: usize,
499 /// Collections information
500 pub collections: Vec<CollectionStats>,
501}
502
503/// Collection statistics
504#[derive(Debug, Clone, Serialize, Deserialize)]
505pub struct CollectionStats {
506 /// Collection name
507 pub name: String,
508 /// Number of vectors
509 pub vector_count: usize,
510 /// Vector dimension
511 pub dimension: usize,
512 /// Memory estimate in bytes
513 pub memory_estimate_bytes: usize,
514}
515
516/// Batch text request
517#[derive(Debug, Clone, Serialize, Deserialize)]
518pub struct BatchTextRequest {
519 /// Text ID
520 pub id: String,
521 /// Text content
522 pub text: String,
523 /// Optional metadata
524 pub metadata: Option<HashMap<String, String>>,
525}
526
527/// Batch configuration
528#[derive(Debug, Clone, Serialize, Deserialize)]
529pub struct BatchConfig {
530 /// Maximum batch size
531 pub max_batch_size: Option<usize>,
532 /// Number of parallel workers
533 pub parallel_workers: Option<usize>,
534 /// Whether operations should be atomic
535 pub atomic: Option<bool>,
536}
537
538/// Batch insert request
539#[derive(Debug, Clone, Serialize, Deserialize)]
540pub struct BatchInsertRequest {
541 /// Texts to insert
542 pub texts: Vec<BatchTextRequest>,
543 /// Batch configuration
544 pub config: Option<BatchConfig>,
545}
546
547/// Batch response.
548///
549/// Tolerant of both the old (pre-v3) `{success, operation, total_operations,
550/// successful_operations, failed_operations, duration_ms, errors}` shape
551/// and the v3.0.x server's `/insert_texts` response
552/// `{collection, count, inserted, failed, results}`. All fields default to
553/// empty/zero/false when absent so callers can match on whichever pair the
554/// running server emits without branching on version.
555#[derive(Debug, Clone, Serialize, Deserialize)]
556pub struct BatchResponse {
557 /// Whether the operation was successful (pre-v3 shape; v3 emits
558 /// `inserted`/`failed` instead — left `false` and the caller
559 /// should inspect `successful_operations > 0 && failed_operations == 0`).
560 #[serde(default)]
561 pub success: bool,
562 /// Collection name (both shapes emit this).
563 #[serde(default)]
564 pub collection: String,
565 /// Operation type (pre-v3 only; v3 omits).
566 #[serde(default)]
567 pub operation: String,
568 /// Total number of operations (pre-v3 shape). v3 emits `count` —
569 /// normalised into this field via the alias.
570 #[serde(default, alias = "count")]
571 pub total_operations: usize,
572 /// Number of successful operations (pre-v3 shape). v3 emits
573 /// `inserted` — aliased so either maps onto this field.
574 #[serde(default, alias = "inserted")]
575 pub successful_operations: usize,
576 /// Number of failed operations. Same field name in both shapes.
577 #[serde(default, alias = "failed")]
578 pub failed_operations: usize,
579 /// Duration in milliseconds (pre-v3 only).
580 #[serde(default)]
581 pub duration_ms: u64,
582 /// Error messages (pre-v3 shape).
583 #[serde(default)]
584 pub errors: Vec<String>,
585 /// Per-entry result records emitted by v3 `/insert_texts`. Each
586 /// record carries the client-sent id (`client_id`) and the
587 /// server-assigned UUIDs under `vector_ids`; use this when the
588 /// server reassigns ids on insert.
589 #[serde(default)]
590 pub results: Vec<BatchResultEntry>,
591}
592
593/// One entry in `BatchResponse::results` as emitted by the v3
594/// `/insert_texts` handler. Carries the client-provided id alongside
595/// the server-assigned vector UUID(s) so callers can round-trip the
596/// mapping when they need idempotency by client id.
597#[derive(Debug, Clone, Serialize, Deserialize)]
598pub struct BatchResultEntry {
599 /// Original `id` the caller sent in `BatchTextRequest`.
600 #[serde(default)]
601 pub client_id: String,
602 /// Zero-based index of the entry in the original batch.
603 #[serde(default)]
604 pub index: usize,
605 /// `"ok"` or `"error"`.
606 #[serde(default)]
607 pub status: String,
608 /// Whether the server chunked the input (long text → multiple
609 /// vectors).
610 #[serde(default)]
611 pub chunked: bool,
612 /// Server-assigned UUID(s) — one element unless `chunked` is true.
613 #[serde(default)]
614 pub vector_ids: Vec<String>,
615 /// Count of vectors created for this entry (≥1 if `chunked`).
616 #[serde(default)]
617 pub vectors_created: usize,
618 /// Populated only on `status == "error"`.
619 #[serde(default)]
620 pub error: Option<String>,
621}
622
623/// Batch search request
624#[derive(Debug, Clone, Serialize, Deserialize)]
625pub struct BatchSearchRequest {
626 /// Search queries
627 pub queries: Vec<BatchSearchQuery>,
628 /// Batch configuration
629 pub config: Option<BatchConfig>,
630}
631
632/// Batch search response
633#[derive(Debug, Clone, Serialize, Deserialize)]
634pub struct BatchSearchResponse {
635 /// Whether the operation was successful
636 pub success: bool,
637 /// Collection name
638 pub collection: String,
639 /// Total number of queries
640 pub total_queries: usize,
641 /// Number of successful queries
642 pub successful_queries: usize,
643 /// Number of failed queries
644 pub failed_queries: usize,
645 /// Duration in milliseconds
646 pub duration_ms: u64,
647 /// Search results
648 pub results: Vec<Vec<SearchResult>>,
649 /// Error messages
650 pub errors: Vec<String>,
651}
652
653/// Batch vector update
654#[derive(Debug, Clone, Serialize, Deserialize)]
655pub struct BatchVectorUpdate {
656 /// Vector ID
657 pub id: String,
658 /// New vector data (optional)
659 pub data: Option<Vec<f32>>,
660 /// New metadata (optional)
661 pub metadata: Option<HashMap<String, serde_json::Value>>,
662}
663
664/// Batch update request
665#[derive(Debug, Clone, Serialize, Deserialize)]
666pub struct BatchUpdateRequest {
667 /// Vector updates
668 pub updates: Vec<BatchVectorUpdate>,
669 /// Batch configuration
670 pub config: Option<BatchConfig>,
671}
672
673/// Batch delete request
674#[derive(Debug, Clone, Serialize, Deserialize)]
675pub struct BatchDeleteRequest {
676 /// Vector IDs to delete
677 pub vector_ids: Vec<String>,
678 /// Batch configuration
679 pub config: Option<BatchConfig>,
680}
681
682/// Summarization methods
683#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
684#[serde(rename_all = "snake_case")]
685pub enum SummarizationMethod {
686 /// Extractive summarization
687 #[default]
688 Extractive,
689 /// Keyword summarization
690 Keyword,
691 /// Sentence summarization
692 Sentence,
693 /// Abstractive summarization
694 Abstractive,
695}
696
697/// Summarize text request
698#[derive(Debug, Clone, Serialize, Deserialize)]
699pub struct SummarizeTextRequest {
700 /// Text to summarize
701 pub text: String,
702 /// Summarization method
703 pub method: Option<SummarizationMethod>,
704 /// Maximum summary length
705 pub max_length: Option<usize>,
706 /// Compression ratio
707 pub compression_ratio: Option<f32>,
708 /// Language code
709 pub language: Option<String>,
710}
711
712/// Summarize text response
713#[derive(Debug, Clone, Serialize, Deserialize)]
714pub struct SummarizeTextResponse {
715 /// Summary ID
716 pub summary_id: String,
717 /// Original text
718 pub original_text: String,
719 /// Generated summary
720 pub summary: String,
721 /// Method used
722 pub method: String,
723 /// Original text length
724 pub original_length: usize,
725 /// Summary length
726 pub summary_length: usize,
727 /// Compression ratio
728 pub compression_ratio: f32,
729 /// Language
730 pub language: String,
731 /// Status
732 pub status: String,
733 /// Message
734 pub message: String,
735 /// Metadata
736 pub metadata: HashMap<String, String>,
737}
738
739/// Summarize context request
740#[derive(Debug, Clone, Serialize, Deserialize)]
741pub struct SummarizeContextRequest {
742 /// Context to summarize
743 pub context: String,
744 /// Summarization method
745 pub method: Option<SummarizationMethod>,
746 /// Maximum summary length
747 pub max_length: Option<usize>,
748 /// Compression ratio
749 pub compression_ratio: Option<f32>,
750 /// Language code
751 pub language: Option<String>,
752}
753
754/// Summarize context response
755#[derive(Debug, Clone, Serialize, Deserialize)]
756pub struct SummarizeContextResponse {
757 /// Summary ID
758 pub summary_id: String,
759 /// Original context
760 pub original_context: String,
761 /// Generated summary
762 pub summary: String,
763 /// Method used
764 pub method: String,
765 /// Original context length
766 pub original_length: usize,
767 /// Summary length
768 pub summary_length: usize,
769 /// Compression ratio
770 pub compression_ratio: f32,
771 /// Language
772 pub language: String,
773 /// Status
774 pub status: String,
775 /// Message
776 pub message: String,
777 /// Metadata
778 pub metadata: HashMap<String, String>,
779}
780
781/// Get summary response
782#[derive(Debug, Clone, Serialize, Deserialize)]
783pub struct GetSummaryResponse {
784 /// Summary ID
785 pub summary_id: String,
786 /// Original text
787 pub original_text: String,
788 /// Generated summary
789 pub summary: String,
790 /// Method used
791 pub method: String,
792 /// Original text length
793 pub original_length: usize,
794 /// Summary length
795 pub summary_length: usize,
796 /// Compression ratio
797 pub compression_ratio: f32,
798 /// Language
799 pub language: String,
800 /// Creation timestamp
801 pub created_at: String,
802 /// Metadata
803 pub metadata: HashMap<String, String>,
804 /// Status
805 pub status: String,
806}
807
808/// Summary info
809#[derive(Debug, Clone, Serialize, Deserialize)]
810pub struct SummaryInfo {
811 /// Summary ID
812 pub summary_id: String,
813 /// Method used
814 pub method: String,
815 /// Language
816 pub language: String,
817 /// Original text length
818 pub original_length: usize,
819 /// Summary length
820 pub summary_length: usize,
821 /// Compression ratio
822 pub compression_ratio: f32,
823 /// Creation timestamp
824 pub created_at: String,
825 /// Metadata
826 pub metadata: HashMap<String, String>,
827}
828
829/// List summaries response
830#[derive(Debug, Clone, Serialize, Deserialize)]
831pub struct ListSummariesResponse {
832 /// List of summaries
833 pub summaries: Vec<SummaryInfo>,
834 /// Total count
835 pub total_count: usize,
836 /// Status
837 pub status: String,
838}
839
840/// Indexing progress
841#[derive(Debug, Clone, Serialize, Deserialize)]
842pub struct IndexingProgress {
843 /// Whether indexing is in progress
844 pub is_indexing: bool,
845 /// Overall status
846 pub overall_status: String,
847 /// Collections being indexed
848 pub collections: Vec<CollectionProgress>,
849}
850
851/// Collection progress
852#[derive(Debug, Clone, Serialize, Deserialize)]
853pub struct CollectionProgress {
854 /// Collection name
855 pub collection_name: String,
856 /// Status
857 pub status: String,
858 /// Progress percentage
859 pub progress: f32,
860 /// Vector count
861 pub vector_count: usize,
862 /// Error message if any
863 pub error_message: Option<String>,
864 /// Last updated timestamp
865 pub last_updated: String,
866}
867
868// ===== INTELLIGENT SEARCH MODELS =====
869
870/// Intelligent search request
871#[derive(Debug, Clone, Serialize, Deserialize)]
872pub struct IntelligentSearchRequest {
873 /// Search query
874 pub query: String,
875 /// Collections to search (optional - searches all if not specified)
876 pub collections: Option<Vec<String>>,
877 /// Maximum number of results
878 pub max_results: Option<usize>,
879 /// Enable domain expansion
880 pub domain_expansion: Option<bool>,
881 /// Enable technical focus
882 pub technical_focus: Option<bool>,
883 /// Enable MMR diversification
884 pub mmr_enabled: Option<bool>,
885 /// MMR balance parameter (0.0-1.0)
886 pub mmr_lambda: Option<f32>,
887}
888
889/// Semantic search request
890#[derive(Debug, Clone, Serialize, Deserialize)]
891pub struct SemanticSearchRequest {
892 /// Search query
893 pub query: String,
894 /// Collection to search
895 pub collection: String,
896 /// Maximum number of results
897 pub max_results: Option<usize>,
898 /// Enable semantic reranking
899 pub semantic_reranking: Option<bool>,
900 /// Enable cross-encoder reranking
901 pub cross_encoder_reranking: Option<bool>,
902 /// Minimum similarity threshold
903 pub similarity_threshold: Option<f32>,
904}
905
906/// Contextual search request
907#[derive(Debug, Clone, Serialize, Deserialize)]
908pub struct ContextualSearchRequest {
909 /// Search query
910 pub query: String,
911 /// Collection to search
912 pub collection: String,
913 /// Metadata-based context filters
914 pub context_filters: Option<HashMap<String, serde_json::Value>>,
915 /// Maximum number of results
916 pub max_results: Option<usize>,
917 /// Enable context-aware reranking
918 pub context_reranking: Option<bool>,
919 /// Weight of context factors (0.0-1.0)
920 pub context_weight: Option<f32>,
921}
922
923/// Multi-collection search request
924#[derive(Debug, Clone, Serialize, Deserialize)]
925pub struct MultiCollectionSearchRequest {
926 /// Search query
927 pub query: String,
928 /// Collections to search
929 pub collections: Vec<String>,
930 /// Maximum results per collection
931 pub max_per_collection: Option<usize>,
932 /// Maximum total results
933 pub max_total_results: Option<usize>,
934 /// Enable cross-collection reranking
935 pub cross_collection_reranking: Option<bool>,
936}
937
938/// Intelligent search result
939#[derive(Debug, Clone, Serialize, Deserialize)]
940pub struct IntelligentSearchResult {
941 /// Result ID
942 pub id: String,
943 /// Similarity score
944 pub score: f32,
945 /// Result content
946 pub content: String,
947 /// Metadata
948 pub metadata: Option<HashMap<String, serde_json::Value>>,
949 /// Collection name
950 pub collection: Option<String>,
951 /// Query used for this result
952 pub query_used: Option<String>,
953}
954
955/// Intelligent search response
956#[derive(Debug, Clone, Serialize, Deserialize)]
957pub struct IntelligentSearchResponse {
958 /// Search results
959 pub results: Vec<IntelligentSearchResult>,
960 /// Total number of results found
961 pub total_results: usize,
962 /// Search duration in milliseconds
963 pub duration_ms: u64,
964 /// Queries generated
965 pub queries_generated: Option<Vec<String>>,
966 /// Collections searched
967 pub collections_searched: Option<Vec<String>>,
968 /// Search metadata
969 pub metadata: Option<HashMap<String, serde_json::Value>>,
970}
971
972/// Semantic search response
973#[derive(Debug, Clone, Serialize, Deserialize)]
974pub struct SemanticSearchResponse {
975 /// Search results
976 pub results: Vec<IntelligentSearchResult>,
977 /// Total number of results found
978 pub total_results: usize,
979 /// Search duration in milliseconds
980 pub duration_ms: u64,
981 /// Collection searched
982 pub collection: String,
983 /// Search metadata
984 pub metadata: Option<HashMap<String, serde_json::Value>>,
985}
986
987/// Contextual search response
988#[derive(Debug, Clone, Serialize, Deserialize)]
989pub struct ContextualSearchResponse {
990 /// Search results
991 pub results: Vec<IntelligentSearchResult>,
992 /// Total number of results found
993 pub total_results: usize,
994 /// Search duration in milliseconds
995 pub duration_ms: u64,
996 /// Collection searched
997 pub collection: String,
998 /// Context filters applied
999 pub context_filters: Option<HashMap<String, serde_json::Value>>,
1000 /// Search metadata
1001 pub metadata: Option<HashMap<String, serde_json::Value>>,
1002}
1003
1004/// Multi-collection search response
1005#[derive(Debug, Clone, Serialize, Deserialize)]
1006pub struct MultiCollectionSearchResponse {
1007 /// Search results
1008 pub results: Vec<IntelligentSearchResult>,
1009 /// Total number of results found
1010 pub total_results: usize,
1011 /// Search duration in milliseconds
1012 pub duration_ms: u64,
1013 /// Collections searched
1014 pub collections_searched: Vec<String>,
1015 /// Results per collection
1016 pub results_per_collection: Option<HashMap<String, usize>>,
1017 /// Search metadata
1018 pub metadata: Option<HashMap<String, serde_json::Value>>,
1019}
1020
1021// ==================== REPLICATION MODELS ====================
1022
1023/// Status of a replica node
1024#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1025#[serde(rename_all = "PascalCase")]
1026pub enum ReplicaStatus {
1027 /// Replica is connected and healthy
1028 Connected,
1029 /// Replica is syncing data
1030 Syncing,
1031 /// Replica is lagging behind master
1032 Lagging,
1033 /// Replica is disconnected
1034 Disconnected,
1035}
1036
1037/// Information about a replica node
1038#[derive(Debug, Clone, Serialize, Deserialize)]
1039pub struct ReplicaInfo {
1040 /// Unique identifier for the replica
1041 pub replica_id: String,
1042 /// Hostname or IP address of the replica
1043 pub host: String,
1044 /// Port number of the replica
1045 pub port: u16,
1046 /// Current status of the replica
1047 pub status: String,
1048 /// Timestamp of last heartbeat
1049 pub last_heartbeat: DateTime<Utc>,
1050 /// Number of operations successfully synced
1051 pub operations_synced: u64,
1052
1053 // Legacy fields (backwards compatible)
1054 /// Legacy: Current offset on replica (deprecated, use operations_synced)
1055 #[serde(skip_serializing_if = "Option::is_none")]
1056 pub offset: Option<u64>,
1057 /// Legacy: Lag in operations (deprecated, use status)
1058 #[serde(skip_serializing_if = "Option::is_none")]
1059 pub lag: Option<u64>,
1060}
1061
1062/// Statistics for replication status
1063#[derive(Debug, Clone, Serialize, Deserialize)]
1064pub struct ReplicationStats {
1065 // New fields (v1.2.0+)
1066 /// Role of the node: Master or Replica
1067 #[serde(skip_serializing_if = "Option::is_none")]
1068 pub role: Option<String>,
1069 /// Total bytes sent to replicas (Master only)
1070 #[serde(skip_serializing_if = "Option::is_none")]
1071 pub bytes_sent: Option<u64>,
1072 /// Total bytes received from master (Replica only)
1073 #[serde(skip_serializing_if = "Option::is_none")]
1074 pub bytes_received: Option<u64>,
1075 /// Timestamp of last synchronization
1076 #[serde(skip_serializing_if = "Option::is_none")]
1077 pub last_sync: Option<DateTime<Utc>>,
1078 /// Number of operations pending replication
1079 #[serde(skip_serializing_if = "Option::is_none")]
1080 pub operations_pending: Option<usize>,
1081 /// Size of snapshot data in bytes
1082 #[serde(skip_serializing_if = "Option::is_none")]
1083 pub snapshot_size: Option<usize>,
1084 /// Number of connected replicas (Master only)
1085 #[serde(skip_serializing_if = "Option::is_none")]
1086 pub connected_replicas: Option<usize>,
1087
1088 // Legacy fields (backwards compatible - always present)
1089 /// Current offset on master node
1090 pub master_offset: u64,
1091 /// Current offset on replica node
1092 pub replica_offset: u64,
1093 /// Number of operations behind
1094 pub lag_operations: u64,
1095 /// Total operations replicated
1096 pub total_replicated: u64,
1097}
1098
1099/// Response for replication status endpoint
1100#[derive(Debug, Clone, Serialize, Deserialize)]
1101pub struct ReplicationStatusResponse {
1102 /// Overall status message
1103 pub status: String,
1104 /// Detailed replication statistics
1105 pub stats: ReplicationStats,
1106 /// Optional message with additional information
1107 #[serde(skip_serializing_if = "Option::is_none")]
1108 pub message: Option<String>,
1109}
1110
1111/// Response for listing replicas
1112#[derive(Debug, Clone, Serialize, Deserialize)]
1113pub struct ReplicaListResponse {
1114 /// List of replica nodes
1115 pub replicas: Vec<ReplicaInfo>,
1116 /// Total count of replicas
1117 pub count: usize,
1118 /// Status message
1119 pub message: String,
1120}
1121
1122// ===== VECTOR OPERATIONS — NEW METHODS (phase12) =====
1123
1124/// Paginated vector listing returned by `GET /collections/{name}/vectors`.
1125#[derive(Debug, Clone, Serialize, Deserialize)]
1126pub struct VectorPage {
1127 /// Vectors on this page — each entry carries `id`, `vector`, and `payload`.
1128 #[serde(default)]
1129 pub vectors: Vec<serde_json::Value>,
1130 /// Total vector count in the collection (unfiltered).
1131 #[serde(default)]
1132 pub total: usize,
1133 /// The effective `limit` applied by the server.
1134 #[serde(default)]
1135 pub limit: usize,
1136 /// The byte-offset applied by the server.
1137 ///
1138 /// Note: the task spec calls this `page` but the server handler
1139 /// (`GET /collections/{name}/vectors`) uses an `offset` query
1140 /// parameter internally and echoes it back as `offset`.
1141 #[serde(default)]
1142 pub offset: usize,
1143 /// Optional human-readable pagination hint from the server.
1144 #[serde(default)]
1145 pub message: Option<String>,
1146}
1147
1148/// Request body for `update_vector` (`POST /update`).
1149///
1150/// Server reads `id` and `collection` from the top-level body.
1151/// All other keys are treated as payload updates.
1152#[derive(Debug, Clone, Serialize, Deserialize)]
1153pub struct UpdateVectorRequest {
1154 /// Vector id to update.
1155 pub id: String,
1156 /// New metadata / payload to merge (free-form JSON).
1157 #[serde(default, skip_serializing_if = "Option::is_none")]
1158 pub metadata: Option<serde_json::Value>,
1159}
1160
1161/// One item in a `batch_insert_texts` call (`POST /batch_insert`).
1162#[derive(Debug, Clone, Serialize, Deserialize)]
1163pub struct BatchInsertItem {
1164 /// Optional client-supplied id.
1165 #[serde(default, skip_serializing_if = "Option::is_none")]
1166 pub id: Option<String>,
1167 /// Text content to embed and insert.
1168 pub text: String,
1169 /// Optional metadata to attach.
1170 #[serde(default, skip_serializing_if = "Option::is_none")]
1171 pub metadata: Option<serde_json::Value>,
1172}
1173
1174/// Aggregate outcome of a `batch_insert_texts` or `insert_vectors` call.
1175/// Server response shape: `{collection, inserted, failed, count, results}`.
1176#[derive(Debug, Clone, Serialize, Deserialize)]
1177pub struct BatchInsertReport {
1178 /// Collection the vectors were inserted into.
1179 pub collection: String,
1180 /// Vectors successfully inserted.
1181 #[serde(alias = "inserted")]
1182 pub successful: usize,
1183 /// Vectors that failed.
1184 pub failed: usize,
1185 /// Total entries submitted.
1186 #[serde(default, alias = "count")]
1187 pub total: usize,
1188 /// Per-entry results.
1189 #[serde(default)]
1190 pub results: Vec<BatchResultEntry>,
1191}
1192
1193/// One entry in a `batch_update_vectors` call (`POST /batch_update`).
1194#[derive(Debug, Clone, Serialize, Deserialize)]
1195pub struct VectorUpdate {
1196 /// Vector id to update.
1197 pub id: String,
1198 /// Optional new dense vector data.
1199 #[serde(default, skip_serializing_if = "Option::is_none")]
1200 pub vector: Option<Vec<f32>>,
1201 /// Optional new payload.
1202 #[serde(default, skip_serializing_if = "Option::is_none")]
1203 pub payload: Option<serde_json::Value>,
1204}
1205
1206/// Aggregate outcome of a `batch_update_vectors` call.
1207/// Server response: `{collection, count, updated, failed, results}`.
1208#[derive(Debug, Clone, Serialize, Deserialize)]
1209pub struct BatchUpdateReport {
1210 /// Collection the update ran against.
1211 pub collection: String,
1212 /// Total entries submitted.
1213 #[serde(default, alias = "count")]
1214 pub total: usize,
1215 /// Successfully updated entries.
1216 #[serde(alias = "updated")]
1217 pub successful: usize,
1218 /// Failed entries.
1219 pub failed: usize,
1220 /// Per-entry results.
1221 #[serde(default)]
1222 pub results: Vec<serde_json::Value>,
1223}
1224
1225/// One vector entry in an `insert_vectors` call (`POST /insert_vectors`).
1226#[derive(Debug, Clone, Serialize, Deserialize)]
1227pub struct RawVectorInsert {
1228 /// Optional client-supplied id.
1229 #[serde(default, skip_serializing_if = "Option::is_none")]
1230 pub id: Option<String>,
1231 /// Pre-computed embedding (length must match collection dimension).
1232 pub embedding: Vec<f32>,
1233 /// Optional payload (takes precedence over `metadata`).
1234 #[serde(default, skip_serializing_if = "Option::is_none")]
1235 pub payload: Option<serde_json::Value>,
1236 /// Optional metadata (string–string map; used when `payload` absent).
1237 #[serde(default, skip_serializing_if = "Option::is_none")]
1238 pub metadata: Option<HashMap<String, String>>,
1239}
1240
1241/// One query in a `batch_search` call (`POST /batch_search`).
1242#[derive(Debug, Clone, Serialize, Deserialize)]
1243pub struct BatchSearchQuery {
1244 /// Text query (embedded server-side) or raw vector (send via `vector`).
1245 #[serde(default, skip_serializing_if = "Option::is_none")]
1246 pub query: Option<String>,
1247 /// Raw vector query (skips embedding).
1248 #[serde(default, skip_serializing_if = "Option::is_none")]
1249 pub vector: Option<Vec<f32>>,
1250 /// Max results for this query.
1251 #[serde(default, skip_serializing_if = "Option::is_none")]
1252 pub limit: Option<usize>,
1253 /// Score threshold.
1254 #[serde(default, skip_serializing_if = "Option::is_none")]
1255 pub threshold: Option<f64>,
1256}
1257
1258/// Request for `search_by_file` (`POST /collections/{name}/search/file`).
1259#[derive(Debug, Clone, Serialize, Deserialize)]
1260pub struct SearchByFileRequest {
1261 /// File path to search within.
1262 pub file_path: String,
1263 /// Max results.
1264 #[serde(default, skip_serializing_if = "Option::is_none")]
1265 pub limit: Option<usize>,
1266}
1267
1268// ===== DISCOVERY PIPELINE TYPES (phase12) =====
1269
1270/// Request for `broad_discovery` (`POST /discovery/broad_discovery`).
1271///
1272/// Server reads `queries` (array of strings) and optional `k`.
1273#[derive(Debug, Clone, Serialize, Deserialize)]
1274pub struct BroadDiscoveryRequest {
1275 /// Expanded query strings to search across all collections.
1276 pub queries: Vec<String>,
1277 /// Number of top chunks to retrieve per query (default 50).
1278 #[serde(default, skip_serializing_if = "Option::is_none")]
1279 pub k: Option<usize>,
1280}
1281
1282/// Response from `broad_discovery`.
1283/// Server: `{chunks: [{collection, score, content_preview}], count}`.
1284#[derive(Debug, Clone, Serialize, Deserialize)]
1285pub struct BroadDiscoveryResponse {
1286 /// Retrieved chunk summaries.
1287 #[serde(default)]
1288 pub chunks: Vec<serde_json::Value>,
1289 /// Total chunks returned.
1290 #[serde(default)]
1291 pub count: usize,
1292}
1293
1294/// Request for `semantic_focus` (`POST /discovery/semantic_focus`).
1295#[derive(Debug, Clone, Serialize, Deserialize)]
1296pub struct SemanticFocusRequest {
1297 /// Target collection name.
1298 pub collection: String,
1299 /// Queries to focus within the collection.
1300 pub queries: Vec<String>,
1301 /// Number of top results per query (default 15).
1302 #[serde(default, skip_serializing_if = "Option::is_none")]
1303 pub k: Option<usize>,
1304}
1305
1306/// Response from `semantic_focus`.
1307/// Server: `{chunks: [...], count}`.
1308#[derive(Debug, Clone, Serialize, Deserialize)]
1309pub struct SemanticFocusResponse {
1310 /// Retrieved chunk summaries.
1311 #[serde(default)]
1312 pub chunks: Vec<serde_json::Value>,
1313 /// Total chunks returned.
1314 #[serde(default)]
1315 pub count: usize,
1316}
1317
1318/// Request for `promote_readme` (`POST /discovery/promote_readme`).
1319/// `chunks` is an array of `ScoredChunk`-compatible objects.
1320#[derive(Debug, Clone, Serialize, Deserialize)]
1321pub struct PromoteReadmeRequest {
1322 /// Chunks to evaluate for README promotion.
1323 pub chunks: Vec<serde_json::Value>,
1324}
1325
1326/// Response from `promote_readme`.
1327/// Server: `{promoted_chunks: [...], count}`.
1328#[derive(Debug, Clone, Serialize, Deserialize)]
1329pub struct PromoteReadmeResponse {
1330 /// Chunks identified as README-quality.
1331 #[serde(default)]
1332 pub promoted_chunks: Vec<serde_json::Value>,
1333 /// Count of promoted chunks.
1334 #[serde(default)]
1335 pub count: usize,
1336}
1337
1338/// Request for `compress_evidence` (`POST /discovery/compress_evidence`).
1339#[derive(Debug, Clone, Serialize, Deserialize)]
1340pub struct CompressEvidenceRequest {
1341 /// Input chunks to compress into bullets.
1342 pub chunks: Vec<serde_json::Value>,
1343 /// Maximum number of bullets to emit (default 20).
1344 #[serde(default, skip_serializing_if = "Option::is_none")]
1345 pub max_bullets: Option<usize>,
1346 /// Maximum bullets per source document (default 3).
1347 #[serde(default, skip_serializing_if = "Option::is_none")]
1348 pub max_per_doc: Option<usize>,
1349}
1350
1351/// Response from `compress_evidence`.
1352/// Server: `{bullets: [{text, source_id, category, score}], count}`.
1353#[derive(Debug, Clone, Serialize, Deserialize)]
1354pub struct CompressEvidenceResponse {
1355 /// Compressed bullet points.
1356 #[serde(default)]
1357 pub bullets: Vec<serde_json::Value>,
1358 /// Count of bullets.
1359 #[serde(default)]
1360 pub count: usize,
1361}
1362
1363/// Request for `build_answer_plan` (`POST /discovery/build_answer_plan`).
1364#[derive(Debug, Clone, Serialize, Deserialize)]
1365pub struct AnswerPlanRequest {
1366 /// Bullets to organize into a plan.
1367 pub bullets: Vec<serde_json::Value>,
1368}
1369
1370/// Response from `build_answer_plan`.
1371/// Server: `{sections: [...], total_bullets, sources}`.
1372#[derive(Debug, Clone, Serialize, Deserialize)]
1373pub struct AnswerPlan {
1374 /// Organized sections.
1375 #[serde(default)]
1376 pub sections: Vec<serde_json::Value>,
1377 /// Total bullet count across all sections.
1378 #[serde(default)]
1379 pub total_bullets: usize,
1380 /// Source collection names referenced.
1381 #[serde(default)]
1382 pub sources: Vec<String>,
1383}
1384
1385/// Request for `render_llm_prompt` (`POST /discovery/render_llm_prompt`).
1386#[derive(Debug, Clone, Serialize, Deserialize)]
1387pub struct RenderPromptRequest {
1388 /// The answer plan to render.
1389 pub plan: AnswerPlan,
1390}
1391
1392/// Response from `render_llm_prompt`.
1393/// Server: `{prompt, length, estimated_tokens}`.
1394#[derive(Debug, Clone, Serialize, Deserialize)]
1395pub struct LlmPrompt {
1396 /// The rendered prompt string.
1397 pub prompt: String,
1398 /// Byte length of the prompt.
1399 #[serde(default)]
1400 pub length: usize,
1401 /// Rough token estimate (length / 4).
1402 #[serde(default)]
1403 pub estimated_tokens: usize,
1404}
1405
1406// ===== ADMIN / OBSERVABILITY TYPES (phase12) =====
1407
1408/// Server statistics returned by `GET /stats`.
1409///
1410/// Server: `{collections, total_vectors, uptime_seconds, version,
1411/// default_quantization, compression_ratio}`. The last two are
1412/// phase25 §5 additions and default to `("none", 1.0)` on older
1413/// servers that do not emit them.
1414#[derive(Debug, Clone, Serialize, Deserialize)]
1415pub struct Stats {
1416 /// Number of collections.
1417 #[serde(default)]
1418 pub collections: usize,
1419 /// Total vectors across all collections.
1420 #[serde(default)]
1421 pub total_vectors: usize,
1422 /// Server uptime in seconds.
1423 #[serde(default)]
1424 pub uptime_seconds: u64,
1425 /// Server version string.
1426 #[serde(default)]
1427 pub version: String,
1428 /// Most-common quantization label across active collections
1429 /// (`none`, `binary`, `sq-4bit`, `sq-8bit`, `sq-16bit`, `sq`, or
1430 /// `pq`). `none` when the store is empty or the server is older
1431 /// than phase25 §5.
1432 #[serde(default = "default_quantization_label")]
1433 pub default_quantization: String,
1434 /// Mean compression ratio (uncompressed_bytes / compressed_bytes)
1435 /// across the collections sharing `default_quantization`. `1.0`
1436 /// when no collections are present or on older servers.
1437 #[serde(default = "default_compression_ratio")]
1438 pub compression_ratio: f32,
1439}
1440
1441fn default_quantization_label() -> String {
1442 "none".to_string()
1443}
1444
1445fn default_compression_ratio() -> f32 {
1446 1.0
1447}
1448
1449/// Runtime metrics snapshot returned by `GET /metrics/runtime`
1450/// (phase25). Single JSON object refreshed once per second on the
1451/// server. Every field defaults so the SDK tolerates older servers
1452/// that do not emit the route.
1453#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1454pub struct RuntimeMetrics {
1455 /// CPU usage of the server process, 0–100 %.
1456 #[serde(default)]
1457 pub cpu_percent: f64,
1458 /// Resident-set size of the server process in bytes.
1459 #[serde(default)]
1460 pub memory_rss_bytes: u64,
1461 /// Total physical memory of the host in bytes.
1462 #[serde(default)]
1463 pub memory_total_bytes: u64,
1464 /// RSS as a fraction of total memory, 0–100 %.
1465 #[serde(default)]
1466 pub memory_percent: f64,
1467 /// Active HTTP connections at the moment of sampling.
1468 #[serde(default)]
1469 pub active_connections: usize,
1470 /// Seconds since the server process started.
1471 #[serde(default)]
1472 pub uptime_seconds: u64,
1473 /// Rolling 60-second queries-per-second across all routes.
1474 #[serde(default)]
1475 pub qps_window_60s: f64,
1476 /// Fraction of requests in the last 60 s with HTTP 5xx status,
1477 /// 0–1.
1478 #[serde(default)]
1479 pub error_rate_5xx_60s: f64,
1480 /// Per-route latency / throughput. Sorted descending by QPS.
1481 #[serde(default)]
1482 pub throughput_by_route: Vec<RouteStats>,
1483 /// WAL state. Zero-initialised on standalone servers without
1484 /// replication.
1485 #[serde(default)]
1486 pub wal: WalSnapshot,
1487}
1488
1489/// Per-route latency + QPS line in `RuntimeMetrics.throughput_by_route`.
1490#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1491pub struct RouteStats {
1492 /// Route path (raw URI; templated routes are normalised by the server).
1493 #[serde(default)]
1494 pub route: String,
1495 /// Queries per second for this route over the last 60 s.
1496 #[serde(default)]
1497 pub qps: f64,
1498 /// 50th-percentile latency in milliseconds.
1499 #[serde(default)]
1500 pub p50_ms: f64,
1501 /// 99th-percentile latency in milliseconds.
1502 #[serde(default)]
1503 pub p99_ms: f64,
1504}
1505
1506/// WAL state surfaced inside `RuntimeMetrics.wal` (phase25 §3).
1507#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1508pub struct WalSnapshot {
1509 /// Latest offset appended to the WAL.
1510 #[serde(default)]
1511 pub current_seq: u64,
1512 /// On-disk WAL file size in bytes (0 in memory-only mode).
1513 #[serde(default)]
1514 pub size_bytes: u64,
1515 /// Unix timestamp (seconds) at which `last_checkpoint_seq` last
1516 /// advanced. 0 when no replica has confirmed an offset.
1517 #[serde(default)]
1518 pub last_checkpoint_at: u64,
1519 /// Lowest offset that has been confirmed by all replicas.
1520 #[serde(default)]
1521 pub last_checkpoint_seq: u64,
1522}
1523
1524/// Server status returned by `GET /status`.
1525/// Server: `{online, version, uptime_seconds, collections_count}`.
1526#[derive(Debug, Clone, Serialize, Deserialize)]
1527pub struct ServerStatus {
1528 /// Whether the server is online.
1529 #[serde(default)]
1530 pub online: bool,
1531 /// Version string.
1532 #[serde(default)]
1533 pub version: String,
1534 /// Uptime in seconds.
1535 #[serde(default)]
1536 pub uptime_seconds: u64,
1537 /// Number of collections.
1538 #[serde(default)]
1539 pub collections_count: usize,
1540}
1541
1542/// Query parameters for `get_logs` (`GET /logs`).
1543#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1544pub struct LogsQuery {
1545 /// Number of log lines to return (default 100).
1546 #[serde(default, skip_serializing_if = "Option::is_none")]
1547 pub lines: Option<usize>,
1548 /// Optional level filter (`INFO`, `WARN`, `ERROR`, `DEBUG`).
1549 #[serde(default, skip_serializing_if = "Option::is_none")]
1550 pub level: Option<String>,
1551}
1552
1553/// One log entry returned by `GET /logs`.
1554#[derive(Debug, Clone, Serialize, Deserialize)]
1555pub struct LogEntry {
1556 /// ISO-8601 timestamp.
1557 #[serde(default)]
1558 pub timestamp: String,
1559 /// Log level.
1560 #[serde(default)]
1561 pub level: String,
1562 /// Log message.
1563 #[serde(default)]
1564 pub message: String,
1565 /// Source component.
1566 #[serde(default)]
1567 pub source: String,
1568}
1569
1570/// Report returned by `cleanup_empty_collections` (`DELETE /collections/cleanup`).
1571///
1572/// The server shape is inferred; the handler returns a free-form JSON
1573/// `Value` so we use `serde_json::Value` fields with defaults.
1574#[derive(Debug, Clone, Serialize, Deserialize)]
1575pub struct CleanupReport {
1576 /// Whether the cleanup succeeded.
1577 #[serde(default)]
1578 pub success: bool,
1579 /// Number of collections removed.
1580 #[serde(default)]
1581 pub removed: usize,
1582 /// Names of the removed collections.
1583 #[serde(default)]
1584 pub collections: Vec<String>,
1585 /// Any message from the server.
1586 #[serde(default)]
1587 pub message: Option<String>,
1588}
1589
1590/// Server configuration snapshot returned by `GET /config` and `POST /config`.
1591///
1592/// The config is a free-form YAML object loaded from `config.yml`; the
1593/// SDK surfaces it as `serde_json::Value` to avoid coupling to the
1594/// server's internal `Config` struct. Wrap in a newtype for ergonomics.
1595#[derive(Debug, Clone, Serialize, Deserialize)]
1596pub struct ConfigSnapshot(pub serde_json::Value);
1597
1598/// Patch applied to the server configuration by `POST /config`.
1599/// The server accepts the full config as a JSON object and writes it to
1600/// `config.yml`.
1601#[derive(Debug, Clone, Serialize, Deserialize)]
1602pub struct ConfigPatch(pub serde_json::Value);
1603
1604/// Metadata for one server-side backup file returned by `GET /backups`.
1605/// Server shape: `{id, name, date, size, collections}`.
1606#[derive(Debug, Clone, Serialize, Deserialize)]
1607pub struct BackupInfo {
1608 /// UUID of the backup.
1609 pub id: String,
1610 /// Human-readable name.
1611 pub name: String,
1612 /// Creation timestamp (RFC3339).
1613 pub date: String,
1614 /// File size in bytes (estimate).
1615 #[serde(default)]
1616 pub size: u64,
1617 /// Collection names included in the backup.
1618 #[serde(default)]
1619 pub collections: Vec<String>,
1620}
1621
1622/// Request body for `create_backup` (`POST /backups/create`).
1623#[derive(Debug, Clone, Serialize, Deserialize)]
1624pub struct CreateBackupRequest {
1625 /// Backup name.
1626 pub name: String,
1627 /// Collection names to include (empty = all).
1628 #[serde(default)]
1629 pub collections: Vec<String>,
1630}
1631
1632/// Request body for `restore_backup` (`POST /backups/restore`).
1633#[derive(Debug, Clone, Serialize, Deserialize)]
1634pub struct RestoreBackupRequest {
1635 /// ID of the backup to restore.
1636 pub backup_id: String,
1637}
1638
1639/// Workspace config entry returned by `GET /workspace/config` and
1640/// `GET /workspace/list`.
1641///
1642/// The config is a free-form YAML object; surfaced as `serde_json::Value`.
1643#[derive(Debug, Clone, Serialize, Deserialize)]
1644pub struct WorkspaceConfig(pub serde_json::Value);
1645
1646/// Request body for `add_workspace` (`POST /workspace/add`).
1647/// Server reads `path` and `collection_name`.
1648#[derive(Debug, Clone, Serialize, Deserialize)]
1649pub struct AddWorkspaceRequest {
1650 /// File-system path to watch.
1651 pub path: String,
1652 /// Collection name to index files into.
1653 pub collection_name: String,
1654}
1655
1656// ===== AUTH TYPES (phase12) =====
1657
1658/// User record returned by auth endpoints.
1659/// Server shape: `{user_id, username, roles}`.
1660#[derive(Debug, Clone, Serialize, Deserialize)]
1661pub struct User {
1662 /// Opaque user identifier.
1663 pub user_id: String,
1664 /// Username string.
1665 pub username: String,
1666 /// Role names.
1667 #[serde(default)]
1668 pub roles: Vec<String>,
1669}
1670
1671/// JWT token returned by `POST /auth/refresh`.
1672/// Server shape: `{access_token, token_type, expires_in}`.
1673#[derive(Debug, Clone, Serialize, Deserialize)]
1674pub struct JwtToken {
1675 /// Bearer access token.
1676 pub access_token: String,
1677 /// Token type (always `"Bearer"`).
1678 pub token_type: String,
1679 /// Lifetime in seconds.
1680 pub expires_in: u64,
1681}
1682
1683/// Password policy report returned by `POST /auth/validate-password`.
1684/// Server shape: `{valid, errors, strength, strength_label}`.
1685#[derive(Debug, Clone, Serialize, Deserialize)]
1686pub struct PasswordPolicyReport {
1687 /// Whether the password satisfies all policy rules.
1688 pub valid: bool,
1689 /// Validation errors (empty when valid).
1690 #[serde(default)]
1691 pub errors: Vec<String>,
1692 /// Strength score 0–100.
1693 #[serde(default)]
1694 pub strength: u8,
1695 /// Human-readable strength label.
1696 #[serde(default)]
1697 pub strength_label: String,
1698}
1699
1700/// Request body for `create_api_key` (`POST /auth/keys`).
1701#[derive(Debug, Clone, Serialize, Deserialize)]
1702pub struct CreateApiKeyRequest {
1703 /// Key name / description.
1704 pub name: String,
1705 /// Permissions (optional; defaults to Read).
1706 #[serde(default)]
1707 pub permissions: Vec<String>,
1708 /// TTL in seconds from now (None = never expires).
1709 #[serde(default, skip_serializing_if = "Option::is_none")]
1710 pub expires_in: Option<u64>,
1711}
1712
1713/// API key returned by `POST /auth/keys`.
1714///
1715/// The actual key string is only returned once at creation time.
1716/// `GET /auth/keys` returns entries with `api_key` omitted.
1717#[derive(Debug, Clone, Serialize, Deserialize)]
1718pub struct ApiKey {
1719 /// Key UUID.
1720 pub id: String,
1721 /// Key name.
1722 pub name: String,
1723 /// Permissions.
1724 #[serde(default)]
1725 pub permissions: Vec<String>,
1726 /// The raw API key value (only present at creation time).
1727 #[serde(default, skip_serializing_if = "Option::is_none")]
1728 pub api_key: Option<String>,
1729 /// Creation timestamp (Unix epoch seconds).
1730 #[serde(default)]
1731 pub created_at: u64,
1732 /// Last-used timestamp.
1733 #[serde(default, skip_serializing_if = "Option::is_none")]
1734 pub last_used: Option<u64>,
1735 /// Expiry timestamp (None = never expires).
1736 #[serde(default, skip_serializing_if = "Option::is_none")]
1737 pub expires_at: Option<u64>,
1738 /// Whether the key is currently active.
1739 #[serde(default)]
1740 pub active: bool,
1741 /// One-time warning message (present at creation).
1742 #[serde(default, skip_serializing_if = "Option::is_none")]
1743 pub warning: Option<String>,
1744 /// Total successful credential validations recorded against this
1745 /// key. Defaults to 0 for keys that have never been used (or for
1746 /// servers that don't yet emit the field).
1747 #[serde(default)]
1748 pub usage_count: u64,
1749}
1750
1751/// Per-collection scope attached to an API key.
1752#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1753pub struct ApiKeyScope {
1754 /// Collection this scope applies to.
1755 pub collection: String,
1756 /// Permissions granted on that collection.
1757 #[serde(default)]
1758 pub permissions: Vec<String>,
1759}
1760
1761/// Request body for `PUT /auth/keys/{id}/permissions`.
1762#[derive(Debug, Clone, Serialize, Deserialize)]
1763pub struct UpdateApiKeyPermissionsRequest {
1764 /// New permission list. Server rejects an empty list with 400.
1765 pub permissions: Vec<String>,
1766 /// `None` leaves existing scopes untouched. `Some(vec![])` clears
1767 /// scopes (default-deny on scope-aware routes).
1768 #[serde(default, skip_serializing_if = "Option::is_none")]
1769 pub scopes: Option<Vec<ApiKeyScope>>,
1770}
1771
1772/// Flattened key view returned by `update_api_key_permissions` and
1773/// `get_api_key_usage`. Mirrors the server's `ApiKeyView`.
1774#[derive(Debug, Clone, Serialize, Deserialize)]
1775pub struct ApiKeyView {
1776 pub id: String,
1777 pub name: String,
1778 pub user_id: String,
1779 #[serde(default)]
1780 pub permissions: Vec<String>,
1781 #[serde(default)]
1782 pub scopes: Vec<ApiKeyScope>,
1783 pub created_at: u64,
1784 #[serde(default, skip_serializing_if = "Option::is_none")]
1785 pub last_used: Option<u64>,
1786 #[serde(default, skip_serializing_if = "Option::is_none")]
1787 pub expires_at: Option<u64>,
1788 #[serde(default)]
1789 pub active: bool,
1790 #[serde(default)]
1791 pub usage_count: u64,
1792}
1793
1794/// One day's usage bucket from `GET /auth/keys/{id}/usage`.
1795#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1796pub struct ApiKeyUsageBucket {
1797 /// ISO-8601 date (UTC), e.g. `"2026-05-03"`.
1798 pub date: String,
1799 /// Successful validations recorded for that day.
1800 pub count: u64,
1801}
1802
1803/// Response body for `GET /auth/keys/{id}/usage`.
1804#[derive(Debug, Clone, Serialize, Deserialize)]
1805pub struct ApiKeyUsageReport {
1806 /// Live key view with up-to-date `usage_count`.
1807 pub key: ApiKeyView,
1808 /// Daily counter buckets, oldest first. Days with zero validations
1809 /// are still present so the caller can render a continuous
1810 /// sparkline without gap-fill logic.
1811 pub buckets: Vec<ApiKeyUsageBucket>,
1812 /// Sum of `buckets[*].count`.
1813 pub window_total: u64,
1814}
1815
1816/// Request body for `create_user` (`POST /auth/users`).
1817#[derive(Debug, Clone, Serialize, Deserialize)]
1818pub struct CreateUserRequest {
1819 /// Username.
1820 pub username: String,
1821 /// Initial password.
1822 pub password: String,
1823 /// Roles to assign (optional; defaults to `["User"]`).
1824 #[serde(default)]
1825 pub roles: Vec<String>,
1826}
1827
1828// ===== REPLICATION SDK TYPES (phase12) =====
1829
1830/// Replication status returned by `GET /replication/status`.
1831/// Server: `{role, enabled, stats?, replicas?}`.
1832#[derive(Debug, Clone, Serialize, Deserialize)]
1833pub struct ReplicationStatus {
1834 /// Node role (`"Master"`, `"Replica"`, or `"Standalone"`).
1835 pub role: String,
1836 /// Whether replication is enabled on this node.
1837 pub enabled: bool,
1838 /// Replication stats (master or replica depending on role).
1839 #[serde(default, skip_serializing_if = "Option::is_none")]
1840 pub stats: Option<ReplicationStats>,
1841 /// Connected replicas (master only).
1842 #[serde(default, skip_serializing_if = "Option::is_none")]
1843 pub replicas: Option<Vec<ReplicaInfo>>,
1844}
1845
1846/// Request body for `configure_replication` (`POST /replication/configure`).
1847#[derive(Debug, Clone, Serialize, Deserialize)]
1848pub struct ReplicationConfig {
1849 /// Target role: `"master"`, `"replica"`, or `"standalone"`.
1850 pub role: String,
1851 /// Bind address for master nodes.
1852 #[serde(default, skip_serializing_if = "Option::is_none")]
1853 pub bind_address: Option<String>,
1854 /// Master address for replica nodes.
1855 #[serde(default, skip_serializing_if = "Option::is_none")]
1856 pub master_address: Option<String>,
1857 /// Heartbeat interval in milliseconds.
1858 #[serde(default, skip_serializing_if = "Option::is_none")]
1859 pub heartbeat_interval: Option<u64>,
1860 /// Replication log size.
1861 #[serde(default, skip_serializing_if = "Option::is_none")]
1862 pub log_size: Option<usize>,
1863}
1864
1865// ===== HUB TYPES (phase12) =====
1866
1867/// A user-scoped backup entry returned by `GET /hub/backups`.
1868/// Mirrors `vectorizer::hub::backup::UserBackupInfo`.
1869#[derive(Debug, Clone, Serialize, Deserialize)]
1870pub struct UserBackup {
1871 /// Backup UUID.
1872 pub id: String,
1873 /// User UUID (owner).
1874 pub user_id: String,
1875 /// Human-readable backup name.
1876 pub name: String,
1877 /// Optional description.
1878 #[serde(default, skip_serializing_if = "Option::is_none")]
1879 pub description: Option<String>,
1880 /// Collections included.
1881 #[serde(default)]
1882 pub collections: Vec<String>,
1883 /// Creation timestamp (RFC3339).
1884 pub created_at: String,
1885 /// Backup size in bytes.
1886 #[serde(default)]
1887 pub size: u64,
1888 /// Status string (`"active"`, `"creating"`, etc.).
1889 #[serde(default)]
1890 pub status: String,
1891}
1892
1893/// Request for `create_user_backup` (`POST /hub/backups`).
1894#[derive(Debug, Clone, Serialize, Deserialize)]
1895pub struct CreateUserBackupRequest {
1896 /// User UUID who owns the backup.
1897 pub user_id: String,
1898 /// Backup name.
1899 pub name: String,
1900 /// Optional description.
1901 #[serde(default, skip_serializing_if = "Option::is_none")]
1902 pub description: Option<String>,
1903 /// Collections to include (None = all user collections).
1904 #[serde(default, skip_serializing_if = "Option::is_none")]
1905 pub collections: Option<Vec<String>>,
1906}
1907
1908/// Request for `restore_user_backup` (`POST /hub/backups/restore`).
1909#[derive(Debug, Clone, Serialize, Deserialize)]
1910pub struct RestoreUserBackupRequest {
1911 /// User UUID.
1912 pub user_id: String,
1913 /// Backup UUID to restore.
1914 pub backup_id: String,
1915 /// Whether to overwrite existing collections.
1916 #[serde(default)]
1917 pub overwrite: bool,
1918}
1919
1920/// Request for `upload_user_backup` (`POST /hub/backups/upload`).
1921///
1922/// The server actually accepts raw bytes via query params; the SDK
1923/// wraps the upload parameters here. The actual byte payload is passed
1924/// separately by the method.
1925#[derive(Debug, Clone, Serialize, Deserialize)]
1926pub struct UploadUserBackupRequest {
1927 /// User UUID.
1928 pub user_id: String,
1929 /// Optional backup name override.
1930 #[serde(default, skip_serializing_if = "Option::is_none")]
1931 pub name: Option<String>,
1932 /// Binary backup data.
1933 #[serde(skip)]
1934 pub data: Vec<u8>,
1935}
1936
1937/// Usage statistics returned by `GET /hub/usage/statistics`.
1938#[derive(Debug, Clone, Serialize, Deserialize)]
1939pub struct UsageStatistics {
1940 /// Whether the call succeeded.
1941 #[serde(default)]
1942 pub success: bool,
1943 /// Human-readable message.
1944 #[serde(default)]
1945 pub message: String,
1946 /// The statistics payload (free-form per server).
1947 #[serde(default)]
1948 pub stats: Option<serde_json::Value>,
1949}
1950
1951/// Quota information returned by `GET /hub/usage/quota`.
1952#[derive(Debug, Clone, Serialize, Deserialize)]
1953pub struct QuotaInfo {
1954 /// Whether the call succeeded.
1955 #[serde(default)]
1956 pub success: bool,
1957 /// Human-readable message.
1958 #[serde(default)]
1959 pub message: String,
1960 /// The quota payload (free-form per server).
1961 #[serde(default)]
1962 pub quota: Option<serde_json::Value>,
1963}
1964
1965// ===== SCHEMA-EVOLUTION + OBSERVABILITY TYPES (phase14) =====
1966
1967/// Parameters for `reindex_collection` (`POST /collections/{name}/reindex`).
1968///
1969/// All fields carry defaults matching the server handler: `m=16`,
1970/// `ef_construction=200`, `ef_search=100`.
1971#[derive(Debug, Clone, Serialize, Deserialize)]
1972pub struct ReindexParams {
1973 /// HNSW `M` parameter (number of bi-directional links).
1974 pub m: u32,
1975 /// HNSW `ef_construction` (size of the dynamic candidate list during build).
1976 pub ef_construction: u32,
1977 /// HNSW `ef_search` (size of the dynamic candidate list at query time).
1978 pub ef_search: u32,
1979}
1980
1981/// Job descriptor returned by `reindex_collection`
1982/// (`POST /collections/{name}/reindex`).
1983///
1984/// Server contract: `{job_id, collection, state, params, progress}`.
1985#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1986pub struct ReindexJob {
1987 /// Opaque job id generated by the server.
1988 pub job_id: String,
1989 /// Collection that was re-indexed.
1990 pub collection: String,
1991 /// Job state string (`"completed"` on success).
1992 pub state: String,
1993 /// HNSW parameters that were applied.
1994 pub params: serde_json::Value,
1995 /// Progress fraction in `[0.0, 1.0]`.
1996 pub progress: f64,
1997}
1998
1999/// Native snapshot metadata returned by `create_native_snapshot` and
2000/// each entry in `list_native_snapshots`.
2001///
2002/// Server contract: `{id, collection, created_at, size_bytes}`.
2003#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2004pub struct NativeSnapshotInfo {
2005 /// Opaque snapshot id (UUID string).
2006 pub id: String,
2007 /// Collection the snapshot was taken from.
2008 pub collection: String,
2009 /// Creation timestamp in RFC-3339 format.
2010 pub created_at: String,
2011 /// Compressed snapshot size in bytes.
2012 pub size_bytes: u64,
2013}
2014
2015/// Request body for `explain_search`
2016/// (`POST /collections/{name}/explain`).
2017#[derive(Debug, Clone, Serialize, Deserialize)]
2018pub struct ExplainRequest {
2019 /// Query vector (must match the collection's dimension).
2020 pub vector: Vec<f32>,
2021 /// Number of nearest neighbours to retrieve (default 10).
2022 #[serde(default, skip_serializing_if = "Option::is_none")]
2023 pub k: Option<u64>,
2024}
2025
2026/// Execution trace attached to an `ExplainResponse`.
2027///
2028/// Server contract: `{visited_nodes, ef_search, hnsw_search_ms,
2029/// payload_filter_evals, quantization_score_ms, total_ms}`.
2030#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2031pub struct ExplainTrace {
2032 /// Number of HNSW graph nodes visited during the search.
2033 pub visited_nodes: usize,
2034 /// Effective `ef_search` value used.
2035 pub ef_search: usize,
2036 /// Wall-clock time spent inside HNSW traversal (milliseconds).
2037 pub hnsw_search_ms: f64,
2038 /// Number of payload-filter predicate evaluations.
2039 pub payload_filter_evals: usize,
2040 /// Wall-clock time spent on quantized distance scoring (milliseconds).
2041 pub quantization_score_ms: f64,
2042 /// Total wall-clock time for the explain call (milliseconds).
2043 pub total_ms: f64,
2044}
2045
2046/// Response from `explain_search`
2047/// (`POST /collections/{name}/explain`).
2048///
2049/// Server contract: `{collection, k, results, trace}`.
2050#[derive(Debug, Clone, Serialize, Deserialize)]
2051pub struct ExplainResponse {
2052 /// Collection the search ran against.
2053 pub collection: String,
2054 /// Number of neighbours requested.
2055 pub k: usize,
2056 /// Ranked search hits (same shape as regular search results).
2057 #[serde(default)]
2058 pub results: Vec<serde_json::Value>,
2059 /// Full execution trace.
2060 pub trace: ExplainTrace,
2061}
2062
2063/// One entry in the slow-query ring buffer returned by `GET /slow_queries`.
2064///
2065/// Server contract: `{timestamp, collection, k, duration_ms}`.
2066#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2067pub struct SlowQueryEntry {
2068 /// ISO-8601 / RFC-3339 timestamp when the slow query was recorded.
2069 pub timestamp: String,
2070 /// Collection the query ran against.
2071 pub collection: String,
2072 /// Number of neighbours requested (`k`).
2073 pub k: usize,
2074 /// Observed query duration in milliseconds.
2075 pub duration_ms: f64,
2076}
2077
2078/// Slow-query ring-buffer configuration returned by
2079/// `POST /slow_queries/config`.
2080///
2081/// Server contract: `{threshold_ms, capacity, status}`.
2082#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2083pub struct SlowQueryConfig {
2084 /// Minimum duration (ms) for a query to be recorded.
2085 pub threshold_ms: u64,
2086 /// Maximum number of entries retained in the ring buffer.
2087 pub capacity: usize,
2088}
2089
2090/// Validation result returned by `POST /hub/validate-key`.
2091#[derive(Debug, Clone, Serialize, Deserialize)]
2092pub struct HubApiKeyValidation {
2093 /// Whether the key is valid.
2094 pub valid: bool,
2095 /// Tenant id the key belongs to.
2096 #[serde(default)]
2097 pub tenant_id: String,
2098 /// Tenant name.
2099 #[serde(default)]
2100 pub tenant_name: String,
2101 /// Permissions granted by the key.
2102 #[serde(default)]
2103 pub permissions: Vec<String>,
2104 /// Validation timestamp (RFC3339).
2105 #[serde(default)]
2106 pub validated_at: String,
2107}
2108
2109// ===== CLUSTER + AUTH ADMIN TYPES (phase15) =====
2110
2111/// Report returned by `POST /cluster/failover`.
2112///
2113/// Server contract: `{promoted_replica_id, master_offset_at_promotion,
2114/// replica_offset_at_promotion, residual_lag_operations}`.
2115#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2116pub struct FailoverReport {
2117 /// ID of the replica that was promoted to primary.
2118 pub promoted_replica_id: String,
2119 /// Master WAL offset at the time of promotion.
2120 pub master_offset_at_promotion: u64,
2121 /// Replica's confirmed offset at the time of promotion.
2122 pub replica_offset_at_promotion: u64,
2123 /// Remaining lag in WAL operations.
2124 pub residual_lag_operations: u64,
2125}
2126
2127/// Report returned by `POST /cluster/replicas/{id}/resync`.
2128///
2129/// Server contract: `{replica_id, snapshot_offset, full_snapshot}`.
2130#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2131pub struct ResyncJob {
2132 /// ID of the replica being resynced.
2133 pub replica_id: String,
2134 /// Master WAL offset used as the snapshot baseline.
2135 pub snapshot_offset: u64,
2136 /// Whether a full snapshot transfer was initiated.
2137 pub full_snapshot: bool,
2138}
2139
2140/// Information about a newly added cluster peer.
2141///
2142/// Returned by `POST /cluster/peers`.
2143/// Server contract: `{node_id, address, role}`.
2144#[derive(Debug, Clone, Serialize, Deserialize)]
2145pub struct PeerInfo {
2146 /// Opaque node id assigned by the server.
2147 pub node_id: String,
2148 /// Address of the peer (host:port).
2149 pub address: String,
2150 /// Role string: `"member"` or `"observer"`.
2151 pub role: String,
2152}
2153
2154/// Request body for `POST /cluster/peers`.
2155#[derive(Debug, Clone, Serialize, Deserialize)]
2156pub struct AddPeerRequest {
2157 /// Address of the new peer (host:port).
2158 pub address: String,
2159 /// Role: `"member"` (default) or `"observer"`.
2160 #[serde(default)]
2161 pub role: String,
2162}
2163
2164/// Job descriptor returned by `POST /cluster/rebalance`.
2165///
2166/// Server contract: `{job_id, status, shards_to_move, shards_moved,
2167/// last_checkpoint_node, message}`.
2168#[derive(Debug, Clone, Serialize, Deserialize)]
2169pub struct RebalanceJob {
2170 /// Opaque job id.
2171 pub job_id: String,
2172 /// Lifecycle state: `"running"`, `"paused"`, `"completed"`, `"failed"`.
2173 pub status: String,
2174 /// Total shards that need to move.
2175 pub shards_to_move: usize,
2176 /// Shards moved so far.
2177 pub shards_moved: usize,
2178 /// Node-id of the last checkpoint.
2179 #[serde(default, skip_serializing_if = "Option::is_none")]
2180 pub last_checkpoint_node: Option<String>,
2181 /// Human-readable status message.
2182 pub message: String,
2183}
2184
2185/// Response from `POST /auth/keys/{id}/rotate`.
2186///
2187/// Server contract: `{old_key_id, new_key_id, new_token, grace_until}`.
2188#[derive(Debug, Clone, Serialize, Deserialize)]
2189pub struct RotatedKey {
2190 /// The old key id (still valid until `grace_until`).
2191 pub old_key_id: String,
2192 /// The new key id.
2193 pub new_key_id: String,
2194 /// The new key token value — store it securely.
2195 pub new_token: String,
2196 /// Unix timestamp until which the OLD key is still accepted.
2197 pub grace_until: u64,
2198}
2199
2200/// Request body for `POST /auth/keys` — extended with optional per-collection scopes.
2201#[derive(Debug, Clone, Serialize, Deserialize)]
2202pub struct CreateScopedApiKeyRequest {
2203 /// Key name / description.
2204 pub name: String,
2205 /// Global permissions (optional; defaults to `["Read"]`).
2206 #[serde(default)]
2207 pub permissions: Vec<String>,
2208 /// TTL in seconds from now (`None` = never expires).
2209 #[serde(default, skip_serializing_if = "Option::is_none")]
2210 pub expires_in: Option<u64>,
2211 /// Per-collection scopes. Empty = default-deny on scope-enforced routes.
2212 #[serde(default)]
2213 pub scopes: Vec<TokenScope>,
2214}
2215
2216/// Per-collection permission scope sent in [`CreateScopedApiKeyRequest`].
2217#[derive(Debug, Clone, Serialize, Deserialize)]
2218pub struct TokenScope {
2219 /// Collection name this scope applies to.
2220 pub collection: String,
2221 /// Permissions granted on that collection (e.g. `["read", "write"]`).
2222 #[serde(default)]
2223 pub permissions: Vec<String>,
2224}
2225
2226/// RFC 7662 token introspection response from `POST /auth/introspect`.
2227///
2228/// Server contract: `{active, scope?, sub?, exp?, username?}`.
2229#[derive(Debug, Clone, Serialize, Deserialize)]
2230pub struct TokenIntrospection {
2231 /// Whether the token is currently active.
2232 pub active: bool,
2233 /// Space-separated scope string.
2234 #[serde(default, skip_serializing_if = "Option::is_none")]
2235 pub scope: Option<String>,
2236 /// Subject (user_id or key_id).
2237 #[serde(default, skip_serializing_if = "Option::is_none")]
2238 pub sub: Option<String>,
2239 /// Expiry (Unix timestamp).
2240 #[serde(default, skip_serializing_if = "Option::is_none")]
2241 pub exp: Option<u64>,
2242 /// Username (non-standard extension; omitted for inactive tokens).
2243 #[serde(default, skip_serializing_if = "Option::is_none")]
2244 pub username: Option<String>,
2245}
2246
2247/// One entry in the admin audit log returned by `GET /auth/audit`.
2248///
2249/// Server contract: `{actor, action, target, at, correlation_id}`.
2250#[derive(Debug, Clone, Serialize, Deserialize)]
2251pub struct AuditEntry {
2252 /// Username or key-id of the actor.
2253 pub actor: String,
2254 /// Canonical action name, e.g. `"create_api_key"`.
2255 pub action: String,
2256 /// Target resource.
2257 pub target: String,
2258 /// UTC timestamp (RFC-3339).
2259 pub at: String,
2260 /// Correlation-ID propagated from the request middleware.
2261 #[serde(default, skip_serializing_if = "Option::is_none")]
2262 pub correlation_id: Option<String>,
2263}
2264
2265/// Query parameters for `GET /auth/audit`.
2266#[derive(Debug, Clone, Default, Serialize)]
2267pub struct AuditQuery {
2268 /// Filter by actor.
2269 #[serde(skip_serializing_if = "Option::is_none")]
2270 pub actor: Option<String>,
2271 /// Filter by action name.
2272 #[serde(skip_serializing_if = "Option::is_none")]
2273 pub action: Option<String>,
2274 /// Entries at or after this RFC-3339 timestamp.
2275 #[serde(skip_serializing_if = "Option::is_none")]
2276 pub since: Option<String>,
2277 /// Entries at or before this RFC-3339 timestamp.
2278 #[serde(skip_serializing_if = "Option::is_none")]
2279 pub until: Option<String>,
2280 /// Maximum entries to return (server default 200).
2281 #[serde(skip_serializing_if = "Option::is_none")]
2282 pub limit: Option<usize>,
2283}