Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
"""Retire stale vision_analyze / screenshot tool images inside the protected tail.
|
||||
|
||||
Issue #92699: native embeds sitting in protect_last_n ride every later
|
||||
request, compression savings stay tiny, and anti-thrash disables further
|
||||
compaction. Pass 2 of ``_prune_old_tool_results`` only strips images
|
||||
*outside* the tail; this suite pins the extra keep-newest pass.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from agent.context_compressor import (
|
||||
ContextCompressor,
|
||||
_MAX_KEEP_TOOL_IMAGES,
|
||||
_content_has_images,
|
||||
_tool_content_has_images,
|
||||
)
|
||||
|
||||
|
||||
def _compressor() -> ContextCompressor:
|
||||
c = ContextCompressor.__new__(ContextCompressor)
|
||||
c.quiet_mode = True
|
||||
return c
|
||||
|
||||
|
||||
def _image_tool(i: int, *, blob: str = "A" * 400) -> list[dict]:
|
||||
return [
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": None,
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": f"call_{i}",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "vision_analyze",
|
||||
"arguments": f'{{"image_url":"shot{i}.png"}}',
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"tool_call_id": f"call_{i}",
|
||||
"content": [
|
||||
{"type": "text", "text": f"Image attached natively shot {i}"},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {"url": f"data:image/png;base64,{blob}{i}"},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _image_bearing_tools(messages: list[dict]) -> list[dict]:
|
||||
return [
|
||||
m
|
||||
for m in messages
|
||||
if m.get("role") == "tool" and _tool_content_has_images(m.get("content"))
|
||||
]
|
||||
|
||||
|
||||
class TestRetireStaleToolImagesInProtectedTail:
|
||||
def test_keep_newest_images_inside_a_large_protect_window(self):
|
||||
"""Five native embeds all fit in protect_last_n=20; only newest N stay."""
|
||||
msgs: list[dict] = [{"role": "user", "content": "screenshot QA"}]
|
||||
for i in range(5):
|
||||
msgs.extend(_image_tool(i))
|
||||
msgs.append({"role": "user", "content": "compare the last few"})
|
||||
|
||||
out, pruned = _compressor()._prune_old_tool_results(
|
||||
msgs, protect_tail_count=20,
|
||||
)
|
||||
assert pruned >= 5 - _MAX_KEEP_TOOL_IMAGES
|
||||
kept = _image_bearing_tools(out)
|
||||
assert len(kept) == _MAX_KEEP_TOOL_IMAGES
|
||||
kept_ids = [m["tool_call_id"] for m in kept]
|
||||
assert kept_ids == [f"call_{i}" for i in range(5 - _MAX_KEEP_TOOL_IMAGES, 5)]
|
||||
|
||||
def test_multimodal_envelopes_outside_keep_window_become_text(self):
|
||||
msgs: list[dict] = [{"role": "user", "content": "go"}]
|
||||
for i in range(_MAX_KEEP_TOOL_IMAGES + 1):
|
||||
msgs.append({
|
||||
"role": "assistant",
|
||||
"content": None,
|
||||
"tool_calls": [{
|
||||
"id": f"env_{i}",
|
||||
"type": "function",
|
||||
"function": {"name": "vision_analyze", "arguments": "{}"},
|
||||
}],
|
||||
})
|
||||
msgs.append({
|
||||
"role": "tool",
|
||||
"tool_call_id": f"env_{i}",
|
||||
"content": {
|
||||
"_multimodal": True,
|
||||
"content": [
|
||||
{"type": "text", "text": f"shot {i}"},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {"url": f"data:image/png;base64,XYZ{i}"},
|
||||
},
|
||||
],
|
||||
"text_summary": f"native shot {i}",
|
||||
},
|
||||
})
|
||||
|
||||
out, _ = _compressor()._prune_old_tool_results(
|
||||
msgs, protect_tail_count=20,
|
||||
)
|
||||
oldest = next(m for m in out if m.get("tool_call_id") == "env_0")
|
||||
assert isinstance(oldest["content"], str)
|
||||
assert "screenshot removed" in oldest["content"]
|
||||
assert "native shot 0" in oldest["content"]
|
||||
newest = next(
|
||||
m for m in out
|
||||
if m.get("tool_call_id") == f"env_{_MAX_KEEP_TOOL_IMAGES}"
|
||||
)
|
||||
assert _tool_content_has_images(newest["content"])
|
||||
|
||||
def test_user_uploads_are_not_retired(self):
|
||||
user_img = {
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "look"},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {"url": "data:image/png;base64,USERUPLOAD"},
|
||||
},
|
||||
],
|
||||
}
|
||||
msgs = [user_img]
|
||||
for i in range(_MAX_KEEP_TOOL_IMAGES + 2):
|
||||
msgs.extend(_image_tool(i))
|
||||
|
||||
out, _ = _compressor()._prune_old_tool_results(
|
||||
msgs, protect_tail_count=20,
|
||||
)
|
||||
assert _content_has_images(out[0]["content"])
|
||||
assert out[0]["content"][1]["image_url"]["url"].endswith("USERUPLOAD")
|
||||
|
||||
|
||||
class TestSharedImageStripHelper:
|
||||
"""One strip policy for pass 3.5 and the demote pass (#92783 follow-up)."""
|
||||
|
||||
def test_demote_pass_drops_stale_api_content_on_image_strip(self):
|
||||
"""Pass 2's image demotion must drop the api_content sidecar.
|
||||
|
||||
Before the shared _strip_images_from_tool_msg helper, only pass 3.5
|
||||
dropped the sidecar; the demote branches left it behind, letting
|
||||
replay restore pre-strip bytes.
|
||||
"""
|
||||
from agent.context_compressor import _strip_images_from_tool_msg
|
||||
|
||||
msg = {
|
||||
"role": "tool",
|
||||
"tool_call_id": "c1",
|
||||
"api_content": "stale exact-wire copy with image bytes",
|
||||
"content": [
|
||||
{"type": "text", "text": "shot"},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {"url": "data:image/png;base64," + "A" * 400},
|
||||
},
|
||||
],
|
||||
}
|
||||
new_msg = _strip_images_from_tool_msg(msg)
|
||||
assert new_msg is not None
|
||||
assert "api_content" not in new_msg
|
||||
# Input untouched (copy-on-write).
|
||||
assert "api_content" in msg
|
||||
assert _content_has_images(msg["content"])
|
||||
assert not _content_has_images(new_msg["content"])
|
||||
|
||||
def test_envelope_collapses_to_summary_string(self):
|
||||
from agent.context_compressor import _strip_images_from_tool_msg
|
||||
|
||||
msg = {
|
||||
"role": "tool",
|
||||
"tool_call_id": "c2",
|
||||
"api_content": "stale",
|
||||
"content": {
|
||||
"_multimodal": True,
|
||||
"content": [
|
||||
{"type": "text", "text": "s"},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {"url": "data:image/png;base64,XYZ"},
|
||||
},
|
||||
],
|
||||
"text_summary": "native shot",
|
||||
},
|
||||
}
|
||||
new_msg = _strip_images_from_tool_msg(msg)
|
||||
assert new_msg is not None
|
||||
assert isinstance(new_msg["content"], str)
|
||||
assert "screenshot removed" in new_msg["content"]
|
||||
assert "native shot" in new_msg["content"]
|
||||
assert "api_content" not in new_msg
|
||||
|
||||
def test_imageless_content_returns_none(self):
|
||||
from agent.context_compressor import _strip_images_from_tool_msg
|
||||
|
||||
msg = {"role": "tool", "tool_call_id": "c3", "content": "plain text"}
|
||||
assert _strip_images_from_tool_msg(msg) is None
|
||||
msg2 = {
|
||||
"role": "tool",
|
||||
"tool_call_id": "c4",
|
||||
"content": [{"type": "text", "text": "no images here"}],
|
||||
}
|
||||
assert _strip_images_from_tool_msg(msg2) is None
|
||||
Reference in New Issue
Block a user