"""Screenshot pruning: keep first + previous + current, collapse the rest. Vision images are ~1.3-2k tokens each and the model re-reads every one on every turn; pruning to 3 anchors (first/previous/current) and stubbing the rest cuts the re-prefilled image tokens without losing the agent's memory (URL + ReportProgress text stay). These pin the keep-set, the in-place mutation, and tool_result safety. """ from backend.apps.agents.browser.browser_history import ( prune_old_screenshots, OMITTED_SCREENSHOT_STUB, ) def p_img(tag): return {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": tag}} def p_shot_turn(tag, url): # mirrors _format_tool_result for BrowserScreenshot: [image, text(url)] return {"role": "user", "content": [{ "type": "tool_result", "tool_use_id": f"t_{tag}", "content": [p_img(tag), {"type": "text", "text": f"Screenshot captured. URL: {url}"}], }]} def p_count_images(messages): n = 0 for m in messages: for b in m.get("content", []): if isinstance(b, dict): if b.get("type") == "image": n += 1 elif b.get("type") == "tool_result": n += sum(1 for x in b.get("content", []) if isinstance(x, dict) and x.get("type") == "image") return n def test_keeps_first_and_last_two_collapses_middle(): msgs = [p_shot_turn(str(i), f"https://site/{i}") for i in range(5)] # images 0..4 collapsed = prune_old_screenshots(msgs) assert collapsed == 2 # 5 images - (first + last 2) = 2 stubbed assert p_count_images(msgs) == 3 # image tags that survive are 0 (first), 3 and 4 (last two) surviving = [b["source"]["data"] for m in msgs for tr in m["content"] for b in tr["content"] if b.get("type") == "image"] assert surviving == ["0", "3", "4"] def test_three_or_fewer_is_a_noop(): msgs = [p_shot_turn(str(i), f"u{i}") for i in range(3)] assert prune_old_screenshots(msgs) == 0 assert p_count_images(msgs) == 3 def test_stub_preserves_the_url_text_block(): msgs = [p_shot_turn(str(i), f"https://site/{i}") for i in range(4)] prune_old_screenshots(msgs) # the collapsed shot (#1) keeps its "URL:" text, only the image became a stub collapsed_tr = msgs[1]["content"][0]["content"] assert any(b.get("text") == OMITTED_SCREENSHOT_STUB for b in collapsed_tr) assert any("URL: https://site/1" in b.get("text", "") for b in collapsed_tr) def test_handles_direct_image_blocks_too(): msgs = [ {"role": "user", "content": [p_img("a"), {"type": "text", "text": "hi"}]}, {"role": "user", "content": [p_img("b")]}, {"role": "user", "content": [p_img("c")]}, {"role": "user", "content": [p_img("d")]}, ] collapsed = prune_old_screenshots(msgs) assert collapsed == 1 # keep a (first), c+d (last two); stub b assert msgs[1]["content"][0] == {"type": "text", "text": OMITTED_SCREENSHOT_STUB} def test_keep_recent_is_tunable(): msgs = [p_shot_turn(str(i), f"u{i}") for i in range(6)] prune_old_screenshots(msgs, keep_first=False, keep_recent=1) # only the most recent survives assert p_count_images(msgs) == 1 def p_tool_use_msg(tu_id, name): return {"role": "assistant", "content": [ {"type": "tool_use", "id": tu_id, "name": name, "input": {}}, ]} def p_tool_result_msg(tu_id, text): return {"role": "user", "content": [ {"type": "tool_result", "tool_use_id": tu_id, "content": [{"type": "text", "text": text}]}, ]} def test_prune_stale_page_state_keeps_last_two_attachments(): from backend.apps.agents.browser.browser_history import ( PAGE_STATE_MARKER, prune_stale_page_state, ) msgs = [] for i in range(4): msgs.append(p_tool_use_msg(f"t{i}", "BrowserClickIndex")) msgs.append(p_tool_result_msg( f"t{i}", f"Clicked [{i}]\n\n{PAGE_STATE_MARKER}\n[1]