feat(tasks): add skill-templated task graph execution

This commit is contained in:
2026-06-23 10:22:58 +08:00
parent 6843d89b2c
commit 53b13e8eac
53 changed files with 4773 additions and 756 deletions

View File

@ -1,8 +1,10 @@
import asyncio
import json
from contextlib import suppress
from typing import Any
from beaver.engine import AgentLoop, AgentRunResult, EngineLoader
from beaver.engine import loop as loop_module
def _run_result(run_id: str, output_text: str) -> AgentRunResult:
@ -45,3 +47,37 @@ def test_running_loop_handles_reentrant_submit_direct(tmp_path) -> None:
assert calls == ["outer", "inner"]
asyncio.run(run_case())
def test_web_search_loop_guard_stops_after_repeated_low_quality_results() -> None:
guard = loop_module._WebSearchLoopGuard()
low_quality = json.dumps(
{
"success": True,
"query": "weather beijing",
"quality": "low",
"results": [{"title": "Example", "url": "https://example.com", "snippet": ""}],
}
)
assert guard.observe_result("web_search", low_quality) is None
assert guard.observe_result("web_search", low_quality) is None
guidance = guard.observe_result("web_search", low_quality)
assert guidance is not None
assert guidance["finish_reason"] == "web_search_low_quality_budget"
assert "weather beijing" in guidance["message"]
def test_web_search_loop_guard_resets_after_useful_result() -> None:
guard = loop_module._WebSearchLoopGuard()
low_quality = json.dumps({"success": True, "query": "weather", "quality": "low", "results": []})
useful = json.dumps({"success": True, "query": "weather", "quality": "high", "results": []})
assert guard.observe_result("web_search", low_quality) is None
assert guard.observe_result("web_search", useful) is None
assert guard.observe_result("web_search", low_quality) is None
assert guard.observe_result("web_search", low_quality) is None
assert guard.observe_result("web_search", low_quality) is not None