mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-01 12:02:05 +00:00
fix(config): use read_raw_config() in migrations to prevent expanding defaults (#40821)
This commit is contained in:
parent
5cc4009deb
commit
c0568ca95f
2 changed files with 52 additions and 9 deletions
|
|
@ -4827,7 +4827,7 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
|
|||
|
||||
# ── Version 3 → 4: migrate tool progress from .env to config.yaml ──
|
||||
if current_ver < 4:
|
||||
config = load_config()
|
||||
config = read_raw_config()
|
||||
display = config.get("display", {})
|
||||
if not isinstance(display, dict):
|
||||
display = {}
|
||||
|
|
@ -4850,7 +4850,7 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
|
|||
|
||||
# ── Version 4 → 5: add timezone field ──
|
||||
if current_ver < 5:
|
||||
config = load_config()
|
||||
config = read_raw_config()
|
||||
if "timezone" not in config:
|
||||
old_tz = os.getenv("HERMES_TIMEZONE", "")
|
||||
if old_tz and old_tz.strip():
|
||||
|
|
@ -4878,7 +4878,7 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
|
|||
|
||||
# ── Version 11 → 12: migrate custom_providers list → providers dict ──
|
||||
if current_ver < 12:
|
||||
config = load_config()
|
||||
config = read_raw_config()
|
||||
custom_list = config.get("custom_providers")
|
||||
if isinstance(custom_list, list) and custom_list:
|
||||
providers_dict = config.get("providers", {})
|
||||
|
|
@ -4969,7 +4969,7 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
|
|||
if isinstance(raw_stt, dict) and "model" in raw_stt:
|
||||
legacy_model = raw_stt["model"]
|
||||
provider = raw_stt.get("provider", "local")
|
||||
config = load_config()
|
||||
config = read_raw_config()
|
||||
stt = config.get("stt", {})
|
||||
# Remove the legacy flat key
|
||||
stt.pop("model", None)
|
||||
|
|
@ -5434,7 +5434,7 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
|
|||
missing_config = get_missing_config_fields()
|
||||
|
||||
if missing_config:
|
||||
config = load_config()
|
||||
config = read_raw_config()
|
||||
|
||||
for field in missing_config:
|
||||
key = field["key"]
|
||||
|
|
@ -5450,7 +5450,7 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
|
|||
save_config(config)
|
||||
elif current_ver < latest_ver:
|
||||
# Just update version
|
||||
config = load_config()
|
||||
config = read_raw_config()
|
||||
config["_config_version"] = latest_ver
|
||||
save_config(config)
|
||||
|
||||
|
|
@ -5472,7 +5472,7 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
|
|||
|
||||
if answer in {"y", "yes"}:
|
||||
print()
|
||||
config = load_config()
|
||||
config = read_raw_config()
|
||||
try:
|
||||
from agent.skill_utils import SKILL_CONFIG_PREFIX
|
||||
except Exception:
|
||||
|
|
|
|||
|
|
@ -1062,7 +1062,7 @@ class TestDiscordChannelPromptsConfig:
|
|||
def test_default_config_includes_discord_channel_prompts(self):
|
||||
assert DEFAULT_CONFIG["discord"]["channel_prompts"] == {}
|
||||
|
||||
def test_migrate_adds_discord_channel_prompts_default(self, tmp_path):
|
||||
def test_migrate_does_not_expand_discord_channel_prompts_default(self, tmp_path):
|
||||
config_path = tmp_path / "config.yaml"
|
||||
config_path.write_text(
|
||||
yaml.safe_dump({"_config_version": 17, "discord": {"auto_thread": True}}),
|
||||
|
|
@ -1076,7 +1076,50 @@ class TestDiscordChannelPromptsConfig:
|
|||
from hermes_cli.config import DEFAULT_CONFIG
|
||||
assert raw["_config_version"] == DEFAULT_CONFIG["_config_version"]
|
||||
assert raw["discord"]["auto_thread"] is True
|
||||
assert raw["discord"]["channel_prompts"] == {}
|
||||
# channel_prompts is a DEFAULT_CONFIG value that should NOT be expanded
|
||||
# into the user's file — read_raw_config() preserves only what the user
|
||||
# explicitly wrote (fixes #40821: config migration expanding defaults).
|
||||
assert "channel_prompts" not in raw.get("discord", {})
|
||||
|
||||
def test_migrate_preserves_custom_providers_and_no_defaults_dump(self, tmp_path):
|
||||
"""Migration must not expand config.yaml to a defaults dump (#40821).
|
||||
|
||||
Before the fix, migrations used load_config() which deep-merges
|
||||
DEFAULT_CONFIG, then save_config() wrote the full ~13KB expanded
|
||||
result — destroying comments and structure. Using read_raw_config()
|
||||
keeps the file small and preserves only the user's actual config.
|
||||
"""
|
||||
config_path = tmp_path / "config.yaml"
|
||||
config_path.write_text(
|
||||
yaml.safe_dump({
|
||||
"_config_version": 3,
|
||||
"model": {"default": "test-model", "provider": "openrouter"},
|
||||
"custom_providers": [
|
||||
{"name": "local-llm", "base_url": "http://localhost:8080/v1",
|
||||
"models": {"test": {}}}
|
||||
],
|
||||
}),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
with patch.dict(os.environ, {"HERMES_HOME": str(tmp_path)}):
|
||||
migrate_config(interactive=False, quiet=True)
|
||||
raw = yaml.safe_load(config_path.read_text(encoding="utf-8"))
|
||||
|
||||
# custom_providers migrated to providers dict (by design, v11->v12)
|
||||
assert "custom_providers" not in raw
|
||||
assert "providers" in raw
|
||||
assert "local-llm" in raw["providers"]
|
||||
assert raw["providers"]["local-llm"]["api"] == "http://localhost:8080/v1"
|
||||
|
||||
# File must NOT be a defaults dump — assert specific DEFAULT_CONFIG
|
||||
# top-level keys are absent (they should only appear via load_config's
|
||||
# deep-merge, not be written to the user's file by migration).
|
||||
for default_key in ("tts", "compression", "security", "whatsapp", "bedrock"):
|
||||
assert default_key not in raw, (
|
||||
f"{default_key} should not be in migrated config file — "
|
||||
f"migration should use read_raw_config() to avoid defaults dump"
|
||||
)
|
||||
|
||||
|
||||
class TestUserMessagePreviewConfig:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue