Skip to main content

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// ===== CLIENT-SIDE REPLICATION CONFIGURATION =====
21
22/// Read preference for routing read operations.
23/// Similar to MongoDB's read preferences.
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
25#[serde(rename_all = "snake_case")]
26pub enum ReadPreference {
27    /// Route all reads to master
28    Master,
29    /// Route reads to replicas (round-robin)
30    #[default]
31    Replica,
32    /// Route to the node with lowest latency
33    Nearest,
34}
35
36/// Host configuration for master/replica topology.
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct HostConfig {
39    /// Master node URL (receives all write operations)
40    pub master: String,
41    /// Replica node URLs (receive read operations based on read_preference)
42    pub replicas: Vec<String>,
43}
44
45/// Options that can be passed to read operations for per-operation override.
46#[derive(Debug, Clone, Default)]
47pub struct ReadOptions {
48    /// Override the default read preference for this operation
49    pub read_preference: Option<ReadPreference>,
50}
51
52/// Vector similarity metrics
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
54#[serde(rename_all = "snake_case")]
55pub enum SimilarityMetric {
56    /// Cosine similarity
57    #[default]
58    Cosine,
59    /// Euclidean distance
60    Euclidean,
61    /// Dot product
62    DotProduct,
63}
64
65/// Vector representation
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct Vector {
68    /// Unique identifier for the vector
69    pub id: String,
70    /// Vector data as an array of numbers
71    pub data: Vec<f32>,
72    /// Optional metadata associated with the vector
73    pub metadata: Option<HashMap<String, serde_json::Value>>,
74    /// Optional ECC public key for payload encryption (PEM, base64, or hex format)
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub public_key: Option<String>,
77}
78
79/// Collection representation
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct Collection {
82    /// Collection name
83    pub name: String,
84    /// Vector dimension
85    pub dimension: usize,
86    /// Similarity metric used for search (API may return as 'metric')
87    #[serde(alias = "similarity_metric")]
88    pub metric: Option<String>,
89    /// Optional description
90    #[serde(default)]
91    pub description: Option<String>,
92    /// Creation timestamp
93    #[serde(default)]
94    pub created_at: Option<String>,
95    /// Last update timestamp
96    #[serde(default)]
97    pub updated_at: Option<String>,
98    /// Vector count
99    #[serde(default)]
100    pub vector_count: usize,
101    /// Document count
102    #[serde(default)]
103    pub document_count: usize,
104    /// Embedding provider
105    #[serde(default)]
106    pub embedding_provider: Option<String>,
107    /// Indexing status
108    #[serde(default)]
109    pub indexing_status: Option<serde_json::Value>,
110    /// Normalization config
111    #[serde(default)]
112    pub normalization: Option<serde_json::Value>,
113    /// Quantization config
114    #[serde(default)]
115    pub quantization: Option<serde_json::Value>,
116    /// Size info
117    #[serde(default)]
118    pub size: Option<serde_json::Value>,
119}
120
121/// Collection information.
122///
123/// The v3.0.0 REST surface returns `metric` in Rust-Debug form
124/// (e.g. `"Cosine"`), plus new top-level blocks (`size`, `quantization`,
125/// `normalization`, `status`). Every field beyond `name` + `dimension`
126/// carries `#[serde(default)]` so the model tolerates pre-v3 servers
127/// and future additions (request models keep the strict posture; this
128/// is a response-only struct).
129#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct CollectionInfo {
131    /// Collection name
132    pub name: String,
133    /// Vector dimension
134    pub dimension: usize,
135    /// Similarity metric used for search. The v3 server emits this in
136    /// Rust-Debug form (`"Cosine"` / `"Euclidean"` / `"DotProduct"`);
137    /// callers that compare against `"cosine"` etc. should go through
138    /// `.to_lowercase()`.
139    #[serde(default, alias = "similarity_metric")]
140    pub metric: String,
141    /// Number of vectors in the collection
142    #[serde(default)]
143    pub vector_count: usize,
144    /// Number of documents in the collection
145    #[serde(default)]
146    pub document_count: usize,
147    /// Creation timestamp (RFC3339). Optional — pre-v3 servers may omit.
148    #[serde(default)]
149    pub created_at: String,
150    /// Last update timestamp (RFC3339). Optional — pre-v3 servers may omit.
151    #[serde(default)]
152    pub updated_at: String,
153    /// Indexing status. Absent on the v3 server; some legacy servers send it.
154    #[serde(default)]
155    pub indexing_status: Option<IndexingStatus>,
156    /// Size block emitted by v3 (`{total, total_bytes, index, index_bytes,
157    /// payload, payload_bytes}`).
158    #[serde(default)]
159    pub size: Option<serde_json::Value>,
160    /// Quantization block emitted by v3 (`{enabled, type, bits}`).
161    #[serde(default)]
162    pub quantization: Option<serde_json::Value>,
163    /// Normalization block emitted by v3.
164    #[serde(default)]
165    pub normalization: Option<serde_json::Value>,
166    /// Ready/indexing/error state emitted by v3.
167    #[serde(default)]
168    pub status: Option<String>,
169}
170
171/// Indexing status
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct IndexingStatus {
174    /// Status
175    pub status: String,
176    /// Progress percentage
177    pub progress: f32,
178    /// Total documents
179    pub total_documents: usize,
180    /// Processed documents
181    pub processed_documents: usize,
182    /// Vector count
183    pub vector_count: usize,
184    /// Estimated time remaining
185    pub estimated_time_remaining: Option<String>,
186    /// Last updated timestamp
187    pub last_updated: String,
188}
189
190/// Search result
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct SearchResult {
193    /// Vector ID
194    pub id: String,
195    /// Similarity score
196    pub score: f32,
197    /// Vector content (if available)
198    pub content: Option<String>,
199    /// Optional metadata
200    pub metadata: Option<HashMap<String, serde_json::Value>>,
201}
202
203/// Search response
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct SearchResponse {
206    /// Search results
207    pub results: Vec<SearchResult>,
208    /// Query time in milliseconds
209    pub query_time_ms: f64,
210}
211
212/// Embedding request
213#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct EmbeddingRequest {
215    /// Text to embed
216    pub text: String,
217    /// Optional model to use for embedding
218    pub model: Option<String>,
219    /// Optional parameters for embedding generation
220    pub parameters: Option<EmbeddingParameters>,
221}
222
223/// Embedding parameters
224#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct EmbeddingParameters {
226    /// Maximum sequence length
227    pub max_length: Option<usize>,
228    /// Whether to normalize the embedding
229    pub normalize: Option<bool>,
230    /// Optional prefix for the text
231    pub prefix: Option<String>,
232}
233
234/// Embedding response
235#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct EmbeddingResponse {
237    /// Generated embedding vector
238    pub embedding: Vec<f32>,
239    /// Model used for embedding
240    pub model: String,
241    /// Text that was embedded
242    pub text: String,
243    /// Embedding dimension
244    pub dimension: usize,
245    /// Provider used
246    pub provider: String,
247}
248
249/// Health status
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct HealthStatus {
252    /// Service status
253    pub status: String,
254    /// Service version
255    pub version: String,
256    /// Timestamp
257    pub timestamp: String,
258    /// Uptime in seconds
259    pub uptime: Option<u64>,
260    /// Number of collections
261    pub collections: Option<usize>,
262    /// Total number of vectors
263    pub total_vectors: Option<usize>,
264}
265
266/// Collections list response
267#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct CollectionsResponse {
269    /// List of collections
270    pub collections: Vec<Collection>,
271}
272
273/// Create collection response
274#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct CreateCollectionResponse {
276    /// Success message
277    pub message: String,
278    /// Collection name
279    pub collection: String,
280}
281
282/// Database statistics
283#[derive(Debug, Clone, Serialize, Deserialize)]
284pub struct DatabaseStats {
285    /// Total number of collections
286    pub total_collections: usize,
287    /// Total number of vectors
288    pub total_vectors: usize,
289    /// Total memory estimate in bytes
290    pub total_memory_estimate_bytes: usize,
291    /// Collections information
292    pub collections: Vec<CollectionStats>,
293}
294
295/// Collection statistics
296#[derive(Debug, Clone, Serialize, Deserialize)]
297pub struct CollectionStats {
298    /// Collection name
299    pub name: String,
300    /// Number of vectors
301    pub vector_count: usize,
302    /// Vector dimension
303    pub dimension: usize,
304    /// Memory estimate in bytes
305    pub memory_estimate_bytes: usize,
306}
307
308/// Batch text request
309#[derive(Debug, Clone, Serialize, Deserialize)]
310pub struct BatchTextRequest {
311    /// Text ID
312    pub id: String,
313    /// Text content
314    pub text: String,
315    /// Optional metadata
316    pub metadata: Option<HashMap<String, String>>,
317}
318
319/// Batch configuration
320#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct BatchConfig {
322    /// Maximum batch size
323    pub max_batch_size: Option<usize>,
324    /// Number of parallel workers
325    pub parallel_workers: Option<usize>,
326    /// Whether operations should be atomic
327    pub atomic: Option<bool>,
328}
329
330/// Batch insert request
331#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct BatchInsertRequest {
333    /// Texts to insert
334    pub texts: Vec<BatchTextRequest>,
335    /// Batch configuration
336    pub config: Option<BatchConfig>,
337}
338
339/// Batch response
340#[derive(Debug, Clone, Serialize, Deserialize)]
341pub struct BatchResponse {
342    /// Whether the operation was successful
343    pub success: bool,
344    /// Collection name
345    pub collection: String,
346    /// Operation type
347    pub operation: String,
348    /// Total number of operations
349    pub total_operations: usize,
350    /// Number of successful operations
351    pub successful_operations: usize,
352    /// Number of failed operations
353    pub failed_operations: usize,
354    /// Duration in milliseconds
355    pub duration_ms: u64,
356    /// Error messages
357    pub errors: Vec<String>,
358}
359
360/// Batch search query
361#[derive(Debug, Clone, Serialize, Deserialize)]
362pub struct BatchSearchQuery {
363    /// Query text
364    pub query: String,
365    /// Maximum number of results
366    pub limit: Option<usize>,
367    /// Minimum score threshold
368    pub score_threshold: Option<f32>,
369}
370
371/// Batch search request
372#[derive(Debug, Clone, Serialize, Deserialize)]
373pub struct BatchSearchRequest {
374    /// Search queries
375    pub queries: Vec<BatchSearchQuery>,
376    /// Batch configuration
377    pub config: Option<BatchConfig>,
378}
379
380/// Batch search response
381#[derive(Debug, Clone, Serialize, Deserialize)]
382pub struct BatchSearchResponse {
383    /// Whether the operation was successful
384    pub success: bool,
385    /// Collection name
386    pub collection: String,
387    /// Total number of queries
388    pub total_queries: usize,
389    /// Number of successful queries
390    pub successful_queries: usize,
391    /// Number of failed queries
392    pub failed_queries: usize,
393    /// Duration in milliseconds
394    pub duration_ms: u64,
395    /// Search results
396    pub results: Vec<Vec<SearchResult>>,
397    /// Error messages
398    pub errors: Vec<String>,
399}
400
401/// Batch vector update
402#[derive(Debug, Clone, Serialize, Deserialize)]
403pub struct BatchVectorUpdate {
404    /// Vector ID
405    pub id: String,
406    /// New vector data (optional)
407    pub data: Option<Vec<f32>>,
408    /// New metadata (optional)
409    pub metadata: Option<HashMap<String, serde_json::Value>>,
410}
411
412/// Batch update request
413#[derive(Debug, Clone, Serialize, Deserialize)]
414pub struct BatchUpdateRequest {
415    /// Vector updates
416    pub updates: Vec<BatchVectorUpdate>,
417    /// Batch configuration
418    pub config: Option<BatchConfig>,
419}
420
421/// Batch delete request
422#[derive(Debug, Clone, Serialize, Deserialize)]
423pub struct BatchDeleteRequest {
424    /// Vector IDs to delete
425    pub vector_ids: Vec<String>,
426    /// Batch configuration
427    pub config: Option<BatchConfig>,
428}
429
430/// Summarization methods
431#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
432#[serde(rename_all = "snake_case")]
433pub enum SummarizationMethod {
434    /// Extractive summarization
435    #[default]
436    Extractive,
437    /// Keyword summarization
438    Keyword,
439    /// Sentence summarization
440    Sentence,
441    /// Abstractive summarization
442    Abstractive,
443}
444
445/// Summarize text request
446#[derive(Debug, Clone, Serialize, Deserialize)]
447pub struct SummarizeTextRequest {
448    /// Text to summarize
449    pub text: String,
450    /// Summarization method
451    pub method: Option<SummarizationMethod>,
452    /// Maximum summary length
453    pub max_length: Option<usize>,
454    /// Compression ratio
455    pub compression_ratio: Option<f32>,
456    /// Language code
457    pub language: Option<String>,
458}
459
460/// Summarize text response
461#[derive(Debug, Clone, Serialize, Deserialize)]
462pub struct SummarizeTextResponse {
463    /// Summary ID
464    pub summary_id: String,
465    /// Original text
466    pub original_text: String,
467    /// Generated summary
468    pub summary: String,
469    /// Method used
470    pub method: String,
471    /// Original text length
472    pub original_length: usize,
473    /// Summary length
474    pub summary_length: usize,
475    /// Compression ratio
476    pub compression_ratio: f32,
477    /// Language
478    pub language: String,
479    /// Status
480    pub status: String,
481    /// Message
482    pub message: String,
483    /// Metadata
484    pub metadata: HashMap<String, String>,
485}
486
487/// Summarize context request
488#[derive(Debug, Clone, Serialize, Deserialize)]
489pub struct SummarizeContextRequest {
490    /// Context to summarize
491    pub context: String,
492    /// Summarization method
493    pub method: Option<SummarizationMethod>,
494    /// Maximum summary length
495    pub max_length: Option<usize>,
496    /// Compression ratio
497    pub compression_ratio: Option<f32>,
498    /// Language code
499    pub language: Option<String>,
500}
501
502/// Summarize context response
503#[derive(Debug, Clone, Serialize, Deserialize)]
504pub struct SummarizeContextResponse {
505    /// Summary ID
506    pub summary_id: String,
507    /// Original context
508    pub original_context: String,
509    /// Generated summary
510    pub summary: String,
511    /// Method used
512    pub method: String,
513    /// Original context length
514    pub original_length: usize,
515    /// Summary length
516    pub summary_length: usize,
517    /// Compression ratio
518    pub compression_ratio: f32,
519    /// Language
520    pub language: String,
521    /// Status
522    pub status: String,
523    /// Message
524    pub message: String,
525    /// Metadata
526    pub metadata: HashMap<String, String>,
527}
528
529/// Get summary response
530#[derive(Debug, Clone, Serialize, Deserialize)]
531pub struct GetSummaryResponse {
532    /// Summary ID
533    pub summary_id: String,
534    /// Original text
535    pub original_text: String,
536    /// Generated summary
537    pub summary: String,
538    /// Method used
539    pub method: String,
540    /// Original text length
541    pub original_length: usize,
542    /// Summary length
543    pub summary_length: usize,
544    /// Compression ratio
545    pub compression_ratio: f32,
546    /// Language
547    pub language: String,
548    /// Creation timestamp
549    pub created_at: String,
550    /// Metadata
551    pub metadata: HashMap<String, String>,
552    /// Status
553    pub status: String,
554}
555
556/// Summary info
557#[derive(Debug, Clone, Serialize, Deserialize)]
558pub struct SummaryInfo {
559    /// Summary ID
560    pub summary_id: String,
561    /// Method used
562    pub method: String,
563    /// Language
564    pub language: String,
565    /// Original text length
566    pub original_length: usize,
567    /// Summary length
568    pub summary_length: usize,
569    /// Compression ratio
570    pub compression_ratio: f32,
571    /// Creation timestamp
572    pub created_at: String,
573    /// Metadata
574    pub metadata: HashMap<String, String>,
575}
576
577/// List summaries response
578#[derive(Debug, Clone, Serialize, Deserialize)]
579pub struct ListSummariesResponse {
580    /// List of summaries
581    pub summaries: Vec<SummaryInfo>,
582    /// Total count
583    pub total_count: usize,
584    /// Status
585    pub status: String,
586}
587
588/// Indexing progress
589#[derive(Debug, Clone, Serialize, Deserialize)]
590pub struct IndexingProgress {
591    /// Whether indexing is in progress
592    pub is_indexing: bool,
593    /// Overall status
594    pub overall_status: String,
595    /// Collections being indexed
596    pub collections: Vec<CollectionProgress>,
597}
598
599/// Collection progress
600#[derive(Debug, Clone, Serialize, Deserialize)]
601pub struct CollectionProgress {
602    /// Collection name
603    pub collection_name: String,
604    /// Status
605    pub status: String,
606    /// Progress percentage
607    pub progress: f32,
608    /// Vector count
609    pub vector_count: usize,
610    /// Error message if any
611    pub error_message: Option<String>,
612    /// Last updated timestamp
613    pub last_updated: String,
614}
615
616// ===== INTELLIGENT SEARCH MODELS =====
617
618/// Intelligent search request
619#[derive(Debug, Clone, Serialize, Deserialize)]
620pub struct IntelligentSearchRequest {
621    /// Search query
622    pub query: String,
623    /// Collections to search (optional - searches all if not specified)
624    pub collections: Option<Vec<String>>,
625    /// Maximum number of results
626    pub max_results: Option<usize>,
627    /// Enable domain expansion
628    pub domain_expansion: Option<bool>,
629    /// Enable technical focus
630    pub technical_focus: Option<bool>,
631    /// Enable MMR diversification
632    pub mmr_enabled: Option<bool>,
633    /// MMR balance parameter (0.0-1.0)
634    pub mmr_lambda: Option<f32>,
635}
636
637/// Semantic search request
638#[derive(Debug, Clone, Serialize, Deserialize)]
639pub struct SemanticSearchRequest {
640    /// Search query
641    pub query: String,
642    /// Collection to search
643    pub collection: String,
644    /// Maximum number of results
645    pub max_results: Option<usize>,
646    /// Enable semantic reranking
647    pub semantic_reranking: Option<bool>,
648    /// Enable cross-encoder reranking
649    pub cross_encoder_reranking: Option<bool>,
650    /// Minimum similarity threshold
651    pub similarity_threshold: Option<f32>,
652}
653
654/// Contextual search request
655#[derive(Debug, Clone, Serialize, Deserialize)]
656pub struct ContextualSearchRequest {
657    /// Search query
658    pub query: String,
659    /// Collection to search
660    pub collection: String,
661    /// Metadata-based context filters
662    pub context_filters: Option<HashMap<String, serde_json::Value>>,
663    /// Maximum number of results
664    pub max_results: Option<usize>,
665    /// Enable context-aware reranking
666    pub context_reranking: Option<bool>,
667    /// Weight of context factors (0.0-1.0)
668    pub context_weight: Option<f32>,
669}
670
671/// Multi-collection search request
672#[derive(Debug, Clone, Serialize, Deserialize)]
673pub struct MultiCollectionSearchRequest {
674    /// Search query
675    pub query: String,
676    /// Collections to search
677    pub collections: Vec<String>,
678    /// Maximum results per collection
679    pub max_per_collection: Option<usize>,
680    /// Maximum total results
681    pub max_total_results: Option<usize>,
682    /// Enable cross-collection reranking
683    pub cross_collection_reranking: Option<bool>,
684}
685
686/// Intelligent search result
687#[derive(Debug, Clone, Serialize, Deserialize)]
688pub struct IntelligentSearchResult {
689    /// Result ID
690    pub id: String,
691    /// Similarity score
692    pub score: f32,
693    /// Result content
694    pub content: String,
695    /// Metadata
696    pub metadata: Option<HashMap<String, serde_json::Value>>,
697    /// Collection name
698    pub collection: Option<String>,
699    /// Query used for this result
700    pub query_used: Option<String>,
701}
702
703/// Intelligent search response
704#[derive(Debug, Clone, Serialize, Deserialize)]
705pub struct IntelligentSearchResponse {
706    /// Search results
707    pub results: Vec<IntelligentSearchResult>,
708    /// Total number of results found
709    pub total_results: usize,
710    /// Search duration in milliseconds
711    pub duration_ms: u64,
712    /// Queries generated
713    pub queries_generated: Option<Vec<String>>,
714    /// Collections searched
715    pub collections_searched: Option<Vec<String>>,
716    /// Search metadata
717    pub metadata: Option<HashMap<String, serde_json::Value>>,
718}
719
720/// Semantic search response
721#[derive(Debug, Clone, Serialize, Deserialize)]
722pub struct SemanticSearchResponse {
723    /// Search results
724    pub results: Vec<IntelligentSearchResult>,
725    /// Total number of results found
726    pub total_results: usize,
727    /// Search duration in milliseconds
728    pub duration_ms: u64,
729    /// Collection searched
730    pub collection: String,
731    /// Search metadata
732    pub metadata: Option<HashMap<String, serde_json::Value>>,
733}
734
735/// Contextual search response
736#[derive(Debug, Clone, Serialize, Deserialize)]
737pub struct ContextualSearchResponse {
738    /// Search results
739    pub results: Vec<IntelligentSearchResult>,
740    /// Total number of results found
741    pub total_results: usize,
742    /// Search duration in milliseconds
743    pub duration_ms: u64,
744    /// Collection searched
745    pub collection: String,
746    /// Context filters applied
747    pub context_filters: Option<HashMap<String, serde_json::Value>>,
748    /// Search metadata
749    pub metadata: Option<HashMap<String, serde_json::Value>>,
750}
751
752/// Multi-collection search response
753#[derive(Debug, Clone, Serialize, Deserialize)]
754pub struct MultiCollectionSearchResponse {
755    /// Search results
756    pub results: Vec<IntelligentSearchResult>,
757    /// Total number of results found
758    pub total_results: usize,
759    /// Search duration in milliseconds
760    pub duration_ms: u64,
761    /// Collections searched
762    pub collections_searched: Vec<String>,
763    /// Results per collection
764    pub results_per_collection: Option<HashMap<String, usize>>,
765    /// Search metadata
766    pub metadata: Option<HashMap<String, serde_json::Value>>,
767}
768
769// ==================== REPLICATION MODELS ====================
770
771/// Status of a replica node
772#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
773#[serde(rename_all = "PascalCase")]
774pub enum ReplicaStatus {
775    /// Replica is connected and healthy
776    Connected,
777    /// Replica is syncing data
778    Syncing,
779    /// Replica is lagging behind master
780    Lagging,
781    /// Replica is disconnected
782    Disconnected,
783}
784
785/// Information about a replica node
786#[derive(Debug, Clone, Serialize, Deserialize)]
787pub struct ReplicaInfo {
788    /// Unique identifier for the replica
789    pub replica_id: String,
790    /// Hostname or IP address of the replica
791    pub host: String,
792    /// Port number of the replica
793    pub port: u16,
794    /// Current status of the replica
795    pub status: String,
796    /// Timestamp of last heartbeat
797    pub last_heartbeat: DateTime<Utc>,
798    /// Number of operations successfully synced
799    pub operations_synced: u64,
800
801    // Legacy fields (backwards compatible)
802    /// Legacy: Current offset on replica (deprecated, use operations_synced)
803    #[serde(skip_serializing_if = "Option::is_none")]
804    pub offset: Option<u64>,
805    /// Legacy: Lag in operations (deprecated, use status)
806    #[serde(skip_serializing_if = "Option::is_none")]
807    pub lag: Option<u64>,
808}
809
810/// Statistics for replication status
811#[derive(Debug, Clone, Serialize, Deserialize)]
812pub struct ReplicationStats {
813    // New fields (v1.2.0+)
814    /// Role of the node: Master or Replica
815    #[serde(skip_serializing_if = "Option::is_none")]
816    pub role: Option<String>,
817    /// Total bytes sent to replicas (Master only)
818    #[serde(skip_serializing_if = "Option::is_none")]
819    pub bytes_sent: Option<u64>,
820    /// Total bytes received from master (Replica only)
821    #[serde(skip_serializing_if = "Option::is_none")]
822    pub bytes_received: Option<u64>,
823    /// Timestamp of last synchronization
824    #[serde(skip_serializing_if = "Option::is_none")]
825    pub last_sync: Option<DateTime<Utc>>,
826    /// Number of operations pending replication
827    #[serde(skip_serializing_if = "Option::is_none")]
828    pub operations_pending: Option<usize>,
829    /// Size of snapshot data in bytes
830    #[serde(skip_serializing_if = "Option::is_none")]
831    pub snapshot_size: Option<usize>,
832    /// Number of connected replicas (Master only)
833    #[serde(skip_serializing_if = "Option::is_none")]
834    pub connected_replicas: Option<usize>,
835
836    // Legacy fields (backwards compatible - always present)
837    /// Current offset on master node
838    pub master_offset: u64,
839    /// Current offset on replica node
840    pub replica_offset: u64,
841    /// Number of operations behind
842    pub lag_operations: u64,
843    /// Total operations replicated
844    pub total_replicated: u64,
845}
846
847/// Response for replication status endpoint
848#[derive(Debug, Clone, Serialize, Deserialize)]
849pub struct ReplicationStatusResponse {
850    /// Overall status message
851    pub status: String,
852    /// Detailed replication statistics
853    pub stats: ReplicationStats,
854    /// Optional message with additional information
855    #[serde(skip_serializing_if = "Option::is_none")]
856    pub message: Option<String>,
857}
858
859/// Response for listing replicas
860#[derive(Debug, Clone, Serialize, Deserialize)]
861pub struct ReplicaListResponse {
862    /// List of replica nodes
863    pub replicas: Vec<ReplicaInfo>,
864    /// Total count of replicas
865    pub count: usize,
866    /// Status message
867    pub message: String,
868}