Quellcode durchsuchen

Seed extra only from env vars that are actually set

hermes_env_enablement_fn promised to return None when the platform is
not configured, but always returned a seed dict with a fabricated
base_url default — and the gateway commits that dict verbatim onto the
platform's extra, overwriting a base_url the user set in config.yaml
whenever CHATTO_BASE_URL was unset. The function now returns exactly
the set env values (never None), and 'nothing set' stays where it
belongs: each ConfigField falls back to its declared default on its
own.
Paul Klumpp vor 1 Woche
Ursprung
Commit
8aa5368c35
2 geänderte Dateien mit 59 neuen und 24 gelöschten Zeilen
  1. 18 24
      adapter.py
  2. 41 0
      test_adapter.py

+ 18 - 24
adapter.py

@@ -2876,35 +2876,29 @@ def hermes_setup_fn() -> None:
     print_success("\n✓ Chatto configured. Restart the gateway to activate.")
 
 
-def hermes_env_enablement_fn() -> dict | None:
-    """Seed PlatformConfig.extra from env vars.
-
-    Returns a dict compatible with the PlatformConfig merge hook (or None
-    when no env-provided values are present).
-
-    Called by the platform registry during load_gateway_config().
-    Return None when the platform isn't minimally configured — the
-    caller then skips auto-enabling. Return a dict to seed extras.
-
-    The special 'home_channel' key is extracted and becomes a proper
-    HomeChannel dataclass on the PlatformConfig; every other key is
-    merged into PlatformConfig.extra.
+def hermes_env_enablement_fn() -> dict:
+    """Seed PlatformConfig.extra from the CHATTO_* environment variables.
+
+    Returns a dict compatible with the PlatformConfig merge hook, holding
+    exactly the values that are actually set via env — never fabricated
+    defaults. Called by the platform registry during load_gateway_config(),
+    which commits this dict onto the platform's ``extra`` verbatim; seeding a
+    value that was not configured would overwrite what the user set in
+    config.yaml (e.g. a YAML base_url clobbered by the ChattoHQ default).
+    "Nothing set" therefore stays the adapter's business: every ConfigField
+    falls back to its declared default (base_url to ChattoHQ) on its own.
+
+    Seed keys are the config.yaml "extra" keys (config_key), NOT the env var
+    names — ChattoConfiguration reads extra[config_key]. The special
+    'home_channel' key is extracted by the gateway and becomes a proper
+    HomeChannel dataclass on the PlatformConfig; every other key is merged
+    into PlatformConfig.extra.
 
     Function name should be the same as register argument name with "hermes_" prefix, so we
     know that it is needed for plugin register().
     """
-    # Seed keys must be the config.yaml "extra" keys (config_key), NOT the env
-    # var names — ChattoConfiguration reads extra[config_key].
-    seed: dict[str, Any] = {
-        ChattoConfiguration.base_url.field_name: (
-            os.getenv(ChattoConfiguration.base_url.env_name)
-            or ChattoClient.DEFAULT_BASE_URL
-        ).strip(),
-    }
-
+    seed: dict[str, Any] = {}
     for field in ChattoConfiguration.fields():
-        if field.field_name == ChattoConfiguration.base_url.field_name:
-            continue
         env_value = os.getenv(field.env_name)
         if env_value:
             seed[field.config_key] = env_value.strip()

+ 41 - 0
test_adapter.py

@@ -70,6 +70,7 @@ from adapter import (
     HermesChatType,
     _capabilities,
     chat_type_for_room_kind,
+    hermes_env_enablement_fn,
     register,
 )
 from adapter import (
@@ -2114,6 +2115,46 @@ class TestConstants:
 # -- Client creation credentials --
 
 
+class TestEnvEnablement:
+    """hermes_env_enablement_fn seeds extra verbatim from set env vars only.
+
+    The gateway commits this dict onto the platform's extra unconditionally,
+    so a fabricated default would overwrite what the user set in config.yaml.
+    """
+
+    def _seed(self, monkeypatch, **env):
+        for key in _CHATTO_ENV_KEYS:
+            monkeypatch.delenv(key, raising=False)
+        for key, value in env.items():
+            monkeypatch.setenv(key, value)
+        return hermes_env_enablement_fn()
+
+    def test_no_env_vars_yield_an_empty_seed(self, monkeypatch):
+        assert self._seed(monkeypatch) == {}
+
+    def test_set_base_url_is_seeded(self, monkeypatch):
+        seed = self._seed(monkeypatch, CHATTO_BASE_URL="  https://chat.example.com  ")
+        assert seed == {"base_url": "https://chat.example.com"}
+
+    def test_unset_base_url_is_not_fabricated(self, monkeypatch):
+        """The ChattoHQ default is the ConfigField's business, not a seed
+        entry that would clobber a YAML-configured base_url."""
+        assert "base_url" not in self._seed(monkeypatch)
+
+    def test_values_are_seeded_under_their_config_keys(self, monkeypatch):
+        seed = self._seed(
+            monkeypatch,
+            CHATTO_HOME_CHANNEL="room-9",
+            CHATTO_REQUIRE_MENTION_ROOMS="a,b",
+            CHATTO_AUTO_THREAD="false",
+        )
+        assert seed == {
+            "home_channel": "room-9",
+            "require_mention_rooms": "a,b",
+            "auto_thread": "false",
+        }
+
+
 class TestOpenClientCredentials:
     """Token wins; nothing configured raises as a normal creation failure."""