agent-harness / scripts /preflight_study5.py
cuber12's picture
Publish agent harness research code and paper artifacts
d61821a verified
Raw
History Blame Contribute Delete
8.18 kB
#!/usr/bin/env python3
"""Outcome-blind runtime, design, repository, and executor preflight for Study 5."""
from __future__ import annotations
from dataclasses import asdict
import importlib.metadata
import json
from pathlib import Path
import platform
import shutil
import sys
import time
from typing import Any
from preflight_study3 import PACKAGES, _command, _executor_conformance, _tool_probe
from agent_harness.lm_studio import LMStudioClient
from agent_harness.lm_studio_embeddings import LMStudioEmbeddingClient
from agent_harness.lm_studio_management import LMStudioResidencyManager, LMStudioServer
from agent_harness.pilot import research_code_revision
from agent_harness.protocol_experiment import protocol_tool_definitions
from agent_harness.specs import (
load_edit_interfaces,
load_embeddings,
load_experiments,
load_harnesses,
load_models,
load_repositories,
load_task_split,
load_tasks,
validate_configuration_tree,
)
from agent_harness.study2_experiment import tokenizer_for
EXPERIMENTS = ("E13", "E14", "E15")
EXPECTED = {"E13": 1440, "E14": 540, "E15": 540}
def run(root: Path) -> dict[str, Any]:
revision = research_code_revision(root)
errors, warnings = validate_configuration_tree(root)
if errors or warnings:
raise RuntimeError(f"configuration failed: errors={errors}, warnings={warnings}")
for experiment_id in EXPERIMENTS:
raw = root / "results" / "raw" / experiment_id
if raw.exists() and any(raw.rglob("*")):
raise RuntimeError(f"{experiment_id} raw outcomes exist before preflight")
audit = json.loads((root / "docs" / "STUDY5_DESIGN_AUDIT.json").read_text())
if not audit.get("outcome_blind"):
raise RuntimeError("Study 5 design audit is not outcome blind")
manifests: dict[str, Any] = {}
for experiment_id in EXPERIMENTS:
manifest = json.loads(
(root / "configs" / "study5" / f"{experiment_id}_cells.json").read_text()
)
if manifest.get("planned_cells") != EXPECTED[experiment_id]:
raise RuntimeError(f"{experiment_id} manifest count mismatch")
if len(manifest.get("cells", [])) != EXPECTED[experiment_id]:
raise RuntimeError(f"{experiment_id} cell array mismatch")
manifests[experiment_id] = manifest
tasks = load_tasks(root)
fresh = load_task_split(root / "tasks" / "splits" / "study5_fresh.txt")
if len(fresh) != 17 or len({tasks[item].gold_commit for item in fresh}) != 17:
raise RuntimeError("Study 5 fresh split must contain 17 unique gold commits")
prior = {task.gold_commit for task_id, task in tasks.items() if task_id not in fresh}
if prior & {tasks[item].gold_commit for item in fresh}:
raise RuntimeError("Study 5 fresh split overlaps a prior task commit")
disk = shutil.disk_usage(root)
if disk.free < 20 * 1024**3:
raise RuntimeError(f"less than 20 GiB free before Study 5: {disk.free} bytes")
models = load_models(root)
experiments = load_experiments(root)
embedding = load_embeddings(root)[experiments["E13"].embedding_id]
repositories = load_repositories(root)
harnesses = load_harnesses(root)
interfaces = load_edit_interfaces(root)
first_task = tasks[manifests["E15"]["cells"][0]["task_id"]]
tool_signatures = {
harness_id: [
item["function"]["name"]
for item in protocol_tool_definitions(interfaces["P002"], first_task, harnesses[harness_id])
]
for harness_id in experiments["E15"].harness_ids
}
if tool_signatures["H008"] == tool_signatures["H011"]:
raise RuntimeError("unified and specialized search signatures did not separate")
server = LMStudioServer(port=1234)
server_state = server.ensure_running()
residency = LMStudioResidencyManager(
models["M002"].base_url, models["M002"].api_token_env, timeout_seconds=1_800
)
report: dict[str, Any] = {
"schema_version": 1,
"study": "Study 5 / E13--E15",
"outcome_blind": True,
"research_code_revision": revision,
"planned_cells": sum(EXPECTED.values()),
"design_audit": audit,
"fresh_task_count": len(fresh),
"started_unix": time.time(),
"platform": platform.platform(),
"python": sys.version,
"torch_used": False,
"disk": {"total": disk.total, "used": disk.used, "free": disk.free},
"lms_version": _command([str(server.cli_path), "--version"]),
"server_start": server_state,
"dependencies": {package: importlib.metadata.version(package) for package in PACKAGES},
"executor_conformance": _executor_conformance(root),
"tool_signatures": tool_signatures,
"repositories": {},
"embedding": {},
"models": {},
}
try:
residency.unload_all()
for repository_id, repository in sorted(repositories.items()):
observed = _command(["git", "rev-parse", "HEAD"], cwd=root / repository.local_path)
if observed["returncode"] or observed["stdout"].strip() != repository.pinned_head:
raise RuntimeError(f"repository head mismatch: {repository_id}: {observed}")
report["repositories"][repository_id] = {
"spec": asdict(repository),
"observed_head": observed["stdout"].strip(),
}
transition = residency.ensure_exclusive(
embedding.model_key, embedding.loaded_context_length
)
client = LMStudioEmbeddingClient(embedding, timeout_seconds=1_800)
report["embedding"] = {
"spec": asdict(embedding),
"config_hash": embedding.config_hash,
"transition": transition.to_dict(),
"resolved": client.resolve(),
"probe": client.probe().to_dict(),
"unload": residency.unload_all().to_dict(),
}
for model_id in experiments["E13"].model_ids:
model = models[model_id]
transition = residency.ensure_exclusive(model.expected_inference_key, model.context_length)
model_client = LMStudioClient(model, timeout_seconds=1_800)
discovery, resolved = model_client.resolve()
tokenizer = tokenizer_for(model)
report["models"][model_id] = {
"spec": asdict(model),
"config_hash": model.config_hash,
"transition": transition.to_dict(),
"resolved": resolved.to_dict(),
"discovery_errors": discovery.endpoint_errors,
"tokenizer_path": str(tokenizer.path),
"tokenizer_sha256": tokenizer.sha256,
"tool_probe": _tool_probe(model_client, resolved.inference_key),
"unload": residency.unload_all().to_dict(),
}
report["passed"] = True
return report
finally:
cleanup_errors = []
try:
report["final_unload"] = residency.unload_all().to_dict()
except Exception as exc:
cleanup_errors.append(f"unload_all: {exc}")
try:
status = server.status()
report["server_stop"] = (
server.stop() if status["running"] else {"action": "already_stopped", "status": status}
)
except Exception as exc:
cleanup_errors.append(f"server_stop: {exc}")
report["cleanup_errors"] = cleanup_errors
report["finished_unix"] = time.time()
def main() -> None:
root = Path(__file__).resolve().parents[1]
output = root / "results" / "reports" / "study5_preflight.json"
output.parent.mkdir(parents=True, exist_ok=True)
report: dict[str, Any] = {}
try:
report = run(root)
except Exception as exc:
report = {**report, "passed": False, "error": repr(exc)}
output.write_text(json.dumps(report, indent=2, sort_keys=True, default=str) + "\n")
raise
output.write_text(json.dumps(report, indent=2, sort_keys=True, default=str) + "\n")
print(json.dumps({"passed": True, "report": str(output)}, indent=2))
if __name__ == "__main__":
main()