diff --git a/backend/packages/harness/deerflow/config/memory_config.py b/backend/packages/harness/deerflow/config/memory_config.py index ab878dd24..b0ad30e73 100644 --- a/backend/packages/harness/deerflow/config/memory_config.py +++ b/backend/packages/harness/deerflow/config/memory_config.py @@ -121,31 +121,7 @@ _memory_config: MemoryConfig = MemoryConfig() def get_memory_config() -> MemoryConfig: - """Get the current memory configuration. - - ``_memory_config`` is only refreshed as a side effect of ``get_app_config()`` - reloading (via ``_apply_singleton_configs`` -> ``load_memory_config_from_dict``). - A reader that reaches memory config without going through ``get_app_config()`` - first -- e.g. the agent factory deciding whether to bind the memory tools -- - would otherwise see a stale ``memory.mode`` after a ``config.yaml`` edit, even - though ``memory.*`` is documented as hot-reloadable. Trigger the same - signature-checked reload here so the singleton follows the config file. - - If no config file is on disk (tests, or a minimal deployment running purely - on defaults / ``set_memory_config``), there is nothing to reload from, so we - keep the pre-existing behavior of returning the in-memory singleton instead of - becoming more fragile than before. The catch is deliberately narrow: a config - file that exists but fails to load (malformed YAML, failed validation) is a - real error and is surfaced here just as it is at every other unguarded - ``get_app_config()`` call site, leaving the last-good singleton untouched. - """ - # Lazy import: app_config imports this module, so a top-level import cycles. - from .app_config import get_app_config - - try: - get_app_config() - except FileNotFoundError: - pass + """Get the current memory configuration.""" return _memory_config diff --git a/backend/tests/test_app_config_reload.py b/backend/tests/test_app_config_reload.py index 3d355635c..a5a8dd7a1 100644 --- a/backend/tests/test_app_config_reload.py +++ b/backend/tests/test_app_config_reload.py @@ -530,75 +530,3 @@ def test_get_app_config_does_not_mutate_singletons_when_reload_validation_fails( assert get_store() is initial_store finally: _reset_config_singletons() - - -def test_get_memory_config_follows_config_file_reload(tmp_path, monkeypatch): - """get_memory_config() picks up a config.yaml memory.mode edit on its own (#4204). - - ``_memory_config`` used to refresh only as a side effect of ``get_app_config()``, - so a reader reaching memory config directly (e.g. the agent factory deciding - whether to bind the memory tools) saw a stale mode after a config edit even - though ``memory.*`` is documented as hot-reloadable. - """ - config_path = tmp_path / "config.yaml" - extensions_path = tmp_path / "extensions_config.json" - _write_extensions_config(extensions_path) - _write_config_with_sections(config_path, {"memory": {"enabled": True, "mode": "middleware"}}) - - monkeypatch.setenv("DEER_FLOW_CONFIG_PATH", str(config_path)) - monkeypatch.setenv("DEER_FLOW_EXTENSIONS_CONFIG_PATH", str(extensions_path)) - _reset_config_singletons() - - try: - assert get_memory_config().mode == "middleware" - - _write_config_with_sections(config_path, {"memory": {"enabled": True, "mode": "tool"}}) - next_mtime = config_path.stat().st_mtime + 5 - os.utime(config_path, (next_mtime, next_mtime)) - - # No explicit get_app_config() call: get_memory_config() must reflect the - # new mode on its own. - assert get_memory_config().mode == "tool" - finally: - _reset_config_singletons() - - -def test_get_memory_config_surfaces_malformed_config_edit(tmp_path, monkeypatch): - """A malformed config edit surfaces through get_memory_config() rather than - being swallowed. - - The ``except FileNotFoundError`` guard is deliberately narrow: it covers only - the genuinely-absent file (tests, or a defaults-only deployment) where there - is nothing to reload from. A file that exists but fails validation is a real - error and propagates -- consistent with every other unguarded - ``get_app_config()`` caller -- and the last-good singleton is left intact - rather than cleared or half-applied. - """ - import deerflow.config.memory_config as memory_config_module - - config_path = tmp_path / "config.yaml" - extensions_path = tmp_path / "extensions_config.json" - _write_extensions_config(extensions_path) - _write_config_with_sections(config_path, {"memory": {"enabled": True, "mode": "middleware"}}) - - monkeypatch.setenv("DEER_FLOW_CONFIG_PATH", str(config_path)) - monkeypatch.setenv("DEER_FLOW_EXTENSIONS_CONFIG_PATH", str(extensions_path)) - _reset_config_singletons() - - try: - assert get_memory_config().mode == "middleware" - - # Same file, now invalid: `title: false` fails AppConfig validation while - # the edit also flips memory.mode -- so a half-applied reload would leak - # "tool" into the singleton. - _write_config_with_sections(config_path, {"memory": {"enabled": True, "mode": "tool"}, "title": False}) - next_mtime = config_path.stat().st_mtime + 5 - os.utime(config_path, (next_mtime, next_mtime)) - - with pytest.raises(ValidationError): - get_memory_config() - - # Failed reload: the last-good singleton stays "middleware", not "tool". - assert memory_config_module._memory_config.mode == "middleware" - finally: - _reset_config_singletons()