microharness 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
benchmarks/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ """benchmarks: Standardized benchmark suites for microharness."""
2
+
3
+ from benchmarks.mini_swe import get_mini_swe_suite
4
+ from benchmarks.runner import BenchmarkReport, run_benchmark_suite
5
+
6
+ __all__ = ["get_mini_swe_suite", "run_benchmark_suite", "BenchmarkReport"]
benchmarks/harbor.py ADDED
@@ -0,0 +1,215 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import subprocess
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ try:
10
+ import tomllib
11
+ except ModuleNotFoundError:
12
+ import tomli as tomllib # type: ignore
13
+
14
+ from core.environment import ContainerEnvironment, Environment
15
+ from core.types import EvalResult, StepTrace, Task
16
+
17
+ DEEPSWE_MINI_TASKS: list[str] = [
18
+ "aiomonitor-task-snapshots-diff",
19
+ "bandit-structured-nosec-directives",
20
+ "dasel-html-document-format",
21
+ "dynamodb-toolbox-lazy-recursive-schemas",
22
+ "expr-try-catch-errors",
23
+ "ipython-session-bundle-replay",
24
+ "koota-composite-trait-aspects",
25
+ "narwhals-rolling-window-suite",
26
+ "ofetch-per-origin-circuit-breaker",
27
+ "prometheus-transactional-reload-status",
28
+ "prometheus-typed-label-sorting",
29
+ "scc-bounded-memory-spilling",
30
+ "termenv-preserve-ansi-resets",
31
+ "textual-kitty-key-phases",
32
+ "valibot-recursive-schema-composition",
33
+ "yaegi-go-embed-directives",
34
+ ]
35
+
36
+
37
+ @dataclass
38
+ class HarborTaskDef:
39
+ task_id: str
40
+ display_title: str
41
+ language: str
42
+ repository_url: str
43
+ base_commit: str
44
+ docker_image: str
45
+ instruction: str
46
+ task_dir: Path
47
+
48
+
49
+ class HarborEvaluator:
50
+ """Evaluates Harbor and DeepSWE tasks inside an isolated Docker sandbox."""
51
+
52
+ def __init__(
53
+ self,
54
+ task_dir: Path | str,
55
+ base_commit: str,
56
+ collect_cmd: str | None = None,
57
+ timeout: float = 600.0,
58
+ ):
59
+ self.task_dir = Path(task_dir).resolve()
60
+ self.base_commit = base_commit
61
+ self.collect_cmd = collect_cmd
62
+ self.timeout = timeout
63
+
64
+ def evaluate(self, env: Environment, steps: list[StepTrace]) -> EvalResult:
65
+ if not isinstance(env, ContainerEnvironment) or not env.is_running:
66
+ return EvalResult(
67
+ passed=False,
68
+ score=0.0,
69
+ feedback="Harbor evaluation requires an active DockerEnvironment (ContainerEnvironment).",
70
+ )
71
+
72
+ container = env.container_name
73
+ tests_dir = self.task_dir / "tests"
74
+
75
+ # 1. Dynamically inject pristine tests into container at /tests/
76
+ if tests_dir.is_dir():
77
+ subprocess.run(
78
+ ["docker", "cp", f"{tests_dir}/.", f"{container}:/tests/"],
79
+ stdout=subprocess.DEVNULL,
80
+ stderr=subprocess.DEVNULL,
81
+ check=False,
82
+ )
83
+
84
+ # 2. Collect patch against base_commit so both staged & unstaged changes are preserved
85
+ collect_core = self.collect_cmd or (
86
+ f"git diff --binary {self.base_commit} HEAD > /logs/artifacts/model.patch"
87
+ )
88
+ collect_script = (
89
+ "mkdir -p /logs/artifacts /logs/verifier && cd /app && "
90
+ "git config --global --add safe.directory /app >/dev/null 2>&1 || true && "
91
+ "git add -A >/dev/null 2>&1 || true && "
92
+ "git -c user.name='Verifier' -c user.email='verifier@eval' commit -m 'eval_staging' >/dev/null 2>&1 || true && "
93
+ f"{collect_core} && "
94
+ f"git checkout -f {self.base_commit} >/dev/null 2>&1 || true && "
95
+ "git clean -fd >/dev/null 2>&1 || true"
96
+ )
97
+ env.execute_bash(collect_script, timeout=60.0)
98
+
99
+ # 3. Execute verifier script /tests/test.sh inside container
100
+ exit_code, test_output = env.execute_bash("bash /tests/test.sh", timeout=self.timeout)
101
+
102
+ # 4. Read reward artifact /logs/verifier/reward.json
103
+ _, reward_raw = env.execute_bash("cat /logs/verifier/reward.json", timeout=10.0)
104
+
105
+ reward_data: dict[str, Any] = {}
106
+ if reward_raw.strip():
107
+ try:
108
+ reward_data = json.loads(reward_raw.strip())
109
+ except Exception:
110
+ pass
111
+
112
+ reward = reward_data.get("reward", 0)
113
+ passed = reward == 1
114
+ partial = reward_data.get("partial")
115
+ score = float(partial) if partial is not None else float(reward)
116
+ f2p = reward_data.get("f2p_passed", 0)
117
+ f2p_tot = reward_data.get("f2p_total", 0)
118
+ p2p = reward_data.get("p2p_passed", 0)
119
+ p2p_tot = reward_data.get("p2p_total", 0)
120
+ partial_val = float(partial) if partial is not None else float(reward)
121
+
122
+ output_preview = test_output.strip()[-2000:] if test_output.strip() else "(no output)"
123
+ feedback = (
124
+ f"Harbor/DeepSWE Verification {'PASSED ✅' if passed else 'FAILED ❌'} (exit {exit_code})\n"
125
+ f"Score: {score:.2f} | Partial: {partial_val:.2f} | F2P: {f2p}/{f2p_tot} | P2P: {p2p}/{p2p_tot}\n"
126
+ f"Verifier Output:\n{output_preview}"
127
+ )
128
+ return EvalResult(passed=passed, score=score, feedback=feedback)
129
+
130
+
131
+ def load_harbor_task(
132
+ task_dir: Path | str,
133
+ max_steps: int = 30,
134
+ timeout_seconds: float = 900.0,
135
+ ) -> tuple[Task, HarborEvaluator]:
136
+ """Loads a Harbor/DeepSWE task definition from its directory."""
137
+ path = Path(task_dir).resolve()
138
+ toml_file = path / "task.toml"
139
+ instruction_file = path / "instruction.md"
140
+
141
+ if not toml_file.is_file():
142
+ raise FileNotFoundError(f"Missing task.toml in {path}")
143
+ if not instruction_file.is_file():
144
+ raise FileNotFoundError(f"Missing instruction.md in {path}")
145
+
146
+ with open(toml_file, "rb") as f:
147
+ cfg = tomllib.load(f)
148
+
149
+ meta = cfg.get("metadata", {})
150
+ task_id = meta.get("task_id") or path.name
151
+ base_commit = meta.get("base_commit_hash", "HEAD~1")
152
+ docker_image = cfg.get("environment", {}).get("docker_image")
153
+ if not docker_image:
154
+ raise ValueError(f"Task '{task_id}' has no docker_image in task.toml [environment].")
155
+
156
+ instruction = instruction_file.read_text(encoding="utf-8", errors="replace").strip()
157
+
158
+ collect_cmd = None
159
+ collect_list = cfg.get("verifier", {}).get("collect", [])
160
+ if isinstance(collect_list, list) and len(collect_list) > 0:
161
+ collect_cmd = collect_list[0].get("command")
162
+
163
+ task = Task(
164
+ id=task_id,
165
+ instruction=instruction,
166
+ max_steps=max_steps,
167
+ timeout_seconds=timeout_seconds,
168
+ docker_image=docker_image,
169
+ init_workdir="/app",
170
+ )
171
+ evaluator = HarborEvaluator(task_dir=path, base_commit=base_commit, collect_cmd=collect_cmd)
172
+ return task, evaluator
173
+
174
+
175
+ def get_deepswe_mini_suite(
176
+ dataset_dir: Path | str | None = None,
177
+ task_filter: str | None = None,
178
+ max_steps: int = 30,
179
+ timeout_seconds: float = 900.0,
180
+ ) -> list[tuple[Task, HarborEvaluator]]:
181
+ """Returns list of (Task, HarborEvaluator) for the 16 DeepSWE-mini tasks."""
182
+ base_path = (
183
+ Path(dataset_dir).resolve() if dataset_dir else Path("datasets/deep-swe/tasks").resolve()
184
+ )
185
+ if not base_path.is_dir():
186
+ # Fallback to current project root/datasets/deep-swe/tasks
187
+ candidate = Path(__file__).resolve().parent.parent / "datasets" / "deep-swe" / "tasks"
188
+ if candidate.is_dir():
189
+ base_path = candidate
190
+
191
+ if not base_path.is_dir():
192
+ return []
193
+
194
+ suite: list[tuple[Task, HarborEvaluator]] = []
195
+ for tid in DEEPSWE_MINI_TASKS:
196
+ if task_filter:
197
+ tf = task_filter.lower().strip()
198
+ if tf not in tid.lower() and tid.lower() not in tf:
199
+ continue
200
+
201
+ task_path = base_path / tid
202
+ if not task_path.is_dir():
203
+ continue
204
+
205
+ try:
206
+ task, evaluator = load_harbor_task(
207
+ task_path,
208
+ max_steps=max_steps,
209
+ timeout_seconds=timeout_seconds,
210
+ )
211
+ suite.append((task, evaluator))
212
+ except Exception as exc:
213
+ print(f"⚠️ Warning: Failed to load DeepSWE task '{tid}': {exc}")
214
+
215
+ return suite