mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-08-06 04:48:44 +00:00
fix(memory): treat explicit null backend_config values as omitted in DeerMemConfig (#4217)
config.example.yaml ships backend_config.model: as a bare key whose children
are all comments, which YAML parses to None (make config-upgrade then writes
an explicit model: null). DeerMemConfig.model is a non-Optional field with a
default, so from_backend_config(**{"model": None}) raised a ValidationError
and every run failed with "Input should be a valid dictionary or instance of
DeerMemModelConfig". Drop None entries in from_backend_config so YAML null /
empty keys fall back to field defaults, matching the documented "empty =
host default LLM" semantics. Upstream bug (#4122 schema); regression-pinned
in test_deermem_self_contained.py.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
1769b2de0d
commit
5c80c07dfe
@ -203,10 +203,16 @@ class DeerMemConfig(BaseModel):
|
|||||||
typo (e.g. ``storage_pat`` missing the ``h``) does not silently fall
|
typo (e.g. ``storage_pat`` missing the ``h``) does not silently fall
|
||||||
back to the default and write memory to an unintended location --
|
back to the default and write memory to an unintended location --
|
||||||
mirrors the host layer's ``load_memory_config_from_dict`` warning.
|
mirrors the host layer's ``load_memory_config_from_dict`` warning.
|
||||||
|
|
||||||
|
``None`` values are dropped so they fall back to the field default:
|
||||||
|
YAML renders an empty key (``model:`` with only commented children, as
|
||||||
|
shipped in ``config.example.yaml``) as ``None``, which non-Optional
|
||||||
|
fields like ``model`` would otherwise reject even though omitting the
|
||||||
|
key entirely is valid.
|
||||||
"""
|
"""
|
||||||
if not backend_config:
|
if not backend_config:
|
||||||
return cls()
|
return cls()
|
||||||
known = {k: v for k, v in backend_config.items() if k in cls.model_fields}
|
known = {k: v for k, v in backend_config.items() if k in cls.model_fields and v is not None}
|
||||||
unknown = sorted(k for k in backend_config if k not in cls.model_fields)
|
unknown = sorted(k for k in backend_config if k not in cls.model_fields)
|
||||||
if unknown:
|
if unknown:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
|
|||||||
@ -396,3 +396,37 @@ def test_from_backend_config_silent_on_known_keys(caplog):
|
|||||||
with caplog.at_level("WARNING", logger=cfg_logger):
|
with caplog.at_level("WARNING", logger=cfg_logger):
|
||||||
DeerMemConfig.from_backend_config({"storage_path": "/tmp/x", "max_facts": 20})
|
DeerMemConfig.from_backend_config({"storage_path": "/tmp/x", "max_facts": 20})
|
||||||
assert not any("Unknown backend_config keys" in r.message for r in caplog.records)
|
assert not any("Unknown backend_config keys" in r.message for r in caplog.records)
|
||||||
|
|
||||||
|
|
||||||
|
def test_from_backend_config_null_values_fall_back_to_defaults():
|
||||||
|
"""Explicit YAML ``null`` values must behave like omitted keys, not crash.
|
||||||
|
|
||||||
|
``config.example.yaml`` ships ``backend_config.model:`` as a bare key with
|
||||||
|
commented children, which YAML parses to ``None`` (and ``make
|
||||||
|
config-upgrade`` writes it out as an explicit ``model: null``). Non-Optional
|
||||||
|
fields like ``model: DeerMemModelConfig`` reject an explicit ``None`` even
|
||||||
|
though the omitted key would use the field default — so the shipped example
|
||||||
|
config crashed every run with a DeerMemConfig ValidationError."""
|
||||||
|
from deerflow.agents.memory.backends.deermem.deermem.config import (
|
||||||
|
DeerMemConfig,
|
||||||
|
DeerMemModelConfig,
|
||||||
|
)
|
||||||
|
|
||||||
|
cfg = DeerMemConfig.from_backend_config({"model": None, "debounce_seconds": None, "storage_path": "/tmp/x"})
|
||||||
|
|
||||||
|
# None entries fall back to field defaults; real values still parse
|
||||||
|
assert isinstance(cfg.model, DeerMemModelConfig)
|
||||||
|
assert cfg.model.model is None # default = no extraction LLM configured
|
||||||
|
assert cfg.debounce_seconds == DeerMemConfig().debounce_seconds
|
||||||
|
assert cfg.storage_path == "/tmp/x"
|
||||||
|
|
||||||
|
|
||||||
|
def test_from_backend_config_null_values_do_not_warn_as_unknown(caplog):
|
||||||
|
"""Dropped ``None`` entries are known keys — they must not trip the
|
||||||
|
unknown-key typo warning."""
|
||||||
|
from deerflow.agents.memory.backends.deermem.deermem.config import DeerMemConfig
|
||||||
|
|
||||||
|
cfg_logger = "deerflow.agents.memory.backends.deermem.deermem.config"
|
||||||
|
with caplog.at_level("WARNING", logger=cfg_logger):
|
||||||
|
DeerMemConfig.from_backend_config({"model": None})
|
||||||
|
assert not any("Unknown backend_config keys" in r.message for r in caplog.records)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user