agent-harness / src /agent_harness /study2_experiment.py
cuber12's picture
Publish agent harness research code and paper artifacts
d61821a verified
Raw
History Blame Contribute Delete
36.6 kB
"""Prospective E08 multi-repository live-agent experiment.
Component harness cells reuse the audited E07 engine with generalized inputs.
A001 is a three-stage Agentless-style controlled adaptation; A002 uses the
same engine with a controlled SWE-agent-style tool interface.
"""
from __future__ import annotations
from dataclasses import asdict, replace
from hashlib import sha256
import json
from pathlib import Path
import resource
import time
from typing import Any, Sequence
from .live_agent_experiment import (
AgentWorkspace,
TaskRetrieval,
_build_task_retrieval,
_failure_validation,
_usage_totals,
run_live_agent_cell,
)
from .lm_studio import LMStudioClient, LMStudioTransportError
from .lm_studio_embeddings import LMStudioEmbeddingClient
from .lm_studio_management import LMStudioResidencyManager, LMStudioServer
from .pilot import research_code_revision, retrieval_metrics
from .repair_experiment import (
PatchOutputError,
extract_unified_diff,
isolated_git_tree,
validate_generated_patch,
)
from .repository import GitSnapshot
from .retrieval import SQLiteEmbeddingCache
from .specs import (
AgentSystemSpec,
EmbeddingSpec,
ExperimentSpec,
ModelSpec,
RepositorySpec,
TaskSpec,
load_agent_systems,
load_embeddings,
load_experiments,
load_harnesses,
load_models,
load_repositories,
load_task_split,
load_tasks,
)
from .telemetry import EventWriter, RunIdentity, load_completed_or_archive_incomplete
from .tokenization import QwenTokenCounter
AGENTLESS_INDEX_TOKENS = 42_000
AGENTLESS_SOURCE_TOKENS = 48_000
TOKENIZER_PATHS = {
"M002": Path.home()
/ ".lmstudio/models/lmstudio-community/Qwen3.6-35B-A3B-MLX-4bit/tokenizer.json",
"M003": Path.home()
/ ".lmstudio/models/mlx-community/gpt-oss-20b-MXFP4-Q8/tokenizer.json",
"M004": Path.home()
/ ".lmstudio/models/lmstudio-community/Qwen3-Coder-30B-A3B-Instruct-MLX-4bit/tokenizer.json",
}
class Study2ExperimentError(RuntimeError):
"""Raised when E08 cannot preserve its preregistered protocol."""
class _RuntimeLease:
"""Guarantee exclusive-model cleanup on success and on infrastructure errors."""
def __init__(
self,
server: LMStudioServer,
residency: LMStudioResidencyManager,
stop_server: bool,
):
self.server = server
self.residency = residency
self.stop_server = stop_server
self.server_state: dict[str, Any] | None = None
self.final_transition: dict[str, Any] | None = None
self.stop_state: dict[str, Any] | None = None
self.cleanup_errors: list[str] = []
def __enter__(self) -> dict[str, Any]:
self.server_state = self.server.ensure_running()
return self.server_state
def __exit__(self, exc_type: Any, exc: Any, traceback: Any) -> None:
try:
self.final_transition = self.residency.unload_all().to_dict()
except Exception as cleanup_error:
self.cleanup_errors.append(f"unload_all: {cleanup_error}")
if self.stop_server:
try:
status = self.server.status()
if status["running"]:
self.stop_state = self.server.stop()
else:
self.stop_state = {
"action": "already_stopped",
"status": status,
}
except Exception as cleanup_error:
self.cleanup_errors.append(f"server_stop: {cleanup_error}")
if exc_type is None and self.cleanup_errors:
raise Study2ExperimentError(
"runtime cleanup failed: " + "; ".join(self.cleanup_errors)
)
def tokenizer_for(model: ModelSpec) -> QwenTokenCounter:
try:
path = TOKENIZER_PATHS[model.model_id]
except KeyError as exc:
raise Study2ExperimentError(f"no frozen tokenizer for {model.model_id}") from exc
if not path.exists():
raise Study2ExperimentError(f"frozen tokenizer is unavailable: {path}")
return QwenTokenCounter(path)
def _identity(
experiment: ExperimentSpec,
task: TaskSpec,
system: AgentSystemSpec,
model: ModelSpec,
revision: str,
seed: int,
repetition: int,
) -> RunIdentity:
return RunIdentity(
experiment_id=experiment.experiment_id,
task_id=task.task_id,
harness_id=system.system_id,
harness_hash=system.config_hash,
model_id=model.model_id,
model_key=model.expected_inference_key,
model_config_hash=model.config_hash,
context_budget=experiment.context_budgets[0],
seed=seed,
repetition=repetition,
repository_sha=task.base_commit,
code_revision=revision,
)
def _assistant_content(response: dict[str, Any]) -> str:
try:
content = response["choices"][0]["message"]["content"]
except (KeyError, IndexError, TypeError) as exc:
raise Study2ExperimentError("agentless stage returned no assistant message") from exc
if not isinstance(content, str):
raise Study2ExperimentError("agentless stage assistant content is not text")
return content
def _json_object(content: str) -> dict[str, Any]:
stripped = content.strip()
fence = chr(96) * 3
if stripped.startswith(fence):
stripped = stripped.strip(chr(96))
if "\n" in stripped:
stripped = stripped.split("\n", 1)[1]
start, end = stripped.find("{"), stripped.rfind("}")
if start < 0 or end < start:
raise ValueError("response contains no JSON object")
value = json.loads(stripped[start : end + 1])
if not isinstance(value, dict):
raise ValueError("response JSON is not an object")
return value
def _select_files(content: str, tracked_paths: Sequence[str]) -> tuple[str, ...]:
allowed = set(tracked_paths)
selected: list[str] = []
try:
value = _json_object(content)
raw = value.get("files", [])
if isinstance(raw, list):
selected.extend(str(item) for item in raw if str(item) in allowed)
except (ValueError, json.JSONDecodeError):
pass
if not selected:
selected.extend(path for path in tracked_paths if path in content)
return tuple(dict.fromkeys(selected))[:10]
def _pack_blocks(
tokenizer: QwenTokenCounter,
blocks: Sequence[tuple[str, str]],
budget: int,
) -> tuple[str, tuple[str, ...], int]:
selected: list[str] = []
paths: list[str] = []
used = 0
for path, block in blocks:
count = tokenizer.count(block)
if count > budget and not selected:
block = block[: budget * 3]
count = tokenizer.count(block)
if used + count > budget:
continue
selected.append(block)
paths.append(path)
used += count
return "\n".join(selected), tuple(paths), used
def _repository_index(
retrieval: TaskRetrieval,
tracked_paths: Sequence[str],
tokenizer: QwenTokenCounter,
) -> tuple[str, int]:
blocks: list[tuple[str, str]] = []
for path in sorted(tracked_paths):
symbols = retrieval.graph.by_path.get(path, ())
names = ", ".join(item.name for item in symbols[:30])
line = path if not names else f"{path} :: {names}"
blocks.append((path, line + "\n"))
text, _, tokens = _pack_blocks(tokenizer, blocks, AGENTLESS_INDEX_TOKENS)
return text, tokens
def _source_context(
tree: Path,
paths: Sequence[str],
tokenizer: QwenTokenCounter,
) -> tuple[str, tuple[str, ...], int]:
blocks: list[tuple[str, str]] = []
for path in paths:
target = tree / path
if target.is_file():
blocks.append(
(
path,
f"\n--- FILE: {path} ---\n"
+ target.read_text(encoding="utf-8", errors="replace")
+ "\n",
)
)
return _pack_blocks(tokenizer, blocks, AGENTLESS_SOURCE_TOKENS)
def run_agentless_cell(
root: Path,
repository: Path,
experiment: ExperimentSpec,
task: TaskSpec,
system: AgentSystemSpec,
model: ModelSpec,
embedding: EmbeddingSpec,
retrieval: TaskRetrieval,
residency: LMStudioResidencyManager,
server: LMStudioServer,
tokenizer: QwenTokenCounter,
revision: str,
seed: int = 0,
repetition: int = 0,
) -> dict[str, Any]:
if system.system_id != "A001":
raise Study2ExperimentError("agentless cell requires A001")
identity = _identity(experiment, task, system, model, revision, seed, repetition)
completed = load_completed_or_archive_incomplete(root / "results", identity)
if completed is not None:
return completed
initial_transition = residency.ensure_exclusive(
model.expected_inference_key, model.context_length
)
client = LMStudioClient(model, timeout_seconds=experiment.timeout_seconds)
_, resolved = client.resolve()
suffixes = {"go": (".go",), "python": (".py",)}
tracked_paths = GitSnapshot(repository).tracked_paths(
task.base_commit, suffixes[task.language]
)
resolved_model = {
"agent_model": asdict(model),
"embedding_model": asdict(embedding),
"agent_runtime": resolved.to_dict(),
"tokenizer_path": str(tokenizer.path),
"tokenizer_sha256": tokenizer.sha256,
"initial_residency_transition": initial_transition.to_dict(),
}
with isolated_git_tree(repository, task.base_commit, task.repository_url) as tree, EventWriter(
root / "results", identity, asdict(system), resolved_model
) as writer:
workspace = AgentWorkspace(tree, tracked_paths, task, system.max_test_runs)
transitions: list[dict[str, Any]] = [initial_transition.to_dict()]
responses: list[dict[str, Any]] = []
stage_records: list[dict[str, Any]] = []
protocol_violations: list[str] = []
model_elapsed = 0.0
selected_paths: tuple[str, ...] = ()
patch = ""
finished_reason = "completed_three_stages"
cell_started = time.monotonic()
def call_stage(name: str, prompt: str, max_tokens: int) -> str:
nonlocal model_elapsed
transition = residency.ensure_exclusive(
model.expected_inference_key, model.context_length
)
transitions.append(transition.to_dict())
writer.emit(
"resource_sample",
{"kind": "model_residency_transition", **transition.to_dict()},
)
messages = [
{
"role": "system",
"content": (
"You are one stage in a controlled Agentless-style coding "
"pipeline. Follow the requested output schema exactly. Hidden "
"tests and gold changes are unavailable."
),
},
{"role": "user", "content": prompt},
]
started = time.monotonic()
try:
_, active = client.resolve()
response = client.chat_completions(
active.inference_key,
messages,
max_tokens=max_tokens,
seed=seed,
)
except LMStudioTransportError as first_error:
recovery = server.ensure_running()
writer.emit(
"resource_sample",
{"kind": "server_recovery", "error": str(first_error), "recovery": recovery},
)
transition = residency.ensure_exclusive(
model.expected_inference_key, model.context_length
)
transitions.append(transition.to_dict())
_, active = client.resolve()
response = client.chat_completions(
active.inference_key,
messages,
max_tokens=max_tokens,
seed=seed,
)
elapsed = time.monotonic() - started
model_elapsed += elapsed
responses.append(response)
content = _assistant_content(response)
record = {
"stage": name,
"prompt": prompt,
"response": content,
"elapsed_seconds": elapsed,
"usage": response.get("usage", {}),
"prompt_tokens_local": tokenizer.count(prompt),
}
stage_records.append(record)
turn = len(stage_records)
writer.write_artifact(
f"model_response_{turn:02d}.json",
json.dumps(response, indent=2, sort_keys=True) + "\n",
)
writer.emit(
"model_call",
{
"turn": turn,
"stage": name,
"elapsed_seconds": elapsed,
"usage": response.get("usage", {}),
"input_conversation_tokens": record["prompt_tokens_local"],
},
)
return content
writer.emit(
"run_started",
{
"confirmatory": True,
"blinded": True,
"task_config_hash": task.config_hash,
"stages": ["file_localization", "line_localization", "repair"],
"budgets": {
"model_calls": system.model_calls,
"tool_calls": system.max_tool_calls,
"test_runs": system.max_test_runs,
"timeout_seconds": experiment.timeout_seconds,
},
},
)
index_tokens = 0
source_tokens = 0
try:
index, index_tokens = _repository_index(retrieval, tracked_paths, tokenizer)
stage1 = call_stage(
"file_localization",
f"""ISSUE:
{task.statement}
REPOSITORY FILE/SYMBOL INDEX:
{index}
Select at most 10 likely production files. Return only JSON:
{{"files": ["path/from/index"], "rationale": "brief"}}""",
max_tokens=2_048,
)
selected_paths = _select_files(stage1, tracked_paths)
if not selected_paths:
protocol_violations.append("A001 file localization selected no valid paths")
finished_reason = "file_localization_failure"
raise ValueError("no valid localized files")
source, included_paths, source_tokens = _source_context(
tree, selected_paths, tokenizer
)
selected_paths = included_paths
stage2 = call_stage(
"line_localization",
f"""ISSUE:
{task.statement}
CANDIDATE SOURCE:
{source}
Identify the exact functions or line regions that require change. Return only JSON:
{{"locations": [{{"path": "...", "symbol_or_lines": "...", "reason": "..."}}]}}""",
max_tokens=2_048,
)
stage3 = call_stage(
"repair",
f"""ISSUE:
{task.statement}
LOCALIZATION:
{stage2}
CANDIDATE SOURCE:
{source}
Return only a standard unified diff. Modify production files only, do not add tests,
and make the smallest correct change.""",
max_tokens=model.max_tokens,
)
patch = extract_unified_diff(
{"choices": [{"message": {"content": stage3}}]}
)
apply_result = workspace.apply_patch(patch)
writer.emit("edit", apply_result)
if not apply_result["accepted"]:
finished_reason = "patch_apply_failure"
elif system.max_test_runs:
command = task.pass_to_pass_tests[0]
public_test = workspace.run_tests(command)
writer.emit("test_run", public_test)
except (ValueError, PatchOutputError, json.JSONDecodeError) as exc:
protocol_violations.append(f"A001 protocol: {exc}")
if not patch:
finished_reason = "protocol_failure"
final_patch = workspace.final_patch()
if final_patch:
validation = validate_generated_patch(
root,
repository,
task,
final_patch,
preserve_git_metadata=True,
)
else:
validation = _failure_validation("empty_patch")
edited_paths = tuple(sorted(workspace.edited_paths))
usage = _usage_totals(responses)
elapsed = time.monotonic() - cell_started
final = {
"run_id": identity.run_id,
"experiment_id": experiment.experiment_id,
"task_id": task.task_id,
"harness_id": system.system_id,
"resolved_at_1": validation["resolved_at_1"],
"failure_stage": validation["failure_stage"],
"patch_applied": bool(
validation.get("model_patch_apply")
and validation["model_patch_apply"].get("returncode") == 0
),
"fail_to_pass": validation["fail_to_pass"],
"pass_to_pass": validation["pass_to_pass"],
"modified_files": edited_paths,
"localization_metrics": retrieval_metrics(edited_paths, task.gold_files),
"search_localization_metrics": retrieval_metrics(
selected_paths, task.gold_files
),
"read_localization_metrics": retrieval_metrics(
selected_paths, task.gold_files
),
"finished_reason": finished_reason,
"finish_summary": "",
"protocol_violations": protocol_violations,
"model_calls": len(responses),
"tool_calls": 0,
"tool_counts": {},
"test_runs": len(workspace.test_runs),
"usage": usage,
"elapsed_seconds": elapsed,
"model_elapsed_seconds": model_elapsed,
"model_switch_count": sum(
not item.get("reused") for item in transitions
),
"model_switch_seconds": sum(
float(item["elapsed_seconds"])
for item in transitions
if not item.get("reused")
),
"peak_process_rss_platform_units": resource.getrusage(
resource.RUSAGE_SELF
).ru_maxrss,
"dense_index_stats": retrieval.dense_index_stats,
"agentless_index_tokens": index_tokens,
"agentless_source_tokens": source_tokens,
"trajectory_sha256": sha256(
json.dumps(stage_records, sort_keys=True).encode()
).hexdigest(),
"patch_sha256": sha256(final_patch.encode()).hexdigest()
if final_patch
else None,
"residency_transitions": transitions,
"test_results": validation["tests"],
}
writer.write_artifact(
"messages.json", json.dumps(stage_records, indent=2, sort_keys=True) + "\n"
)
writer.write_artifact("model.patch", final_patch)
writer.write_artifact(
"validation.json", json.dumps(validation, indent=2, sort_keys=True) + "\n"
)
writer.write_artifact(
"final_metrics.json", json.dumps(final, indent=2, sort_keys=True) + "\n"
)
writer.emit(
"run_finished",
{
"status": "completed",
"resolved_at_1": validation["resolved_at_1"],
"failure_stage": validation["failure_stage"],
"finished_reason": finished_reason,
},
)
return final
def _repository_for_task(
repositories: dict[str, RepositorySpec],
task: TaskSpec,
) -> RepositorySpec:
matches = [
repository
for repository in repositories.values()
if repository.repository_url == task.repository_url
]
if len(matches) != 1:
raise Study2ExperimentError(
f"{task.task_id} has no unique repository registry entry"
)
return matches[0]
def _write_progress(
root: Path,
experiment: ExperimentSpec,
revision: str,
rows: Sequence[dict[str, Any]],
task_summaries: Sequence[dict[str, Any]],
) -> Path:
path = root / "results" / "reports" / f"{experiment.experiment_id}_progress.json"
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(
json.dumps(
{
"schema_version": 1,
"experiment_id": experiment.experiment_id,
"code_revision": revision,
"completed_cells": len(rows),
"resolved_cells": sum(bool(item["resolved_at_1"]) for item in rows),
"task_summaries": task_summaries,
"rows": rows,
},
indent=2,
sort_keys=True,
)
+ "\n",
encoding="utf-8",
)
return path
def run_study2_experiment(
root: Path,
experiment_id: str = "E08",
task_filter: set[str] | None = None,
treatment_filter: set[str] | None = None,
model_filter: set[str] | None = None,
stop_server_when_complete: bool = True,
) -> dict[str, Any]:
revision = research_code_revision(root)
experiment = load_experiments(root)[experiment_id]
if experiment.mode != "study2_live_agent":
raise Study2ExperimentError("Study 2 runner requires mode=study2_live_agent")
harness_catalog = load_harnesses(root)
system_catalog = load_agent_systems(root)
model_catalog = load_models(root)
repository_catalog = load_repositories(root)
task_catalog = load_tasks(root)
embedding = load_embeddings(root)[experiment.embedding_id]
split = load_task_split(
root / "tasks" / "splits" / f"{experiment.task_split}.txt"
)
tasks = [
task_catalog[item]
for item in split
if task_filter is None or item in task_filter
]
harnesses = [
harness_catalog[item]
for item in experiment.harness_ids
if treatment_filter is None or item in treatment_filter
]
systems = [
system_catalog[item]
for item in experiment.agent_system_ids
if treatment_filter is None or item in treatment_filter
]
models = [
model_catalog[item]
for item in experiment.model_ids
if model_filter is None or item in model_filter
]
if not tasks or not models or not (harnesses or systems):
raise Study2ExperimentError("Study 2 filters selected an empty execution block")
if any(task.validation_status != "end_to_end_ready" for task in tasks):
raise Study2ExperimentError("Study 2 includes a task without hidden-test validation")
server = LMStudioServer(port=1234)
residency = LMStudioResidencyManager(
models[0].base_url,
models[0].api_token_env,
timeout_seconds=experiment.timeout_seconds,
)
embedding_client = LMStudioEmbeddingClient(
embedding, timeout_seconds=experiment.timeout_seconds
)
cache_path = root / "indexes" / "embeddings" / f"{embedding.config_hash}.sqlite3"
treatments: list[tuple[str, Any]] = [
*((item.harness_id, item) for item in harnesses),
*((item.system_id, item) for item in systems),
]
rows: list[dict[str, Any]] = []
task_summaries: list[dict[str, Any]] = []
runtime = _RuntimeLease(server, residency, stop_server_when_complete)
with runtime as server_state, SQLiteEmbeddingCache(cache_path, embedding) as cache:
for task_index, task in enumerate(tasks):
repository_spec = _repository_for_task(repository_catalog, task)
repository = (root / repository_spec.local_path).resolve()
snapshot = GitSnapshot(repository)
snapshot.verify_commit(task.base_commit)
index_transition = residency.ensure_exclusive(
embedding.model_key, embedding.loaded_context_length
)
embedding_client.resolve()
index_started = time.monotonic()
retrieval = _build_task_retrieval(
snapshot, task, embedding, embedding_client, cache
)
index_elapsed = time.monotonic() - index_started
model_order = models if task_index % 2 == 0 else list(reversed(models))
offset = task_index % len(treatments)
treatment_order = treatments[offset:] + treatments[:offset]
task_rows: list[dict[str, Any]] = []
for model in model_order:
tokenizer = tokenizer_for(model)
for treatment_id, treatment in treatment_order:
if treatment_id.startswith("H"):
row = run_live_agent_cell(
root,
repository,
experiment,
task,
treatment,
model,
embedding,
retrieval,
residency,
server,
tokenizer,
revision,
seed=experiment.seeds[0],
repetition=0,
preserve_git_metadata=True,
)
elif treatment_id == "A001":
row = run_agentless_cell(
root,
repository,
experiment,
task,
treatment,
model,
embedding,
retrieval,
residency,
server,
tokenizer,
revision,
seed=experiment.seeds[0],
repetition=0,
)
elif treatment_id == "A002":
row = run_live_agent_cell(
root,
repository,
experiment,
task,
harness_catalog["H000"],
model,
embedding,
retrieval,
residency,
server,
tokenizer,
revision,
agent_system=treatment,
seed=experiment.seeds[0],
repetition=0,
preserve_git_metadata=True,
)
else:
raise Study2ExperimentError(
f"unsupported Study 2 treatment: {treatment_id}"
)
rows.append(row)
task_rows.append(row)
_write_progress(root, experiment, revision, rows, task_summaries)
summary = {
"task_id": task.task_id,
"repository_id": repository_spec.repository_id,
"language": task.language,
"model_order": [item.model_id for item in model_order],
"treatment_order": [item[0] for item in treatment_order],
"embedding_index_transition": index_transition.to_dict(),
"index_elapsed_seconds": index_elapsed,
"dense_index_stats": retrieval.dense_index_stats,
"cells": len(task_rows),
"resolved": sum(bool(item["resolved_at_1"]) for item in task_rows),
}
task_summaries.append(summary)
_write_progress(root, experiment, revision, rows, task_summaries)
report = {
"experiment_id": experiment.experiment_id,
"code_revision": revision,
"server_lifecycle": server_state,
"server_stop": runtime.stop_state,
"run_count": len(rows),
"resolved_count": sum(bool(item["resolved_at_1"]) for item in rows),
"task_summaries": task_summaries,
"final_residency_transition": runtime.final_transition,
"cleanup_errors": runtime.cleanup_errors,
"rows": rows,
}
report_dir = root / "results" / "reports"
report_dir.mkdir(parents=True, exist_ok=True)
report_path = report_dir / f"E08_{revision[:12]}_{int(time.time())}.json"
report_path.write_text(
json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
return {**report, "report_path": str(report_path)}
def run_study2_reliability(
root: Path,
manifest_path: Path | None = None,
stop_server_when_complete: bool = True,
) -> dict[str, Any]:
"""Run the 24 frozen non-oracle cells under the stochastic sensitivity profile."""
revision = research_code_revision(root)
experiment = load_experiments(root)["E08"]
path = manifest_path or root / "configs" / "reliability" / "E08_repeat_cells.json"
value = json.loads(path.read_text(encoding="utf-8"))
cells = value.get("cells", [])
seeds = tuple(int(item) for item in value.get("seeds", []))
temperature = float(value.get("temperature", -1.0))
top_p = float(value.get("top_p", -1.0))
if len(cells) != 24 or seeds != (0, 1, 2):
raise Study2ExperimentError(
"reliability manifest must freeze 24 cells and seeds 0,1,2"
)
if temperature != 0.2 or top_p != 1.0:
raise Study2ExperimentError(
"reliability manifest must freeze temperature=0.2 and top_p=1.0"
)
harness_catalog = load_harnesses(root)
system_catalog = load_agent_systems(root)
model_catalog = load_models(root)
repository_catalog = load_repositories(root)
task_catalog = load_tasks(root)
embedding = load_embeddings(root)[experiment.embedding_id]
server = LMStudioServer(port=1234)
residency = LMStudioResidencyManager(
model_catalog["M002"].base_url,
model_catalog["M002"].api_token_env,
timeout_seconds=experiment.timeout_seconds,
)
embedding_client = LMStudioEmbeddingClient(
embedding, timeout_seconds=experiment.timeout_seconds
)
cache_path = root / "indexes" / "embeddings" / f"{embedding.config_hash}.sqlite3"
rows: list[dict[str, Any]] = []
grouped: dict[str, list[dict[str, str]]] = {}
for raw in cells:
if not isinstance(raw, dict):
raise Study2ExperimentError("reliability cell must be an object")
cell = {key: str(raw[key]) for key in ("task_id", "treatment_id", "model_id")}
grouped.setdefault(cell["task_id"], []).append(cell)
runtime = _RuntimeLease(server, residency, stop_server_when_complete)
with runtime as server_state, SQLiteEmbeddingCache(cache_path, embedding) as cache:
for task_id, task_cells in grouped.items():
task = task_catalog[task_id]
repository_spec = _repository_for_task(repository_catalog, task)
repository = (root / repository_spec.local_path).resolve()
snapshot = GitSnapshot(repository)
index_transition = residency.ensure_exclusive(
embedding.model_key, embedding.loaded_context_length
)
embedding_client.resolve()
retrieval = _build_task_retrieval(
snapshot, task, embedding, embedding_client, cache
)
for cell in task_cells:
treatment_id = cell["treatment_id"]
base_model = model_catalog[cell["model_id"]]
model = replace(base_model, temperature=temperature, top_p=top_p)
tokenizer = tokenizer_for(model)
for seed in seeds:
if treatment_id.startswith("H"):
row = run_live_agent_cell(
root,
repository,
experiment,
task,
harness_catalog[treatment_id],
model,
embedding,
retrieval,
residency,
server,
tokenizer,
revision,
seed=seed,
repetition=1,
preserve_git_metadata=True,
)
elif treatment_id == "A001":
row = run_agentless_cell(
root,
repository,
experiment,
task,
system_catalog[treatment_id],
model,
embedding,
retrieval,
residency,
server,
tokenizer,
revision,
seed=seed,
repetition=1,
)
elif treatment_id == "A002":
row = run_live_agent_cell(
root,
repository,
experiment,
task,
harness_catalog["H000"],
model,
embedding,
retrieval,
residency,
server,
tokenizer,
revision,
agent_system=system_catalog[treatment_id],
seed=seed,
repetition=1,
preserve_git_metadata=True,
)
else:
raise Study2ExperimentError(
f"unknown reliability treatment: {treatment_id}"
)
rows.append(row)
progress = root / "results" / "reports" / "E08_reliability_progress.json"
progress.parent.mkdir(parents=True, exist_ok=True)
progress.write_text(
json.dumps(
{
"schema_version": 1,
"code_revision": revision,
"completed_cells": len(rows),
"planned_cells": 72,
"generation_profile": {
"temperature": temperature,
"top_p": top_p,
"seeds": seeds,
},
"rows": rows,
"last_index_transition": index_transition.to_dict(),
},
indent=2,
sort_keys=True,
)
+ "\n",
encoding="utf-8",
)
report = {
"experiment_id": "E08_reliability",
"code_revision": revision,
"server_lifecycle": server_state,
"server_stop": runtime.stop_state,
"run_count": len(rows),
"resolved_count": sum(bool(item["resolved_at_1"]) for item in rows),
"generation_profile": {
"temperature": temperature,
"top_p": top_p,
"seeds": seeds,
},
"final_residency_transition": runtime.final_transition,
"cleanup_errors": runtime.cleanup_errors,
"rows": rows,
}
report_path = (
root
/ "results"
/ "reports"
/ f"E08_reliability_{revision[:12]}_{int(time.time())}.json"
)
report_path.write_text(
json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
return {**report, "report_path": str(report_path)}