Vendored deer-flow upstream (bytedance/deer-flow) plus prompt-injection hardening: - New deerflow.security package: content_delimiter, html_cleaner, sanitizer (8 layers — invisible chars, control chars, symbols, NFC, PUA, tag chars, horizontal whitespace collapse with newline/tab preservation, length cap) - New deerflow.community.searx package: web_search, web_fetch, image_search backed by a private SearX instance, every external string sanitized and wrapped in <<<EXTERNAL_UNTRUSTED_CONTENT>>> delimiters - All native community web providers (ddg_search, tavily, exa, firecrawl, jina_ai, infoquest, image_search) replaced with hard-fail stubs that raise NativeWebToolDisabledError at import time, so a misconfigured tool.use path fails loud rather than silently falling back to unsanitized output - Native client back-doors (jina_client.py, infoquest_client.py) stubbed too - Native-tool tests quarantined under tests/_disabled_native/ (collect_ignore_glob via local conftest.py) - Sanitizer Layer 7 fix: only collapse horizontal whitespace, preserve newlines and tabs so list/table structure survives - Hardened runtime config.yaml references only the searx-backed tools - Factory overlay (backend/) kept in sync with deer-flow tree as a reference / source See HARDENING.md for the full audit trail and verification steps.
63 lines
2.2 KiB
Python
63 lines
2.2 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 Literal
|
|
|
|
from langchain.agents.middleware import AgentMiddleware
|
|
|
|
|
|
@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
|
|
summarization: Literal[False] | AgentMiddleware = False
|
|
subagent: bool | AgentMiddleware = False
|
|
vision: bool | AgentMiddleware = False
|
|
auto_title: bool | AgentMiddleware = False
|
|
guardrail: Literal[False] | 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
|