feat: add guided exploration and share user-started MCP backend

This commit is contained in:
chenmingxuan committed 2026-10-16 18:32:46 +08:00
1 parent f86db1ced6
commit ee256ad7ce
144 files changed
+8625 -2075

No files matched your search

+73
View File
@@ -51,6 +51,79 @@ DONE_PLAN_WITH_CHECKS = (
DONE_PLAN_NO_CHECKS = "- [x] Create alarm\n"
@pytest.fixture
def exploration_exit(tmp_path, monkeypatch):
from apps.admin_console.services.automation_store import AutomationStore
from artemis.config.exploration import ExplorationConfig
from artemis.exploration.store import ExplorationStore
from artemis.runtime import task_control
repo = ExplorationStore(AutomationStore(tmp_path / "history.db"))
session = repo.create("逐步探索,听用户指导", ExplorationConfig(), {}, "create")
run = repo.reserve_run(session["id"], "run", kind="exploration", goal="探索", task_config={})
repo.set_run_state(run["id"], "running")
monkeypatch.setattr("artemis.exploration.runtime.explorations", repo)
monkeypatch.setattr("artemis.exploration.store.explorations", repo)
monkeypatch.setattr(task_control, "TRACES_PATH", tmp_path)
ctx = _make_ctx(tmp_path)
ctx.exploration_id = session["id"]
ctx.exploration_run_id = run["id"]
ctx.exploration_guidance_count = 0
ctx.on_execution_review = AsyncMock()
ctx.on_execution_resume = AsyncMock()
_write_plan(tmp_path, DONE_PLAN_NO_CHECKS)
return ctx, repo, session, run
@pytest.mark.asyncio
async def test_exploration_question_prevents_completed_plan_from_starting_final_review(
exploration_exit,
):
ctx, repo, session, run = exploration_exit
repo.ask(run["id"], "下一步做什么?", "返回主页", "ask")
with patch("artemis.graph.graph.run_final_check", new=AsyncMock()) as reviewer:
assert convergence_gate(_make_state(), ctx) == "continue"
result = await exit_settlement_node(_make_state(), ctx)
assert result == {"exit_settlement_route": "continue"}
reviewer.assert_not_called()
assert repo.get(session["id"])["pending_question"]
@pytest.mark.asyncio
async def test_exploration_late_return_home_guidance_invalidates_final_review(exploration_exit):
ctx, repo, session, run = exploration_exit
async def review(*args, **kwargs):
repo.add_message(
session["id"], "先回主页,然后结束", "home", metadata={"run_id": run["id"]}
)
return CheckReport(verdicts=[], unmet_subgoals=[])
with patch("artemis.graph.graph.run_final_check", new=review):
result = await exit_settlement_node(_make_state(), ctx)
assert result == {"exit_settlement_route": "continue"}
assert repo.run(run["id"])["status"] == "running"
assert repo.run(run["id"])["user_guidance_count"] == 1
assert not (ctx.data_engine.base_dir / "run_outcome.json").exists()
ctx.on_execution_review.assert_awaited_once()
ctx.on_execution_resume.assert_awaited_once()
@pytest.mark.asyncio
async def test_review_stops_capture_before_waiting_and_seals_exploration(exploration_exit):
ctx, repo, session, run = exploration_exit
async def review(*args, **kwargs):
ctx.on_execution_review.assert_awaited_once()
return CheckReport(verdicts=[], unmet_subgoals=[])
with patch("artemis.graph.graph.run_final_check", new=review):
result = await exit_settlement_node(_make_state(), ctx)
assert result["exit_settlement_route"] == "end"
assert repo.get(session["id"])["status"] == "finalizing"
ctx.on_execution_resume.assert_not_called()
def _make_ctx(tmp_path, **setup_kwargs):
setup_kwargs.setdefault("disable_checker", False)
ctx = MagicMock(spec=ArtemisContext)