feat: add guided exploration and share user-started MCP backend
This commit is contained in:
1 parent
f86db1ced6
commit
ee256ad7ce
144 files changed
+8625
-2075
No files matched your search
@@ -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")])
|
||||
|
||||
Reference in new issue
Block a user