1use std::num::NonZeroUsize;
5
6use crate::providers::{ProviderEntry, SttConfig, default_stt_language, default_stt_provider};
7use crate::root::Config;
8
9impl Config {
10 pub fn apply_env_overrides(&mut self) {
11 self.apply_env_overrides_core();
12 self.apply_env_overrides_security();
13 }
14
15 fn apply_env_overrides_core(&mut self) {
16 self.apply_env_overrides_core_1();
17 self.apply_env_overrides_core_1b();
18 self.apply_env_overrides_core_2();
19 self.apply_env_overrides_core_2b();
20 }
21
22 fn apply_env_overrides_core_1(&mut self) {
23 let has_provider_env = std::env::var("ZEPH_LLM_PROVIDER").is_ok()
24 || std::env::var("ZEPH_LLM_BASE_URL").is_ok()
25 || std::env::var("ZEPH_LLM_MODEL").is_ok();
26 if has_provider_env && self.llm.providers.is_empty() {
27 self.llm.providers.push(ProviderEntry::default());
28 }
29
30 if let Ok(v) = std::env::var("ZEPH_LLM_PROVIDER")
31 && let Some(entry) = self.llm.providers.first_mut()
32 {
33 if let Ok(kind) = serde_json::from_value(serde_json::Value::String(v.clone())) {
34 entry.provider_type = kind;
35 } else {
36 tracing::warn!("ignoring invalid ZEPH_LLM_PROVIDER value: {v}");
37 }
38 }
39 if let Ok(v) = std::env::var("ZEPH_LLM_BASE_URL")
40 && let Some(entry) = self.llm.providers.first_mut()
41 {
42 entry.base_url = Some(v);
43 }
44 if let Ok(v) = std::env::var("ZEPH_LLM_MODEL")
45 && let Some(entry) = self.llm.providers.first_mut()
46 {
47 entry.model = Some(v);
48 }
49 if let Ok(v) = std::env::var("ZEPH_LLM_EMBEDDING_MODEL") {
50 self.llm.embedding_model = v;
51 }
52 if let Ok(v) = std::env::var("ZEPH_SQLITE_PATH") {
53 self.memory.sqlite_path = v;
54 }
55 if let Ok(v) = std::env::var("ZEPH_QDRANT_URL") {
56 self.memory.qdrant_url = v;
57 }
58 if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_ENABLED")
59 && let Ok(enabled) = v.parse::<bool>()
60 {
61 self.memory.semantic.enabled = enabled;
62 }
63 if let Ok(v) = std::env::var("ZEPH_MEMORY_RECALL_LIMIT")
64 && let Ok(limit) = v.parse::<usize>()
65 {
66 self.memory.semantic.recall_limit = limit;
67 }
68 if let Ok(v) = std::env::var("ZEPH_MEMORY_SUMMARIZATION_THRESHOLD")
69 && let Ok(threshold) = v.parse::<usize>()
70 {
71 self.memory.summarization_threshold = threshold;
72 }
73 if let Ok(v) = std::env::var("ZEPH_MEMORY_CONTEXT_BUDGET_TOKENS")
74 && let Ok(tokens) = v.parse::<usize>()
75 {
76 self.memory.context_budget_tokens = tokens;
77 }
78 if let Ok(v) = std::env::var("ZEPH_MEMORY_COMPACTION_THRESHOLD")
79 && let Ok(threshold) = v.parse::<f32>()
80 {
81 self.memory.hard_compaction_threshold = threshold;
82 }
83 if let Ok(v) = std::env::var("ZEPH_MEMORY_SOFT_COMPACTION_THRESHOLD")
84 && let Ok(threshold) = v.parse::<f32>()
85 {
86 self.memory.soft_compaction_threshold = threshold;
87 }
88 if let Ok(v) = std::env::var("ZEPH_MEMORY_COMPACTION_PRESERVE_TAIL")
89 && let Ok(tail) = v.parse::<usize>()
90 {
91 self.memory.compaction_preserve_tail = tail;
92 }
93 if let Ok(v) = std::env::var("ZEPH_MEMORY_AUTO_BUDGET")
94 && let Ok(enabled) = v.parse::<bool>()
95 {
96 self.memory.auto_budget = enabled;
97 }
98 if let Ok(v) = std::env::var("ZEPH_MEMORY_PRUNE_PROTECT_TOKENS")
99 && let Ok(tokens) = v.parse::<usize>()
100 {
101 self.memory.prune_protect_tokens = tokens;
102 }
103 if let Ok(v) = std::env::var("ZEPH_MEMORY_VECTOR_BACKEND") {
104 match v.to_lowercase().as_str() {
105 "sqlite" => {
106 self.memory.vector_backend = crate::memory::VectorBackend::Sqlite;
107 }
108 "qdrant" => {
109 self.memory.vector_backend = crate::memory::VectorBackend::Qdrant;
110 }
111 _ => {}
112 }
113 }
114 }
115
116 fn apply_env_overrides_core_1b(&mut self) {
117 if let Ok(v) = std::env::var("ZEPH_SKILLS_MAX_ACTIVE")
118 && let Ok(n) = v.parse::<usize>()
119 && let Some(nz) = NonZeroUsize::new(n)
120 {
121 self.skills.max_active_skills = nz;
122 }
123 if let Ok(v) = std::env::var("ZEPH_SKILLS_LEARNING_ENABLED")
124 && let Ok(enabled) = v.parse::<bool>()
125 {
126 self.skills.learning.enabled = enabled;
127 }
128 if let Ok(v) = std::env::var("ZEPH_SKILLS_LEARNING_AUTO_ACTIVATE")
129 && let Ok(auto_activate) = v.parse::<bool>()
130 {
131 self.skills.learning.auto_activate = auto_activate;
132 }
133 if let Ok(v) = std::env::var("ZEPH_SKILLS_PROMPT_MODE") {
134 match v.to_lowercase().as_str() {
135 "full" => self.skills.prompt_mode = crate::features::SkillPromptMode::Full,
136 "compact" => self.skills.prompt_mode = crate::features::SkillPromptMode::Compact,
137 "auto" => self.skills.prompt_mode = crate::features::SkillPromptMode::Auto,
138 _ => {}
139 }
140 }
141 if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_TEMPORAL_DECAY_ENABLED")
142 && let Ok(enabled) = v.parse::<bool>()
143 {
144 self.memory.semantic.temporal_decay_enabled = enabled;
145 }
146 if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_TEMPORAL_DECAY_HALF_LIFE_DAYS")
147 && let Ok(days) = v.parse::<u32>()
148 {
149 self.memory.semantic.temporal_decay_half_life_days = days;
150 }
151 if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_MMR_ENABLED")
152 && let Ok(enabled) = v.parse::<bool>()
153 {
154 self.memory.semantic.mmr_enabled = enabled;
155 }
156 if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_MMR_LAMBDA")
157 && let Ok(lambda) = v.parse::<f32>()
158 {
159 self.memory.semantic.mmr_lambda = lambda;
160 }
161 if let Ok(v) = std::env::var("ZEPH_MEMORY_TOKEN_SAFETY_MARGIN")
162 && let Ok(margin) = v.parse::<f32>()
163 {
164 self.memory.token_safety_margin = margin.clamp(0.1, 10.0);
165 }
166 if let Ok(v) = std::env::var("ZEPH_TOOLS_SUMMARIZE_OUTPUT")
167 && let Ok(enabled) = v.parse::<bool>()
168 {
169 self.tools.summarize_output = enabled;
170 }
171 if let Ok(v) = std::env::var("ZEPH_TOOLS_SHELL_ALLOWED_COMMANDS") {
172 self.tools.shell.allowed_commands = v
173 .split(',')
174 .map(|s| s.trim().to_owned())
175 .filter(|s| !s.is_empty())
176 .collect();
177 }
178 if let Ok(v) = std::env::var("ZEPH_TOOLS_TIMEOUT")
179 && let Ok(secs) = v.parse::<u64>()
180 {
181 self.tools.shell.timeout = secs;
182 }
183 if let Ok(v) = std::env::var("ZEPH_TOOLS_SCRAPE_TIMEOUT")
184 && let Ok(secs) = v.parse::<u64>()
185 {
186 self.tools.scrape.timeout = secs;
187 }
188 if let Ok(v) = std::env::var("ZEPH_TOOLS_SCRAPE_MAX_BODY")
189 && let Ok(bytes) = v.parse::<usize>()
190 {
191 self.tools.scrape.max_body_bytes = bytes;
192 }
193 }
194
195 fn apply_env_overrides_core_2(&mut self) {
196 if let Ok(v) = std::env::var("ZEPH_INDEX_ENABLED")
197 && let Ok(enabled) = v.parse::<bool>()
198 {
199 self.index.enabled = enabled;
200 }
201 if let Ok(v) = std::env::var("ZEPH_INDEX_MAX_CHUNKS")
202 && let Ok(n) = v.parse::<usize>()
203 {
204 self.index.max_chunks = n;
205 }
206 if let Ok(v) = std::env::var("ZEPH_INDEX_SCORE_THRESHOLD")
207 && let Ok(t) = v.parse::<f32>()
208 {
209 self.index.score_threshold = t.clamp(0.0, 1.0);
210 }
211 if let Ok(v) = std::env::var("ZEPH_INDEX_BUDGET_RATIO")
212 && let Ok(r) = v.parse::<f32>()
213 {
214 self.index.budget_ratio = r.clamp(0.0, 1.0);
215 }
216 if let Ok(v) = std::env::var("ZEPH_INDEX_REPO_MAP_TOKENS")
217 && let Ok(n) = v.parse::<usize>()
218 {
219 self.index.repo_map_tokens = n;
220 }
221 if let Ok(v) = std::env::var("ZEPH_STT_PROVIDER") {
222 let stt = self.llm.stt.get_or_insert_with(|| SttConfig {
223 provider: default_stt_provider(),
224 language: default_stt_language(),
225 });
226 stt.provider = v;
227 }
228 if let Ok(v) = std::env::var("ZEPH_STT_LANGUAGE") {
229 let stt = self.llm.stt.get_or_insert_with(|| SttConfig {
230 provider: default_stt_provider(),
231 language: default_stt_language(),
232 });
233 stt.language = v;
234 }
235 if std::env::var("ZEPH_STT_MODEL").is_ok() {
236 tracing::warn!(
237 "env var ZEPH_STT_MODEL is no longer supported; set `stt_model` on the \
238 [[llm.providers]] entry instead"
239 );
240 }
241 if std::env::var("ZEPH_STT_BASE_URL").is_ok() {
242 tracing::warn!(
243 "env var ZEPH_STT_BASE_URL is no longer supported; set `base_url` on the \
244 [[llm.providers]] entry instead"
245 );
246 }
247 }
248
249 fn apply_env_overrides_core_2b(&mut self) {
250 if let Ok(v) = std::env::var("ZEPH_AUTO_UPDATE_CHECK")
251 && let Ok(enabled) = v.parse::<bool>()
252 {
253 self.agent.auto_update_check = enabled;
254 }
255 if let Ok(v) = std::env::var("ZEPH_A2A_ENABLED")
256 && let Ok(enabled) = v.parse::<bool>()
257 {
258 self.a2a.enabled = enabled;
259 }
260 if let Ok(v) = std::env::var("ZEPH_A2A_HOST") {
261 self.a2a.host = v;
262 }
263 if let Ok(v) = std::env::var("ZEPH_A2A_PORT")
264 && let Ok(port) = v.parse::<u16>()
265 {
266 self.a2a.port = port;
267 }
268 if let Ok(v) = std::env::var("ZEPH_A2A_PUBLIC_URL") {
269 self.a2a.public_url = v;
270 }
271 if let Ok(v) = std::env::var("ZEPH_A2A_RATE_LIMIT")
272 && let Ok(rate) = v.parse::<u32>()
273 {
274 self.a2a.rate_limit = rate;
275 }
276 if let Ok(v) = std::env::var("ZEPH_MEMORY_AUTOSAVE_ASSISTANT")
277 && let Ok(enabled) = v.parse::<bool>()
278 {
279 self.memory.autosave_assistant = enabled;
280 }
281 if let Ok(v) = std::env::var("ZEPH_MEMORY_AUTOSAVE_MIN_LENGTH")
282 && let Ok(len) = v.parse::<usize>()
283 {
284 self.memory.autosave_min_length = len;
285 }
286 if let Ok(v) = std::env::var("ZEPH_LLM_RESPONSE_CACHE_ENABLED")
287 && let Ok(enabled) = v.parse::<bool>()
288 {
289 self.llm.response_cache_enabled = enabled;
290 }
291 if let Ok(v) = std::env::var("ZEPH_LLM_RESPONSE_CACHE_TTL_SECS")
292 && let Ok(secs) = v.parse::<u64>()
293 {
294 self.llm.response_cache_ttl_secs = secs;
295 }
296 if let Ok(v) = std::env::var("ZEPH_LLM_SEMANTIC_CACHE_ENABLED")
297 && let Ok(enabled) = v.parse::<bool>()
298 {
299 self.llm.semantic_cache_enabled = enabled;
300 }
301 if let Ok(v) = std::env::var("ZEPH_LLM_SEMANTIC_CACHE_THRESHOLD")
302 && let Ok(threshold) = v.parse::<f32>()
303 {
304 self.llm.semantic_cache_threshold = threshold;
305 }
306 if let Ok(v) = std::env::var("ZEPH_LLM_SEMANTIC_CACHE_MAX_CANDIDATES")
307 && let Ok(max) = v.parse::<u32>()
308 {
309 self.llm.semantic_cache_max_candidates = max;
310 }
311 if let Ok(v) = std::env::var("ZEPH_ACP_ENABLED")
312 && let Ok(enabled) = v.parse::<bool>()
313 {
314 self.acp.enabled = enabled;
315 }
316 if let Ok(v) = std::env::var("ZEPH_ACP_AGENT_NAME") {
317 self.acp.agent_name = v;
318 }
319 if let Ok(v) = std::env::var("ZEPH_ACP_AGENT_VERSION") {
320 self.acp.agent_version = v;
321 }
322 if let Ok(v) = std::env::var("ZEPH_ACP_MAX_SESSIONS")
323 && let Ok(n) = v.parse::<usize>()
324 {
325 self.acp.max_sessions = n;
326 }
327 if let Ok(v) = std::env::var("ZEPH_ACP_SESSION_IDLE_TIMEOUT_SECS")
328 && let Ok(secs) = v.parse::<u64>()
329 {
330 self.acp.session_idle_timeout_secs = secs;
331 }
332 if let Ok(v) = std::env::var("ZEPH_ACP_PERMISSION_FILE") {
333 self.acp.permission_file = Some(std::path::PathBuf::from(v));
334 }
335 if let Ok(v) = std::env::var("ZEPH_ACP_AUTH_TOKEN") {
336 self.acp.auth_token = Some(v);
337 }
338 if let Ok(v) = std::env::var("ZEPH_ACP_DISCOVERY_ENABLED")
339 && let Ok(enabled) = v.parse::<bool>()
340 {
341 self.acp.discovery_enabled = enabled;
342 }
343 if let Ok(v) = std::env::var("ZEPH_LOG_FILE") {
344 self.logging.file = v;
345 }
346 if let Ok(v) = std::env::var("ZEPH_LOG_LEVEL") {
347 self.logging.level = v;
348 }
349 }
350
351 fn apply_env_overrides_security(&mut self) {
352 if let Ok(v) = std::env::var("ZEPH_TOOLS_SHELL_ALLOWED_PATHS") {
353 self.tools.shell.allowed_paths = v
354 .split(',')
355 .map(|s| s.trim().to_owned())
356 .filter(|s| !s.is_empty())
357 .collect();
358 }
359 if let Ok(v) = std::env::var("ZEPH_TOOLS_SHELL_ALLOW_NETWORK")
360 && let Ok(allow) = v.parse::<bool>()
361 {
362 self.tools.shell.allow_network = allow;
363 }
364 if let Ok(v) = std::env::var("ZEPH_TOOLS_AUDIT_ENABLED")
365 && let Ok(enabled) = v.parse::<bool>()
366 {
367 self.tools.audit.enabled = enabled;
368 }
369 if let Ok(v) = std::env::var("ZEPH_TOOLS_AUDIT_DESTINATION") {
370 self.tools.audit.destination = v;
371 }
372 if let Ok(v) = std::env::var("ZEPH_SECURITY_REDACT_SECRETS")
373 && let Ok(redact) = v.parse::<bool>()
374 {
375 self.security.redact_secrets = redact;
376 }
377 if let Ok(v) = std::env::var("ZEPH_TIMEOUT_LLM")
378 && let Ok(secs) = v.parse::<u64>()
379 {
380 self.timeouts.llm_seconds = secs;
381 }
382 if let Ok(v) = std::env::var("ZEPH_TIMEOUT_LLM_REQUEST")
383 && let Ok(secs) = v.parse::<u64>()
384 {
385 self.timeouts.llm_request_timeout_secs = secs;
386 }
387 if let Ok(v) = std::env::var("ZEPH_TIMEOUT_EMBEDDING")
388 && let Ok(secs) = v.parse::<u64>()
389 {
390 self.timeouts.embedding_seconds = secs;
391 }
392 if let Ok(v) = std::env::var("ZEPH_TIMEOUT_A2A")
393 && let Ok(secs) = v.parse::<u64>()
394 {
395 self.timeouts.a2a_seconds = secs;
396 }
397 if let Ok(v) = std::env::var("ZEPH_A2A_REQUIRE_TLS")
398 && let Ok(require) = v.parse::<bool>()
399 {
400 self.a2a.require_tls = require;
401 }
402 if let Ok(v) = std::env::var("ZEPH_A2A_SSRF_PROTECTION")
403 && let Ok(ssrf) = v.parse::<bool>()
404 {
405 self.a2a.ssrf_protection = ssrf;
406 }
407 if let Ok(v) = std::env::var("ZEPH_A2A_MAX_BODY_SIZE")
408 && let Ok(size) = v.parse::<usize>()
409 {
410 self.a2a.max_body_size = size;
411 }
412 }
413}