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

+21
View File
@@ -730,6 +730,27 @@ async def test_final_review_reports_no_guidance_explicitly():
assert "# User guidance received during the run\n(none)\n" in text
@pytest.mark.asyncio
async def test_exploration_review_sees_conversation_and_can_read_earlier_messages():
ctx = _mock_ctx()
ctx.exploration_id = "exploration"
conversation = HumanMessage(content="用户已确认系统设置;示范不代表独立复跑")
history = _text_tool("read_exploration_messages", "历史回复")
review = AsyncMock(return_value=CheckReport())
with (
patch("artemis.agents.checker.checker._run_check_loop", review),
patch(
"artemis.agents.checker.checker._capture_final_screen",
AsyncMock(return_value=(None, "elements")),
),
patch("artemis.exploration.runtime.conversation_message", return_value=conversation),
patch("artemis.exploration.runtime.history_tool", return_value=history),
):
await run_final_check(ctx, goal="探索设置", plan_text="", ledger=[], check_items=[])
assert conversation in review.call_args.args[1]
assert history in review.call_args.args[2]
# --- tool results: status is structural, never sniffed from the words -----------------
@@ -29,6 +29,13 @@ async def test_flash_reuses_checker_with_original_prompt_and_final_report(
check = AsyncMock(return_value=CheckReport(test_result=result))
monkeypatch.setattr(flash, "run_final_check", check)
ctx = context(tmp_path)
ctx.on_execution_review = AsyncMock()
async def review_after_capture_stops(*args, **kwargs):
ctx.on_execution_review.assert_awaited_once()
return CheckReport(test_result=result)
check.side_effect = review_after_capture_stops
execution = {"status": "completed", "explanation": "操作已执行"}
data = await flash.settle_flash_outcome(ctx, "打开设置,预期看到应用入口", execution)
assert check.call_count == 1
@@ -93,6 +93,57 @@ async def test_prose_retry_keeps_context_and_recovers_once(operator, monkeypatch
assert any(isinstance(m, SystemMessage) and "No tool call" in m.content for m in messages)
@pytest.mark.asyncio
@pytest.mark.parametrize(
"scenario", ["complete", "pending", "unfinished", "unchanged", "failed", "continuous", "pro"]
)
async def test_completed_exploration_plan_does_not_force_an_extra_action(
operator, monkeypatch, scenario
):
node, state = operator
node.ctx.exploration_id = None if scenario == "pro" else "exploration"
node.ctx.data_engine = None
plan = {"text": "- [ ] Return home"}
if scenario == "unchanged":
plan["text"] = "- [x] Return home"
node._read_task_plan_text = lambda: plan["text"]
monkeypatch.setattr("artemis.exploration.runtime.publish_response", Mock())
monkeypatch.setattr(
"artemis.exploration.runtime.needs_exploration_turn", lambda ctx: scenario == "pending"
)
async def save_note(**kwargs):
plan["text"] = "- [ ] Return home" if scenario == "unfinished" else "- [x] Return home"
if scenario == "continuous":
plan["text"] += " [Loop:continuous]"
return ToolMessage(
content="Saved plan",
tool_call_id="plan",
status="error" if scenario == "failed" else "success",
)
monkeypatch.setattr("artemis.agents.operator.operator.invoke_tool_with_injection", save_note)
response = AIMessage(
content="Confirmed home; recorded evidence.",
tool_calls=[{"name": "save_note", "args": {}, "id": "plan"}],
)
invoke = AsyncMock(side_effect=[response, click()])
monkeypatch.setattr("artemis.agents.operator.operator.acomplete", invoke)
messages = []
result = await node._invoke_llm_loop(
Mock(), messages, [SimpleNamespace(name="save_note")], [], state
)
if scenario == "complete":
assert invoke.await_count == 1
assert result[0] is None
assert result[1] == "Confirmed home; recorded evidence."
assert result[3] is False
else:
assert invoke.await_count == 2
assert result[0]
assert any(isinstance(m, ToolMessage) and m.tool_call_id == "plan" for m in messages)
@pytest.mark.asyncio
async def test_invalid_target_can_be_corrected_before_limit(operator, monkeypatch):
result, messages, invoke = await run_loop(operator, monkeypatch, [click("14"), click("13")])