-
-
Notifications
You must be signed in to change notification settings - Fork 182
Expand file tree
/
Copy pathconfig_loader.py
More file actions
137 lines (111 loc) · 5.05 KB
/
config_loader.py
File metadata and controls
137 lines (111 loc) · 5.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"""Shared config loader — merges config.toml + config.local.toml.
Used by run.py, wrapper.py, and wrapper_api.py so the server and all
wrappers see the same agent definitions.
Per-invocation overrides: the following environment variables, if set,
override values from config.toml. This lets dotfiles/launcher layers run
isolated instances per project without editing the repo's config file.
AGENTCHATTR_DATA_DIR → server.data_dir
AGENTCHATTR_PORT → server.port (int)
AGENTCHATTR_MCP_HTTP_PORT → mcp.http_port (int)
AGENTCHATTR_MCP_SSE_PORT → mcp.sse_port (int)
AGENTCHATTR_UPLOAD_DIR → images.upload_dir
Relative paths in env var overrides resolve against the current working
directory (where the user invoked the command from), not agentchattr's
install directory.
"""
import os
import sys
import tomllib
from pathlib import Path
ROOT = Path(__file__).parent
# Mapping: env var name → (config section, key, is_int)
_ENV_OVERRIDES = [
("AGENTCHATTR_DATA_DIR", "server", "data_dir", False),
("AGENTCHATTR_PORT", "server", "port", True),
("AGENTCHATTR_MCP_HTTP_PORT", "mcp", "http_port", True),
("AGENTCHATTR_MCP_SSE_PORT", "mcp", "sse_port", True),
("AGENTCHATTR_UPLOAD_DIR", "images", "upload_dir", False),
]
# Mapping: CLI flag → env var (for apply_cli_overrides)
CLI_OVERRIDE_FLAGS = [
("--data-dir", "AGENTCHATTR_DATA_DIR"),
("--port", "AGENTCHATTR_PORT"),
("--mcp-http-port", "AGENTCHATTR_MCP_HTTP_PORT"),
("--mcp-sse-port", "AGENTCHATTR_MCP_SSE_PORT"),
("--upload-dir", "AGENTCHATTR_UPLOAD_DIR"),
]
def apply_cli_overrides(argv: list[str] | None = None) -> None:
"""Scan argv for --data-dir/--port/etc and set matching env vars in-place.
Called by run.py, wrapper.py, and wrapper_api.py BEFORE load_config() so
all entry points respect the same overrides when launched with the same
flags. No effect if a flag isn't present. Supports both `--flag value`
and `--flag=value` forms.
Arguments after a literal `--` are treated as pass-through (e.g. for the
agent CLI in wrapper.py) and are NOT scanned — `python wrapper.py claude
-- --port 9999` sets `--port 9999` on the agent, not on agentchattr.
"""
if argv is None:
argv = sys.argv
# Truncate at pass-through separator so agent CLI args don't leak in.
try:
end = argv.index("--")
scan = argv[:end]
except ValueError:
scan = argv
for flag, env in CLI_OVERRIDE_FLAGS:
# Iterate in order; first match wins (ignore later duplicates).
for i, arg in enumerate(scan):
if arg == flag and i + 1 < len(scan):
os.environ[env] = scan[i + 1]
break
if arg.startswith(flag + "="):
os.environ[env] = arg.split("=", 1)[1]
break
def _apply_env_overrides(config: dict) -> None:
"""Apply AGENTCHATTR_* env vars to the config dict in-place."""
for env_var, section, key, is_int in _ENV_OVERRIDES:
raw = os.environ.get(env_var)
if raw is None or raw == "":
continue
if is_int:
try:
value = int(raw)
except ValueError:
print(f" Warning: {env_var}={raw!r} is not a valid integer, ignoring")
continue
else:
# Path values: resolve relative paths against current working dir,
# not against agentchattr's install directory.
p = Path(raw)
if not p.is_absolute():
p = (Path.cwd() / p).resolve()
value = str(p)
config.setdefault(section, {})[key] = value
def load_config(root: Path | None = None) -> dict:
"""Load config.toml and merge config.local.toml if it exists.
config.local.toml is gitignored and intended for user-specific agents
(e.g. local LLM endpoints) that shouldn't be committed.
Only the [agents] section is merged — local entries are added alongside
(not replacing) the agents defined in config.toml.
AGENTCHATTR_* environment variables override values from config.toml
(see module docstring for the list).
"""
root = root or ROOT
config_path = root / "config.toml"
with open(config_path, "rb") as f:
config = tomllib.load(f)
local_path = root / "config.local.toml"
if local_path.exists():
with open(local_path, "rb") as f:
local = tomllib.load(f)
# Merge [agents] section — local agents are added ONLY if they don't already exist.
# This protects the "holy trinity" (claude, codex, gemini) from being overridden.
local_agents = local.get("agents", {})
config_agents = config.setdefault("agents", {})
for name, agent_cfg in local_agents.items():
if name not in config_agents:
config_agents[name] = agent_cfg
else:
print(f" Warning: Ignoring local agent '{name}' (already defined in config.toml)")
_apply_env_overrides(config)
return config