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 +6 -0
- benchmarks/harbor.py +215 -0
- benchmarks/mini_swe.py +543 -0
- benchmarks/pier_agent.py +69 -0
- benchmarks/runner.py +396 -0
- core/__init__.py +3 -0
- core/auth.py +286 -0
- core/auth_sigv4.py +354 -0
- core/commands.py +128 -0
- core/config.py +324 -0
- core/discovery.py +305 -0
- core/environment.py +400 -0
- core/evaluator.py +98 -0
- core/harness.py +806 -0
- core/interactive.py +644 -0
- core/loop.py +94 -0
- core/memory.py +257 -0
- core/model.py +570 -0
- core/prompts.py +85 -0
- core/protocols.py +110 -0
- core/providers.json +94 -0
- core/providers.py +776 -0
- core/session.py +491 -0
- core/skills.py +240 -0
- core/tools.py +447 -0
- core/tracer.py +511 -0
- core/tui.py +1664 -0
- core/tui_session.py +177 -0
- core/types.py +155 -0
- core/usage.py +179 -0
- main.py +755 -0
- microharness/__init__.py +8 -0
- microharness/__main__.py +4 -0
- microharness-0.1.0.dist-info/METADATA +491 -0
- microharness-0.1.0.dist-info/RECORD +38 -0
- microharness-0.1.0.dist-info/WHEEL +4 -0
- microharness-0.1.0.dist-info/entry_points.txt +3 -0
- microharness-0.1.0.dist-info/licenses/LICENSE +21 -0
benchmarks/__init__.py
ADDED
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
|