fix(gateway): bridge shared-key loop to nested platform config blocks
The shared-key bridging loop (allow_from, require_mention,
free_response_channels, …) read only the top-level yaml platform block
(yaml_cfg.get(plat.value)). When a user configured a platform solely
under ``platforms:`` or ``gateway.platforms:`` with no top-level block,
the loop skipped that platform entirely and all bridged keys were silently
dropped into PlatformConfig.extra — making allow_from, require_mention,
etc. ineffective for nested-only configs.
The apply_yaml_config_fn dispatch already received this same fallback in
44f3e51 to handle plugin adapters (e.g. Discord allow_from). The
shared-key loop now mirrors it: if yaml_cfg.get(plat.value) is absent,
fall back to gateway.platforms.<name> then platforms.<name>.
The enabled field is deliberately excluded from the nested fallback
(guarded by _cfg_toplevel): _merge_platform_map already merged it with
the correct precedence, so re-applying it from a single nested source
would overwrite the correctly-merged value.
Two new regression tests assert that allow_from and require_mention
configured under platforms.telegram and gateway.platforms.telegram are
bridged into PlatformConfig.extra. All 54 existing config tests pass.
This commit is contained in:
@ -843,6 +843,25 @@ def load_gateway_config() -> GatewayConfig:
|
||||
if plat == Platform.LOCAL:
|
||||
continue
|
||||
platform_cfg = yaml_cfg.get(plat.value)
|
||||
_cfg_toplevel = isinstance(platform_cfg, dict)
|
||||
# Fall back to the platform's block under ``platforms`` /
|
||||
# ``gateway.platforms`` so shared-key bridging (allow_from,
|
||||
# require_mention, free_response_channels, …) still runs when
|
||||
# the user configured the platform only under those nested paths
|
||||
# and not via a top-level block. Mirrors the identical fallback
|
||||
# already applied to the apply_yaml_config_fn dispatch below
|
||||
# (#44f3e51).
|
||||
# Note: ``enabled`` is only written to plat_data from a
|
||||
# top-level block (``_cfg_toplevel``); for nested-only configs
|
||||
# ``_merge_platform_map`` already merged it with the correct
|
||||
# precedence, so re-applying it here would overwrite that.
|
||||
if not _cfg_toplevel:
|
||||
for _src in (gateway_platforms, yaml_cfg.get("platforms")):
|
||||
if isinstance(_src, dict):
|
||||
_candidate = _src.get(plat.value)
|
||||
if isinstance(_candidate, dict):
|
||||
platform_cfg = _candidate
|
||||
break
|
||||
if not isinstance(platform_cfg, dict):
|
||||
continue
|
||||
# Collect bridgeable keys from this platform section
|
||||
@ -903,7 +922,7 @@ def load_gateway_config() -> GatewayConfig:
|
||||
bridged["channel_prompts"] = channel_prompts
|
||||
if "gateway_restart_notification" in platform_cfg:
|
||||
bridged["gateway_restart_notification"] = platform_cfg["gateway_restart_notification"]
|
||||
enabled_was_explicit = "enabled" in platform_cfg
|
||||
enabled_was_explicit = _cfg_toplevel and "enabled" in platform_cfg
|
||||
if not bridged and not enabled_was_explicit:
|
||||
continue
|
||||
plat_data, extra = _ensure_platform_extra_dict(platforms_data, plat.value)
|
||||
|
||||
@ -477,6 +477,69 @@ class TestLoadGatewayConfig:
|
||||
assert telegram.token == "top-token"
|
||||
assert telegram.extra["reply_prefix"] == "top"
|
||||
|
||||
def test_shared_key_loop_bridges_allow_from_from_nested_platforms(self, tmp_path, monkeypatch):
|
||||
"""Regression: shared-key loop must bridge allow_from / require_mention
|
||||
into PlatformConfig.extra even when the platform is configured only
|
||||
under ``platforms:`` (no top-level ``telegram:`` block).
|
||||
|
||||
Before the fix, ``platform_cfg = yaml_cfg.get('telegram')`` returned
|
||||
None for nested-only configs, so the loop skipped the platform entirely
|
||||
and allow_from was silently ignored. The apply_yaml_config_fn dispatch
|
||||
received the same fix in #44f3e51; the shared-key loop now mirrors it.
|
||||
"""
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
config_path = hermes_home / "config.yaml"
|
||||
config_path.write_text(
|
||||
"platforms:\n"
|
||||
" telegram:\n"
|
||||
" allow_from:\n"
|
||||
" - \"111222333\"\n"
|
||||
" - \"444555666\"\n"
|
||||
" require_mention: true\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
|
||||
config = load_gateway_config()
|
||||
|
||||
telegram = config.platforms[Platform.TELEGRAM]
|
||||
assert telegram.extra.get("allow_from") == ["111222333", "444555666"], (
|
||||
"allow_from configured under platforms.telegram must be bridged "
|
||||
"into PlatformConfig.extra by the shared-key loop"
|
||||
)
|
||||
assert telegram.extra.get("require_mention") is True, (
|
||||
"require_mention configured under platforms.telegram must be "
|
||||
"bridged into PlatformConfig.extra by the shared-key loop"
|
||||
)
|
||||
|
||||
def test_shared_key_loop_bridges_allow_from_from_nested_gateway_platforms(self, tmp_path, monkeypatch):
|
||||
"""Same regression check for ``gateway.platforms:`` path."""
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
config_path = hermes_home / "config.yaml"
|
||||
config_path.write_text(
|
||||
"gateway:\n"
|
||||
" platforms:\n"
|
||||
" telegram:\n"
|
||||
" allow_from:\n"
|
||||
" - \"777888999\"\n"
|
||||
" require_mention: false\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
|
||||
|
||||
config = load_gateway_config()
|
||||
|
||||
telegram = config.platforms[Platform.TELEGRAM]
|
||||
assert telegram.extra.get("allow_from") == ["777888999"], (
|
||||
"allow_from configured under gateway.platforms.telegram must be "
|
||||
"bridged into PlatformConfig.extra by the shared-key loop"
|
||||
)
|
||||
assert telegram.extra.get("require_mention") is False
|
||||
|
||||
def test_bridges_quoted_false_session_notify_from_config_yaml(self, tmp_path, monkeypatch):
|
||||
hermes_home = tmp_path / ".hermes"
|
||||
hermes_home.mkdir()
|
||||
|
||||
Reference in New Issue
Block a user