use serde::{Deserialize, Serialize};
fn default_max_processes() -> u32 {
64
}
fn default_health_check_interval_secs() -> u64 {
30
}
fn default_enabled() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterNetworkConfig {
#[serde(default = "default_replication_factor", alias = "replicationFactor")]
pub replication_factor: usize,
#[serde(default = "default_shard_count", alias = "shardCount")]
pub shard_count: u32,
#[serde(
default = "default_cluster_heartbeat",
alias = "heartbeatIntervalSecs"
)]
pub heartbeat_interval_secs: u64,
#[serde(default = "default_node_timeout", alias = "nodeTimeoutSecs")]
pub node_timeout_secs: u64,
#[serde(default = "default_enable_consensus", alias = "enableConsensus")]
pub enable_consensus: bool,
#[serde(default = "default_min_quorum", alias = "minQuorumSize")]
pub min_quorum_size: usize,
#[serde(default, alias = "seedNodes")]
pub seed_nodes: Vec<String>,
#[serde(default, alias = "nodeName")]
pub node_name: Option<String>,
}
fn default_replication_factor() -> usize {
3
}
fn default_shard_count() -> u32 {
64
}
fn default_cluster_heartbeat() -> u64 {
5
}
fn default_node_timeout() -> u64 {
30
}
fn default_enable_consensus() -> bool {
true
}
fn default_min_quorum() -> usize {
2
}
impl Default for ClusterNetworkConfig {
fn default() -> Self {
Self {
replication_factor: default_replication_factor(),
shard_count: default_shard_count(),
heartbeat_interval_secs: default_cluster_heartbeat(),
node_timeout_secs: default_node_timeout(),
enable_consensus: default_enable_consensus(),
min_quorum_size: default_min_quorum(),
seed_nodes: Vec::new(),
node_name: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KernelConfig {
#[serde(default = "default_enabled")]
pub enabled: bool,
#[serde(default = "default_max_processes", alias = "maxProcesses")]
pub max_processes: u32,
#[serde(
default = "default_health_check_interval_secs",
alias = "healthCheckIntervalSecs"
)]
pub health_check_interval_secs: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cluster: Option<ClusterNetworkConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chain: Option<ChainConfig>,
#[serde(default, skip_serializing_if = "Option::is_none", alias = "resourceTree")]
pub resource_tree: Option<ResourceTreeConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub vector: Option<VectorConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub profiles: Option<ProfilesConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pairing: Option<PairingConfig>,
}
impl Default for KernelConfig {
fn default() -> Self {
Self {
enabled: true,
max_processes: default_max_processes(),
health_check_interval_secs: default_health_check_interval_secs(),
cluster: None,
chain: None,
resource_tree: None,
vector: None,
profiles: None,
pairing: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfilesConfig {
#[serde(default = "default_profiles_enabled")]
pub enabled: bool,
#[serde(default = "default_profiles_storage_path")]
pub storage_path: String,
#[serde(default = "default_profile_name")]
pub default_profile: String,
}
fn default_profiles_enabled() -> bool {
true
}
fn default_profiles_storage_path() -> String {
".weftos/profiles".to_owned()
}
fn default_profile_name() -> String {
"default".to_owned()
}
impl Default for ProfilesConfig {
fn default() -> Self {
Self {
enabled: default_profiles_enabled(),
storage_path: default_profiles_storage_path(),
default_profile: default_profile_name(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PairingConfig {
#[serde(default = "default_pairing_persist_path")]
pub persist_path: String,
#[serde(default = "default_pairing_window_secs")]
pub default_window_secs: u64,
}
fn default_pairing_persist_path() -> String {
".weftos/runtime/paired_hosts.json".to_owned()
}
fn default_pairing_window_secs() -> u64 {
30
}
impl Default for PairingConfig {
fn default() -> Self {
Self {
persist_path: default_pairing_persist_path(),
default_window_secs: default_pairing_window_secs(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChainConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_checkpoint_interval", alias = "checkpointInterval")]
pub checkpoint_interval: u64,
#[serde(default)]
pub chain_id: u32,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "checkpointPath"
)]
pub checkpoint_path: Option<String>,
}
fn default_true() -> bool {
true
}
fn default_checkpoint_interval() -> u64 {
1000
}
impl Default for ChainConfig {
fn default() -> Self {
Self {
enabled: true,
checkpoint_interval: default_checkpoint_interval(),
chain_id: 0,
checkpoint_path: None,
}
}
}
impl ChainConfig {
pub fn effective_checkpoint_path(&self) -> Option<String> {
if self.checkpoint_path.is_some() {
return self.checkpoint_path.clone();
}
#[cfg(feature = "native")]
{
dirs::home_dir().map(|h| {
h.join(".clawft")
.join("chain.json")
.to_string_lossy()
.into_owned()
})
}
#[cfg(not(feature = "native"))]
{
None
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceTreeConfig {
#[serde(default = "default_true_rt")]
pub enabled: bool,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "checkpointPath"
)]
pub checkpoint_path: Option<String>,
}
fn default_true_rt() -> bool {
true
}
impl Default for ResourceTreeConfig {
fn default() -> Self {
Self {
enabled: true,
checkpoint_path: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum VectorBackendKind {
#[default]
Hnsw,
DiskAnn,
Hybrid,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VectorHnswConfig {
#[serde(default = "default_ef_construction")]
pub ef_construction: usize,
#[serde(default = "default_m")]
pub m: usize,
#[serde(default = "default_max_elements")]
pub max_elements: usize,
}
fn default_ef_construction() -> usize {
200
}
fn default_m() -> usize {
16
}
fn default_max_elements() -> usize {
100_000
}
impl Default for VectorHnswConfig {
fn default() -> Self {
Self {
ef_construction: default_ef_construction(),
m: default_m(),
max_elements: default_max_elements(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VectorDiskAnnConfig {
#[serde(default = "default_diskann_max_points")]
pub max_points: usize,
#[serde(default = "default_diskann_dimensions")]
pub dimensions: usize,
#[serde(default = "default_diskann_num_neighbors")]
pub num_neighbors: usize,
#[serde(default = "default_diskann_search_list_size")]
pub search_list_size: usize,
#[serde(default = "default_diskann_data_path")]
pub data_path: String,
#[serde(default = "default_diskann_use_pq")]
pub use_pq: bool,
#[serde(default = "default_diskann_pq_num_chunks")]
pub pq_num_chunks: usize,
}
fn default_diskann_max_points() -> usize {
10_000_000
}
fn default_diskann_dimensions() -> usize {
384
}
fn default_diskann_num_neighbors() -> usize {
64
}
fn default_diskann_search_list_size() -> usize {
100
}
fn default_diskann_data_path() -> String {
".weftos/diskann".to_owned()
}
fn default_diskann_use_pq() -> bool {
true
}
fn default_diskann_pq_num_chunks() -> usize {
48
}
impl Default for VectorDiskAnnConfig {
fn default() -> Self {
Self {
max_points: default_diskann_max_points(),
dimensions: default_diskann_dimensions(),
num_neighbors: default_diskann_num_neighbors(),
search_list_size: default_diskann_search_list_size(),
data_path: default_diskann_data_path(),
use_pq: default_diskann_use_pq(),
pq_num_chunks: default_diskann_pq_num_chunks(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum VectorEvictionPolicy {
#[default]
Lru,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VectorHybridConfig {
#[serde(default = "default_hybrid_hot_capacity")]
pub hot_capacity: usize,
#[serde(default = "default_hybrid_promotion_threshold")]
pub promotion_threshold: u32,
#[serde(default)]
pub eviction_policy: VectorEvictionPolicy,
}
fn default_hybrid_hot_capacity() -> usize {
50_000
}
fn default_hybrid_promotion_threshold() -> u32 {
3
}
impl Default for VectorHybridConfig {
fn default() -> Self {
Self {
hot_capacity: default_hybrid_hot_capacity(),
promotion_threshold: default_hybrid_promotion_threshold(),
eviction_policy: VectorEvictionPolicy::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VectorConfig {
#[serde(default)]
pub backend: VectorBackendKind,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hnsw: Option<VectorHnswConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub diskann: Option<VectorDiskAnnConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hybrid: Option<VectorHybridConfig>,
}
impl Default for VectorConfig {
fn default() -> Self {
Self {
backend: VectorBackendKind::default(),
hnsw: None,
diskann: None,
hybrid: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_kernel_config() {
let cfg = KernelConfig::default();
assert!(cfg.enabled);
assert_eq!(cfg.max_processes, 64);
assert_eq!(cfg.health_check_interval_secs, 30);
}
#[test]
fn deserialize_empty() {
let cfg: KernelConfig = serde_json::from_str("{}").unwrap();
assert!(cfg.enabled);
assert_eq!(cfg.max_processes, 64);
}
#[test]
fn deserialize_camel_case() {
let json = r#"{"maxProcesses": 128, "healthCheckIntervalSecs": 15}"#;
let cfg: KernelConfig = serde_json::from_str(json).unwrap();
assert_eq!(cfg.max_processes, 128);
assert_eq!(cfg.health_check_interval_secs, 15);
}
#[test]
fn serde_roundtrip() {
let cfg = KernelConfig {
enabled: true,
max_processes: 256,
health_check_interval_secs: 10,
cluster: None,
chain: None,
resource_tree: None,
vector: None,
profiles: None,
pairing: None,
};
let json = serde_json::to_string(&cfg).unwrap();
let restored: KernelConfig = serde_json::from_str(&json).unwrap();
assert_eq!(restored.enabled, cfg.enabled);
assert_eq!(restored.max_processes, cfg.max_processes);
}
#[test]
fn profiles_config_defaults() {
let cfg = ProfilesConfig::default();
assert!(cfg.enabled);
assert_eq!(cfg.storage_path, ".weftos/profiles");
assert_eq!(cfg.default_profile, "default");
}
#[test]
fn profiles_config_deserialize() {
let json = r#"{"enabled": false, "storage_path": "/tmp/profiles", "default_profile": "admin"}"#;
let cfg: ProfilesConfig = serde_json::from_str(json).unwrap();
assert!(!cfg.enabled);
assert_eq!(cfg.storage_path, "/tmp/profiles");
assert_eq!(cfg.default_profile, "admin");
}
#[test]
fn pairing_config_defaults() {
let cfg = PairingConfig::default();
assert_eq!(cfg.persist_path, ".weftos/runtime/paired_hosts.json");
assert_eq!(cfg.default_window_secs, 30);
}
#[test]
fn pairing_config_deserialize() {
let json = r#"{"persist_path": "/opt/pairing.json", "default_window_secs": 60}"#;
let cfg: PairingConfig = serde_json::from_str(json).unwrap();
assert_eq!(cfg.persist_path, "/opt/pairing.json");
assert_eq!(cfg.default_window_secs, 60);
}
#[test]
fn kernel_config_with_profiles_and_pairing() {
let json = r#"{"profiles": {"enabled": true}, "pairing": {"default_window_secs": 45}}"#;
let cfg: KernelConfig = serde_json::from_str(json).unwrap();
assert!(cfg.profiles.is_some());
assert!(cfg.profiles.unwrap().enabled);
assert!(cfg.pairing.is_some());
assert_eq!(cfg.pairing.unwrap().default_window_secs, 45);
}
#[test]
fn vector_config_defaults() {
let cfg = VectorConfig::default();
assert_eq!(cfg.backend, VectorBackendKind::Hnsw);
assert!(cfg.hnsw.is_none());
assert!(cfg.diskann.is_none());
assert!(cfg.hybrid.is_none());
}
#[test]
fn vector_config_deserialize_hybrid() {
let json = r#"{"backend": "hybrid", "hybrid": {"hot_capacity": 1000, "promotion_threshold": 5}}"#;
let cfg: VectorConfig = serde_json::from_str(json).unwrap();
assert_eq!(cfg.backend, VectorBackendKind::Hybrid);
let h = cfg.hybrid.unwrap();
assert_eq!(h.hot_capacity, 1000);
assert_eq!(h.promotion_threshold, 5);
}
#[test]
fn vector_config_deserialize_diskann() {
let json = r#"{"backend": "diskann", "diskann": {"max_points": 5000000}}"#;
let cfg: VectorConfig = serde_json::from_str(json).unwrap();
assert_eq!(cfg.backend, VectorBackendKind::DiskAnn);
let d = cfg.diskann.unwrap();
assert_eq!(d.max_points, 5_000_000);
}
#[test]
fn kernel_config_with_vector() {
let json = r#"{"vector": {"backend": "hnsw"}}"#;
let cfg: KernelConfig = serde_json::from_str(json).unwrap();
assert!(cfg.vector.is_some());
assert_eq!(cfg.vector.unwrap().backend, VectorBackendKind::Hnsw);
}
}