Skip to main content

zeph_config/
env.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use 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_SKILLS_MAX_ACTIVE")
119            && let Ok(n) = v.parse::<usize>()
120            && let Some(nz) = NonZeroUsize::new(n)
121        {
122            self.skills.max_active_skills = nz;
123        }
124        if let Ok(v) = std::env::var("ZEPH_SKILLS_LEARNING_ENABLED")
125            && let Ok(enabled) = v.parse::<bool>()
126        {
127            self.skills.learning.enabled = enabled;
128        }
129        if let Ok(v) = std::env::var("ZEPH_SKILLS_LEARNING_AUTO_ACTIVATE")
130            && let Ok(auto_activate) = v.parse::<bool>()
131        {
132            self.skills.learning.auto_activate = auto_activate;
133        }
134        if let Ok(v) = std::env::var("ZEPH_SKILLS_PROMPT_MODE") {
135            match v.to_lowercase().as_str() {
136                "full" => self.skills.prompt_mode = crate::features::SkillPromptMode::Full,
137                "compact" => self.skills.prompt_mode = crate::features::SkillPromptMode::Compact,
138                "auto" => self.skills.prompt_mode = crate::features::SkillPromptMode::Auto,
139                _ => {}
140            }
141        }
142        if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_TEMPORAL_DECAY_ENABLED")
143            && let Ok(enabled) = v.parse::<bool>()
144        {
145            self.memory.semantic.temporal_decay_enabled = enabled;
146        }
147        if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_TEMPORAL_DECAY_HALF_LIFE_DAYS")
148            && let Ok(days) = v.parse::<u32>()
149        {
150            self.memory.semantic.temporal_decay_half_life_days = days;
151        }
152        if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_MMR_ENABLED")
153            && let Ok(enabled) = v.parse::<bool>()
154        {
155            self.memory.semantic.mmr_enabled = enabled;
156        }
157        if let Ok(v) = std::env::var("ZEPH_MEMORY_SEMANTIC_MMR_LAMBDA")
158            && let Ok(lambda) = v.parse::<f32>()
159        {
160            self.memory.semantic.mmr_lambda = lambda;
161        }
162        if let Ok(v) = std::env::var("ZEPH_MEMORY_TOKEN_SAFETY_MARGIN")
163            && let Ok(margin) = v.parse::<f32>()
164        {
165            self.memory.token_safety_margin = margin.clamp(0.1, 10.0);
166        }
167        if let Ok(v) = std::env::var("ZEPH_TOOLS_SUMMARIZE_OUTPUT")
168            && let Ok(enabled) = v.parse::<bool>()
169        {
170            self.tools.summarize_output = enabled;
171        }
172        if let Ok(v) = std::env::var("ZEPH_TOOLS_SHELL_ALLOWED_COMMANDS") {
173            self.tools.shell.allowed_commands = v
174                .split(',')
175                .map(|s| s.trim().to_owned())
176                .filter(|s| !s.is_empty())
177                .collect();
178        }
179        if let Ok(v) = std::env::var("ZEPH_TOOLS_TIMEOUT")
180            && let Ok(secs) = v.parse::<u64>()
181        {
182            self.tools.shell.timeout = secs;
183        }
184        if let Ok(v) = std::env::var("ZEPH_TOOLS_SCRAPE_TIMEOUT")
185            && let Ok(secs) = v.parse::<u64>()
186        {
187            self.tools.scrape.timeout = secs;
188        }
189        if let Ok(v) = std::env::var("ZEPH_TOOLS_SCRAPE_MAX_BODY")
190            && let Ok(bytes) = v.parse::<usize>()
191        {
192            self.tools.scrape.max_body_bytes = bytes;
193        }
194    }
195
196    fn apply_env_overrides_core_2(&mut self) {
197        if let Ok(v) = std::env::var("ZEPH_INDEX_ENABLED")
198            && let Ok(enabled) = v.parse::<bool>()
199        {
200            self.index.enabled = enabled;
201        }
202        if let Ok(v) = std::env::var("ZEPH_INDEX_MAX_CHUNKS")
203            && let Ok(n) = v.parse::<usize>()
204        {
205            self.index.max_chunks = n;
206        }
207        if let Ok(v) = std::env::var("ZEPH_INDEX_SCORE_THRESHOLD")
208            && let Ok(t) = v.parse::<f32>()
209        {
210            self.index.score_threshold = t.clamp(0.0, 1.0);
211        }
212        if let Ok(v) = std::env::var("ZEPH_INDEX_BUDGET_RATIO")
213            && let Ok(r) = v.parse::<f32>()
214        {
215            self.index.budget_ratio = r.clamp(0.0, 1.0);
216        }
217        if let Ok(v) = std::env::var("ZEPH_INDEX_REPO_MAP_TOKENS")
218            && let Ok(n) = v.parse::<usize>()
219        {
220            self.index.repo_map_tokens = n;
221        }
222        if let Ok(v) = std::env::var("ZEPH_STT_PROVIDER") {
223            let stt = self.llm.stt.get_or_insert_with(|| SttConfig {
224                provider: ProviderName::default(),
225                language: default_stt_language(),
226            });
227            stt.provider = ProviderName::new(v);
228        }
229        if let Ok(v) = std::env::var("ZEPH_STT_LANGUAGE") {
230            let stt = self.llm.stt.get_or_insert_with(|| SttConfig {
231                provider: ProviderName::default(),
232                language: default_stt_language(),
233            });
234            stt.language = v;
235        }
236        if std::env::var("ZEPH_STT_MODEL").is_ok() {
237            tracing::warn!(
238                "env var ZEPH_STT_MODEL is no longer supported; set `stt_model` on the \
239                 [[llm.providers]] entry instead"
240            );
241        }
242        if std::env::var("ZEPH_STT_BASE_URL").is_ok() {
243            tracing::warn!(
244                "env var ZEPH_STT_BASE_URL is no longer supported; set `base_url` on the \
245                 [[llm.providers]] entry instead"
246            );
247        }
248    }
249
250    fn apply_env_overrides_core_2b(&mut self) {
251        if let Ok(v) = std::env::var("ZEPH_AUTO_UPDATE_CHECK")
252            && let Ok(enabled) = v.parse::<bool>()
253        {
254            self.agent.auto_update_check = enabled;
255        }
256        if let Ok(v) = std::env::var("ZEPH_A2A_ENABLED")
257            && let Ok(enabled) = v.parse::<bool>()
258        {
259            self.a2a.enabled = enabled;
260        }
261        if let Ok(v) = std::env::var("ZEPH_A2A_HOST") {
262            self.a2a.host = v;
263        }
264        if let Ok(v) = std::env::var("ZEPH_A2A_PORT")
265            && let Ok(port) = v.parse::<u16>()
266        {
267            self.a2a.port = port;
268        }
269        if let Ok(v) = std::env::var("ZEPH_A2A_PUBLIC_URL") {
270            self.a2a.public_url = v;
271        }
272        if let Ok(v) = std::env::var("ZEPH_A2A_RATE_LIMIT")
273            && let Ok(rate) = v.parse::<u32>()
274        {
275            self.a2a.rate_limit = rate;
276        }
277        if let Ok(v) = std::env::var("ZEPH_MEMORY_AUTOSAVE_ASSISTANT")
278            && let Ok(enabled) = v.parse::<bool>()
279        {
280            self.memory.autosave_assistant = enabled;
281        }
282        if let Ok(v) = std::env::var("ZEPH_MEMORY_AUTOSAVE_MIN_LENGTH")
283            && let Ok(len) = v.parse::<usize>()
284        {
285            self.memory.autosave_min_length = len;
286        }
287        if let Ok(v) = std::env::var("ZEPH_LLM_RESPONSE_CACHE_ENABLED")
288            && let Ok(enabled) = v.parse::<bool>()
289        {
290            self.llm.response_cache_enabled = enabled;
291        }
292        if let Ok(v) = std::env::var("ZEPH_LLM_RESPONSE_CACHE_TTL_SECS")
293            && let Ok(secs) = v.parse::<u64>()
294        {
295            self.llm.response_cache_ttl_secs = secs;
296        }
297        if let Ok(v) = std::env::var("ZEPH_LLM_SEMANTIC_CACHE_ENABLED")
298            && let Ok(enabled) = v.parse::<bool>()
299        {
300            self.llm.semantic_cache_enabled = enabled;
301        }
302        if let Ok(v) = std::env::var("ZEPH_LLM_SEMANTIC_CACHE_THRESHOLD")
303            && let Ok(threshold) = v.parse::<f32>()
304        {
305            self.llm.semantic_cache_threshold = threshold;
306        }
307        if let Ok(v) = std::env::var("ZEPH_LLM_SEMANTIC_CACHE_MAX_CANDIDATES")
308            && let Ok(max) = v.parse::<u32>()
309        {
310            self.llm.semantic_cache_max_candidates = max;
311        }
312        if let Ok(v) = std::env::var("ZEPH_ACP_ENABLED")
313            && let Ok(enabled) = v.parse::<bool>()
314        {
315            self.acp.enabled = enabled;
316        }
317        if let Ok(v) = std::env::var("ZEPH_ACP_AGENT_NAME") {
318            self.acp.agent_name = v;
319        }
320        if let Ok(v) = std::env::var("ZEPH_ACP_AGENT_VERSION") {
321            self.acp.agent_version = v;
322        }
323        if let Ok(v) = std::env::var("ZEPH_ACP_MAX_SESSIONS")
324            && let Ok(n) = v.parse::<usize>()
325        {
326            self.acp.max_sessions = n;
327        }
328        if let Ok(v) = std::env::var("ZEPH_ACP_SESSION_IDLE_TIMEOUT_SECS")
329            && let Ok(secs) = v.parse::<u64>()
330        {
331            self.acp.session_idle_timeout_secs = secs;
332        }
333        if let Ok(v) = std::env::var("ZEPH_ACP_PERMISSION_FILE") {
334            self.acp.permission_file = Some(std::path::PathBuf::from(v));
335        }
336        if let Ok(v) = std::env::var("ZEPH_ACP_AUTH_TOKEN") {
337            self.acp.auth_token = Some(v);
338        }
339        if let Ok(v) = std::env::var("ZEPH_ACP_DISCOVERY_ENABLED")
340            && let Ok(enabled) = v.parse::<bool>()
341        {
342            self.acp.discovery_enabled = enabled;
343        }
344        if let Ok(v) = std::env::var("ZEPH_LOG_FILE") {
345            self.logging.file = v;
346        }
347        if let Ok(v) = std::env::var("ZEPH_LOG_LEVEL") {
348            self.logging.level = v;
349        }
350    }
351
352    fn apply_env_overrides_security(&mut self) {
353        if let Ok(v) = std::env::var("ZEPH_TOOLS_SHELL_ALLOWED_PATHS") {
354            self.tools.shell.allowed_paths = v
355                .split(',')
356                .map(|s| s.trim().to_owned())
357                .filter(|s| !s.is_empty())
358                .collect();
359        }
360        if let Ok(v) = std::env::var("ZEPH_TOOLS_SHELL_ALLOW_NETWORK")
361            && let Ok(allow) = v.parse::<bool>()
362        {
363            self.tools.shell.allow_network = allow;
364        }
365        if let Ok(v) = std::env::var("ZEPH_TOOLS_AUDIT_ENABLED")
366            && let Ok(enabled) = v.parse::<bool>()
367        {
368            self.tools.audit.enabled = enabled;
369        }
370        if let Ok(v) = std::env::var("ZEPH_TOOLS_AUDIT_DESTINATION") {
371            self.tools.audit.destination = match v.as_str() {
372                "stdout" => crate::AuditDestination::Stdout,
373                "stderr" => crate::AuditDestination::Stderr,
374                path => crate::AuditDestination::File(std::path::PathBuf::from(path)),
375            };
376        }
377        if let Ok(v) = std::env::var("ZEPH_SECURITY_REDACT_SECRETS")
378            && let Ok(redact) = v.parse::<bool>()
379        {
380            self.security.redact_secrets = redact;
381        }
382        if let Ok(v) = std::env::var("ZEPH_TIMEOUT_LLM")
383            && let Ok(secs) = v.parse::<u64>()
384        {
385            self.timeouts.llm_seconds = secs;
386        }
387        if let Ok(v) = std::env::var("ZEPH_TIMEOUT_LLM_REQUEST")
388            && let Ok(secs) = v.parse::<u64>()
389        {
390            self.timeouts.llm_request_timeout_secs = secs;
391        }
392        if let Ok(v) = std::env::var("ZEPH_TIMEOUT_EMBEDDING")
393            && let Ok(secs) = v.parse::<u64>()
394        {
395            self.timeouts.embedding_seconds = secs;
396        }
397        if let Ok(v) = std::env::var("ZEPH_TIMEOUT_A2A")
398            && let Ok(secs) = v.parse::<u64>()
399        {
400            self.timeouts.a2a_seconds = secs;
401        }
402        if let Ok(v) = std::env::var("ZEPH_A2A_REQUIRE_TLS")
403            && let Ok(require) = v.parse::<bool>()
404        {
405            self.a2a.require_tls = require;
406        }
407        if let Ok(v) = std::env::var("ZEPH_A2A_SSRF_PROTECTION")
408            && let Ok(ssrf) = v.parse::<bool>()
409        {
410            self.a2a.ssrf_protection = ssrf;
411        }
412        if let Ok(v) = std::env::var("ZEPH_A2A_MAX_BODY_SIZE")
413            && let Ok(size) = v.parse::<usize>()
414        {
415            self.a2a.max_body_size = size;
416        }
417    }
418}