Vanzeren c2002d9fac
feat(memory): add memory tool sets (#4023)
* feat: add memory-as-tool mode alongside existing middleware mode

- Add memory.mode config field (middleware|tool, default middleware)
- Add search_memory_facts() for case-insensitive fact lookup
- Add 4 memory tools: memory_search, memory_add, memory_update, memory_delete
- Wire mode gating in factory.py and lead_agent/agent.py
- 256 memory tests passing, zero regressions

* fix: harden tool-mode memory scoping and docs

* fix: address memory tool mode review feedback

* fix(memory): address tool mode review feedback

* fix: update config_version to 22 in values.yaml
2026-07-11 15:28:16 +08:00

71 lines
2.6 KiB
Python

"""Declarative feature flags and middleware positioning for create_deerflow_agent.
Pure data classes and decorators — no I/O, no side effects.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING, Literal
from langchain.agents.middleware import AgentMiddleware
if TYPE_CHECKING:
from deerflow.config.memory_config import MemoryConfig
@dataclass
class RuntimeFeatures:
"""Declarative feature flags for ``create_deerflow_agent``.
Most features accept:
- ``True``: use the built-in default middleware
- ``False``: disable
- An ``AgentMiddleware`` instance: use this custom implementation instead
``summarization`` and ``guardrail`` have no built-in default — they only
accept ``False`` (disable) or an ``AgentMiddleware`` instance (custom).
"""
sandbox: bool | AgentMiddleware = True
memory: bool | AgentMiddleware = False
# Explicit memory config for direct create_deerflow_agent(features=...) callers.
# The lead-agent AppConfig path passes resolved_app_config.memory directly.
memory_config: MemoryConfig | None = None
summarization: Literal[False] | AgentMiddleware = False
subagent: bool | AgentMiddleware = False
vision: bool | AgentMiddleware = False
auto_title: bool | AgentMiddleware = False
guardrail: Literal[False] | AgentMiddleware = False
loop_detection: bool | AgentMiddleware = True
token_budget: bool | AgentMiddleware = False
# ---------------------------------------------------------------------------
# Middleware positioning decorators
# ---------------------------------------------------------------------------
def Next(anchor: type[AgentMiddleware]):
"""Declare this middleware should be placed after *anchor* in the chain."""
if not (isinstance(anchor, type) and issubclass(anchor, AgentMiddleware)):
raise TypeError(f"@Next expects an AgentMiddleware subclass, got {anchor!r}")
def decorator(cls: type[AgentMiddleware]) -> type[AgentMiddleware]:
cls._next_anchor = anchor # type: ignore[attr-defined]
return cls
return decorator
def Prev(anchor: type[AgentMiddleware]):
"""Declare this middleware should be placed before *anchor* in the chain."""
if not (isinstance(anchor, type) and issubclass(anchor, AgentMiddleware)):
raise TypeError(f"@Prev expects an AgentMiddleware subclass, got {anchor!r}")
def decorator(cls: type[AgentMiddleware]) -> type[AgentMiddleware]:
cls._prev_anchor = anchor # type: ignore[attr-defined]
return cls
return decorator