feat(skill-evolution): register SkillReviewMiddleware in lead agent chain

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
xuhdandClaude Sonnet 4.6 committed 2026-06-05 13:32:19 +08:00
1 parent 0e7a1de4db
commit c645142f82
2 files changed
+49

No files matched your search

@@ -321,6 +321,13 @@ def _build_middlewares(
max_concurrent_subagents = cfg.get("max_concurrent_subagents", 3)
middlewares.append(SubagentLimitMiddleware(max_concurrent=max_concurrent_subagents))
# SkillReviewMiddleware — trigger background skill review after each turn.
# Gated by skill_evolution.enabled + skill_evolution.review_worker.enabled.
if resolved_app_config.skill_evolution.enabled and resolved_app_config.skill_evolution.review_worker.enabled:
from deerflow.agents.middlewares.skill_review_middleware import SkillReviewMiddleware
middlewares.append(SkillReviewMiddleware())
# LoopDetectionMiddleware — detect and break repetitive tool call loops
middlewares.append(LoopDetectionMiddleware())
@@ -68,3 +68,45 @@ async def test_after_agent_exception_is_swallowed():
with patch("deerflow.agents.middlewares.skill_review_middleware.get_app_config", side_effect=RuntimeError("boom")):
# Must not raise
await middleware.after_agent(state, runtime)
def test_skill_review_middleware_in_chain_when_enabled():
"""SkillReviewMiddleware appears in middleware chain before LoopDetectionMiddleware when enabled."""
from unittest.mock import patch, MagicMock
from deerflow.agents.lead_agent.agent import _build_middlewares
from deerflow.agents.middlewares.skill_review_middleware import SkillReviewMiddleware
config = {
"configurable": {
"thread_id": "test",
"is_plan_mode": False,
"is_bootstrap": False,
"is_scheduled_run": False,
}
}
app_cfg = MagicMock()
app_cfg.skill_evolution.enabled = True
app_cfg.skill_evolution.review_worker.enabled = True
app_cfg.token_usage.enabled = False
app_cfg.tool_search.enabled = False
app_cfg.skill_stop.enabled = False
app_cfg.summarization.enabled = False
app_cfg.memory = MagicMock()
app_cfg.guardrails.enabled = False
app_cfg.title = MagicMock()
app_cfg.title.model_name = None
with patch("deerflow.agents.lead_agent.agent.get_app_config", return_value=app_cfg):
with patch("deerflow.agents.lead_agent.agent.build_lead_runtime_middlewares", return_value=[]):
with patch("deerflow.agents.lead_agent.agent._create_summarization_middleware", return_value=None):
with patch("deerflow.agents.lead_agent.agent._create_todo_list_middleware", return_value=None):
with patch("deerflow.models.vision.vision_enabled", return_value=False):
middlewares = _build_middlewares(config, model_name=None, app_config=app_cfg)
type_names = [type(m).__name__ for m in middlewares]
assert "SkillReviewMiddleware" in type_names, f"SkillReviewMiddleware not in chain: {type_names}"
review_idx = type_names.index("SkillReviewMiddleware")
loop_idx = type_names.index("LoopDetectionMiddleware")
assert review_idx < loop_idx, f"SkillReviewMiddleware ({review_idx}) must be before LoopDetectionMiddleware ({loop_idx})"