103 lines
4.3 KiB
Python
103 lines
4.3 KiB
Python
"""Bounded, source-labelled end-of-run evidence from the existing step ledger."""
|
|
|
|
import base64
|
|
from pathlib import Path
|
|
import re
|
|
|
|
from artemis.data_engine.step_evidence import step_image_name
|
|
|
|
|
|
def final_evidence(engine, *, max_sources: int = 8) -> list[dict]:
|
|
"""Attach the last two independently captured frames per resource.
|
|
|
|
These are historical captures, never advertised as live device state.
|
|
Equal hashes retain both capture labels but share one image block. No
|
|
screenshots from another resource are substituted for missing evidence.
|
|
"""
|
|
if engine is None:
|
|
return []
|
|
steps = engine.storage.get_steps(engine.current_session_id) or []
|
|
sources = {}
|
|
for step in steps:
|
|
result = step.last_execution_result
|
|
execution = result.get("execution") if isinstance(result, dict) else None
|
|
actions = execution if isinstance(execution, list) else step.action_taken
|
|
actions = actions if isinstance(actions, list) else [actions]
|
|
for action in actions:
|
|
if not isinstance(action, dict):
|
|
continue
|
|
plugin = action.get("plugin_id")
|
|
source = action.get("source") or {}
|
|
if plugin:
|
|
identity = (plugin, source.get("kind", "other"), source.get("target_id", ""))
|
|
name = action.get("post_image_name")
|
|
if len(actions) == 1:
|
|
name = name or step_image_name(step, "post")
|
|
pre = action.get("pre_image_name") if action.get("pre_image_valid") else None
|
|
else:
|
|
# A pure helper/report's decision frame is not a fresh post-action observation.
|
|
if str(action.get("action", "")).startswith("plug_"):
|
|
continue
|
|
identity = ("device", "android", "primary")
|
|
name, pre = step.post_image_name, step.pre_image_name
|
|
if not name and not plugin:
|
|
continue
|
|
label = f"Step {step.step_number} post-action; {identity}; {source.get('label', '')}"
|
|
label += f"; captured_at={source.get('captured_at') or step.timestamp}"
|
|
if pre and pre == name:
|
|
label += (
|
|
"; fresh post-capture identical to pre-capture (not proof of success by itself)"
|
|
)
|
|
frames = sources.pop(identity, [])
|
|
sources[identity] = [*frames[-1:], (name, label)]
|
|
selected = list(sources.values())[-max_sources:]
|
|
blocks = []
|
|
attached = set()
|
|
for frames in selected:
|
|
for name, label in frames[-2:]:
|
|
if not name:
|
|
blocks.append(
|
|
{
|
|
"type": "text",
|
|
"text": f"Recorded evidence: {label}; no post-action image. Earlier captures do not prove the state after this call.",
|
|
}
|
|
)
|
|
continue
|
|
if not isinstance(name, str) or not re.fullmatch(r"[0-9a-f]{64}", name):
|
|
continue
|
|
path = Path(engine.get_image_path(name))
|
|
blocks.append({"type": "text", "text": f"Recorded evidence: {label}; image={name}"})
|
|
if name in attached:
|
|
blocks.append(
|
|
{"type": "text", "text": "Same image as the preceding capture with this hash."}
|
|
)
|
|
continue
|
|
try:
|
|
image = path.read_bytes()
|
|
except OSError:
|
|
blocks.append(
|
|
{
|
|
"type": "text",
|
|
"text": "Recorded image unavailable; do not infer its contents.",
|
|
}
|
|
)
|
|
continue
|
|
mime = "image/png" if image.startswith(b"\x89PNG") else "image/jpeg"
|
|
if image.startswith(b"RIFF") and image[8:12] == b"WEBP":
|
|
mime = "image/webp"
|
|
blocks.append(
|
|
{
|
|
"type": "image_url",
|
|
"image_url": {"url": f"data:{mime};base64,{base64.b64encode(image).decode()}"},
|
|
}
|
|
)
|
|
attached.add(name)
|
|
if len(sources) > max_sources:
|
|
blocks.append(
|
|
{
|
|
"type": "text",
|
|
"text": "Additional resources omitted from this bounded preview; inspect them with get_step_screenshot before judging.",
|
|
}
|
|
)
|
|
return blocks
|