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