Download tests/test_intent.py from jkorstad/m3d-core: direct link, hf CLI and curl.
- Browser
- Download file 3.19 kB
-
https://huggingface.co/jkorstad/m3d-core/resolve/main/tests/test_intent.py
- Command line
-
hf download hf://jkorstad/m3d-core/tests/test_intent.py
-
curl -L -o test_intent.py https://huggingface.co/jkorstad/m3d-core/resolve/main/tests/test_intent.py
3.19 kB
| """Intent-driven editing contract tests: intent -> plan -> approval-gated execution.""" | |
| from __future__ import annotations | |
| import json | |
| import wave | |
| import pytest | |
| from m3d.modules.gltf_mcp import build_module as build_gltf | |
| from m3d.modules.intent_planner import build_module as build_planner | |
| from m3d.modules.mock_edit import build_sfx_module, build_texture_module | |
| from m3d.runner import Runner | |
| from m3d.store import Store | |
| from tests.fixtures import make_character_glb, make_sample_image | |
| def _env(tmp_path): | |
| store = Store(root=tmp_path / "cas") | |
| modules = { | |
| "std/gltf-mcp": build_gltf(store), | |
| "std/mock-texture": build_texture_module(store), | |
| "std/mock-sfx": build_sfx_module(store), | |
| "std/intent-planner": build_planner(store), | |
| } | |
| runner = Runner(store, modules, gates_dir=tmp_path / "gates") | |
| return store, modules, runner | |
| def _hero_asset(tmp_path, store): | |
| """A hero-knight asset: mesh GLB + texture PNG + footsteps WAV + .asset.json.""" | |
| mesh = tmp_path / "hero.glb" | |
| make_character_glb(mesh) | |
| tex = tmp_path / "cloak.png" | |
| make_sample_image(tex) | |
| wav_path = tmp_path / "footsteps.wav" | |
| with wave.open(str(wav_path), "w") as w: | |
| w.setnchannels(1) | |
| w.setsampwidth(2) | |
| w.setframerate(8000) | |
| w.writeframes(b"\x00\x00" * 8000) | |
| manifest = { | |
| "asset": store.put_file(mesh), | |
| "canonical": "hero.glb", | |
| "parts": [ | |
| {"role": "mesh", "uri": str(mesh)}, | |
| {"role": "texture/albedo", "uri": str(tex)}, | |
| {"role": "audio", "uri": str(wav_path)}, | |
| ], | |
| "provenance": [], | |
| } | |
| manifest_uri = store.put_bytes(json.dumps(manifest).encode(), "hero.asset.json") | |
| return manifest_uri | |
| def test_intent_plan_and_execute(tmp_path): | |
| store, modules, runner = _env(tmp_path) | |
| manifest_uri = _hero_asset(tmp_path, store) | |
| # 1) intent -> plan | |
| planner = modules["std/intent-planner"] | |
| res = planner.run_tool("plan", { | |
| "intent": "make the cloak tattered and the footsteps heavier", | |
| "manifest_uri": manifest_uri, | |
| }) | |
| assert res["ok"] is True | |
| plan_wf = res["plan"] | |
| node_ids = {n["id"] for n in plan_wf["nodes"]} | |
| # texture + audio modules AND the Blender human-gate, but rig untouched | |
| assert "tex" in node_ids and "sfx" in node_ids and "mesh-edit" in node_ids | |
| assert "rig" not in node_ids | |
| # the planner must respect the manifest: no matching part -> no invented nodes | |
| with pytest.raises(ValueError): | |
| planner.run_tool("plan", {"intent": "re-rig the character", "manifest_uri": manifest_uri}) | |
| # 2) execute the emitted plan through the same runner, approvals pre-seeded | |
| for nid in ("mesh-edit", "review"): | |
| stem = Runner._gate_stem(plan_wf["id"], nid) | |
| (tmp_path / "gates" / f"{stem}.approved.json").write_text(json.dumps({"decision": "approve"})) | |
| result = runner.run(plan_wf) | |
| out = result["outputs"] | |
| assert out["tex"]["output_kind"] == "uri:image" | |
| assert out["sfx"]["output_kind"] == "uri:audio" | |
| assert out["review"]["decision"] == "approve" | |
| assert out["post"]["counts"]["meshes"] == 1 # conditioned edge taken |