Files
odysseus/tests/test_workspace_artifact_tool_floor.py
T

2299 lines
83 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from pathlib import Path
def test_unoffered_artifact_recovery_is_bounded():
from src.agent_loop import _artifact_unoffered_recovery_exhausted
assert not _artifact_unoffered_recovery_exhausted(1)
assert not _artifact_unoffered_recovery_exhausted(2)
assert _artifact_unoffered_recovery_exhausted(3)
assert _artifact_unoffered_recovery_exhausted(20)
def test_artifact_mutation_is_not_counted_as_post_correction_verification():
from src.agent_loop import _artifact_calls_are_verification_only
from src.tool_types import ToolBlock
mutation = ToolBlock("write_file", "/workspace/output.html\n<html></html>")
verification = ToolBlock(
"private_browser",
'{"action":"open","url":"file:///workspace/output.html"}',
)
assert not _artifact_calls_are_verification_only([])
assert not _artifact_calls_are_verification_only([mutation])
assert not _artifact_calls_are_verification_only([mutation, verification])
assert _artifact_calls_are_verification_only([verification])
def test_post_convergence_artifact_repair_is_bounded_after_success():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert "_artifact_finish_convergence_sent" in source
assert "_artifact_finish_post_correction_mutation_seen = True" in source
assert "its automatic preview is terminal" in source
def test_consumed_post_correction_verification_closes_tool_surface():
from src.agent_loop import _post_correction_verification_available
assert _post_correction_verification_available(
correction_seen=True, tool_used=False, mutation_seen=False
)
assert not _post_correction_verification_available(
correction_seen=True, tool_used=True, mutation_seen=False
)
assert not _post_correction_verification_available(
correction_seen=True, tool_used=False, mutation_seen=True
)
assert not _post_correction_verification_available(
correction_seen=False, tool_used=False, mutation_seen=False
)
def test_workspace_artifact_floor_is_narrow_and_includes_writers():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert "_artifact_creation_requested" in source
assert '"python", "write_file", "read_file"' in source
assert "(workspace or _native_artifact_runtime)" in source
assert "_artifact_mutation_surface_for_missing" in source
assert "Do not use `python` until every text/table artifact has been written" in source
assert '"write_file",' in source
assert '"python",' in source
assert '"inspect_media",' in source
assert 'not path.startswith("/workspace/fixtures/")' in source
assert "写在|输出|放进" in source
assert "native terminal sandbox re-enabled workspace tools" in source
assert "set(tool_policy.disabled_tools) - _native_reenabled_tools" in source
def test_native_declared_artifact_exposes_writer_without_creation_verb():
"""Native benchmark contracts must not depend on prompt wording.
Video/MME tasks commonly say "list ... in /workspace/answer.txt" rather
than "write" or "create". The completion contract is authoritative and
must still make write_file available to the model.
"""
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert "_declared_native_artifacts" in source
assert "bool(_declared_native_artifacts)" in source
assert "_native_completion.get(\"required_artifacts\")" in source
def test_artifact_creation_flag_is_initialized_before_route_builder():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
flag = "_artifact_creation_requested = False"
route = " def _route_relevant_tools(candidate_model: str):"
assert source.index(flag) < source.index(route)
def test_html_artifacts_keep_native_browser_for_render_verification():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert 'Path(path).suffix.casefold() in {".html", ".htm"}' in source
assert "HTML artifact requires native private_browser verification" in source
assert 'if "private_browser" not in _hard_blocked_tools:' in source
assert "or _html_artifact_requested" in source
assert source.count("or _html_artifact_requested") >= 3
def test_compact_html_artifacts_keep_native_browser_schema():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{"private_browser", "python", "write_file", "read_file"},
"Create /workspace/output.html from /workspace/fixtures/config.json",
set(),
)
assert "private_browser" in selected
def test_completed_html_mutation_preserves_browser_verification_only():
from src.agent_loop import (
_artifact_browser_render_required,
_completed_artifact_acquisition_tools_to_remove,
)
rendered = _completed_artifact_acquisition_tools_to_remove(browser_render=True)
ordinary = _completed_artifact_acquisition_tools_to_remove(browser_render=False)
assert rendered == {"pdf_extract", "web_fetch", "web_search"}
assert ordinary == {
"pdf_extract", "private_browser", "web_fetch", "web_search",
}
assert _artifact_browser_render_required("Create a PNG", ["/workspace/output.html"])
assert not _artifact_browser_render_required("Create a PNG", [])
def test_compact_html_artifact_ignores_false_email_and_cookbook_domains():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{
"python", "write_file", "read_file", "private_browser",
"list_emails", "read_email", "bulk_email", "serve_model",
"stop_served_model", "list_served_models",
},
"Create /workspace/output.html with a Happy New Year message.",
{"email", "cookbook"},
)
assert {"python", "write_file", "read_file", "private_browser"} <= selected
assert not (
{
"list_emails", "read_email", "bulk_email", "serve_model",
"stop_served_model", "list_served_models",
}
& selected
)
def test_compact_artifact_keeps_explicit_email_and_cookbook_tools():
from src.agent_loop import _compact_native_route_tools
email_selected = _compact_native_route_tools(
{"write_file", "list_emails", "read_email", "download_attachment"},
"Read the email attachment and save /workspace/summary.txt.",
{"email", "files"},
)
cookbook_selected = _compact_native_route_tools(
{"write_file", "download_model", "serve_model", "list_served_models"},
"Download the model checkpoint and save it under /workspace/model.",
{"cookbook", "files"},
)
assert {"list_emails", "read_email", "download_attachment"} <= email_selected
assert {"serve_model", "list_served_models"} <= cookbook_selected
def test_compact_native_media_artifact_keeps_writer_when_rag_misses_it():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{"inspect_media", "transcribe_media", "apply_patch"},
"写入 /workspace/output.txt,内容来自 /workspace/fixtures/video.webm",
set(),
)
assert "write_file" in selected
def test_compact_native_transformed_media_keeps_shell_mutation_floor():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{"inspect_media", "transcribe_media", "read_file", "ls"},
(
"分析 /workspace/fixtures/video.mp4,剪辑并拼接后导出 "
"/workspace/band_cut.mp4,同时写入 /workspace/timestamps.txt。"
),
{"files"},
)
assert {"inspect_media", "write_file", "bash"} <= selected
def test_compact_local_report_artifact_keeps_shell_for_workspace_generator():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{"python", "write_file", "read_file", "glob"},
(
"Run /workspace/process_report.py to analyze /workspace/fixtures/data.csv "
"and generate the report and chart at /workspace/report.csv and "
"/workspace/chart.png."
),
{"web", "documents"},
)
assert "bash" in selected
def test_url_report_artifact_still_hides_shell():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{"python", "write_file", "read_file", "bash", "web_search", "web_fetch"},
"Read https://example.com/report.pdf and write /workspace/summary.txt.",
{"web", "documents"},
)
assert "bash" not in selected
def test_terminal_html_artifact_queues_one_native_render_check():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert "_html_artifact_verification_required" in source
assert '"action": "open"' in source
assert "queued native HTML render verification" in source
def test_workspace_file_mutation_paths_distinguishes_helper_from_html_target():
import json
from src.agent_loop import _workspace_file_mutation_paths
from src.tool_types import ToolBlock
helper = ToolBlock("write_file", json.dumps({
"path": "/workspace/create_page.py",
"content": "print('helper')",
}))
target = ToolBlock("write_file", json.dumps({
"path": "/workspace/output.html",
"content": "<html></html>",
}))
assert _workspace_file_mutation_paths(helper) == {"/workspace/create_page.py"}
assert _workspace_file_mutation_paths(target) == {"/workspace/output.html"}
def test_workspace_file_mutation_paths_reads_canonical_native_write_file():
import json
from src.agent_loop import _workspace_file_mutation_paths
from src.tool_schemas import function_call_to_tool_block
block = function_call_to_tool_block(
"write_file",
json.dumps({
"path": "/workspace/output.html",
"content": "<!doctype html><title>artifact</title>",
}),
)
assert block is not None
assert block.content.startswith("/workspace/output.html\n")
assert _workspace_file_mutation_paths(block) == {"/workspace/output.html"}
def test_evidenced_workspace_mutation_paths_recognizes_python_required_output():
from src.agent_evidence import CompletionRequirements
from src.agent_loop import _evidenced_workspace_mutation_paths
requirements = CompletionRequirements(
required_artifacts=("/workspace/output.html",),
)
events = [{
"round": 2,
"tool": "python",
"command": "open('/workspace/output.html', 'w').write('<h1>ok</h1>')",
"output": "created",
"exit_code": 0,
}]
assert _evidenced_workspace_mutation_paths(
events,
requirements,
round_num=2,
) == {"/workspace/output.html"}
assert not _evidenced_workspace_mutation_paths(
events,
requirements,
round_num=3,
)
def test_workspace_file_mutation_paths_reads_apply_patch_targets():
from src.agent_loop import _workspace_file_mutation_paths
from src.tool_types import ToolBlock
block = ToolBlock(
"apply_patch",
"*** Begin Patch\n*** Update File: /workspace/output.html\n@@\n*** End Patch",
)
assert _workspace_file_mutation_paths(block) == {"/workspace/output.html"}
def test_missing_raster_after_svg_write_uses_native_svg_renderer():
from src.agent_loop import _svg_render_recovery_blocks
blocks = _svg_render_recovery_blocks(
["/workspace/floorplan.png"],
{"inspect_media"},
set(),
[{
"tool": "write_file",
"command": "/workspace/floorplan.svg\n<svg></svg>",
"exit_code": 0,
}],
)
assert blocks is not None
assert blocks[0].tool_type == "inspect_media"
assert '"path": "/workspace/floorplan.svg"' in blocks[0].content
assert '"output_path": "/workspace/floorplan.png"' in blocks[0].content
def test_native_terminal_sft_owner_keeps_workspace_writers():
from src.agent_loop import _strip_workspace_tools_for_sft
selected = _strip_workspace_tools_for_sft(
{"bash", "python", "read_file", "write_file"},
"sft_clawmm_eval",
{"surface": "odysseus-native", "terminal_agent": True},
)
assert selected == {"bash", "python", "read_file", "write_file"}
def test_native_terminal_runtime_bypasses_chat_only_general_no_tool_clamp():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert "_native_terminal_runtime = bool(" in source
assert "elif _ody_general_no_tool_mode and not _native_terminal_runtime:" in source
def test_pdf_extract_reuses_exact_user_supplied_source_url():
import json
from src.agent_loop import _normalize_pdf_extract_source_url
from src.tool_types import ToolBlock
exact = (
"https://openaccess.example.org/Video-MME_The_First_"
"Benchmark_in_CVPR_2025_paper.pdf"
)
malformed = exact.replace("_in_CVPR_", "_in_CVPR")
block = ToolBlock(
"pdf_extract",
json.dumps({"url": malformed, "query": "main results table"}),
)
repaired = _normalize_pdf_extract_source_url(
block, f"Please download and read this PDF:\n{exact}\nThen extract the table."
)
assert json.loads(repaired.content) == {
"url": exact,
"query": "main results table",
}
def test_pdf_extract_normalizes_local_file_url():
import json
from src.agent_loop import _normalize_pdf_extract_source_url
from src.tool_types import ToolBlock
block = ToolBlock(
"pdf_extract",
json.dumps({
"url": "file:///workspace/fixtures/paper.pdf",
"query": "Table 3 Spatial scores",
}),
)
repaired = _normalize_pdf_extract_source_url(block, "Read the local PDF")
assert json.loads(repaired.content)["url"] == "/workspace/fixtures/paper.pdf"
def test_pdf_extract_does_not_replace_with_ambiguous_unrelated_pdf():
import json
from src.agent_loop import _normalize_pdf_extract_source_url
from src.tool_types import ToolBlock
called = "https://third.example.net/report.pdf"
block = ToolBlock(
"pdf_extract", json.dumps({"url": called, "query": "results"})
)
user_text = (
"Compare https://one.example.org/a.pdf with "
"https://two.example.org/b.pdf"
)
assert _normalize_pdf_extract_source_url(block, user_text) == block
def test_pdf_extract_query_inherits_requested_technical_entities():
import json
from src.agent_loop import ToolBlock, _normalize_pdf_extract_query_entities
block = ToolBlock(
"pdf_extract",
json.dumps({
"url": "https://example.test/report.pdf",
"query": "AIME Pass@1 accuracy",
}),
)
user_text = (
"Read the PDF and extract scores for DeepSeek-R1, DeepSeek-R1-Zero, "
"Qwen3-235B-A22B, and OpenAI-o1. Save /workspace/reasoning_math.csv."
)
repaired = _normalize_pdf_extract_query_entities(block, user_text)
args = json.loads(repaired.content)
assert args["query"].startswith("AIME Pass@1 accuracy")
assert "DeepSeek-R1" in args["query"]
assert "DeepSeek-R1-Zero" in args["query"]
assert "Qwen3-235B-A22B" in args["query"]
assert "OpenAI-o1" in args["query"]
assert "reasoning_math.csv" not in args["query"]
def test_unscoped_local_pdf_inspection_uses_user_table_terms():
import json
from src.agent_loop import _normalize_local_pdf_inspection_query
from src.tool_types import ToolBlock
block = ToolBlock(
"inspect_media",
json.dumps({"path": "/workspace/fixtures/report.pdf", "pages": 4}),
)
repaired = _normalize_local_pdf_inspection_query(
block,
"Extract the Qwen3-VL-A22B and Gemini-2.5-Pro rows from the main table.",
)
args = json.loads(repaired.content)
assert args["query"] == (
"Extract the Qwen3-VL-A22B and Gemini-2.5-Pro rows from the main table."
)
def test_scoped_local_pdf_inspection_keeps_explicit_page():
import json
from src.agent_loop import _normalize_local_pdf_inspection_query
from src.tool_types import ToolBlock
block = ToolBlock(
"inspect_media",
json.dumps({"path": "/workspace/report.pdf", "page": 7}),
)
assert _normalize_local_pdf_inspection_query(block, "Find another row") == block
def test_binary_artifacts_are_not_text_synthesis_targets():
from src.agent_loop import _binary_artifact_path
assert _binary_artifact_path("/workspace/frame.png")
assert _binary_artifact_path("/workspace/clip.mp4")
assert not _binary_artifact_path("/workspace/index.html")
assert not _binary_artifact_path("/workspace/result.json")
def test_write_file_rejects_svg_markup_under_raster_extension(tmp_path):
import asyncio
import json
from src.agent_tools.filesystem_tools import WriteFileTool
from src.tool_execution import _active_workspace
token = _active_workspace.set(str(tmp_path))
try:
result = asyncio.run(WriteFileTool().execute(json.dumps({
"path": "/workspace/floorplan.png",
"content": '<svg xmlns="http://www.w3.org/2000/svg"/>',
}), {}))
finally:
_active_workspace.reset(token)
assert result["exit_code"] == 1
assert result["artifact_format_error"] is True
assert "inspect_media" in result["error"]
assert not (tmp_path / "floorplan.png").exists()
def test_explicit_local_media_paths_are_detected_without_matching_documents():
from src.agent_loop import (
_explicit_local_media_files,
_explicit_local_media_inputs,
_native_local_media_inputs,
_runtime_local_media_inputs,
)
assert _explicit_local_media_files(
"Inspect /workspace/fixtures/video.mp4 and save /workspace/clip.mp4."
) == ["/workspace/fixtures/video.mp4", "/workspace/clip.mp4"]
assert _explicit_local_media_files("Read /workspace/report.pdf") == [
"/workspace/report.pdf"
]
assert _explicit_local_media_files("Read /workspace/report.csv") == []
assert _explicit_local_media_inputs(
"Read a paper and save /workspace/chart.png"
) == []
assert _explicit_local_media_inputs(
"Watch /workspace/fixtures/video.mp4 and save /workspace/clip.mp4"
) == ["/workspace/fixtures/video.mp4"]
runtime = {
"surface": "odysseus-native",
"input_files": [
"/workspace/fixtures/implicit.mp4",
"/workspace/fixtures/notes.txt",
],
}
assert _runtime_local_media_inputs(runtime) == [
"/workspace/fixtures/implicit.mp4"
]
assert _native_local_media_inputs("watch this video", runtime) == [
"/workspace/fixtures/implicit.mp4"
]
assert _runtime_local_media_inputs({
"surface": "odysseus-native",
"input_files": ["path=/workspace/fixtures/forwarded.mp4"],
}) == ["/workspace/fixtures/forwarded.mp4"]
def test_current_market_question_keeps_web_available_after_media_identification():
from src.agent_loop import _local_media_needs_web_lookup
assert _local_media_needs_web_lookup("How much does this car sell for now?")
assert _local_media_needs_web_lookup("现在这车卖多少钱?")
assert _local_media_needs_web_lookup(
"Scan the local PDF reference list, then verify which arXiv preprints "
"have been officially accepted or published in formal conferences."
)
assert _local_media_needs_web_lookup(
"Check the cited papers' publication status as of March 19, 2026."
)
assert _local_media_needs_web_lookup(
"I identified the paper from this video; has its code been open-sourced?"
)
assert _local_media_needs_web_lookup(
"Is the implementation publicly available in a GitHub repository?"
)
assert not _local_media_needs_web_lookup("Summarize this local training video")
assert not _local_media_needs_web_lookup(
"I'm currently editing a video and need help extracting Chinese subtitles."
)
assert not _local_media_needs_web_lookup(
"I'm currently analyzing a local recording; save its transcript to a file."
)
assert not _local_media_needs_web_lookup(
"Verify that the values extracted from this local PDF match its table."
)
def test_compact_local_pdf_research_keeps_web_verification_tools():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{
"pdf_extract", "inspect_media", "read_file", "write_file",
"python", "web_search", "web_fetch",
},
"Scan /workspace/fixtures/paper.pdf and identify all arXiv preprints. "
"Verify which papers were officially accepted or published in formal "
"academic conferences, then save /workspace/publications.csv.",
{"files"},
)
assert {"pdf_extract", "web_search", "web_fetch", "write_file"} <= selected
def test_local_media_browser_render_intent_is_narrow():
from src.agent_loop import _local_media_needs_browser_render
assert _local_media_needs_browser_render(
"View /workspace/fixtures/image.png, recreate the webpage in HTML, "
"then render the generated HTML as an image and save as /workspace/output.png."
)
assert not _local_media_needs_browser_render(
"Inspect /workspace/fixtures/image.png and describe the layout."
)
assert not _local_media_needs_browser_render(
"Inspect /workspace/fixtures/video.mp4 and export a still image."
)
def test_artifact_recovery_keeps_browser_for_html_screenshot():
from src.agent_loop import (
_artifact_mutation_surface_for_missing,
_artifact_recovery_messages,
_browser_render_recovery_blocks,
)
assert "private_browser" in _artifact_mutation_surface_for_missing(
["/workspace/output.png"],
local_media_derivation=True,
browser_render=True,
)
assert "private_browser" not in _artifact_mutation_surface_for_missing(
["/workspace/output.png"],
local_media_derivation=True,
)
recovery = _artifact_recovery_messages(
[{
"role": "user",
"content": (
"Recreate the webpage in HTML, then render the generated HTML "
"as an image and save as /workspace/output.png."
),
}],
[{
"tool": "write_file",
"command": "/workspace/task_browser.html\n<html></html>",
"output": "wrote file",
"exit_code": 0,
}],
["/workspace/output.png"],
)
recovery_text = "\n".join(str(message.get("content") or "") for message in recovery)
assert "private_browser" in recovery_text
assert "file:///workspace" in recovery_text
blocks = _browser_render_recovery_blocks(
"Recreate the webpage in HTML, then render the generated HTML "
"as an image and save as /workspace/output.png.",
["/workspace/output.png"],
{"private_browser"},
set(),
[{
"tool": "write_file",
"command": "/workspace/task_browser.html\n<html></html>",
"output": "wrote file",
"exit_code": 0,
}],
)
assert blocks is not None
assert [block.tool_type for block in blocks] == [
"private_browser", "private_browser"
]
assert '"action": "screenshot"' in blocks[1].content
assert "/workspace/output.png" in blocks[1].content
def test_media_observation_is_classified_as_read_only_during_recovery():
from src.agent_loop import _workspace_inspection_tool_block
from src.tool_types import ToolBlock
assert _workspace_inspection_tool_block(ToolBlock(
"inspect_media",
'{"path":"/workspace/fixtures/video.mp4","frames":4}',
))
assert _workspace_inspection_tool_block(ToolBlock(
"transcribe_media",
'{"path":"/workspace/fixtures/video.mp4"}',
))
def test_private_browser_duplicate_guard_only_covers_read_only_observations():
from src.agent_loop import (
_read_only_repeat_limit,
_workspace_inspection_tool_block,
)
from src.tool_types import ToolBlock
opened = ToolBlock("private_browser", '{"action":"open","url":"https://example.com"}')
snapshot = ToolBlock("private_browser", '{"action":"snapshot"}')
clicked = ToolBlock("private_browser", '{"action":"click","ref":"e2"}')
assert _workspace_inspection_tool_block(opened)
assert _workspace_inspection_tool_block(snapshot)
assert not _workspace_inspection_tool_block(clicked)
assert _read_only_repeat_limit(opened) == 2
assert _read_only_repeat_limit(snapshot) == 3
assert _read_only_repeat_limit(clicked) == 0
def test_private_browser_duplicate_guard_is_bounded_and_state_sensitive():
from src.agent_loop import _redundant_read_should_block
from src.tool_types import ToolBlock
snapshot = ToolBlock("private_browser", '{"action":"snapshot"}')
history = {"count": 3, "mutation_epoch": 2, "browser_epoch": 4}
assert _redundant_read_should_block(history, snapshot, 2, 4)
assert not _redundant_read_should_block({**history, "count": 2}, snapshot, 2, 4)
assert not _redundant_read_should_block(history, snapshot, 2, 5)
def test_local_media_floor_preserves_native_vision_and_prunes_web_tools():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert '"inspect_media", "transcribe_media", "bash", "read_file", "ls"' in source
assert "创建|生成|保存|写入|写在|输出|放进|制作|截取|剪辑|拼接|导出" in source
assert "_native_local_media_inputs(_last_user, client_runtime_context)" in source
assert "_relevant_tools.difference_update(_irrelevant_web_tools)" in source
def test_compact_native_route_keeps_local_media_off_the_web_path():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{"inspect_media", "bash", "read_file", "ls", "web_search", "web_fetch"},
"容器里有以下文件:/workspace/fixtures/video.mp4",
{"web"},
)
assert selected == {
"inspect_media", "bash", "read_file", "ls", "get_workspace",
}
def test_compact_local_pdf_artifact_route_avoids_shell_pdf_probe():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{
"inspect_media", "pdf_extract", "bash", "python", "write_file",
"read_file", "ls", "web_search",
},
"Read /workspace/fixtures/paper.pdf and generate /workspace/chart.png.",
{"web", "documents"},
)
assert "pdf_extract" in selected
assert "inspect_media" in selected
assert "python" in selected
assert "write_file" in selected
assert "bash" not in selected
def test_compact_native_route_keeps_browser_for_local_html_render():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{
"inspect_media", "bash", "read_file", "ls", "private_browser",
"web_search", "web_fetch", "pdf_extract",
},
"View /workspace/fixtures/image.png, recreate it in HTML, then render "
"the generated HTML as an image and save /workspace/output.png.",
{"web"},
)
assert "inspect_media" in selected
assert "private_browser" in selected
assert "web_search" not in selected
assert "web_fetch" not in selected
assert "pdf_extract" not in selected
def test_compact_native_artifact_bundle_prunes_project_management_tools():
from src.agent_loop import _compact_native_artifact_tools
selected = _compact_native_artifact_tools(
{
"apply_patch", "bash", "edit_file", "get_workspace", "glob",
"grep", "inspect_media", "ls", "manage_bg_jobs",
"private_browser", "python", "read_file", "todowrite",
"transcribe_media", "write_file",
},
text=(
"View /workspace/fixtures/reference.png and generate "
"/workspace/output.html. Preview and adjust it."
),
artifacts=["/workspace/output.html"],
media_inputs=["/workspace/fixtures/reference.png"],
)
assert selected == {
"inspect_media", "ls", "private_browser", "python", "read_file",
"write_file",
}
def test_compact_png_artifact_keeps_browser_for_html_render_contract():
from src.agent_loop import _compact_native_artifact_tools
selected = _compact_native_artifact_tools(
{"inspect_media", "private_browser", "python", "read_file", "write_file"},
text=(
"Recreate the reference as HTML, render the generated HTML as an "
"image, and save /workspace/output.png."
),
artifacts=["/workspace/output.png"],
media_inputs=["/workspace/fixtures/reference.png"],
)
assert "private_browser" in selected
def test_compact_native_artifact_bundle_preserves_script_execution():
from src.agent_loop import _compact_native_artifact_tools
selected = _compact_native_artifact_tools(
{"bash", "manage_bg_jobs", "python", "read_file", "write_file"},
text="Run /workspace/build.py and create /workspace/output.png",
artifacts=["/workspace/output.png"],
media_inputs=[],
)
assert {"bash", "manage_bg_jobs", "python", "read_file", "write_file"} <= selected
def test_compact_native_artifact_bundle_preserves_explicit_edit_tools():
from src.agent_loop import _compact_native_artifact_tools
selected = _compact_native_artifact_tools(
{"apply_patch", "edit_file", "glob", "grep", "read_file", "write_file"},
text="Fix /workspace/output.html and update the existing animation",
artifacts=["/workspace/output.html"],
media_inputs=[],
)
assert {"apply_patch", "edit_file", "glob", "grep"} <= selected
def test_compact_native_route_keeps_writers_for_chinese_web_artifact():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{"inspect_media", "python", "write_file", "read_file", "ls", "web_search"},
"请制作网页 /workspace/index.html 并保存。视频在 /workspace/fixtures/video.mp4",
{"web"},
)
assert {"inspect_media", "python", "write_file", "read_file", "ls"} <= selected
assert "web_search" not in selected
def test_compact_native_route_keeps_native_pdf_extractor_for_local_pdf():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{"inspect_media", "pdf_extract", "web_fetch", "read_file", "ls"},
"Read the chart in /workspace/paper.pdf",
{"web", "documents"},
)
assert "inspect_media" in selected
assert "pdf_extract" in selected
assert "web_fetch" not in selected
def test_pdf_extract_accepts_task_local_pdf_path(tmp_path):
import asyncio
from unittest.mock import patch
from src.agent_tools.web_tools import PdfExtractTool
from src.tool_execution import _active_workspace
local_pdf = tmp_path / "fixtures" / "paper.pdf"
local_pdf.parent.mkdir()
local_pdf.write_bytes(b"%PDF-local-test")
token = _active_workspace.set(str(tmp_path))
try:
with patch.object(
PdfExtractTool,
"_positioned_table_evidence",
return_value="Table 3 local evidence",
):
result = asyncio.run(PdfExtractTool().execute(
'{"url":"/workspace/fixtures/paper.pdf","query":"Table 3"}',
{},
))
finally:
_active_workspace.reset(token)
assert result == {
"output": "Source: /workspace/fixtures/paper.pdf\n\nTable 3 local evidence",
"exit_code": 0,
}
def test_pdf_extract_accepts_confined_physical_file_uri(tmp_path):
import asyncio
import json
from unittest.mock import patch
from src.agent_tools.web_tools import PdfExtractTool
from src.tool_execution import _active_workspace
local_pdf = tmp_path / "fixtures" / "report with spaces.pdf"
local_pdf.parent.mkdir()
local_pdf.write_bytes(b"%PDF-local-test")
token = _active_workspace.set(str(tmp_path))
try:
with patch.object(
PdfExtractTool,
"_positioned_table_evidence",
return_value="Focused local evidence",
):
result = asyncio.run(PdfExtractTool().execute(json.dumps({
"url": local_pdf.as_uri(),
"query": "focused evidence",
}), {}))
finally:
_active_workspace.reset(token)
assert result == {
"output": (
"Source: /workspace/fixtures/report with spaces.pdf\n\n"
"Focused local evidence"
),
"exit_code": 0,
}
assert PdfExtractTool._local_pdf_path("file:///etc/passwd") is None
def test_compact_native_route_preserves_pdf_extractor_for_online_pdf():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{"pdf_extract", "web_fetch", "web_search", "write_file", "python"},
"Read https://arxiv.org/pdf/2406.04264 and save /workspace/result.csv",
{"web", "files"},
)
assert "pdf_extract" in selected
def test_compact_native_route_discovers_named_paper_without_url():
from src.agent_loop import _compact_native_route_tools
selected = _compact_native_route_tools(
{"bash", "web_search", "web_fetch", "pdf_extract", "write_file", "python"},
'From the paper "How Far Are We to GPT-4V?" extract Table 2 and save '
"/workspace/result.csv",
{"files"},
)
assert {"web_search", "web_fetch", "pdf_extract", "write_file", "python"} <= selected
assert "bash" not in selected
def test_per_model_route_reapplies_local_media_after_contextual_web_clamps():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
route_start = source.index(" def _route_relevant_tools(candidate_model: str):")
route_end = source.index("\n (\n _ody_qwen_finetune_model", route_start)
route_source = source[route_start:route_end]
assert route_source.rfind(
"_native_local_media_inputs(_last_user, client_runtime_context)"
) > route_source.rfind(
"_contextual_public_web_followup"
)
assert 'route_tools.add("private_browser")' in route_source
assert 'route_tools.difference_update(' in route_source
def test_compact_request_reapplies_runner_declared_media_after_compaction():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
compact_start = source.index(' if tool_surface == "compact":')
prompt_start = source.index(" prompt_route_tools =", compact_start)
compact_source = source[compact_start:prompt_start]
assert "_compact_native_route_tools(" in compact_source
assert compact_source.rfind("_native_local_media_inputs(") > compact_source.rfind(
"_compact_native_route_tools("
)
assert '"inspect_media", "transcribe_media"' in compact_source
def test_native_runtime_context_can_cap_endpoint_context_budget():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert '(client_runtime_context or {}).get("model_context_window")' in source
assert "min(candidate_context, runtime_context_window)" in source
def test_native_local_media_prunes_host_bridge_and_cookbook_tools():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert 'client_runtime_context.get("surface") or "") == "odysseus-native"' in source
assert '_base_relevant_tools.discard("host_shell")' in source
assert '_DOMAIN_TOOL_MAP.get("cookbook", set())' in source
def test_terminal_artifact_observation_budget_forces_mutation_recovery():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert "_artifact_observation_rounds >= 6" in source
assert '"reason": "artifact_observation_budget"' in source
assert "_artifact_mutation_only_mode = True" in source
assert '"python",' in source
assert "code must generate a chart/image" in source
assert source.count("and not _artifact_mutation_only_mode") >= 2
def test_missing_text_and_binary_artifacts_keep_python_for_synthesis():
from src.agent_loop import _artifact_mutation_surface_for_missing
assert "python" in _artifact_mutation_surface_for_missing(
["/workspace/rows.csv", "/workspace/chart.png"]
)
def test_direct_source_media_extraction_is_narrow_and_bilingual():
from src.agent_loop import (
_direct_source_media_extraction_requested,
_visible_media_caption_requested,
)
assert _direct_source_media_extraction_requested(
"Save a frame screenshot for each athlete as /workspace/student1.png.",
["/workspace/student1.png"],
)
assert _direct_source_media_extraction_requested(
"把每位同学对应的帧截图保存为 /workspace/student1.png。",
["/workspace/student1.png"],
)
assert not _direct_source_media_extraction_requested(
"Generate a chart from the video data as /workspace/chart.png.",
["/workspace/chart.png"],
)
assert not _direct_source_media_extraction_requested(
"Extract the chase clip, make video and audio 2x speed, and save chase_2x.mp4.",
["/workspace/chase_2x.mp4"],
)
assert not _direct_source_media_extraction_requested(
"提取追逐片段,将视频和音频都加速制作成2倍速版本。",
["/workspace/chase_2x.mp4"],
)
assert not _visible_media_caption_requested(
"Save a frame screenshot for each athlete."
)
assert _visible_media_caption_requested(
"Add a visible caption to each saved screenshot."
)
assert _visible_media_caption_requested("给截图添加文字标签。")
def test_visual_text_extraction_is_distinct_from_speech_transcription():
from src.agent_loop import _visual_text_extraction_requested
assert _visual_text_extraction_requested(
"Extract all flashing English words shown on screen from 0:25 to 0:30."
)
assert _visual_text_extraction_requested("识别视频画面中的文字。")
assert not _visual_text_extraction_requested(
"Transcribe everything the speaker says from 0:25 to 0:30."
)
assert not _visual_text_extraction_requested(
"Extract subtitles from this recording."
)
def test_local_media_routes_remove_transcriber_for_visual_only_text():
import re
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert len(re.findall(
r'if _visual_text_extraction_requested\(_last_user\):\n'
r'\s+_local_media_tools\.discard\("transcribe_media"\)',
source,
)) == 3
def test_workspace_paths_split_on_chinese_list_punctuation():
from src.agent_loop import _explicit_workspace_files
assert _explicit_workspace_files(
"保存为 /workspace/student1.png、/workspace/student2.png;然后完成。"
) == ["/workspace/student1.png", "/workspace/student2.png"]
def test_direct_source_media_extraction_recovery_only_exports_source_pixels():
from src.agent_loop import (
_artifact_mutation_surface_for_missing,
_artifact_recovery_messages,
)
missing = ["/workspace/student1.png", "/workspace/student2.png"]
surface = _artifact_mutation_surface_for_missing(
missing,
local_media_derivation=True,
source_media_extraction=True,
)
assert surface == {"inspect_media"}
recovered = _artifact_recovery_messages(
[{
"role": "user",
"content": (
"Watch /workspace/fixtures/video.mp4 and save frame screenshots "
"as /workspace/student1.png and /workspace/student2.png."
),
}],
[],
missing,
)
text = "\n".join(str(message.get("content") or "") for message in recovered)
assert "native source-media export call" in text
assert "do not synthesize" in text
assert "Python synthesis call" not in text
def test_source_media_recovery_keeps_writer_for_text_companion_artifact():
from src.agent_loop import (
_artifact_mutation_surface_for_missing,
_source_media_text_companion_recovery_tools,
)
surface = _artifact_mutation_surface_for_missing(
["/workspace/timestamp.txt", "/workspace/cropped_frame.png"],
local_media_derivation=True,
source_media_extraction=True,
)
assert surface == {"inspect_media", "write_file"}
assert _source_media_text_companion_recovery_tools(
["/workspace/timestamp.txt", "/workspace/cropped_frame.png"],
recovery_active=True,
) == {"write_file"}
assert _source_media_text_companion_recovery_tools(
["/workspace/cropped_frame.png"],
recovery_active=True,
) == set()
assert _source_media_text_companion_recovery_tools(
["/workspace/timestamp.txt"],
recovery_active=False,
) == set()
def test_source_media_schema_boundary_keeps_text_companion_writer():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
boundary = source.index(
"# Final provenance boundary: route construction, fallbacks, and"
)
materialization = source.index(
"all_tool_schemas = _tool_schemas_for_route(_active_route_state)",
boundary,
)
segment = source[boundary:materialization]
assert "_workspace_artifacts" in segment
assert "recovery_active=True" in segment
assert "_source_companion_tools" in segment
def test_artifact_recovery_does_not_shadow_recovery_message_builder():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert "_recovery_messages = _artifact_recovery_messages(" not in source
assert "messages = _artifact_recovery_messages(" in source
assert "_artifact_recovery_message_list = _artifact_recovery_messages(" in source
def test_source_media_recovery_retains_bounded_candidate_working_notes():
from src.agent_loop import _artifact_recovery_messages
recovered = _artifact_recovery_messages(
[
{
"role": "user",
"content": (
"Watch /workspace/fixtures/video.mp4 and save a frame screenshot "
"as /workspace/student1.png."
),
},
{
"role": "assistant",
"content": (
"Visual candidate: the blue-shirt athlete's chin appears above "
"the bar near 00:00:52.050; verify that timestamp before export."
),
},
],
[],
["/workspace/student1.png"],
)
text = "\n".join(str(message.get("content") or "") for message in recovered)
assert "00:00:52.050" in text
assert "candidate hypotheses" in text
assert "not independent pixel evidence" in text
def test_direct_source_media_extraction_filter_is_reapplied_per_model_route():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
route_start = source.index(" def _route_relevant_tools(candidate_model: str):")
route_end = source.index("\n (\n _ody_qwen_finetune_model,", route_start)
route_source = source[route_start:route_end]
assert "if _source_media_extraction_requested:" in route_source
for tool in ("python", "bash", "generate_image", "edit_image"):
assert f'"{tool}"' in route_source
schema_boundary = source.index(
"# Final provenance boundary: route construction, fallbacks, and"
)
schema_materialization = source.index(
"all_tool_schemas = _tool_schemas_for_route(_active_route_state)",
schema_boundary,
)
assert schema_boundary < schema_materialization
def test_mixed_local_pdf_artifacts_keep_native_media_inspection():
from src.agent_loop import _artifact_mutation_surface_for_missing
assert "inspect_media" in _artifact_mutation_surface_for_missing(
["/workspace/rows.csv", "/workspace/chart.png"],
local_media_derivation=True,
)
def test_local_audio_video_artifact_recovery_keeps_ffmpeg_shell_surface():
from src.agent_loop import _artifact_mutation_surface_for_missing
surface = _artifact_mutation_surface_for_missing(
["/workspace/merged.mp4"],
local_media_derivation=True,
)
assert "bash" in surface
assert "inspect_media" in surface
def test_non_media_binary_recovery_does_not_gain_shell_surface():
from src.agent_loop import _artifact_mutation_surface_for_missing
assert "bash" not in _artifact_mutation_surface_for_missing(
["/workspace/chart.png"],
local_media_derivation=True,
)
def test_text_only_local_media_recovery_requires_mutation_not_reinspection():
from src.agent_loop import _artifact_mutation_surface_for_missing
surface = _artifact_mutation_surface_for_missing(
["/workspace/output.txt"],
local_media_derivation=True,
)
assert "write_file" in surface
assert "inspect_media" not in surface
assert "transcribe_media" not in surface
def test_artifact_recovery_preserves_media_read_and_browser_capabilities():
from src.agent_loop import _artifact_recovery_capability_floor
assert _artifact_recovery_capability_floor(
local_media_derivation=True,
browser_render=True,
) == {"inspect_media", "read_file", "private_browser"}
def test_artifact_recovery_capability_floor_is_not_enabled_for_plain_text():
from src.agent_loop import _artifact_recovery_capability_floor
assert _artifact_recovery_capability_floor() == set()
def test_post_redirect_local_pdf_inspection_is_bounded():
from src.agent_loop import _bounded_local_pdf_inspection_blocks
from src.tool_types import ToolBlock
blocks = [ToolBlock(
"inspect_media",
'{"path":"/workspace/paper.pdf","page":7}',
)]
allowed, used = _bounded_local_pdf_inspection_blocks(
blocks,
local_pdf_turn=True,
already_used=0,
)
assert allowed == blocks
assert used == 1
allowed, used = _bounded_local_pdf_inspection_blocks(
blocks,
local_pdf_turn=True,
already_used=2,
)
assert allowed == []
assert used == 0
allowed, used = _bounded_local_pdf_inspection_blocks(
[*blocks, ToolBlock("read_file", "/workspace/paper.pdf")],
local_pdf_turn=True,
already_used=0,
)
assert allowed == []
assert used == 0
def test_artifact_recovery_treats_native_web_pdf_tools_as_inspection():
from src.agent_loop import _workspace_inspection_tool_block
from src.tool_types import ToolBlock
for tool_name in ("web_search", "web_fetch", "pdf_extract"):
assert _workspace_inspection_tool_block(ToolBlock(tool_name, "query"))
def test_artifact_recovery_keeps_acquisition_until_source_evidence_exists():
from src.agent_loop import _artifact_source_evidence_ready
assert not _artifact_source_evidence_ready(
[{"tool": "web_search", "exit_code": 0, "output": "generic result"}],
'Read the "Attention Is All You Need" paper and extract Table 2.',
)
assert _artifact_source_evidence_ready(
[{"tool": "pdf_extract", "exit_code": 0, "output": "Table 2 ..."}],
'Read the "Attention Is All You Need" paper and extract Table 2.',
)
def test_local_pdf_is_not_complete_evidence_for_external_publication_check():
from src.agent_loop import (
_artifact_acquisition_recovery_messages,
_artifact_source_evidence_ready,
)
request = (
"Scan /workspace/fixtures/paper.pdf and verify which cited arXiv "
"preprints were officially accepted or published in conferences."
)
events = [{
"tool": "pdf_extract",
"exit_code": 0,
"output": "References: Example Paper, arXiv preprint arXiv:2501.00001",
}]
assert not _artifact_source_evidence_ready(events, request)
recovered = _artifact_acquisition_recovery_messages(
[{"role": "user", "content": request}],
events,
["/workspace/publications.csv"],
user_text=request,
)
text = "\n".join(str(message.get("content") or "") for message in recovered)
assert "external verification" in text
assert "followed by `web_fetch`" in text
assert "Do not call `pdf_extract` again" in text
def test_pdf_bibliography_evidence_preserves_columns_and_filters_arxiv_entries():
from src.agent_tools.web_tools import PdfExtractTool
pages = [
(1, "Introduction\nA citation-heavy body page.", "Related work"),
(
2,
"Acknowledgments\nThanks.\nReferences\n"
"[1] Alpha. First preprint. arXiv preprint, abs/2401.00001, 2024.\n"
"[2] Beta. Published in ICML, 2024.",
"[3] Gamma. Another work. ArXiv preprint, abs/2402.00002, 2024.",
),
(
3,
"[4] Delta. Journal article, 2023.",
"[5] Epsilon. Late matching work. arXiv:2403.00003, 2024.",
),
]
evidence = PdfExtractTool._bibliography_evidence(
pages, "Find all references listed as arXiv preprints"
)
assert "3 arXiv/preprint entries" in evidence
assert "[1] Alpha" in evidence
assert "[3] Gamma" in evidence
assert "[5] Epsilon" in evidence
assert "[2] Beta" not in evidence
assert "Acknowledgments" not in evidence
def test_pdf_bibliography_evidence_does_not_override_unrelated_queries():
from src.agent_tools.web_tools import PdfExtractTool
pages = [(7, "References\n[1] Alpha. arXiv preprint.", "")]
assert PdfExtractTool._bibliography_evidence(pages, "Extract Table 3") == ""
def test_pdf_bibliography_evidence_honors_requested_reference_range():
from src.agent_tools.web_tools import PdfExtractTool
pages = [(
9,
"References\n[29] Alpha. Earlier work.\n[30] Beta. Earlier work.\n"
"[31] Gamma. Target work.\n[32] Delta. Target work.",
"[33] Epsilon. Target work.\n[34] Zeta. Later work.",
)]
evidence = PdfExtractTool._bibliography_evidence(
pages, "Extract references [31] through [33]"
)
assert "reference entries 31-33" in evidence
assert "[31] Gamma" in evidence
assert "[32] Delta" in evidence
assert "[33] Epsilon" in evidence
assert "[30] Beta" not in evidence
assert "[34] Zeta" not in evidence
def test_artifact_recovery_hands_off_existing_plot_script_to_python():
from src.agent_loop import _artifact_recovery_messages
recovered = _artifact_recovery_messages(
[{
"role": "user",
"content": "Create /workspace/chart.png from the extracted table.",
}],
[{
"tool": "write_file",
"command": (
"/workspace/chart.py\n"
"import matplotlib.pyplot as plt\n"
"plt.savefig('/workspace/chart.png')"
),
"output": "wrote file",
"exit_code": 0,
}],
["/workspace/chart.png"],
)
text = "\n".join(str(message.get("content") or "") for message in recovered)
assert "Execute it now with the native `python` tool" in text
assert "/workspace/chart.py" in text
assert "Do not rewrite the script" in text
def test_artifact_body_handoff_executes_existing_generator_first():
from src.agent_loop import _artifact_generator_execution_block
block = _artifact_generator_execution_block(
[{
"tool": "write_file",
"command": (
"/workspace/build_report.py\n"
"import pandas as pd\n"
"pd.DataFrame({'value': [1]}).to_csv('/workspace/report.csv')"
),
"output": "wrote file",
"exit_code": 0,
}],
["/workspace/report.csv"],
{"python", "write_file"},
)
assert block is not None
assert block.tool_type == "python"
assert "runpy.run_path" in block.content
assert "/workspace/build_report.py" in block.content
def test_artifact_body_handoff_does_not_run_unrelated_or_failed_script():
from src.agent_loop import _artifact_generator_execution_block
unrelated = [{
"tool": "write_file",
"command": "/workspace/helper.py\nprint('unrelated')",
"output": "wrote file",
"exit_code": 0,
}]
failed = [{
"tool": "write_file",
"command": (
"/workspace/build_report.py\n"
"open('/workspace/report.csv', 'w').write('value\\n1')"
),
"output": "failed",
"exit_code": 1,
}]
assert _artifact_generator_execution_block(
unrelated, ["/workspace/report.csv"], {"python"}
) is None
assert _artifact_generator_execution_block(
failed, ["/workspace/report.csv"], {"python"}
) is None
assert _artifact_generator_execution_block(
unrelated, ["/workspace/report.csv"], {"write_file"}
) is None
def test_artifact_body_handoff_does_not_retry_generator_until_repaired():
import json
from src.agent_loop import _artifact_generator_execution_block
write = {
"tool": "write_file",
"command": (
"/workspace/build_report.py\n"
"open('/workspace/report.csv', 'w').write('value\\n1')"
),
"output": "wrote file",
"exit_code": 0,
}
failed_run = {
"tool": "python",
"command": (
"import runpy\n"
"runpy.run_path(\"/workspace/build_report.py\", run_name='__main__')"
),
"output": "SyntaxError: invalid syntax",
"exit_code": 1,
}
assert _artifact_generator_execution_block(
[write, failed_run], ["/workspace/report.csv"], {"python"}
) is None
repaired = {
"tool": "edit_file",
"command": json.dumps({
"path": "/workspace/build_report.py",
"old_string": "broken",
"new_string": "fixed",
}),
"output": "edited file",
"exit_code": 0,
}
assert _artifact_generator_execution_block(
[write, failed_run, repaired], ["/workspace/report.csv"], {"python"}
) is not None
def test_failed_generator_allows_one_repair_read_but_not_a_read_loop():
from src.agent_loop import _failed_artifact_generator_repair_reads
from src.tool_types import ToolBlock
failed_run = {
"tool": "python",
"command": "runpy.run_path('/workspace/build_report.py')",
"output": "SyntaxError: invalid syntax",
"exit_code": 1,
}
requested = [ToolBlock("read_file", "/workspace/build_report.py")]
assert _failed_artifact_generator_repair_reads(requested, [failed_run]) == requested
prior_read = {
"tool": "read_file",
"command": "/workspace/build_report.py",
"output": "print('broken')",
"exit_code": 0,
}
assert _failed_artifact_generator_repair_reads(
requested, [failed_run, prior_read]
) == []
def test_artifact_recovery_directs_python_for_missing_chart_without_script():
from src.agent_loop import _artifact_recovery_messages
recovered = _artifact_recovery_messages(
[{"role": "user", "content": "Generate /workspace/chart.png."}],
[],
["/workspace/chart.png"],
)
text = "\n".join(str(message.get("content") or "") for message in recovered)
assert "Python synthesis call" in text
assert "Do not rewrite completed CSV/text files" in text
def test_transformed_media_recovery_directs_shell_mutation_not_source_export():
from src.agent_loop import _artifact_recovery_messages
recovered = _artifact_recovery_messages(
[{
"role": "user",
"content": (
"Read /workspace/fixtures/video.mp4, extract the chase clip, "
"make video and audio 2x speed, and save /workspace/chase_2x.mp4."
),
}],
[],
["/workspace/chase_2x.mp4"],
)
text = "\n".join(str(message.get("content") or "") for message in recovered)
assert "Bash" in text
assert "ffmpeg" in text
assert "audio/video transformation" in text
assert "Python synthesis call" not in text
assert "native source-media export call" not in text
def test_ffmpeg_media_transform_is_classified_as_workspace_mutation():
from src.agent_evidence import command_has_mutation_effect
assert command_has_mutation_effect(
"ffmpeg -i /workspace/fixtures/video.mp4 -filter:v setpts=0.5*PTS "
"-filter:a atempo=2 /workspace/chase_2x.mp4"
)
assert command_has_mutation_effect(
"sox /workspace/fixtures/audio.wav /workspace/audio_2x.wav tempo 2"
)
def test_abstract_landing_page_is_not_detail_source_evidence():
from src.agent_loop import _artifact_source_evidence_ready
assert not _artifact_source_evidence_ready(
[{
"tool": "web_fetch",
"output": (
"Attention Is All You Need. We propose a new simple network "
"architecture based solely on attention mechanisms, dispensing "
"with recurrence and convolutions. The paper discusses training "
"costs and English-to-German translation. "
) * 20,
"exit_code": 0,
}],
"Extract Table 2 training FLOPs and costs from the Attention Is All You Need paper",
)
def test_fetched_detail_page_is_source_evidence():
from src.agent_loop import _artifact_source_evidence_ready
assert _artifact_source_evidence_ready(
[{
"tool": "web_fetch",
"output": (
"Attention Is All You Need. Table 2 reports training cost "
"and FLOPs for the EN-DE translation models. "
) * 20,
"exit_code": 0,
}],
"Extract Table 2 training FLOPs and costs from the Attention Is All You Need paper",
)
def test_artifact_recovery_redirects_wrong_http_and_partial_mutations():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert '"artifact_wrong_tool_http"' in source
assert '"artifact_native_acquisition_required"' in source
assert '"reason": "partial_artifact_mutation"' in source
assert "ad-hoc HTTP" in source
wrong_http_start = source.index('"artifact_wrong_tool_http"')
wrong_http_detector_start = source.rfind(
'and "ad-hoc HTTP" in str(',
0,
wrong_http_start,
)
wrong_http_detector = source[wrong_http_detector_start:wrong_http_start]
assert 'get("exit_code") not in (None, 0)' not in wrong_http_detector
assert "partial artifact mutation left required artifacts missing" in source
assert "For `.png` chart artifacts" in source
def test_answer_only_local_media_has_bounded_inspection_budget():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert "_completed_media_inspections >= 8" in source
assert "round_num >= _round_limit - 1" in source
assert "_media_inspection_budget_exhausted" in source
assert "local-media inspection budget exhausted" in source
assert "answer the user's question now from the evidence" in source
assert "_media_artifacts_complete" in source
assert "do not refine it again" in source
def test_force_answer_discards_native_tool_plan_before_grace_synthesis():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
force_answer_start = source.index(" if _force_answer:")
force_answer_end = source.index(" # A single giant SVG", force_answer_start)
force_answer_block = source[force_answer_start:force_answer_end]
assert "if native_tool_calls:" in force_answer_block
assert "discarding unfinished tool-plan text before synthesis" in force_answer_block
assert 'round_response = ""' in force_answer_block
assert "native_tool_calls = []" in force_answer_block
def test_local_media_is_exempt_from_pure_web_schema_and_round_clamps():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
pure_web_start = source.index(" _local_media_turn = bool(")
pure_web_end = source.index("\n if (\n _pure_web_turn", pure_web_start)
assert "and not _local_media_turn" in source[pure_web_start:pure_web_end]
assert source.count("if _pure_web_turn:") >= 3
def test_empty_local_media_round_nudges_export_instead_of_ending():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert "or _local_media_turn" in source
assert 'requested output_path, and timestamp_path when requested' in source
def test_local_media_answer_requires_real_source_evidence():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert '"type": "source_evidence_required"' in source
assert '"reason": "local_media_not_observed"' in source
assert "blocked local-media answer without source evidence" in source
assert "Do not infer source contents from" in source
def test_local_media_gate_allows_only_bounded_workspace_discovery():
from src.agent_loop import _local_media_discovery_call_allowed
assert _local_media_discovery_call_allowed("ls", "/workspace/fixtures")
assert _local_media_discovery_call_allowed("glob", "*.mp4")
assert _local_media_discovery_call_allowed("get_workspace", "")
assert _local_media_discovery_call_allowed(
"bash", "#!bg\nls -la /workspace/fixtures/video.webm"
)
assert _local_media_discovery_call_allowed("bash", "stat /workspace/fixtures/video.webm")
assert not _local_media_discovery_call_allowed(
"bash", "ffmpeg -i /workspace/fixtures/video.webm"
)
assert not _local_media_discovery_call_allowed("bash", "ls /workspace | cat")
assert not _local_media_discovery_call_allowed("python", "os.listdir('/workspace')")
def test_local_media_gate_has_one_automatic_evidence_acquisition_path():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert "_auto_local_media_evidence" in source
assert "_local_media_evidence_block_count == 0" in source
assert 'ToolBlock(\n "inspect_media"' in source
assert "_local_media_files[0]" in source
assert "and not _auto_local_media_evidence" in source
assert "_local_media_evidence_required_block = False" in source
assert "_local_media_evidence_required_block = True" in source
assert "elif _allow_local_media_discovery:" in source
def test_combined_web_workspace_route_keeps_native_writers():
source = (Path(__file__).parents[1] / "routes" / "chat_routes.py").read_text()
assert "_web_workspace_output" in source
assert "if not _web_workspace_output" in source
assert "创建|生成|保存|写入|制作|截取|剪辑|拼接|导出" in source
def test_compact_tool_surface_does_not_expand_to_every_admin_schema():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert 'and tool_surface != "compact"' in source
def test_web_fetch_focused_passages_keeps_matching_context():
from src.agent_tools.web_tools import WebFetchTool
text = "\n".join([*(f"noise {i}" for i in range(30)), "DocVQA 96.4 ChartQA 89.5", *(f"tail {i}" for i in range(30))])
focused = WebFetchTool._focused_passages(text, "DocVQA, ChartQA", 2000)
assert "DocVQA 96.4 ChartQA 89.5" in focused
assert "noise 0" not in focused
def test_web_fetch_focused_passages_tokenizes_natural_query():
from src.agent_tools.web_tools import WebFetchTool
text = "intro\nDocVQA 96.4 ChartQA 89.5 TextVQA 83.5\nfooter"
focused = WebFetchTool._focused_passages(
text,
"DocVQA ChartQA TextVQA benchmark table evaluation scores Qwen2.5-VL-72B",
2000,
)
assert "DocVQA 96.4" in focused
def test_web_fetch_one_large_match_cannot_hide_later_exact_table():
from src.agent_tools.web_tools import WebFetchTool
broad = "Qwen " + ("noise 12.3 " * 2000)
exact = "Qwen DocVQA 96.4 ChartQA 89.5 TextVQA 83.5"
text = "\n".join([broad, *(f"gap {i}" for i in range(40)), exact])
focused = WebFetchTool._focused_passages(
text, "Qwen DocVQA ChartQA TextVQA", 12000
)
assert exact in focused
def test_web_fetch_windows_one_line_arxiv_html_around_exact_table_row():
from src.agent_tools.web_tools import WebFetchTool
text = (
"abstract GPT-4o " + ("background prose " * 1200)
+ " LLaVA-Onevision | 83.5 | 56.4 | 3.75 | 46.7 | 58.4 | 58.0 | 5.09 "
+ " GPT-4o | 83.7 | 68.8 | 4.94 | 42.9 | 47.8 | 57.1 | 6.80"
)
focused = WebFetchTool._focused_passages(
text, "GPT-4o LLaVA-Onevision TR AR NQA ER PQA AO AC", 12000
)
assert "LLaVA-Onevision | 83.5" in focused
assert "GPT-4o | 83.7" in focused
def test_read_file_url_is_routed_to_native_web_fetch():
from src.tool_schemas import function_call_to_tool_block
block = function_call_to_tool_block(
"read_file", '{"path":"https://arxiv.org/pdf/2502.13923"}'
)
assert block.tool_type == "web_fetch"
assert "https://arxiv.org/pdf/2502.13923" in block.content
def test_local_html_web_fetch_is_routed_to_native_browser():
import json
from src.tool_schemas import function_call_to_tool_block
block = function_call_to_tool_block(
"web_fetch", '{"url":"file:///workspace/output.html"}'
)
assert block.tool_type == "private_browser"
assert json.loads(block.content) == {
"action": "open",
"url": "file:///workspace/output.html",
}
def test_workspace_alias_resolves_inside_active_workspace(tmp_path):
from src.tool_execution import _resolve_tool_path_in_workspace
assert _resolve_tool_path_in_workspace(str(tmp_path), "/workspace/result.csv") == str(tmp_path / "result.csv")
def test_combined_web_artifact_floor_hides_generic_shell():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert '_relevant_tools.discard("bash")' in source
assert "combined web artifact task prefers structured tools" in source
assert "_named_online_document" in source
assert '{"web_search", "web_fetch", "pdf_extract"}' in source
def test_source_acquisition_recovery_retains_artifact_mutation_floor():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert '"python", "write_file", "read_file"' in source
assert "set(_native_acquisition_tools)" in source
assert "| _artifact_mutation_tools" in source
def test_failed_artifact_verifier_skips_readonly_followthrough():
source = (Path(__file__).parents[1] / "src" / "agent_loop.py").read_text()
assert "_failed_artifact_verifier = any(" in source
assert '"private_browser", "builtin_browser"' in source
assert "2 if _failed_artifact_verifier else 0" in source
assert "Source acquisition and artifact mutation" in source
assert "_acquisition_mutation_floor" in source
assert "_selected_acquisition_surface" in source
def test_pdf_extract_requires_focus_and_reuses_native_fetch():
import asyncio
from unittest.mock import AsyncMock, patch
from src.agent_tools.web_tools import PdfExtractTool, WebFetchTool
expected = {"output": "Source: test\nDocVQA 96.4", "exit_code": 0}
with patch.object(WebFetchTool, "execute", new=AsyncMock(return_value=expected)) as fetch:
result = asyncio.run(PdfExtractTool().execute(
'{"url":"https://example.test/paper.pdf","query":"Qwen DocVQA"}',
{},
))
assert result["exit_code"] == 0
assert "Align row values to the full column header" in result["output"]
forwarded = fetch.await_args.args[0]
assert '"full": true' in forwarded
assert '"query": "Qwen DocVQA"' in forwarded
def test_pdf_extract_uses_official_arxiv_html_companion():
import asyncio
from unittest.mock import AsyncMock, patch
from src.agent_tools.web_tools import PdfExtractTool, WebFetchTool
assert PdfExtractTool._arxiv_html_url(
"https://arxiv.org/pdf/2406.04264v3#page=13"
) == "https://arxiv.org/html/2406.04264v3"
html = {
"output": "LLaVA-Onevision | 83.5 | 56.4 | 3.75",
"exit_code": 0,
}
with (
patch.object(
PdfExtractTool,
"_positioned_table_evidence",
return_value="GPT-4o | 83.7 | 68.8 | 4.94",
),
patch.object(WebFetchTool, "execute", new=AsyncMock(return_value=html)) as fetch,
):
result = asyncio.run(PdfExtractTool().execute(
'{"url":"https://arxiv.org/pdf/2406.04264",'
'"query":"GPT-4o LLaVA-Onevision TR AR NQA"}',
{},
))
assert result["exit_code"] == 0
assert "LLaVA-Onevision | 83.5" in result["output"]
assert "GPT-4o | 83.7" in result["output"]
assert "skip those columns" in result["output"]
assert fetch.await_count == 2
focused_queries = {
__import__("json").loads(call.args[0])["query"]
for call in fetch.await_args_list
}
assert focused_queries == {"GPT-4o", "LLaVA-Onevision"}
forwarded = fetch.await_args.args[0]
assert "https://arxiv.org/html/2406.04264" in forwarded
def test_pdf_extract_keeps_positioned_table_before_long_html():
import asyncio
from unittest.mock import AsyncMock, patch
from src.agent_tools.web_tools import PdfExtractTool, WebFetchTool
positioned = (
"[Positioned PDF table evidence, page 3.]\n"
"Model | Metric-A | Metric-B\nTarget-7B | 81.2 | 94.6"
)
html = {"output": "prose " * 20_000, "exit_code": 0}
with (
patch.object(PdfExtractTool, "_positioned_table_evidence", return_value=positioned),
patch.object(WebFetchTool, "execute", new=AsyncMock(return_value=html)),
):
result = asyncio.run(PdfExtractTool().execute(
'{"url":"https://arxiv.org/pdf/2406.04264",'
'"query":"Target-7B Metric-A Metric-B"}',
{},
))
output = result["output"]
assert "Target-7B | 81.2 | 94.6" in output
assert output.index("Positioned PDF table evidence") < output.index("Focused HTML evidence")
def test_pdf_table_region_prefers_exact_requested_rows_over_named_variants():
from src.agent_tools.web_tools import PdfExtractTool
rows = [
(10.0, [{"text": "Table"}, {"text": "4:"}, {"text": "Main"}]),
(20.0, [{"text": "Model"}, {"text": "AIME"}, {"text": "MATH-500"}]),
(30.0, [{"text": "DeepSeek-R1"}, {"text": "79.8"}, {"text": "97.3"}]),
(40.0, [{"text": "OpenAI-o1"}, {"text": "74.3"}, {"text": "96.4"}]),
(100.0, [{"text": "Table"}, {"text": "15:"}, {"text": "Distilled"}]),
(110.0, [{"text": "Model"}, {"text": "AIME"}, {"text": "MATH-500"}]),
(120.0, [{"text": "DeepSeek-R1-Distill-Qwen-7B"}, {"text": "55.5"}, {"text": "92.8"}]),
(130.0, [{"text": "DeepSeek-R1-Distill-Qwen-32B"}, {"text": "72.6"}, {"text": "94.3"}]),
]
selected = PdfExtractTool._select_positioned_table_region(
rows,
metric_terms=["AIME", "MATH-500"],
model_terms=["DeepSeek-R1", "OpenAI-o1"],
requested_table_number=None,
)
text = " ".join(word["text"] for _top, words in selected for word in words)
assert "DeepSeek-R1 79.8 97.3" in text
assert "Distill" not in text
def test_pdf_extract_positioned_target_prefers_model_present_in_rows():
from src.agent_tools.web_tools import PdfExtractTool
broad_query_models = [
"GLM-4.6V",
"Qwen3-VL-A22B-Instruct",
"Seed-1.5-VL-Thinking",
]
glm_rows = [
(78.0, [
{"text": "Task", "x0": 111},
{"text": "Benchmark", "x0": 171},
{"text": "GLM-4.6V", "x0": 302},
{"text": "Qwen2.5-VL", "x0": 382},
]),
(480.0, [
{"text": "RefCOCO-avg", "x0": 171},
{"text": "88.6", "x0": 302},
{"text": "90.3", "x0": 382},
]),
]
seed_rows = [
(138.0, [
{"text": "Capability", "x0": 76},
{"text": "Benchmark", "x0": 136},
{"text": "Seed", "x0": 219},
{"text": "Qwen", "x0": 504},
]),
(147.0, [
{"text": "1.5-VL", "x0": 219},
{"text": "2.5-VL", "x0": 504},
]),
(159.0, [
{"text": "thinking", "x0": 219},
{"text": "non-thinking", "x0": 504},
]),
(510.0, [
{"text": "RefCOCO-avg", "x0": 136},
{"text": "91.3", "x0": 219},
{"text": "91.6", "x0": 504},
]),
]
assert (
PdfExtractTool._select_positioned_target_model(broad_query_models, glm_rows)
== "GLM-4.6V"
)
assert (
PdfExtractTool._select_positioned_target_model(broad_query_models, seed_rows)
== "Seed-1.5-VL-Thinking"
)
def test_pdf_extract_is_native_schema_and_rag_tool():
from src.agent_tools import TOOL_HANDLERS
from src.tool_index import BUILTIN_TOOL_DESCRIPTIONS
from src.tool_schemas import FUNCTION_TOOL_SCHEMAS
names = {item["function"]["name"] for item in FUNCTION_TOOL_SCHEMAS}
assert "pdf_extract" in names
assert "pdf_extract" in TOOL_HANDLERS
assert "pdf_extract" in BUILTIN_TOOL_DESCRIPTIONS
def test_positioned_pdf_keeps_continuous_rows_between_table_anchors():
source = (Path(__file__).parents[1] / "src" / "agent_tools" / "web_tools.py").read_text()
assert "table_top = max(0.0, min(anchors) - 100)" in source
assert "table_bottom = max(anchors) + 100" in source
assert "table_top <= row[0] <= table_bottom" in source
def test_pdf_table_model_match_survives_split_hyphenated_name():
from src.agent_tools.web_tools import PdfExtractTool
assert PdfExtractTool._pdf_row_contains_term(
"LLaVA- Onevision [24] 2024-08 32 frm 83.5 56.4",
"LLaVA-Onevision",
)
assert not PdfExtractTool._pdf_row_contains_term(
"GPT-4o 83.7 68.8", "LLaVA-Onevision"
)
def test_pdf_table_exact_model_match_rejects_named_variants():
from src.agent_tools.web_tools import PdfExtractTool
assert PdfExtractTool._pdf_row_exact_term_match(
"DeepSeek-VL2 | 0.4B | 93.3 | 86.0 | 84.2",
"DeepSeek-VL2",
)
assert PdfExtractTool._pdf_row_exact_term_match(
"LLaVA- Onevision | 83.5 | 56.4",
"LLaVA-Onevision",
)
assert not PdfExtractTool._pdf_row_exact_term_match(
"DeepSeek-VL2-Tiny | 0.4B | 88.9 | 81.0 | 80.7",
"DeepSeek-VL2",
)
assert not PdfExtractTool._pdf_row_exact_term_match(
"DeepSeek-VL2-Small | 0.4B | 92.3 | 84.5 | 83.4",
"DeepSeek-VL2",
)
def test_pdf_model_alias_matches_abbreviated_header_not_named_variant():
from src.agent_tools.web_tools import PdfExtractTool
assert "R1-Zero" in PdfExtractTool._pdf_model_suffix_aliases(
"DeepSeek-R1-Zero"
)
assert PdfExtractTool._pdf_row_contains_model_alias(
"Benchmark | R1-Zero | R1-Dev1 | R1",
"DeepSeek-R1-Zero",
)
assert not PdfExtractTool._pdf_row_contains_model_alias(
"DeepSeek-R1-Distill-Qwen-32B | 72.6 | 94.3",
"DeepSeek-R1",
)
def test_pdf_metric_terms_include_short_table_acronyms_without_generic_noise():
from src.agent_tools.web_tools import PdfExtractTool
for term in ("TR", "AR", "NQA", "ER", "PQA", "AO", "AC"):
assert PdfExtractTool._looks_like_pdf_metric_term(term)
for term in ("PDF", "URL", "HTML", "GPT", "LLM", "VLM"):
assert not PdfExtractTool._looks_like_pdf_metric_term(term)
tokens = PdfExtractTool._pdf_query_tokens(
"Table scores for TR AR NQA ER PQA AO AC"
)
assert {"TR", "AR", "NQA", "ER", "PQA", "AO", "AC"} <= set(tokens)
def test_pdf_model_terms_reject_hyphenated_query_prose():
from src.agent_tools.web_tools import PdfExtractTool
for term in ("GPT-4o", "LLaVA-Onevision", "DeepSeek-VL2", "Qwen3-235B-A22B"):
assert PdfExtractTool._looks_like_pdf_model_term(term)
for term in ("multiple-choice", "state-of-the-art", "cross-document"):
assert not PdfExtractTool._looks_like_pdf_model_term(term)
for term in ("1", "2024", "2501.12948"):
assert not PdfExtractTool._looks_like_pdf_model_term(term)
def test_pdf_short_metric_matching_uses_cells_not_substrings():
from src.agent_tools.web_tools import PdfExtractTool
table_words = [{"text": "TR"}, {"text": "AR"}, {"text": "VS"}]
prose_words = [{"text": "arXiv"}, {"text": "training"}, {"text": "report"}]
assert PdfExtractTool._pdf_words_contain_metric(table_words, "TR")
assert PdfExtractTool._pdf_words_contain_metric(table_words, "AR")
assert not PdfExtractTool._pdf_words_contain_metric(prose_words, "TR")
assert not PdfExtractTool._pdf_words_contain_metric(prose_words, "AR")
def test_pdf_table_label_matching_is_exact():
from src.agent_tools.web_tools import PdfExtractTool
rows = [
(90.0, [{"text": "TR"}, {"text": "AR"}]),
(459.0, [{"text": "Table"}, {"text": "2."}, {"text": "Results"}]),
]
assert PdfExtractTool._pdf_rows_contain_table_label(rows, 2)
assert not PdfExtractTool._pdf_rows_contain_table_label(rows, 3)
def test_plain_pdf_web_fetch_redirects_to_pdf_extract():
import asyncio
from src.agent_tools.web_tools import WebFetchTool
result = asyncio.run(WebFetchTool().execute(
'{"url":"https://arxiv.org/pdf/2502.13923"}', {}
))
assert result["exit_code"] == 1
assert "Use pdf_extract" in result["error"]
def test_pdf_web_fetch_auto_focuses_from_active_request():
import asyncio
from unittest.mock import patch
from src.agent_tools.web_tools import WebFetchTool
result_doc = {
"content": "noise\nDocVQA 96.4 ChartQA 89.5 TextVQA 83.5\nnoise",
"title": "paper",
}
ctx = {"client_runtime_context": {"request_text": (
"Read the PDF and extract Qwen2.5-VL-72B DocVQA ChartQA TextVQA"
)}}
with patch("src.search.content.fetch_webpage_content", return_value=result_doc):
result = asyncio.run(WebFetchTool().execute(
'{"url":"https://arxiv.org/pdf/2502.13923"}', ctx
))
assert result["exit_code"] == 0
assert "DocVQA 96.4" in result["output"]
def test_python_http_download_is_rejected_before_subprocess():
import asyncio
from src.agent_tools.subprocess_tools import PythonTool
result = asyncio.run(PythonTool().execute(
'import requests; requests.get("https://example.test/file.pdf")', {}
))
assert result["exit_code"] == 1
assert "Use pdf_extract" in result["error"]
subprocess_download = asyncio.run(PythonTool().execute(
"import subprocess; subprocess.run(['curl', 'https://example.test/a.pdf'])", {}
))
assert subprocess_download["exit_code"] == 1
assert "Use pdf_extract" in subprocess_download["error"]
urlretrieve = asyncio.run(PythonTool().execute(
'import urllib.request; urllib.request.urlretrieve("https://example.test/a.pdf", "/tmp/a.pdf")',
{},
))
assert urlretrieve["exit_code"] == 1
def test_python_surfaces_failed_child_viewer_even_when_script_exits_zero():
import asyncio
from src.agent_tools.subprocess_tools import PythonTool
result = asyncio.run(PythonTool().execute(
"import sys; print(\"xdg-open: no method available for opening '/tmp/a.png'\", file=sys.stderr)",
{},
))
assert result["exit_code"] == 1
assert "child operation failed" in result["error"]
def test_python_keeps_warning_only_stderr_successful():
import asyncio
from src.agent_tools.subprocess_tools import PythonTool
result = asyncio.run(PythonTool().execute(
"import sys; print('ordinary warning', file=sys.stderr)",
{},
))
assert result["exit_code"] == 0
def test_python_emits_one_final_bare_expression_without_duplicating_print():
import asyncio
from src.agent_tools.subprocess_tools import PythonTool
bare = asyncio.run(PythonTool().execute(
"from collections import Counter\nCounter([1, 1, 2])", {},
))
explicit = asyncio.run(PythonTool().execute("print('once')", {}))
assert bare["exit_code"] == 0
assert bare["output"] == "Counter({1: 2, 2: 1})"
assert explicit["output"] == "once"
def test_python_loaded_code_sees_virtual_workspace_alias(monkeypatch):
"""Absolute /workspace paths must work inside generated Python scripts."""
import asyncio
import shutil
if not shutil.which("bwrap"):
return
from pathlib import Path
from src.agent_tools import subprocess_tools
from src import tool_execution
workspace = Path("/home/pewds/odysseus-tool-work")
script = workspace / ".python-workspace-alias-test.py"
output = workspace / ".python-workspace-alias-test.txt"
script.write_text(
"from pathlib import Path; Path('/workspace/.python-workspace-alias-test.txt').write_text('ok')"
)
monkeypatch.setattr(tool_execution, "agent_cwd", lambda: str(workspace))
result = asyncio.run(subprocess_tools.PythonTool().execute(
f"import runpy; runpy.run_path('{script}', run_name='__main__')",
{},
))
assert result["exit_code"] == 0, result
assert output.read_text() == "ok"
script.unlink()
output.unlink()
def test_workspace_namespace_preserves_the_64_bit_dynamic_loader(monkeypatch):
"""The namespace's /lib64 must mirror usrmerge hosts, not /usr/lib."""
import shlex
from src.agent_tools import subprocess_tools
monkeypatch.setattr(subprocess_tools.shutil, "which", lambda name: "/usr/bin/bwrap")
command = subprocess_tools._wrap_workspace_namespace("echo ok", "/tmp/workspace")
assert command is not None
args = shlex.split(command)
assert ["--symlink", "usr/lib64", "/lib64"] == args[args.index("/lib") + 1 : args.index("/lib") + 4]
def test_native_web_tool_shell_wrapper_is_repaired_without_claw_mapping():
from src.agent_loop import _normalize_native_tool_shell_wrapper
from src.tool_types import ToolBlock
block = _normalize_native_tool_shell_wrapper(
ToolBlock("bash", "web_fetch https://arxiv.org/pdf/2502.13923"),
"Compare Qwen2.5-VL-72B DocVQA ChartQA TextVQA",
)
assert block.tool_type == "web_fetch"
assert '"query": "Qwen2.5-VL-72B, DocVQA, ChartQA, TextVQA"' in block.content
untouched = _normalize_native_tool_shell_wrapper(
ToolBlock("bash", "echo web_fetch https://example.test"), ""
)
assert untouched.tool_type == "bash"
def test_bash_rejects_http_download_and_sudo_before_subprocess():
import asyncio
from src.agent_tools.subprocess_tools import BashTool
download = asyncio.run(BashTool().execute(
'curl -L https://example.test/paper.pdf -o /tmp/paper.pdf', {}
))
assert download["exit_code"] == 1
assert "Use pdf_extract" in download["error"]
privileged = asyncio.run(BashTool().execute("sudo apt-get install poppler-utils", {}))
assert privileged["exit_code"] == 1
assert "privilege escalation" in privileged["error"]
def test_forced_finish_keeps_tools_only_for_missing_terminal_artifacts():
from src.agent_loop import _force_answer_keeps_artifact_tools
assert _force_answer_keeps_artifact_tools(
force_answer=True,
artifact_recovery_enabled=True,
artifact_creation_requested=True,
missing_artifacts=("/workspace/output.html",),
)
assert not _force_answer_keeps_artifact_tools(
force_answer=True,
artifact_recovery_enabled=True,
artifact_creation_requested=True,
missing_artifacts=(),
)
assert not _force_answer_keeps_artifact_tools(
force_answer=True,
artifact_recovery_enabled=False,
artifact_creation_requested=True,
missing_artifacts=("/workspace/output.html",),
)
assert _force_answer_keeps_artifact_tools(
force_answer=True,
artifact_recovery_enabled=True,
artifact_creation_requested=True,
missing_artifacts=(),
correction_available=True,
convergence_sent=False,
)
assert not _force_answer_keeps_artifact_tools(
force_answer=True,
artifact_recovery_enabled=True,
artifact_creation_requested=True,
missing_artifacts=(),
correction_available=True,
convergence_sent=True,
)