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