fix: improve cross-device evidence and exploration lifecycle

Align Responses cache affinity across execution and summary calls, preserve required checks and evidence uncertainty, and allow safe deletion of inactive explorations while retaining independent reruns.

Validation: backend suite passed except intentional prompt golden drift, then all 17 transcript tests passed after snapshot refresh; 268 frontend tests and production build passed; Ruff, quality ratchet and core type checks passed.
This commit is contained in:
chenmingxuan committed 2026-10-17 19:43:20 +08:00
1 parent f1bb5d02aa
commit 2328a4df0c
32 files changed
+748 -79

No files matched your search

@@ -93,6 +93,57 @@ async def test_summary_rules_and_goal_prefix_stay_identical_across_steps(mock_co
assert "Step 2 Physical Action" in str(blocks2)
@pytest.mark.asyncio
@pytest.mark.parametrize("duplicate", [True, False])
async def test_summary_keeps_ui_once_without_losing_controller_errors(mock_context, duplicate):
summarizer = VisualStepSummarizer(mock_context)
summarizer._llm = Mock(
ainvoke=AsyncMock(return_value=AIMessage(content="我观察到当前设置,状态变化仍待确认。"))
)
ui = 'Window Brave\n[1] Text "未保存"\n[2] Button "重试"'
outcome = "控制器错误: input rejected\n" + (ui if duplicate else "different earlier tree")
outcome += "\n恢复状态: unknown"
summarizer.dispatch(
step_number=1,
action_name="plug_click",
action_args={},
pre_img_bytes=b"before",
post_img_bytes=b"after",
exec_outcome=outcome,
post_ui_elements_text=ui,
observation_settled=False,
)
await summarizer.flush()
_, blocks = _user_role_prompt(summarizer._llm.ainvoke.call_args.args[0])
text = "\n".join(b.get("text", "") for b in blocks)
assert text.count(ui) == 1
assert "控制器错误: input rejected" in text and "恢复状态: unknown" in text
assert "screen still changing" in text
assert "AFTER ACTION UI ELEMENTS" in text
if not duplicate:
assert "different earlier tree" in text
@pytest.mark.asyncio
async def test_receipt_summary_retains_tree_even_when_duplicate_is_removed_for_model(mock_context):
summarizer = VisualStepSummarizer(mock_context)
summarizer._llm = Mock(ainvoke=AsyncMock())
ui = 'Window Brave\n[1] Text "未保存"'
summarizer.dispatch(
step_number=1,
action_name="plug_get_window_state",
action_args={},
pre_img_bytes=b"same",
post_img_bytes=b"same",
exec_outcome="来源: PC\n" + ui,
post_ui_elements_text=ui,
receipt_only=True,
)
await summarizer.flush()
summarizer._llm.ainvoke.assert_not_awaited()
assert ui in summarizer.get_summary(1)
@pytest.mark.asyncio
async def test_summarizer_dispatch_and_caching(mock_context):
"""Verify non-blocking dispatch and summary generation."""
@@ -54,8 +54,8 @@ from artemis.memory.transcript import (
# SHA-256 snapshots of the legacy system message with the fixed inputs below.
# Update these when an intentional template change alters the rendered prompt.
GOLDEN_EMPTY_PLAN = "d715128625911aec46c83138e15aabfa967b385ab4c878509fe36ab4304fae2a"
GOLDEN_SENTINEL_PLAN = "e2cf4ede75ffd9e283159f70dcd5c94a169c18e9f6dd4d1e7aacf460e956eaf3"
GOLDEN_EMPTY_PLAN = "58bf034527b2d782f7746d8e379cb0decc44a05dfa18cb73153d49cd8265002b"
GOLDEN_SENTINEL_PLAN = "b328052390a0a815c096b8ec4c5286421be487e7b6cd3bec55d6bc62a1f2518f"
SCREENSHOT_B64 = base64.b64encode(b"fake-jpeg-bytes").decode("utf-8")