Loom / tools /sim_service.py
deploy-space action
deploy 6158a00 (c)
b972454
Raw
History Blame Contribute Delete
9.53 kB
from __future__ import annotations
import asyncio
import json
import os
import shutil
import sys
import tempfile
import time
from pathlib import Path
# In-process test of the service wiring for /api/run (no HTTP / no background
# server — those are flaky under the sandbox). Exercises: template listing,
# schematic resolution, the sync run path (run_orchestration_sync), and the
# async path (JobRunner.submit_run + snapshot polling). Zero real API calls.
# MOCK_MODE=1 python tools/sim_service.py
os.environ.setdefault("MOCK_MODE", "1")
os.environ.setdefault("LOOM_LOG", "0")
os.environ["CRITIQUE_TOKEN"] = "test"
os.environ.pop("METRICS_HF_REPO", None)
for key in ("NVIDIA_API_KEY", "CF_API_TOKEN", "CF_ACCOUNT_ID", "OPENROUTER_API_KEY", "GITHUB_TOKEN"):
os.environ[key] = "mock"
for p in ("NVIDIA", "CLOUDFLARE", "OPENROUTER", "GITHUB_MODELS"):
os.environ[f"MOCK_RPD_{p}"] = "1000"
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
import metrics as metrics_mod # noqa: E402
_TMP = Path(tempfile.mkdtemp(prefix="sim-svc-"))
metrics_mod.DATA_DIR = _TMP
metrics_mod.FLUSH_EVERY = 1
import orchestrate # noqa: E402
orchestrate.USER_TEMPLATES_DIR = _TMP / "user_templates"
import critique_service as cs # noqa: E402
from jobs import JobRunner # noqa: E402
FAILS: list[str] = []
def check(name, cond, detail=""):
print(f" [{'PASS' if cond else 'FAIL'}] {name}" + (f" ({detail})" if detail else ""))
if not cond:
FAILS.append(name)
ARTIFACT_SCHEM = {
"task_type": "code_spec", "task": "tiny app",
"stages": [{"name": "build", "role": "generator",
"instructions": "Build the requested project.", "inputs": ["prompt"],
"max_tokens": 2000}],
"output_rules": {"format": "files"},
"judge_config": {"rules": [], "plugins": [], "llm_judges": []}, "max_rounds": 1,
}
async def scenario_templates_and_sync():
print("scenario: templates listing + schematic resolution + sync run")
store = orchestrate.TemplateStore()
ids = [t["id"] for t in store.list()]
check("built-in templates listed", set(ids) >= {"freeform", "lesson_plan", "research_paper"}, f"ids={ids}")
schem, nonce, _ = orchestrate.resolve_schematic(store, "Write notes.", template_id="freeform", schematic_obj=None)
check("freeform resolves, no nonce (markdown)", schem.task_type == "freeform" and nonce == "")
schem2, nonce2, _ = orchestrate.resolve_schematic(store, "x", template_id=None, schematic_obj=ARTIFACT_SCHEM)
check("files schematic gets a nonce", bool(nonce2), f"nonce={nonce2!r}")
panel = cs.Panel()
res = await cs.run_orchestration_sync(panel, schem, "Write a haiku.",
profile="svc", effort="med", nonce=nonce)
check("sync run ok", res.get("ok"), f"err={res.get('error')}")
check("sync run has body", bool(res.get("body")))
res2 = await cs.run_orchestration_sync(panel, schem2, "Build a CLI.",
profile="svc", effort="med", nonce=nonce2)
paths = sorted(a["path"] for a in res2.get("artifacts", []))
check("sync run yields file artifacts", paths == ["README.md", "src/app.py"], f"paths={paths}")
agg = panel.metrics.aggregates("svc")
check("run metrics recorded per profile", agg["events"] >= 2, f"events={agg['events']}")
def scenario_async_jobrunner():
print("scenario: async JobRunner.submit_run + snapshot polling")
panel = cs.Panel()
runner = JobRunner(panel, judge_timeout_s=60.0)
schem, nonce, _ = orchestrate.resolve_schematic(panel.templates, "x", template_id=None, schematic_obj=ARTIFACT_SCHEM)
snap = runner.submit_run(schematic=schem, prompt="Build a CLI.", nonce=nonce,
profile="svc_async", effort="high")
check("submit returns run snapshot", snap.get("type") == "run" and snap.get("status") == "running")
final = None
for _ in range(30):
time.sleep(0.5)
final = runner.snapshot(snap["job_id"])
if final and final.get("status") == "complete":
break
check("async run completes", final and final.get("status") == "complete",
f"status={(final or {}).get('status')}")
result = (final or {}).get("result") or {}
check("async run ok", result.get("ok"), f"err={result.get('error')}")
check("async run artifacts present", sorted(a["path"] for a in result.get("artifacts", [])) == ["README.md", "src/app.py"])
async def scenario_panel_artifacts():
print("scenario: panel-mode artifacts keep each model's tree separate")
panel = cs.Panel()
params = cs.build_panel_params(
panel, input_text="build a tiny tool", role="generator", system=None,
instructions="", output_rules="", template=None,
panel_override=["llama-3.3-70b", "glm-5.1"], merge_mode="none",
max_tokens=2000, want_artifacts=True)
params["profile"] = "pa"
cs.apply_effort(params, "high")
check("artifacts panel got a nonce", bool(params.get("nonce")))
res = await cs.run_sync(panel, params)
judges = [j for j in res["judges"] if j.get("ok")]
check("multiple judges answered", len(judges) >= 2, f"n={len(judges)}")
each_have = all(sorted(a["path"] for a in j.get("artifacts", [])) == ["README.md", "src/app.py"]
for j in judges)
check("each judge has its OWN file tree", each_have,
detail=str([(j["model"], [a["path"] for a in j.get("artifacts", [])]) for j in judges]))
def scenario_materialize():
print("scenario: client materializes a run result to disk")
sys.path.insert(0, str(Path(__file__).resolve().parent))
from orchestrate_client import materialize_run
result = {
"task": "demo", "ok": True, "rounds": 0,
"body": "ignored when artifacts present",
"artifacts": [{"path": "README.md", "content": "# hi\n", "truncated": False},
{"path": "src/app.py", "content": "print(1)\n", "truncated": False}],
"judge": {"hard_fails": [], "soft_flags": ["minor note"]},
"stages": [], "providers_used": ["nvidia"],
}
dest = materialize_run(result, _TMP / "runs")
files = {str(p.relative_to(dest)) for p in dest.rglob("*") if p.is_file()}
check("materialized files on disk", files >= {"README.md", "src/app.py", "_judge.md", "_manifest.json"}, f"files={files}")
check("artifact content written", (dest / "src/app.py").read_text() == "print(1)\n")
# path-traversal defence
bad = materialize_run({"task": "t", "ok": True, "artifacts": [
{"path": "../escape.txt", "content": "x"}], "judge": {}}, _TMP / "runs2")
check("traversal artifact not written outside dir", not (_TMP / "escape.txt").exists())
async def scenario_template_registry():
print("scenario: user template registry (save/list/get/run/delete) + planner auto")
import orchestrate
os.environ["USER_TEMPLATES_DIR"] = str(_TMP / "user_templates")
orchestrate.USER_TEMPLATES_DIR = _TMP / "user_templates"
store = orchestrate.TemplateStore()
# save a user template
summary = store.save("my_builder", ARTIFACT_SCHEM)
check("save returns summary", summary["id"] == "my_builder" and summary["source"] == "user")
ids = [t["id"] for t in store.list()]
check("user template listed alongside built-ins", set(ids) >= {"freeform", "my_builder"}, f"ids={ids}")
check("get_dict returns the schematic", store.get_dict("my_builder")["task_type"] == "code_spec")
# reject built-in overwrite + bad schematic
try:
store.save("freeform", ARTIFACT_SCHEM); check("reject builtin overwrite", False)
except orchestrate.OrchestrateError:
check("reject builtin overwrite", True)
try:
store.save("bad", {"task_type": "x"}); check("reject invalid schematic", False)
except orchestrate.OrchestrateError:
check("reject invalid schematic", True)
# persistence: a fresh store reloads it
store2 = orchestrate.TemplateStore()
check("user template persisted across reload", "my_builder" in store2.ids())
# run by user-template id
panel = cs.Panel()
schem, nonce, plan_mode = orchestrate.resolve_schematic(
panel.templates, "build it", template_id="my_builder", schematic_obj=None)
check("resolve user template", schem.task_type == "code_spec" and bool(nonce) and not plan_mode)
# planner "auto" mode
schem_n, nonce_n, plan_mode_n = orchestrate.resolve_schematic(
panel.templates, "write a poem", template_id="auto", schematic_obj=None)
check("auto -> plan_mode (deferred)", schem_n is None and plan_mode_n)
res = await cs.run_orchestration_sync(panel, None, "Write a short poem.",
profile="plan", effort="med", nonce="", plan_mode=True)
check("planner auto run ok", res.get("ok"), f"err={res.get('error')}")
# delete
check("delete user template", store2.delete("my_builder") and "my_builder" not in orchestrate.TemplateStore().ids())
async def main():
await scenario_templates_and_sync()
scenario_async_jobrunner()
await scenario_panel_artifacts()
scenario_materialize()
await scenario_template_registry()
print()
if FAILS:
print(f"FAILED ({len(FAILS)}): {FAILS}")
return 1
print("ALL SERVICE-WIRING SCENARIOS PASSED (zero real API calls)")
return 0
if __name__ == "__main__":
try:
rc = asyncio.run(main())
finally:
shutil.rmtree(_TMP, ignore_errors=True)
raise SystemExit(rc)