Files
wehub-resource-sync 2c632336aa
CI / Viewer CI (push) Successful in 13m37s
CI / Core CI (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:38 +08:00

687 lines
26 KiB
Python

from __future__ import annotations
import asyncio
import json
import logging
from pathlib import Path
import pytest
from agent import runner
from agent.models import AgentResult, TerminateReason
from agent.prompts import DESIGNER_PROMPT_NAME
from storage.library_manifest import manifest_by_id
from storage.repo import StorageRepo
from tests.helpers import FakeAgent
@pytest.fixture
def fake_agent(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(runner, "ArticraftAgent", FakeAgent)
def _active_revision_dir(record_dir: Path) -> Path:
record = json.loads((record_dir / "record.json").read_text(encoding="utf-8"))
return record_dir / "revisions" / record.get("active_revision_id", "rev_000001")
def _artifact_path(record_dir: Path, filename: str) -> Path:
return _active_revision_dir(record_dir) / filename
class DeletingImageAgent(FakeAgent):
async def run(self, user_content: object): # type: ignore[override]
if isinstance(user_content, list):
for item in user_content:
if not isinstance(item, dict):
continue
image_path = item.get("image_path")
if isinstance(image_path, str):
Path(image_path).unlink()
break
return await super().run(user_content)
class MeshVisualAgent(FakeAgent):
async def run(self, user_content: object): # type: ignore[override]
self.file_path.parent.mkdir(parents=True, exist_ok=True)
self.file_path.write_text(
"from __future__ import annotations\n\nobject_model = None\n",
encoding="utf-8",
)
meshes_dir = self.file_path.parent / "assets" / "meshes"
meshes_dir.mkdir(parents=True, exist_ok=True)
(meshes_dir / "part.obj").write_text(
"\n".join(
[
"o part",
"v 0 0 0",
"v 1 0 0",
"v 0 1 0",
"f 1 2 3",
"",
]
),
encoding="utf-8",
)
cost_path = self.file_path.parent / "cost.json"
cost_path.write_text(
json.dumps({"total": {"costs_usd": {"total": 0.123456}}}, indent=2),
encoding="utf-8",
)
if self.trace_dir is not None:
self.trace_dir.mkdir(parents=True, exist_ok=True)
(self.trace_dir / "trajectory.jsonl").write_text(
'{"type":"message","message":{"role":"assistant","content":"done"}}\n',
encoding="utf-8",
)
return AgentResult(
success=True,
reason=TerminateReason.CODE_VALID,
message="done",
conversation=[{"role": "user", "content": user_content}],
final_code=self.file_path.read_text(encoding="utf-8"),
urdf_xml=(
"<robot name='mesh_visual'>"
"<link name='base'>"
"<visual><geometry><mesh filename='assets/meshes/part.obj'/></geometry></visual>"
"</link>"
"</robot>"
),
compile_warnings=[],
turn_count=3,
tool_call_count=5,
compile_attempt_count=2,
usage={"prompt_tokens": 10, "candidates_tokens": 5, "total_tokens": 15},
)
class MeshVisualWithUnusedAssetsAgent(MeshVisualAgent):
async def run(self, user_content: object): # type: ignore[override]
result = await super().run(user_content)
meshes_dir = self.file_path.parent / "assets" / "meshes"
(meshes_dir / "orphan.obj").write_text("# orphan\n", encoding="utf-8")
glb_dir = self.file_path.parent / "assets" / "glb"
glb_dir.mkdir(parents=True, exist_ok=True)
(glb_dir / "orphan.glb").write_bytes(b"orphan")
return result
class ContextResetAgent(FakeAgent):
async def run(self, user_content: object): # type: ignore[override]
result = await super().run(user_content)
result.context_reset_count = 2
return result
class OverBudgetAgent(FakeAgent):
async def run(self, user_content: object): # type: ignore[override]
self.file_path.parent.mkdir(parents=True, exist_ok=True)
self.file_path.write_text("# over budget\n", encoding="utf-8")
(self.file_path.parent / "cost.json").write_text(
json.dumps({"total": {"costs_usd": {"total": 0.75}}}, indent=2),
encoding="utf-8",
)
return AgentResult(
success=False,
reason=TerminateReason.COST_LIMIT,
message="Cost limit exceeded after turn 2: cumulative $0.750000 exceeded limit $0.500000",
conversation=[{"role": "user", "content": user_content}],
final_code=self.file_path.read_text(encoding="utf-8"),
urdf_xml=None,
compile_warnings=[],
turn_count=2,
tool_call_count=1,
compile_attempt_count=0,
usage={"prompt_tokens": 10, "candidates_tokens": 5, "total_tokens": 15},
)
class AllInTotalsAgent(FakeAgent):
async def run(self, user_content: object): # type: ignore[override]
self.file_path.parent.mkdir(parents=True, exist_ok=True)
self.file_path.write_text(
"from __future__ import annotations\n\nobject_model = None\n",
encoding="utf-8",
)
(self.file_path.parent / "cost.json").write_text(
json.dumps(
{
"total": {
"tokens": {
"prompt_tokens": 100,
"candidates_tokens": 20,
"total_tokens": 120,
"cached_tokens": 0,
},
"costs_usd": {"total": 0.75},
},
"all_in_total": {
"tokens": {
"prompt_tokens": 140,
"candidates_tokens": 25,
"total_tokens": 165,
"cached_tokens": 10,
"uncached_prompt_tokens": 130,
},
"costs_usd": {"total": 0.91},
},
},
indent=2,
),
encoding="utf-8",
)
return AgentResult(
success=True,
reason=TerminateReason.CODE_VALID,
message="done",
conversation=[{"role": "user", "content": user_content}],
final_code=self.file_path.read_text(encoding="utf-8"),
urdf_xml="<robot name='test'/>",
compile_warnings=[],
turn_count=1,
tool_call_count=0,
compile_attempt_count=0,
usage={"prompt_tokens": 100, "candidates_tokens": 20, "total_tokens": 120},
)
def test_library_run_and_rerun_persist_runtime_artifacts(
fake_agent: None,
tmp_path: Path,
) -> None:
repo_root = tmp_path
exit_code = asyncio.run(
runner.run_from_input(
"make a gearbox",
prompt_text="make a gearbox",
display_prompt="make a gearbox",
repo_root=repo_root,
image_path=None,
provider="openai",
thinking_level="high",
max_turns=30,
system_prompt_path=DESIGNER_PROMPT_NAME,
sdk_package="sdk",
label="gearbox try",
tags=["gear", "test"],
)
)
assert exit_code == 0
records_root = repo_root / "data" / "records"
record_dirs = [path for path in records_root.iterdir() if path.is_dir()]
assert len(record_dirs) == 1
record_dir = record_dirs[0]
materialization_dir = repo_root / "data" / "cache" / "record_materialization" / record_dir.name
revision_dir = _active_revision_dir(record_dir)
assert _artifact_path(record_dir, "prompt.txt").read_text(encoding="utf-8") == "make a gearbox"
assert _artifact_path(record_dir, "model.py").exists()
assert _artifact_path(record_dir, "provenance.json").exists()
assert _artifact_path(record_dir, "cost.json").exists()
assert (revision_dir / "traces").exists()
assert (revision_dir / "traces" / "trajectory.jsonl.zst").exists()
assert not (revision_dir / "traces" / "trajectory.jsonl").exists()
assert (materialization_dir / "model.urdf").read_text(
encoding="utf-8"
) == "<robot name='test'/>"
assert (materialization_dir / "compile_report.json").exists()
compile_report = json.loads(
(materialization_dir / "compile_report.json").read_text(encoding="utf-8")
)
assert compile_report["metrics"]["compile_level"] == "full"
assert compile_report["metrics"]["fingerprint_inputs"]["model_py_sha256"]
assert compile_report["metrics"]["materialization_fingerprint"]
assert not (materialization_dir / "assets" / "meshes").exists()
record = json.loads((record_dir / "record.json").read_text(encoding="utf-8"))
assert record["record_id"] == record_dir.name
assert record["label"] == "gearbox try"
assert record["tags"] == ["gear", "test"]
manifest_row = manifest_by_id(StorageRepo(repo_root))[record_dir.name]
assert manifest_row["label"] == "gearbox try"
assert manifest_row["tags"] == ["gear", "test"]
runs_root = repo_root / "data" / "cache" / "runs"
run_dirs = [path for path in runs_root.iterdir() if path.is_dir()]
assert len(run_dirs) == 1
run_metadata = json.loads((run_dirs[0] / "run.json").read_text(encoding="utf-8"))
assert run_metadata["status"] == "success"
results_lines = (run_dirs[0] / "results.jsonl").read_text(encoding="utf-8").splitlines()
assert len(results_lines) == 1
assert json.loads(results_lines[0])["record_id"] == record_dir.name
assert not (run_dirs[0] / "staging" / record_dir.name).exists()
assert not (repo_root / "outputs").exists()
original_record = json.loads((record_dir / "record.json").read_text(encoding="utf-8"))
original_run_id = original_record["source"]["run_id"]
original_created_at = original_record["created_at"]
original_provenance = json.loads(
_artifact_path(record_dir, "provenance.json").read_text(encoding="utf-8")
)
system_prompt_sha = original_provenance["prompting"]["system_prompt_sha256"]
assert (repo_root / "data" / "system_prompts" / f"{system_prompt_sha}.txt").exists()
_artifact_path(record_dir, "provenance.json").write_text(
json.dumps(original_provenance, indent=2) + "\n",
encoding="utf-8",
)
original_record["rating"] = 4
original_record["rated_by"] = "mattzh72"
(record_dir / "record.json").write_text(
json.dumps(original_record, indent=2) + "\n",
encoding="utf-8",
)
_artifact_path(record_dir, "model.py").write_text("# stale\n", encoding="utf-8")
_artifact_path(record_dir, "cost.json").write_text('{"stale": true}\n', encoding="utf-8")
(revision_dir / "traces" / "stale.txt").write_text("stale\n", encoding="utf-8")
stale_glb_dir = record_dir / "assets" / "glb"
stale_glb_dir.mkdir(parents=True, exist_ok=True)
(stale_glb_dir / "stale.glb").write_text("stale\n", encoding="utf-8")
stale_viewer_dir = record_dir / "assets" / "viewer"
stale_viewer_dir.mkdir(parents=True, exist_ok=True)
(stale_viewer_dir / "index.html").write_text("stale\n", encoding="utf-8")
rerun_exit_code = asyncio.run(
runner.rerun_record_in_place(
repo_root=repo_root,
record_id=record_dir.name,
)
)
assert rerun_exit_code == 0
record_dirs = [path for path in records_root.iterdir() if path.is_dir()]
assert [path.name for path in record_dirs] == [record_dir.name]
updated_record = json.loads((record_dir / "record.json").read_text(encoding="utf-8"))
assert updated_record["record_id"] == record_dir.name
assert updated_record["created_at"] == original_created_at
assert updated_record["source"]["run_id"] != original_run_id
assert (
_artifact_path(record_dir, "model.py")
.read_text(encoding="utf-8")
.startswith("from __future__ import annotations")
)
assert (
json.loads(_artifact_path(record_dir, "cost.json").read_text(encoding="utf-8"))["total"][
"costs_usd"
]["total"]
== 0.123456
)
updated_revision_dir = _active_revision_dir(record_dir)
assert (updated_revision_dir / "traces" / "trajectory.jsonl.zst").exists()
assert not (updated_revision_dir / "traces" / "trajectory.jsonl").exists()
assert not (updated_revision_dir / "traces" / "stale.txt").exists()
assert not (record_dir / "assets" / "glb").exists()
assert not (record_dir / "assets" / "viewer").exists()
assert not (materialization_dir / "assets" / "meshes").exists()
assert updated_record["label"] == "gearbox try"
assert updated_record["tags"] == ["gear", "test"]
assert updated_record["rating"] == 4
assert updated_record["rated_by"] == "mattzh72"
manifest_row = manifest_by_id(StorageRepo(repo_root))[record_dir.name]
assert manifest_row["label"] == "gearbox try"
assert manifest_row["tags"] == ["gear", "test"]
assert manifest_row["rating"] == 4
run_dirs = sorted(path for path in runs_root.iterdir() if path.is_dir())
assert len(run_dirs) == 2
latest_run_metadata = json.loads((run_dirs[-1] / "run.json").read_text(encoding="utf-8"))
assert latest_run_metadata["status"] == "success"
latest_results = (run_dirs[-1] / "results.jsonl").read_text(encoding="utf-8").splitlines()
assert len(latest_results) == 1
assert json.loads(latest_results[0])["record_id"] == record_dir.name
assert not (run_dirs[-1] / "staging" / record_dir.name).exists()
def test_run_from_input_stamps_author_and_rerun_preserves_existing_author(
fake_agent: None,
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(runner, "_resolve_runtime_record_author", lambda repo_root: "mattzh72")
exit_code = asyncio.run(
runner.run_from_input(
"make a hinge",
prompt_text="make a hinge",
display_prompt="make a hinge",
repo_root=tmp_path,
image_path=None,
provider="openai",
thinking_level="high",
max_turns=10,
system_prompt_path=DESIGNER_PROMPT_NAME,
sdk_package="sdk",
)
)
assert exit_code == 0
record_dir = next((tmp_path / "data" / "records").iterdir())
original_record = json.loads((record_dir / "record.json").read_text(encoding="utf-8"))
assert original_record["author"] == "mattzh72"
monkeypatch.setattr(runner, "_resolve_runtime_record_author", lambda repo_root: "RuiningLi")
rerun_exit_code = asyncio.run(
runner.rerun_record_in_place(
repo_root=tmp_path,
record_id=record_dir.name,
)
)
assert rerun_exit_code == 0
updated_record = json.loads((record_dir / "record.json").read_text(encoding="utf-8"))
assert updated_record["author"] == "mattzh72"
def test_create_draft_record_stamps_author_and_manifest_immediately(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(runner, "_resolve_runtime_record_author", lambda repo_root: "mattzh72")
record_dir = runner.create_draft_record(
repo_root=tmp_path,
prompt_text="make a draft hinge",
provider="openai",
thinking_level="high",
max_turns=10,
system_prompt_path=DESIGNER_PROMPT_NAME,
sdk_package="sdk",
)
record = json.loads((record_dir / "record.json").read_text(encoding="utf-8"))
assert record["author"] == "mattzh72"
assert manifest_by_id(StorageRepo(tmp_path))[record_dir.name]["record_id"] == record_dir.name
def test_over_budget_run_persists_failed_run_metadata_without_record(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
monkeypatch.setattr(runner, "ArticraftAgent", OverBudgetAgent)
exit_code = asyncio.run(
runner.run_from_input(
"make an expensive model",
prompt_text="make an expensive model",
display_prompt="make an expensive model",
repo_root=tmp_path,
image_path=None,
provider="openai",
thinking_level="high",
max_turns=30,
max_cost_usd=0.5,
system_prompt_path=DESIGNER_PROMPT_NAME,
sdk_package="sdk",
)
)
assert exit_code == 2
records_root = tmp_path / "data" / "records"
assert not records_root.exists() or list(records_root.iterdir()) == []
run_dir = next(
path for path in (tmp_path / "data" / "cache" / "runs").iterdir() if path.is_dir()
)
run_metadata = json.loads((run_dir / "run.json").read_text(encoding="utf-8"))
assert run_metadata["status"] == "failed"
assert run_metadata["settings_summary"]["max_cost_usd"] == 0.5
result_rows = [
json.loads(line)
for line in (run_dir / "results.jsonl").read_text(encoding="utf-8").splitlines()
]
assert len(result_rows) == 1
assert "Cost limit exceeded" in result_rows[0]["message"]
staging_dirs = [path for path in (run_dir / "staging").iterdir() if path.is_dir()]
assert len(staging_dirs) == 1
assert (
json.loads((staging_dirs[0] / "cost.json").read_text(encoding="utf-8"))["total"][
"costs_usd"
]["total"]
== 0.75
)
def test_successful_run_logs_all_in_cost_totals(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
caplog: pytest.LogCaptureFixture,
) -> None:
monkeypatch.setattr(runner, "ArticraftAgent", AllInTotalsAgent)
with caplog.at_level(logging.INFO):
exit_code = asyncio.run(
runner.run_from_input(
"make a model with compaction",
prompt_text="make a model with compaction",
display_prompt="make a model with compaction",
repo_root=tmp_path,
image_path=None,
provider="openai",
thinking_level="high",
max_turns=30,
system_prompt_path=DESIGNER_PROMPT_NAME,
sdk_package="sdk",
label=None,
tags=[],
)
)
assert exit_code == 0
assert (
"Total tokens: {'prompt_tokens': 140, 'cached_tokens': 10, 'uncached_prompt_tokens': 130, 'candidates_tokens': 25, 'total_tokens': 165}"
in caplog.text
)
assert "Total cost: $0.910000" in caplog.text
def test_successful_run_preserves_visual_meshes_as_obj_by_default(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
monkeypatch.setattr(runner, "ArticraftAgent", MeshVisualAgent)
exit_code = asyncio.run(
runner.run_from_input(
"make a mesh part",
prompt_text="make a mesh part",
display_prompt="make a mesh part",
repo_root=tmp_path,
image_path=None,
provider="openai",
thinking_level="high",
max_turns=30,
system_prompt_path=DESIGNER_PROMPT_NAME,
sdk_package="sdk",
label=None,
tags=[],
)
)
assert exit_code == 0
records_root = tmp_path / "data" / "records"
record_dirs = [path for path in records_root.iterdir() if path.is_dir()]
assert len(record_dirs) == 1
record_dir = record_dirs[0]
materialization_dir = tmp_path / "data" / "cache" / "record_materialization" / record_dir.name
assert "assets/meshes/part.obj" in (materialization_dir / "model.urdf").read_text(
encoding="utf-8"
)
assert not (materialization_dir / "assets" / "meshes" / "part.glb").exists()
assert (materialization_dir / "assets" / "meshes" / "part.obj").exists()
def test_successful_run_copies_only_referenced_mesh_assets(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
monkeypatch.setattr(runner, "ArticraftAgent", MeshVisualWithUnusedAssetsAgent)
exit_code = asyncio.run(
runner.run_from_input(
"make a mesh part with extras",
prompt_text="make a mesh part with extras",
display_prompt="make a mesh part with extras",
repo_root=tmp_path,
image_path=None,
provider="openai",
thinking_level="high",
max_turns=30,
system_prompt_path=DESIGNER_PROMPT_NAME,
sdk_package="sdk",
label=None,
tags=[],
)
)
assert exit_code == 0
records_root = tmp_path / "data" / "records"
record_dirs = [path for path in records_root.iterdir() if path.is_dir()]
assert len(record_dirs) == 1
record_dir = record_dirs[0]
materialization_dir = tmp_path / "data" / "cache" / "record_materialization" / record_dir.name
assert "assets/meshes/part.obj" in (materialization_dir / "model.urdf").read_text(
encoding="utf-8"
)
assert not (materialization_dir / "assets" / "meshes" / "part.glb").exists()
assert (materialization_dir / "assets" / "meshes" / "part.obj").exists()
assert not (materialization_dir / "assets" / "meshes" / "orphan.obj").exists()
assert not (materialization_dir / "assets" / "glb").exists()
def test_library_run_and_rerun_preserve_category_metadata(
fake_agent: None,
tmp_path: Path,
) -> None:
repo_root = tmp_path
exit_code = asyncio.run(
runner.run_from_input(
"make a tower crane",
prompt_text="make a tower crane",
display_prompt="make a tower crane",
repo_root=repo_root,
image_path=None,
provider="openai",
thinking_level="high",
max_turns=30,
system_prompt_path=DESIGNER_PROMPT_NAME,
sdk_package="sdk",
category_slug="crane_tower",
)
)
assert exit_code == 0
records_root = repo_root / "data" / "records"
record_dirs = [path for path in records_root.iterdir() if path.is_dir()]
assert len(record_dirs) == 1
record_dir = record_dirs[0]
record = json.loads((record_dir / "record.json").read_text(encoding="utf-8"))
assert record["category_slug"] == "crane_tower"
assert not (record_dir / "collections").exists()
manifest_row = manifest_by_id(StorageRepo(repo_root))[record_dir.name]
assert manifest_row["category_slug"] == "crane_tower"
assert manifest_row["category_title"] == "Crane Tower"
runs_root = repo_root / "data" / "cache" / "runs"
run_dirs = [path for path in runs_root.iterdir() if path.is_dir()]
assert len(run_dirs) == 1
run_metadata = json.loads((run_dirs[0] / "run.json").read_text(encoding="utf-8"))
assert run_metadata["status"] == "success"
assert run_metadata["run_mode"] == "library_single"
assert not (run_dirs[0] / "staging" / record_dir.name).exists()
original_run_id = record["source"]["run_id"]
_artifact_path(record_dir, "model.py").write_text("# stale dataset\n", encoding="utf-8")
rerun_exit_code = asyncio.run(
runner.rerun_record_in_place(
repo_root=repo_root,
record_id=record_dir.name,
)
)
assert rerun_exit_code == 0
record = json.loads((record_dir / "record.json").read_text(encoding="utf-8"))
assert record["record_id"] == record_dir.name
assert record["source"]["run_id"] != original_run_id
assert record["category_slug"] == "crane_tower"
assert (
_artifact_path(record_dir, "model.py")
.read_text(encoding="utf-8")
.startswith("from __future__ import annotations")
)
manifest_row = manifest_by_id(StorageRepo(repo_root))[record_dir.name]
assert manifest_row["category_slug"] == "crane_tower"
assert manifest_row["category_title"] == "Crane Tower"
run_dirs = sorted(path for path in runs_root.iterdir() if path.is_dir())
assert len(run_dirs) == 2
latest_run_metadata = json.loads((run_dirs[-1] / "run.json").read_text(encoding="utf-8"))
assert latest_run_metadata["status"] == "success"
assert latest_run_metadata["run_mode"] == "library_single"
assert not (run_dirs[-1] / "staging" / record_dir.name).exists()
def test_library_run_succeeds_when_persisted_input_image_disappears(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
monkeypatch.setattr(runner, "ArticraftAgent", DeletingImageAgent)
repo_root = tmp_path
image_path = repo_root / "reference.png"
image_path.write_bytes(b"\x89PNG\r\n\x1a\n")
exit_code = asyncio.run(
runner.run_from_input(
[
{"type": "input_text", "text": "make a lamp"},
{"type": "input_image", "image_path": str(image_path), "detail": "high"},
],
prompt_text="make a lamp",
display_prompt="make a lamp",
repo_root=repo_root,
image_path=image_path,
provider="openai",
thinking_level="high",
max_turns=30,
system_prompt_path=DESIGNER_PROMPT_NAME,
sdk_package="sdk",
label="lamp try",
tags=["lamp"],
)
)
assert exit_code == 0
record_dir = next((repo_root / "data" / "records").iterdir())
materialization_dir = repo_root / "data" / "cache" / "record_materialization" / record_dir.name
assert (record_dir / "record.json").exists()
assert _artifact_path(record_dir, "provenance.json").exists()
assert (materialization_dir / "compile_report.json").exists()
compile_report = json.loads(
(materialization_dir / "compile_report.json").read_text(encoding="utf-8")
)
assert compile_report["metrics"]["compile_level"] == "full"
assert compile_report["metrics"]["fingerprint_inputs"]["model_py_sha256"]
assert compile_report["metrics"]["materialization_fingerprint"]
assert list((_active_revision_dir(record_dir) / "inputs").iterdir()) == []
run_dir = next((repo_root / "data" / "cache" / "runs").iterdir())
run_metadata = json.loads((run_dir / "run.json").read_text(encoding="utf-8"))
assert run_metadata["status"] == "success"
results = (run_dir / "results.jsonl").read_text(encoding="utf-8").splitlines()
assert len(results) == 1
assert json.loads(results[0])["status"] == "success"
assert not (run_dir / "staging" / record_dir.name).exists()