Merge refactor/config-deerflow-context into release/2.0-rc

Cherry-pick PR #2271's config refactor onto release/2.0-rc.
Used 'git merge -X theirs' to auto-resolve content conflicts in favor of
the PR's design (frozen AppConfig + explicit-parameter passing).

Limitations:
- Release-only changes that overlapped with PR's refactor in 119 files
  are NOT preserved — those files reflect PR's version. Follow-up commits
  on this branch will need to re-apply release-only modifications where
  meaningful.
- See PR #2271 for design rationale.
This commit is contained in:
greatmengqi
2026-04-27 18:16:42 +08:00
227 changed files with 6965 additions and 5578 deletions
+21 -32
View File
@@ -4,34 +4,23 @@ from types import SimpleNamespace
import anyio
from deerflow.agents.lead_agent import prompt as prompt_module
from deerflow.config.app_config import AppConfig
from deerflow.skills.types import Skill
def _set_skills_cache_state(*, skills=None, active=False, version=0):
prompt_module._get_cached_skills_prompt_section.cache_clear()
with prompt_module._enabled_skills_lock:
prompt_module._enabled_skills_cache = skills
prompt_module._enabled_skills_refresh_active = active
prompt_module._enabled_skills_refresh_version = version
prompt_module._enabled_skills_refresh_event.clear()
def test_build_custom_mounts_section_returns_empty_when_no_mounts(monkeypatch):
def test_build_custom_mounts_section_returns_empty_when_no_mounts():
config = SimpleNamespace(sandbox=SimpleNamespace(mounts=[]))
monkeypatch.setattr("deerflow.config.get_app_config", lambda: config)
assert prompt_module._build_custom_mounts_section() == ""
assert prompt_module._build_custom_mounts_section(config) == ""
def test_build_custom_mounts_section_lists_configured_mounts(monkeypatch):
def test_build_custom_mounts_section_lists_configured_mounts():
mounts = [
SimpleNamespace(container_path="/home/user/shared", read_only=False),
SimpleNamespace(container_path="/mnt/reference", read_only=True),
]
config = SimpleNamespace(sandbox=SimpleNamespace(mounts=mounts))
monkeypatch.setattr("deerflow.config.get_app_config", lambda: config)
section = prompt_module._build_custom_mounts_section()
section = prompt_module._build_custom_mounts_section(config)
assert "**Custom Mounted Directories:**" in section
assert "`/home/user/shared`" in section
@@ -45,15 +34,15 @@ def test_apply_prompt_template_includes_custom_mounts(monkeypatch):
config = SimpleNamespace(
sandbox=SimpleNamespace(mounts=mounts),
skills=SimpleNamespace(container_path="/mnt/skills"),
skill_evolution=SimpleNamespace(enabled=False),
)
monkeypatch.setattr("deerflow.config.get_app_config", lambda: config)
monkeypatch.setattr(prompt_module, "_get_enabled_skills", lambda: [])
monkeypatch.setattr(prompt_module, "get_deferred_tools_prompt_section", lambda: "")
monkeypatch.setattr(prompt_module, "_build_acp_section", lambda: "")
monkeypatch.setattr(prompt_module, "_get_memory_context", lambda agent_name=None: "")
monkeypatch.setattr(prompt_module, "_get_enabled_skills", lambda *a, **k: [])
monkeypatch.setattr(prompt_module, "get_deferred_tools_prompt_section", lambda app_config: "")
monkeypatch.setattr(prompt_module, "_build_acp_section", lambda app_config: "")
monkeypatch.setattr(prompt_module, "_get_memory_context", lambda app_config, agent_name=None: "")
monkeypatch.setattr(prompt_module, "get_agent_soul", lambda agent_name=None: "")
prompt = prompt_module.apply_prompt_template()
prompt = prompt_module.apply_prompt_template(config)
assert "`/home/user/shared`" in prompt
assert "Custom Mounted Directories" in prompt
@@ -63,15 +52,15 @@ def test_apply_prompt_template_includes_relative_path_guidance(monkeypatch):
config = SimpleNamespace(
sandbox=SimpleNamespace(mounts=[]),
skills=SimpleNamespace(container_path="/mnt/skills"),
skill_evolution=SimpleNamespace(enabled=False),
)
monkeypatch.setattr("deerflow.config.get_app_config", lambda: config)
monkeypatch.setattr(prompt_module, "_get_enabled_skills", lambda: [])
monkeypatch.setattr(prompt_module, "get_deferred_tools_prompt_section", lambda: "")
monkeypatch.setattr(prompt_module, "_build_acp_section", lambda: "")
monkeypatch.setattr(prompt_module, "_get_memory_context", lambda agent_name=None: "")
monkeypatch.setattr(prompt_module, "_get_enabled_skills", lambda *a, **k: [])
monkeypatch.setattr(prompt_module, "get_deferred_tools_prompt_section", lambda app_config: "")
monkeypatch.setattr(prompt_module, "_build_acp_section", lambda app_config: "")
monkeypatch.setattr(prompt_module, "_get_memory_context", lambda app_config, agent_name=None: "")
monkeypatch.setattr(prompt_module, "get_agent_soul", lambda agent_name=None: "")
prompt = prompt_module.apply_prompt_template()
prompt = prompt_module.apply_prompt_template(config)
assert "Treat `/mnt/user-data/workspace` as your default current working directory" in prompt
assert "`hello.txt`, `../uploads/data.csv`, and `../outputs/report.md`" in prompt
@@ -92,8 +81,8 @@ def test_refresh_skills_system_prompt_cache_async_reloads_immediately(monkeypatc
)
state = {"skills": [make_skill("first-skill")]}
monkeypatch.setattr(prompt_module, "load_skills", lambda enabled_only=True: list(state["skills"]))
_set_skills_cache_state()
monkeypatch.setattr(prompt_module, "load_skills", lambda *a, **kwargs: list(state["skills"]))
prompt_module._reset_skills_system_prompt_cache_state()
try:
prompt_module.warm_enabled_skills_cache()
@@ -128,7 +117,7 @@ def test_clear_cache_does_not_spawn_parallel_refresh_workers(monkeypatch, tmp_pa
enabled=True,
)
def fake_load_skills(enabled_only=True):
def fake_load_skills(*a, **kwargs):
nonlocal active_loads, max_active_loads, call_count
with lock:
active_loads += 1
@@ -165,7 +154,7 @@ def test_clear_cache_does_not_spawn_parallel_refresh_workers(monkeypatch, tmp_pa
def test_warm_enabled_skills_cache_logs_on_timeout(monkeypatch, caplog):
event = threading.Event()
monkeypatch.setattr(prompt_module, "_ensure_enabled_skills_cache", lambda: event)
monkeypatch.setattr(prompt_module, "_ensure_enabled_skills_cache", lambda *a, **k: event)
with caplog.at_level("WARNING"):
warmed = prompt_module.warm_enabled_skills_cache(timeout_seconds=0.01)