1use serde::{Deserialize, Serialize};
8
9fn default_max_processes() -> u32 {
11 64
12}
13
14fn default_health_check_interval_secs() -> u64 {
16 30
17}
18
19fn default_enabled() -> bool {
21 true
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct ClusterNetworkConfig {
31 #[serde(default = "default_replication_factor", alias = "replicationFactor")]
33 pub replication_factor: usize,
34
35 #[serde(default = "default_shard_count", alias = "shardCount")]
37 pub shard_count: u32,
38
39 #[serde(
41 default = "default_cluster_heartbeat",
42 alias = "heartbeatIntervalSecs"
43 )]
44 pub heartbeat_interval_secs: u64,
45
46 #[serde(default = "default_node_timeout", alias = "nodeTimeoutSecs")]
48 pub node_timeout_secs: u64,
49
50 #[serde(default = "default_enable_consensus", alias = "enableConsensus")]
52 pub enable_consensus: bool,
53
54 #[serde(default = "default_min_quorum", alias = "minQuorumSize")]
56 pub min_quorum_size: usize,
57
58 #[serde(default, alias = "seedNodes")]
60 pub seed_nodes: Vec<String>,
61
62 #[serde(default, alias = "nodeName")]
64 pub node_name: Option<String>,
65}
66
67fn default_replication_factor() -> usize {
68 3
69}
70fn default_shard_count() -> u32 {
71 64
72}
73fn default_cluster_heartbeat() -> u64 {
74 5
75}
76fn default_node_timeout() -> u64 {
77 30
78}
79fn default_enable_consensus() -> bool {
80 true
81}
82fn default_min_quorum() -> usize {
83 2
84}
85
86impl Default for ClusterNetworkConfig {
87 fn default() -> Self {
88 Self {
89 replication_factor: default_replication_factor(),
90 shard_count: default_shard_count(),
91 heartbeat_interval_secs: default_cluster_heartbeat(),
92 node_timeout_secs: default_node_timeout(),
93 enable_consensus: default_enable_consensus(),
94 min_quorum_size: default_min_quorum(),
95 seed_nodes: Vec::new(),
96 node_name: None,
97 }
98 }
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct KernelConfig {
120 #[serde(default = "default_enabled")]
125 pub enabled: bool,
126
127 #[serde(default = "default_max_processes", alias = "maxProcesses")]
129 pub max_processes: u32,
130
131 #[serde(
133 default = "default_health_check_interval_secs",
134 alias = "healthCheckIntervalSecs"
135 )]
136 pub health_check_interval_secs: u64,
137
138 #[serde(default, skip_serializing_if = "Option::is_none")]
140 pub cluster: Option<ClusterNetworkConfig>,
141
142 #[serde(default, skip_serializing_if = "Option::is_none")]
144 pub chain: Option<ChainConfig>,
145
146 #[serde(default, skip_serializing_if = "Option::is_none", alias = "resourceTree")]
148 pub resource_tree: Option<ResourceTreeConfig>,
149
150 #[serde(default, skip_serializing_if = "Option::is_none")]
152 pub vector: Option<VectorConfig>,
153}
154
155impl Default for KernelConfig {
156 fn default() -> Self {
157 Self {
158 enabled: true,
159 max_processes: default_max_processes(),
160 health_check_interval_secs: default_health_check_interval_secs(),
161 cluster: None,
162 chain: None,
163 resource_tree: None,
164 vector: None,
165 }
166 }
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct ChainConfig {
172 #[serde(default = "default_true")]
174 pub enabled: bool,
175
176 #[serde(default = "default_checkpoint_interval", alias = "checkpointInterval")]
178 pub checkpoint_interval: u64,
179
180 #[serde(default)]
182 pub chain_id: u32,
183
184 #[serde(
187 default,
188 skip_serializing_if = "Option::is_none",
189 alias = "checkpointPath"
190 )]
191 pub checkpoint_path: Option<String>,
192}
193
194fn default_true() -> bool {
195 true
196}
197fn default_checkpoint_interval() -> u64 {
198 1000
199}
200
201impl Default for ChainConfig {
202 fn default() -> Self {
203 Self {
204 enabled: true,
205 checkpoint_interval: default_checkpoint_interval(),
206 chain_id: 0,
207 checkpoint_path: None,
208 }
209 }
210}
211
212impl ChainConfig {
213 pub fn effective_checkpoint_path(&self) -> Option<String> {
218 if self.checkpoint_path.is_some() {
219 return self.checkpoint_path.clone();
220 }
221 #[cfg(feature = "native")]
222 {
223 dirs::home_dir().map(|h| {
224 h.join(".clawft")
225 .join("chain.json")
226 .to_string_lossy()
227 .into_owned()
228 })
229 }
230 #[cfg(not(feature = "native"))]
231 {
232 None
233 }
234 }
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct ResourceTreeConfig {
240 #[serde(default = "default_true_rt")]
242 pub enabled: bool,
243
244 #[serde(
246 default,
247 skip_serializing_if = "Option::is_none",
248 alias = "checkpointPath"
249 )]
250 pub checkpoint_path: Option<String>,
251}
252
253fn default_true_rt() -> bool {
254 true
255}
256
257impl Default for ResourceTreeConfig {
258 fn default() -> Self {
259 Self {
260 enabled: true,
261 checkpoint_path: None,
262 }
263 }
264}
265
266#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
270#[serde(rename_all = "lowercase")]
271pub enum VectorBackendKind {
272 #[default]
274 Hnsw,
275 DiskAnn,
277 Hybrid,
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct VectorHnswConfig {
284 #[serde(default = "default_ef_construction")]
286 pub ef_construction: usize,
287
288 #[serde(default = "default_m")]
290 pub m: usize,
291
292 #[serde(default = "default_max_elements")]
294 pub max_elements: usize,
295}
296
297fn default_ef_construction() -> usize {
298 200
299}
300fn default_m() -> usize {
301 16
302}
303fn default_max_elements() -> usize {
304 100_000
305}
306
307impl Default for VectorHnswConfig {
308 fn default() -> Self {
309 Self {
310 ef_construction: default_ef_construction(),
311 m: default_m(),
312 max_elements: default_max_elements(),
313 }
314 }
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize)]
319pub struct VectorDiskAnnConfig {
320 #[serde(default = "default_diskann_max_points")]
322 pub max_points: usize,
323
324 #[serde(default = "default_diskann_dimensions")]
326 pub dimensions: usize,
327
328 #[serde(default = "default_diskann_num_neighbors")]
330 pub num_neighbors: usize,
331
332 #[serde(default = "default_diskann_search_list_size")]
334 pub search_list_size: usize,
335
336 #[serde(default = "default_diskann_data_path")]
338 pub data_path: String,
339
340 #[serde(default = "default_diskann_use_pq")]
342 pub use_pq: bool,
343
344 #[serde(default = "default_diskann_pq_num_chunks")]
346 pub pq_num_chunks: usize,
347}
348
349fn default_diskann_max_points() -> usize {
350 10_000_000
351}
352fn default_diskann_dimensions() -> usize {
353 384
354}
355fn default_diskann_num_neighbors() -> usize {
356 64
357}
358fn default_diskann_search_list_size() -> usize {
359 100
360}
361fn default_diskann_data_path() -> String {
362 ".weftos/diskann".to_owned()
363}
364fn default_diskann_use_pq() -> bool {
365 true
366}
367fn default_diskann_pq_num_chunks() -> usize {
368 48
369}
370
371impl Default for VectorDiskAnnConfig {
372 fn default() -> Self {
373 Self {
374 max_points: default_diskann_max_points(),
375 dimensions: default_diskann_dimensions(),
376 num_neighbors: default_diskann_num_neighbors(),
377 search_list_size: default_diskann_search_list_size(),
378 data_path: default_diskann_data_path(),
379 use_pq: default_diskann_use_pq(),
380 pq_num_chunks: default_diskann_pq_num_chunks(),
381 }
382 }
383}
384
385#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
387#[serde(rename_all = "lowercase")]
388pub enum VectorEvictionPolicy {
389 #[default]
391 Lru,
392}
393
394#[derive(Debug, Clone, Serialize, Deserialize)]
396pub struct VectorHybridConfig {
397 #[serde(default = "default_hybrid_hot_capacity")]
399 pub hot_capacity: usize,
400
401 #[serde(default = "default_hybrid_promotion_threshold")]
403 pub promotion_threshold: u32,
404
405 #[serde(default)]
407 pub eviction_policy: VectorEvictionPolicy,
408}
409
410fn default_hybrid_hot_capacity() -> usize {
411 50_000
412}
413fn default_hybrid_promotion_threshold() -> u32 {
414 3
415}
416
417impl Default for VectorHybridConfig {
418 fn default() -> Self {
419 Self {
420 hot_capacity: default_hybrid_hot_capacity(),
421 promotion_threshold: default_hybrid_promotion_threshold(),
422 eviction_policy: VectorEvictionPolicy::default(),
423 }
424 }
425}
426
427#[derive(Debug, Clone, Serialize, Deserialize)]
451pub struct VectorConfig {
452 #[serde(default)]
454 pub backend: VectorBackendKind,
455
456 #[serde(default, skip_serializing_if = "Option::is_none")]
458 pub hnsw: Option<VectorHnswConfig>,
459
460 #[serde(default, skip_serializing_if = "Option::is_none")]
462 pub diskann: Option<VectorDiskAnnConfig>,
463
464 #[serde(default, skip_serializing_if = "Option::is_none")]
466 pub hybrid: Option<VectorHybridConfig>,
467}
468
469impl Default for VectorConfig {
470 fn default() -> Self {
471 Self {
472 backend: VectorBackendKind::default(),
473 hnsw: None,
474 diskann: None,
475 hybrid: None,
476 }
477 }
478}
479
480#[cfg(test)]
481mod tests {
482 use super::*;
483
484 #[test]
485 fn default_kernel_config() {
486 let cfg = KernelConfig::default();
487 assert!(cfg.enabled);
488 assert_eq!(cfg.max_processes, 64);
489 assert_eq!(cfg.health_check_interval_secs, 30);
490 }
491
492 #[test]
493 fn deserialize_empty() {
494 let cfg: KernelConfig = serde_json::from_str("{}").unwrap();
495 assert!(cfg.enabled);
496 assert_eq!(cfg.max_processes, 64);
497 }
498
499 #[test]
500 fn deserialize_camel_case() {
501 let json = r#"{"maxProcesses": 128, "healthCheckIntervalSecs": 15}"#;
502 let cfg: KernelConfig = serde_json::from_str(json).unwrap();
503 assert_eq!(cfg.max_processes, 128);
504 assert_eq!(cfg.health_check_interval_secs, 15);
505 }
506
507 #[test]
508 fn serde_roundtrip() {
509 let cfg = KernelConfig {
510 enabled: true,
511 max_processes: 256,
512 health_check_interval_secs: 10,
513 cluster: None,
514 chain: None,
515 resource_tree: None,
516 vector: None,
517 };
518 let json = serde_json::to_string(&cfg).unwrap();
519 let restored: KernelConfig = serde_json::from_str(&json).unwrap();
520 assert_eq!(restored.enabled, cfg.enabled);
521 assert_eq!(restored.max_processes, cfg.max_processes);
522 }
523
524 #[test]
525 fn vector_config_defaults() {
526 let cfg = VectorConfig::default();
527 assert_eq!(cfg.backend, VectorBackendKind::Hnsw);
528 assert!(cfg.hnsw.is_none());
529 assert!(cfg.diskann.is_none());
530 assert!(cfg.hybrid.is_none());
531 }
532
533 #[test]
534 fn vector_config_deserialize_hybrid() {
535 let json = r#"{"backend": "hybrid", "hybrid": {"hot_capacity": 1000, "promotion_threshold": 5}}"#;
536 let cfg: VectorConfig = serde_json::from_str(json).unwrap();
537 assert_eq!(cfg.backend, VectorBackendKind::Hybrid);
538 let h = cfg.hybrid.unwrap();
539 assert_eq!(h.hot_capacity, 1000);
540 assert_eq!(h.promotion_threshold, 5);
541 }
542
543 #[test]
544 fn vector_config_deserialize_diskann() {
545 let json = r#"{"backend": "diskann", "diskann": {"max_points": 5000000}}"#;
546 let cfg: VectorConfig = serde_json::from_str(json).unwrap();
547 assert_eq!(cfg.backend, VectorBackendKind::DiskAnn);
548 let d = cfg.diskann.unwrap();
549 assert_eq!(d.max_points, 5_000_000);
550 }
551
552 #[test]
553 fn kernel_config_with_vector() {
554 let json = r#"{"vector": {"backend": "hnsw"}}"#;
555 let cfg: KernelConfig = serde_json::from_str(json).unwrap();
556 assert!(cfg.vector.is_some());
557 assert_eq!(cfg.vector.unwrap().backend, VectorBackendKind::Hnsw);
558 }
559}