1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(rename_all = "lowercase")]
11pub enum NodeRole {
12 #[default]
14 All,
15 Orchestrator,
17 Kernel,
19 Voice,
21 Worker,
23 NightAgent,
25}
26
27impl NodeRole {
28 pub fn as_str(&self) -> &'static str {
29 match self {
30 Self::All => "all",
31 Self::Orchestrator => "orchestrator",
32 Self::Kernel => "kernel",
33 Self::Voice => "voice",
34 Self::Worker => "worker",
35 Self::NightAgent => "nightagent",
36 }
37 }
38}
39
40impl std::fmt::Display for NodeRole {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 f.write_str(self.as_str())
43 }
44}
45
46#[derive(Debug, Clone, Deserialize)]
47#[serde(default)]
48pub struct NodeConfig {
49 pub name: String,
50 pub role: NodeRole,
52}
53
54impl Default for NodeConfig {
55 fn default() -> Self {
56 Self {
57 name: String::new(),
58 role: NodeRole::All,
59 }
60 }
61}
62
63#[derive(Clone, Deserialize, Default)]
64#[serde(default)]
65pub struct TailscaleConfig {
66 pub enabled: bool,
67 pub auth_key: String,
68}
69
70impl std::fmt::Debug for TailscaleConfig {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 f.debug_struct("TailscaleConfig")
73 .field("enabled", &self.enabled)
74 .field("auth_key", &"[REDACTED]")
75 .finish()
76 }
77}
78
79#[derive(Debug, Clone, Deserialize)]
80#[serde(default)]
81pub struct MeshConfig {
82 pub transport: String,
83 pub discovery: String,
84 pub peers: Vec<String>,
85 pub tailscale: TailscaleConfig,
86}
87
88impl Default for MeshConfig {
89 fn default() -> Self {
90 Self {
91 transport: "lan".to_string(),
92 discovery: "mdns".to_string(),
93 peers: Vec::new(),
94 tailscale: TailscaleConfig::default(),
95 }
96 }
97}
98
99#[derive(Debug, Clone, Deserialize, PartialEq)]
100#[serde(default)]
101pub struct InferenceFallbackConfig {
102 pub max_attempts: usize,
103 pub t1: Vec<String>,
104 pub t2: Vec<String>,
105 pub t3: Vec<String>,
106 pub t4: Vec<String>,
107}
108
109impl Default for InferenceFallbackConfig {
110 fn default() -> Self {
111 Self {
112 max_attempts: 3,
113 t1: vec!["local".into(), "haiku".into(), "sonnet".into()],
114 t2: vec!["haiku".into(), "local".into(), "sonnet".into()],
115 t3: vec!["sonnet".into(), "opus".into()],
116 t4: vec!["opus".into(), "sonnet".into()],
117 }
118 }
119}
120
121#[derive(Debug, Clone, Deserialize)]
122#[serde(default)]
123pub struct InferenceConfig {
124 pub default_model: String,
125 pub api_key_env: String,
126 pub fallback: InferenceFallbackConfig,
127}
128
129impl Default for InferenceConfig {
130 fn default() -> Self {
131 Self {
132 default_model: "claude-sonnet-4-6".to_string(),
133 api_key_env: "ANTHROPIC_API_KEY".to_string(),
134 fallback: InferenceFallbackConfig::default(),
135 }
136 }
137}
138
139#[derive(Debug, Clone, Deserialize)]
140#[serde(default)]
141pub struct KernelConfig {
142 pub model: String,
143 pub model_path: String,
144 pub escalation_model: String,
145 pub max_tokens: u32,
146}
147
148impl Default for KernelConfig {
149 fn default() -> Self {
150 Self {
151 model: "none".to_string(),
152 model_path: String::new(),
153 escalation_model: String::new(),
154 max_tokens: 2048,
155 }
156 }
157}
158
159#[derive(Clone, Deserialize, Default)]
160#[serde(default)]
161pub struct TelegramConfig {
162 pub enabled: bool,
163 pub token_keychain: String,
164}
165
166impl std::fmt::Debug for TelegramConfig {
167 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168 f.debug_struct("TelegramConfig")
169 .field("enabled", &self.enabled)
170 .field("token_keychain", &"[REDACTED]")
171 .finish()
172 }
173}
174
175#[derive(Debug, Clone, Deserialize)]
176#[serde(default)]
177pub struct NightConfig {
178 pub night_mode: bool,
179 pub night_hours: String,
181 pub night_model: String,
182}
183
184impl Default for NightConfig {
185 fn default() -> Self {
186 Self {
187 night_mode: false,
188 night_hours: "23:00-07:00".to_string(),
189 night_model: "claude-haiku-4-5".to_string(),
190 }
191 }
192}
193
194#[derive(Debug, Clone, Deserialize)]
195#[serde(default)]
196pub struct DaemonConfig {
197 pub port: u16,
198 pub quiet_hours: Option<String>,
199 pub timezone: Option<String>,
200 pub auto_update: bool,
201}
202
203impl Default for DaemonConfig {
204 fn default() -> Self {
205 Self {
206 port: 8420,
207 quiet_hours: None,
208 timezone: None,
209 auto_update: true,
210 }
211 }
212}
213
214#[derive(Debug, Clone, Deserialize, Default)]
216#[serde(default)]
217pub struct ConvergioConfig {
218 pub node: NodeConfig,
219 pub daemon: DaemonConfig,
220 pub night: NightConfig,
221 pub mesh: MeshConfig,
222 pub inference: InferenceConfig,
223 pub kernel: KernelConfig,
224 pub telegram: TelegramConfig,
225}