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:
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")
|
||||
|
||||
|
||||
Reference in new issue
Block a user