mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-04-19 17:39:55 +00:00
feat(config): add {env:VAR} syntax for environment variable references
Add secret_resolver module to resolve {env:VAR_NAME} placeholders in
config files. Allows storing sensitive values like API keys in
environment variables instead of config.json.
This commit is contained in:
parent
214bf66a29
commit
beaa8de2c5
@ -4,6 +4,7 @@ import json
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from nanobot.config.schema import Config
|
from nanobot.config.schema import Config
|
||||||
|
from nanobot.config.secret_resolver import resolve_config
|
||||||
|
|
||||||
|
|
||||||
# Global variable to store current config path (for multi-instance support)
|
# Global variable to store current config path (for multi-instance support)
|
||||||
@ -40,6 +41,7 @@ def load_config(config_path: Path | None = None) -> Config:
|
|||||||
with open(path, encoding="utf-8") as f:
|
with open(path, encoding="utf-8") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
data = _migrate_config(data)
|
data = _migrate_config(data)
|
||||||
|
data = resolve_config(data) # Resolve {env:VAR} references
|
||||||
return Config.model_validate(data)
|
return Config.model_validate(data)
|
||||||
except (json.JSONDecodeError, ValueError) as e:
|
except (json.JSONDecodeError, ValueError) as e:
|
||||||
print(f"Warning: Failed to load config from {path}: {e}")
|
print(f"Warning: Failed to load config from {path}: {e}")
|
||||||
|
|||||||
48
nanobot/config/secret_resolver.py
Normal file
48
nanobot/config/secret_resolver.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
"""Secret reference resolver for configuration.
|
||||||
|
|
||||||
|
Supports {env:VARIABLE_NAME} syntax to reference environment variables.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Pattern matches {env:VAR_NAME} where VAR_NAME follows env var naming conventions
|
||||||
|
_REF_PATTERN = re.compile(r"\{env:([A-Z_][A-Z0-9_]*)\}")
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_env_vars(value: str) -> str:
|
||||||
|
"""Resolve {env:VAR} references in a string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: String that may contain {env:VAR} references.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
String with all {env:VAR} references replaced by their values.
|
||||||
|
Unresolved references are left unchanged.
|
||||||
|
"""
|
||||||
|
def replacer(match: re.Match[str]) -> str:
|
||||||
|
var_name = match.group(1)
|
||||||
|
env_value = os.environ.get(var_name)
|
||||||
|
if env_value is None:
|
||||||
|
return match.group(0) # Keep original if env var doesn't exist
|
||||||
|
return env_value
|
||||||
|
|
||||||
|
return _REF_PATTERN.sub(replacer, value)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_config(obj):
|
||||||
|
"""Recursively resolve {env:VAR} references in a configuration object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj: Configuration value (str, dict, list, or other).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Configuration with all {env:VAR} references resolved.
|
||||||
|
"""
|
||||||
|
if isinstance(obj, str):
|
||||||
|
return resolve_env_vars(obj)
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
return {k: resolve_config(v) for k, v in obj.items()}
|
||||||
|
if isinstance(obj, list):
|
||||||
|
return [resolve_config(item) for item in obj]
|
||||||
|
return obj
|
||||||
Loading…
x
Reference in New Issue
Block a user