@tangle-network/agent-bench 0.1.0 → 0.3.5
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.
- package/CHANGELOG.md +17 -0
- package/HARNESS.md +302 -0
- package/README.md +26 -1
- package/fixtures/aec-bench.json +18 -0
- package/fixtures/agentbench-dbbench.json +22 -0
- package/fixtures/bfcl.json +45 -0
- package/fixtures/commit0.json +72 -0
- package/fixtures/crag.json +10 -0
- package/fixtures/dabstep.json +22 -0
- package/fixtures/enterpriseops-gym.json +103 -0
- package/fixtures/finresearchbench.json +21 -0
- package/fixtures/finsearchcomp.json +66 -0
- package/fixtures/frames.json +26 -0
- package/fixtures/hotpotqa.json +182 -0
- package/fixtures/nomiracl.json +26 -0
- package/fixtures/open-rag-bench.json +16 -0
- package/fixtures/pier-agent/no-model-task/environment/Dockerfile +16 -0
- package/fixtures/pier-agent/no-model-task/environment/seed/src/status.txt +1 -0
- package/fixtures/pier-agent/no-model-task/instruction.md +6 -0
- package/fixtures/pier-agent/no-model-task/pre_artifacts.sh +6 -0
- package/fixtures/pier-agent/no-model-task/task.toml +35 -0
- package/fixtures/pier-agent/no-model-task/tests/Dockerfile +17 -0
- package/fixtures/pier-agent/no-model-task/tests/seed/src/status.txt +1 -0
- package/fixtures/pier-agent/no-model-task/tests/test.sh +19 -0
- package/fixtures/programbench.json +17 -0
- package/fixtures/ragbench.json +21 -0
- package/fixtures/simpleqa.json +121 -0
- package/fixtures/t2-ragbench.json +13 -0
- package/fixtures/tau2-bench.json +16 -0
- package/fixtures/tau3-banking.json +16 -0
- package/fixtures/toollm.json +28 -0
- package/fixtures/webarena-verified.json +20 -0
- package/package.json +39 -15
- package/pier_agents/__init__.py +18 -0
- package/pier_agents/candidate_contract.py +755 -0
- package/pier_agents/process_boundary.py +321 -0
- package/pier_agents/tangle_candidate.py +907 -0
- package/pier_agents/workspace_boundary.py +368 -0
- package/scripts/appworld_driver.py +359 -0
- package/scripts/cadbench_prepare.py +22 -0
- package/scripts/cadgenbench_hard_parts.py +48 -0
- package/scripts/clbench_codebase_judge.py +73 -0
- package/scripts/commit0_judge.py +170 -0
- package/scripts/dabstep_judge.py +42 -0
- package/scripts/enterpriseops_gym_judge.py +281 -0
- package/scripts/programbench_judge.py +120 -0
- package/scripts/render-gate-chart.mjs +176 -0
- package/scripts/run-package-tests.mjs +56 -0
- package/scripts/terminate-pier-trial.mts +66 -0
- package/scripts/trata-hedge/README.md +56 -0
- package/scripts/trata-hedge/run.sh +60 -0
- package/scripts/trata-hedge/solve.py +83 -0
- package/scripts/verify-packed-consumer.mjs +224 -0
- package/scripts/verify-pier-agent.mts +715 -0
- package/scripts/verify-pier-pair.mts +74 -0
- package/scripts/verify-pier-recovery.mts +139 -0
- package/src/adapters.ts +26 -0
- package/src/benchmarks/_harness.test.mts +178 -0
- package/src/benchmarks/_harness.ts +239 -16
- package/src/benchmarks/agentbench.ts +163 -0
- package/src/benchmarks/appworld.test.mts +15 -9
- package/src/benchmarks/bfcl.ts +346 -0
- package/src/benchmarks/crag.ts +137 -0
- package/src/benchmarks/dabstep.test.mts +70 -0
- package/src/benchmarks/dabstep.ts +212 -0
- package/src/benchmarks/external-adapters.test.mts +150 -0
- package/src/benchmarks/finresearchbench.ts +269 -0
- package/src/benchmarks/humaneval.ts +20 -8
- package/src/benchmarks/nomiracl.ts +180 -0
- package/src/benchmarks/open-rag-bench.ts +153 -0
- package/src/benchmarks/rag-benchmarks.test.mts +138 -0
- package/src/benchmarks/rag-shared.ts +327 -0
- package/src/benchmarks/ragbench.ts +171 -0
- package/src/benchmarks/swe-bench.test.mts +61 -0
- package/src/benchmarks/swe-bench.ts +201 -19
- package/src/benchmarks/t2-ragbench.ts +166 -0
- package/src/benchmarks/tau-bench-shared.ts +214 -0
- package/src/benchmarks/tau2-bench.ts +30 -0
- package/src/benchmarks/tau3-banking.ts +29 -0
- package/src/benchmarks/terminal-bench.test.mts +33 -0
- package/src/benchmarks/terminal-bench.ts +23 -8
- package/src/benchmarks/toollm.ts +254 -0
- package/src/benchmarks/types.ts +42 -0
- package/src/benchmarks/webarena-verified.ts +200 -0
- package/src/commit0-prereqs.sh +0 -0
- package/src/coordination-mcp-container-reach.mts +181 -0
- package/src/decoder-live.mts +1 -1
- package/src/examples/README.md +103 -39
- package/src/examples/benchmark-matrix.mts +101 -0
- package/src/examples/lean-proof-gate.README.md +77 -0
- package/src/examples/lean-proof-gate.mts +162 -0
- package/src/examples/lean-verify.ts +95 -0
- package/src/examples/lean.Dockerfile +12 -0
- package/src/examples/math-demo.mts +9 -7
- package/src/examples/strategy-demo.mts +10 -12
- package/src/gate.ts +3 -2
- package/src/hev-eval.mts +69 -0
- package/src/hev-improve.mts +169 -0
- package/src/hev-structural.mts +688 -0
- package/src/index.ts +73 -0
- package/src/mbpp-structural.mts +662 -0
- package/src/pier-agent.test-fixtures.mts +19 -0
- package/src/pier-agent.test.mts +363 -0
- package/src/pier-agent.ts +657 -0
- package/src/pier-result-grader.mjs +30 -0
- package/src/pier-result-grader.test.mts +62 -0
- package/src/pier-result-grader.ts +108 -0
- package/src/pier-task-outcome.test.mts +117 -0
- package/src/pier-task-outcome.ts +240 -0
- package/src/pier-trial-controller.test.mts +412 -0
- package/src/pier-trial-controller.ts +858 -0
- package/src/pier-trial-supervisor.mjs +352 -0
- package/src/resolve-client.ts +25 -2
- package/src/run-benchmarks-cli.mts +72 -0
- package/src/run-benchmarks-report.ts +66 -0
- package/src/run-benchmarks.test.mts +231 -0
- package/src/run-benchmarks.ts +589 -0
- package/src/smoke-structural-rollout.mts +393 -0
- package/src/swe-bench-env.test.ts +207 -0
- package/src/swe-bench-env.ts +554 -0
- package/src/swe-jail.ts +293 -0
- package/src/swe-self-improve.mts +84 -0
- package/src/swe-structural-judge-policy.test.ts +117 -0
- package/src/swe-structural-judge-policy.ts +133 -0
- package/src/swe-structural-policy.test.ts +124 -0
- package/src/swe-structural-policy.ts +132 -0
- package/src/swe-structural-provenance.test.ts +93 -0
- package/src/swe-structural-provenance.ts +138 -0
- package/src/swe-structural.mts +1260 -0
- package/src/swe-temp.ts +14 -0
- package/src/tb-container-executor.mts +234 -0
- package/src/tb-container-executor.test.mts +99 -0
- package/src/tb-supervisor-sidecar.mts +222 -0
- package/src/trata-gepa.mts +1 -1
- package/steerers/eops-itsm-population.json +1 -0
- package/tb_agents/opencode_refine_agent.py +117 -0
- package/tb_agents/opencode_router_agent.py +406 -0
- package/tb_agents/opencode_supervisor_agent.py +239 -0
- package/tb_agents/script_agent.py +66 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
|
|
3
|
+
import { InMemoryTraceStore } from '@tangle-network/agent-eval'
|
|
4
|
+
|
|
5
|
+
import { createPierCandidateRecoveryExecutor } from '../src/pier-agent'
|
|
6
|
+
import { FilePierCandidateTrialController } from '../src/pier-trial-controller'
|
|
7
|
+
|
|
8
|
+
const [directoryArg, executionId, executionPlanDigest, ...extra] = process.argv.slice(2)
|
|
9
|
+
if (!directoryArg || !executionId || !executionPlanDigest || extra.length > 0) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
'usage: terminate-pier-trial.mts <control-directory> <execution-id> <execution-plan-digest>',
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(executionPlanDigest)) {
|
|
15
|
+
throw new Error('execution-plan-digest must be a SHA-256 digest')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const dockerConnectionId = process.env.PIER_DOCKER_CONNECTION_ID
|
|
19
|
+
const dockerEnvironmentNames = process.env.PIER_DOCKER_ENV_NAMES
|
|
20
|
+
if ((dockerConnectionId === undefined) !== (dockerEnvironmentNames === undefined)) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
'PIER_DOCKER_CONNECTION_ID and PIER_DOCKER_ENV_NAMES must be set together',
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
const dockerConnection = (() => {
|
|
26
|
+
if (dockerConnectionId === undefined || dockerEnvironmentNames === undefined) return undefined
|
|
27
|
+
const names = dockerEnvironmentNames
|
|
28
|
+
.split(',')
|
|
29
|
+
.map((name) => name.trim())
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
if (new Set(names).size !== names.length) {
|
|
32
|
+
throw new Error('PIER_DOCKER_ENV_NAMES must not contain duplicates')
|
|
33
|
+
}
|
|
34
|
+
const env: Record<string, string> = {}
|
|
35
|
+
for (const name of names) {
|
|
36
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
|
|
37
|
+
throw new Error('PIER_DOCKER_ENV_NAMES contains an invalid environment name')
|
|
38
|
+
}
|
|
39
|
+
const value = process.env[name]
|
|
40
|
+
if (value === undefined) throw new Error(`required Docker environment variable ${name} is unset`)
|
|
41
|
+
env[name] = value
|
|
42
|
+
}
|
|
43
|
+
return { id: dockerConnectionId, env }
|
|
44
|
+
})()
|
|
45
|
+
|
|
46
|
+
const controller = new FilePierCandidateTrialController({
|
|
47
|
+
directory: path.resolve(directoryArg),
|
|
48
|
+
...(dockerConnection ? { dockerConnection } : {}),
|
|
49
|
+
})
|
|
50
|
+
const executor = createPierCandidateRecoveryExecutor(controller)
|
|
51
|
+
const recovered = await executor.stop(
|
|
52
|
+
{
|
|
53
|
+
executionId,
|
|
54
|
+
executionPlanDigest: executionPlanDigest as `sha256:${string}`,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
traceStore: new InMemoryTraceStore(),
|
|
58
|
+
reason: 'failed',
|
|
59
|
+
signal: new AbortController().signal,
|
|
60
|
+
deadlineAtMs: Date.now() + 30_000,
|
|
61
|
+
},
|
|
62
|
+
)
|
|
63
|
+
if (recovered.stopped !== true) throw new Error('Pier recovery executor did not stop the trial')
|
|
64
|
+
process.stdout.write(
|
|
65
|
+
`${JSON.stringify({ processExited: true, containersRemoved: true })}\n`,
|
|
66
|
+
)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# trata-hedge-bench — evaluating our system against an LLM-judge benchmark
|
|
2
|
+
|
|
3
|
+
[trata-hedge-bench](https://github.com/Trata-Inc/trata-hedge-bench) is a 102-task
|
|
4
|
+
financial-analyst benchmark (Harbor format): the agent reads a company's data corpus
|
|
5
|
+
(earnings calls, 10-K/10-Q, financials, press releases) and writes an analysis; the
|
|
6
|
+
verifier is a **Gemini-3.1-pro 3-task cascade** (hallucination-check → per-move-hit →
|
|
7
|
+
synthesis) that grades *concept match* against the expert analyst's documented moves
|
|
8
|
+
(`ground_truth.txt`). Graded `score` is 0–4 (themes fully covered); `reward.txt` is
|
|
9
|
+
sparse (1 iff all themes). No deployable ground-truth checker — it's an **oracle judge**.
|
|
10
|
+
|
|
11
|
+
## What's admissible here (and what isn't)
|
|
12
|
+
|
|
13
|
+
- **NOT** the verifier-grounded selector gate (our HumanEval/commit0 headline). The only
|
|
14
|
+
checker is the judge itself, so selecting by it = selecting by the eval metric (an
|
|
15
|
+
oracle, a Goodhart trap). See `docs/results.md`.
|
|
16
|
+
- **IS** admissible: (1) more-compute headroom (pass@k), (2) the **improvement loop**
|
|
17
|
+
(`selfImprove`/GEPA optimizing the analyst directive *against this judge*, held-out
|
|
18
|
+
gated — the legitimate use of a judge domain), with the honest caveat that an
|
|
19
|
+
LLM-judge can be *gamed*, so a held-out lift means "higher judge score," which without
|
|
20
|
+
ground truth we can't independently certify as "better analysis."
|
|
21
|
+
|
|
22
|
+
## Status (2026-06-06): pipeline PROVEN end-to-end
|
|
23
|
+
|
|
24
|
+
Our solver → their **real** Gemini-3.1-pro judge → a genuine graded result. Every link
|
|
25
|
+
works. The naive **single-shot** baseline (gpt-4o, ~3 of N corpus files in one context
|
|
26
|
+
window) scores **0/4** — a floor: it hit only 1/3 moves on a few themes with
|
|
27
|
+
hallucinations flagged, because it could not explore the full corpus. The bench is built
|
|
28
|
+
for **agentic** exploration; a fair baseline needs our sandbox runtime as the solver
|
|
29
|
+
(browse + cite selectively), like the commit0 gate — pending sandbox-gateway health.
|
|
30
|
+
|
|
31
|
+
## Run it
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# clone the bench once (102 envs, ~580MB; or sparse-checkout one env)
|
|
35
|
+
git clone https://github.com/Trata-Inc/trata-hedge-bench /tmp/thb
|
|
36
|
+
|
|
37
|
+
dotenvx run -f ~/company/devops/secrets/.env.keys -f ~/company/devops/secrets/agent-state.env -- \
|
|
38
|
+
bash bench/scripts/trata-hedge/run.sh /tmp/thb/environments/<env-name> gpt-4o
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Gotchas (each cost a debugging cycle)
|
|
42
|
+
|
|
43
|
+
- **Use `GOOGLE_AI_KEY`, not `GEMINI_API_KEY`** — the latter in the secrets is
|
|
44
|
+
`API_KEY_INVALID`; `GOOGLE_AI_KEY` is the working Gemini key (`gemini-3.1-pro-preview`
|
|
45
|
+
returns 200). `run.sh` maps `GOOGLE_API_KEY=$GOOGLE_AI_KEY` for `grade.py`.
|
|
46
|
+
- The router is behind Cloudflare bot-fight: a default urllib User-Agent → 403 (CF 1010)
|
|
47
|
+
on large bodies; the solver sends a browser UA.
|
|
48
|
+
- `grade.py` hardcodes `/app/answer.txt` (only that path); `/app` is sudo-writable.
|
|
49
|
+
`DATA_DIR` / `GROUND_TRUTH_PATH` / `REWARD_PATH` / `DETAILS_PATH` are env-overridable, so
|
|
50
|
+
the grader runs **unmodified** (faithful).
|
|
51
|
+
|
|
52
|
+
## Next steps
|
|
53
|
+
|
|
54
|
+
1. Agentic solver: our sandbox runtime browses the corpus + cites (fair baseline).
|
|
55
|
+
2. The improvement loop: `selfImprove` on the analyst directive vs this judge, held-out
|
|
56
|
+
gated, across the 102 tasks — with the judge-gaming caveat stated.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Evaluate our system against trata-hedge-bench's OWN Gemini-3.1-pro judge.
|
|
3
|
+
#
|
|
4
|
+
# Pipeline: our solver writes a cited analysis -> their grade.py (unmodified, a
|
|
5
|
+
# 3-task Gemini cascade: hallucination-check -> per-move-hit -> synthesis) scores it
|
|
6
|
+
# against the expert analyst's documented moves (ground_truth.txt).
|
|
7
|
+
#
|
|
8
|
+
# Prereqs (all present in this environment):
|
|
9
|
+
# - GOOGLE_AI_KEY — a VALID Gemini key (NOTE: GEMINI_API_KEY in the secrets is
|
|
10
|
+
# API_KEY_INVALID; GOOGLE_AI_KEY is the working one).
|
|
11
|
+
# - TANGLE_API_KEY — our router (the solver).
|
|
12
|
+
# - uv + Docker. /app is sudo-writable (grade.py hardcodes /app/answer.txt).
|
|
13
|
+
#
|
|
14
|
+
# Usage (wrap in dotenvx so the keys resolve):
|
|
15
|
+
# dotenvx run -f ~/company/devops/secrets/.env.keys -f ~/company/devops/secrets/agent-state.env -- \
|
|
16
|
+
# bash bench/scripts/trata-hedge/run.sh <path-to-trata-env-dir> [WORKER_MODEL]
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
ENV="${1:?usage: run.sh <trata-env-dir> [model]}"
|
|
20
|
+
MODEL="${2:-${WORKER_MODEL:-gpt-4o}}"
|
|
21
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
22
|
+
ANS=/tmp/thb-answer.txt
|
|
23
|
+
REWARD=/tmp/thb-reward.txt
|
|
24
|
+
DETAILS=/tmp/thb-details.json
|
|
25
|
+
|
|
26
|
+
echo "[trata] solve: $ENV (model=$MODEL)"
|
|
27
|
+
WORKER_MODEL="$MODEL" python3 "$HERE/solve.py" "$ENV" "$ANS"
|
|
28
|
+
|
|
29
|
+
sudo -n mkdir -p /app && sudo -n cp "$ANS" /app/answer.txt && sudo -n chmod 644 /app/answer.txt
|
|
30
|
+
|
|
31
|
+
echo "[trata] grade: their grade.py (gemini-3.1-pro)"
|
|
32
|
+
GRADE_LOG=/tmp/thb-grade.log
|
|
33
|
+
# Fail loud: capture the grader's output; if it errors (bad key, import, network) or
|
|
34
|
+
# writes no details, surface ITS error rather than crashing the reporter cryptically.
|
|
35
|
+
set +e
|
|
36
|
+
( cd "$ENV/tests" && \
|
|
37
|
+
GOOGLE_API_KEY="$GOOGLE_AI_KEY" \
|
|
38
|
+
DATA_DIR="$ENV/environment/data" \
|
|
39
|
+
GROUND_TRUTH_PATH="$ENV/tests/ground_truth.txt" \
|
|
40
|
+
REWARD_PATH="$REWARD" DETAILS_PATH="$DETAILS" \
|
|
41
|
+
uvx --with google-genai python3 grade.py ) >"$GRADE_LOG" 2>&1
|
|
42
|
+
grade_status=$?
|
|
43
|
+
set -e
|
|
44
|
+
if [ "$grade_status" -ne 0 ] || [ ! -f "$DETAILS" ]; then
|
|
45
|
+
echo "[trata] grade.py FAILED (exit $grade_status) — last output:" >&2
|
|
46
|
+
tail -n 15 "$GRADE_LOG" >&2
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
python3 - "$DETAILS" "$REWARD" <<'PY'
|
|
51
|
+
import json, sys
|
|
52
|
+
d = json.load(open(sys.argv[1]))
|
|
53
|
+
reward = open(sys.argv[2]).read().strip() if __import__("os").path.exists(sys.argv[2]) else "?"
|
|
54
|
+
print(f" sparse reward (1 iff all themes): {reward}")
|
|
55
|
+
print(f" graded score (0-4): {d.get('score')} | themes_covered: {d.get('themes_covered')}/{d.get('num_themes')} | hallucinations: {d.get('hallucinations_detected')}")
|
|
56
|
+
for t in d.get("themes_hit", []):
|
|
57
|
+
print(f" HIT : {t.get('label')}")
|
|
58
|
+
for t in d.get("themes_missed", []):
|
|
59
|
+
print(f" MISS: {t.get('label')} ({len(t.get('moves_hit',[]))}/{t.get('n_moves')} moves)")
|
|
60
|
+
PY
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""trata-hedge-bench solver — our system's analyst, graded by THEIR Gemini judge.
|
|
2
|
+
|
|
3
|
+
Reads a task's instruction.md + data corpus, writes a cited analysis to answer.txt.
|
|
4
|
+
The corpus is large (~5MB/task), so a single-shot router call is a LOWER-BOUND
|
|
5
|
+
baseline (it sees only what fits in context) — the bench is built for agentic
|
|
6
|
+
exploration, which our sandbox runtime provides (see README; pending sandbox health).
|
|
7
|
+
|
|
8
|
+
Notes learned the hard way:
|
|
9
|
+
- router.tangle.tools is behind Cloudflare bot-fight: a default Python-urllib
|
|
10
|
+
User-Agent gets 403 (CF 1010) on large bodies → send a browser UA.
|
|
11
|
+
- gpt-4.1 is NOT keyed for direct router calls (403); gpt-4o / claude-sonnet-4-6 /
|
|
12
|
+
gpt-4o-mini are. Very large bodies (~350KB) can 503 — keep DATA_BUDGET in range.
|
|
13
|
+
|
|
14
|
+
WORKER_MODEL=gpt-4o DATA_BUDGET=160000 python3 solve.py <env-dir> <out.txt>
|
|
15
|
+
"""
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
import urllib.request
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
env = Path(sys.argv[1])
|
|
23
|
+
out = sys.argv[2] if len(sys.argv) > 2 else "/tmp/thb-answer.txt"
|
|
24
|
+
model = os.environ.get("WORKER_MODEL", "gpt-4o")
|
|
25
|
+
budget = int(os.environ.get("DATA_BUDGET", "160000"))
|
|
26
|
+
instruction = (env / "instruction.md").read_text()
|
|
27
|
+
data_dir = env / "environment" / "data"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def rank(p: Path) -> int:
|
|
31
|
+
s = str(p)
|
|
32
|
+
return (0 if "earnings_call" in s else 1 if "financials" in s else 2 if "company_profiles" in s
|
|
33
|
+
else 3 if "press_releases" in s else 4)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
files = sorted((p for p in data_dir.rglob("*") if p.is_file()), key=rank)
|
|
37
|
+
blocks, used, cited = [], 0, []
|
|
38
|
+
for p in files:
|
|
39
|
+
rel = p.relative_to(data_dir)
|
|
40
|
+
try:
|
|
41
|
+
c = p.read_text(errors="replace")
|
|
42
|
+
except Exception:
|
|
43
|
+
continue
|
|
44
|
+
if used + len(c) > budget:
|
|
45
|
+
continue
|
|
46
|
+
used += len(c)
|
|
47
|
+
cited.append(str(rel))
|
|
48
|
+
blocks.append(f"\n=== FILE: data/{rel} ===\n{c}")
|
|
49
|
+
print(f"[solve] {len(cited)}/{len(files)} files in context ({used} chars)", file=sys.stderr)
|
|
50
|
+
|
|
51
|
+
prompt = (
|
|
52
|
+
instruction
|
|
53
|
+
+ "\n\n--- AVAILABLE DATA (cite files by their `data/<path>` name inline) ---\n"
|
|
54
|
+
+ "".join(blocks)
|
|
55
|
+
+ "\n\n--- END DATA ---\nWrite ONLY the full analysis (no preamble). Inline-cite every claim with its `data/<path>`."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
key = os.environ["TANGLE_API_KEY"]
|
|
59
|
+
base = os.environ.get("ROUTER_BASE", "https://router.tangle.tools/v1")
|
|
60
|
+
body = json.dumps({
|
|
61
|
+
"model": model,
|
|
62
|
+
"messages": [{"role": "user", "content": prompt}],
|
|
63
|
+
"temperature": float(os.environ.get("TEMPERATURE", "0.5")),
|
|
64
|
+
"max_tokens": int(os.environ.get("MAX_TOKENS", "6000")),
|
|
65
|
+
}).encode()
|
|
66
|
+
req = urllib.request.Request(
|
|
67
|
+
f"{base}/chat/completions", data=body,
|
|
68
|
+
headers={
|
|
69
|
+
"Authorization": f"Bearer {key}",
|
|
70
|
+
"Content-Type": "application/json",
|
|
71
|
+
# browser UA: router is behind Cloudflare bot-fight, default urllib UA → 403 on big bodies
|
|
72
|
+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36",
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
try:
|
|
76
|
+
with urllib.request.urlopen(req, timeout=300) as r:
|
|
77
|
+
resp = json.load(r)
|
|
78
|
+
except urllib.error.HTTPError as e:
|
|
79
|
+
print(f"[solve] HTTP {e.code}: {e.read().decode(errors='replace')[:400]}", file=sys.stderr)
|
|
80
|
+
raise
|
|
81
|
+
answer = resp["choices"][0]["message"]["content"]
|
|
82
|
+
Path(out).write_text(answer)
|
|
83
|
+
print(f"[solve] wrote {len(answer)} chars -> {out} (model={model})", file=sys.stderr)
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process'
|
|
2
|
+
import { mkdtemp, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import { promisify } from 'node:util'
|
|
7
|
+
|
|
8
|
+
const execFileAsync = promisify(execFile)
|
|
9
|
+
const benchDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
10
|
+
const repoRoot = path.resolve(benchDir, '..')
|
|
11
|
+
const scratch = await mkdtemp(path.join(tmpdir(), 'agent-bench-consumer-'))
|
|
12
|
+
const args = new Set(process.argv.slice(2))
|
|
13
|
+
const useLocalRuntime = args.delete('--local-runtime')
|
|
14
|
+
if (args.size > 0) throw new Error(`unknown arguments: ${[...args].join(', ')}`)
|
|
15
|
+
|
|
16
|
+
const TYPESCRIPT_5 = '5.9.3'
|
|
17
|
+
const TYPESCRIPT_6 = '6.0.3'
|
|
18
|
+
|
|
19
|
+
async function run(command, args, cwd, env = process.env) {
|
|
20
|
+
try {
|
|
21
|
+
return await execFileAsync(command, args, {
|
|
22
|
+
cwd,
|
|
23
|
+
env,
|
|
24
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
25
|
+
timeout: 120_000,
|
|
26
|
+
})
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error?.stdout) process.stdout.write(error.stdout)
|
|
29
|
+
if (error?.stderr) process.stderr.write(error.stderr)
|
|
30
|
+
const invocation = [command, ...args].join(' ')
|
|
31
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
32
|
+
throw new Error(`${invocation} failed: ${message}`, { cause: error })
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function resolveRuntimePackage(packDir) {
|
|
37
|
+
if (process.env.AGENT_RUNTIME_PACKAGE) {
|
|
38
|
+
return path.resolve(process.env.AGENT_RUNTIME_PACKAGE)
|
|
39
|
+
}
|
|
40
|
+
if (!useLocalRuntime) return undefined
|
|
41
|
+
const manifest = JSON.parse(await readFile(path.join(repoRoot, 'package.json'), 'utf8'))
|
|
42
|
+
if (manifest.name !== '@tangle-network/agent-runtime') {
|
|
43
|
+
throw new Error('--local-runtime requires an agent-runtime source workspace')
|
|
44
|
+
}
|
|
45
|
+
await run('pnpm', ['pack', '--pack-destination', packDir], repoRoot)
|
|
46
|
+
const tarballs = (await readdir(packDir)).filter((name) => name.endsWith('.tgz'))
|
|
47
|
+
if (tarballs.length !== 1) {
|
|
48
|
+
throw new Error(`expected one packed agent-runtime tarball, found ${tarballs.length}`)
|
|
49
|
+
}
|
|
50
|
+
return path.join(packDir, tarballs[0])
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const packDir = path.join(scratch, 'pack')
|
|
55
|
+
const runtimePackDir = path.join(scratch, 'runtime-pack')
|
|
56
|
+
const consumerDir = path.join(scratch, 'consumer')
|
|
57
|
+
await mkdir(packDir)
|
|
58
|
+
await mkdir(runtimePackDir)
|
|
59
|
+
await mkdir(consumerDir)
|
|
60
|
+
|
|
61
|
+
await run('pnpm', ['pack', '--pack-destination', packDir], benchDir)
|
|
62
|
+
const packedFiles = (await readdir(packDir)).filter((name) => name.endsWith('.tgz'))
|
|
63
|
+
if (packedFiles.length !== 1) {
|
|
64
|
+
throw new Error(`expected one packed agent-bench tarball, found ${packedFiles.length}`)
|
|
65
|
+
}
|
|
66
|
+
const tarball = path.join(packDir, packedFiles[0])
|
|
67
|
+
const runtimePackage = await resolveRuntimePackage(runtimePackDir)
|
|
68
|
+
const manifest = JSON.parse(await readFile(path.join(benchDir, 'package.json'), 'utf8'))
|
|
69
|
+
const devDependencies = manifest.devDependencies
|
|
70
|
+
if (
|
|
71
|
+
typeof devDependencies?.['@types/node'] !== 'string' ||
|
|
72
|
+
typeof devDependencies.typescript !== 'string' ||
|
|
73
|
+
typeof devDependencies.tsx !== 'string' ||
|
|
74
|
+
!devDependencies['@types/node'] ||
|
|
75
|
+
!devDependencies.typescript ||
|
|
76
|
+
!devDependencies.tsx
|
|
77
|
+
) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
'package verification requires @types/node, typescript, and tsx devDependencies',
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
const publicTsconfig = JSON.parse(
|
|
83
|
+
await readFile(path.join(benchDir, 'tsconfig.public.json'), 'utf8'),
|
|
84
|
+
)
|
|
85
|
+
if (!publicTsconfig.compilerOptions)
|
|
86
|
+
throw new Error('tsconfig.public.json must define compilerOptions')
|
|
87
|
+
|
|
88
|
+
await writeFile(
|
|
89
|
+
path.join(consumerDir, 'package.json'),
|
|
90
|
+
`${JSON.stringify(
|
|
91
|
+
{
|
|
92
|
+
name: 'agent-bench-package-verifier',
|
|
93
|
+
private: true,
|
|
94
|
+
type: 'module',
|
|
95
|
+
dependencies: {
|
|
96
|
+
'@tangle-network/agent-bench': `file:${tarball}`,
|
|
97
|
+
...(runtimePackage
|
|
98
|
+
? { '@tangle-network/agent-runtime': `file:${runtimePackage}` }
|
|
99
|
+
: {}),
|
|
100
|
+
},
|
|
101
|
+
devDependencies: {
|
|
102
|
+
'@types/node': devDependencies['@types/node'],
|
|
103
|
+
typescript: TYPESCRIPT_5,
|
|
104
|
+
tsx: devDependencies.tsx,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
null,
|
|
108
|
+
2,
|
|
109
|
+
)}\n`,
|
|
110
|
+
)
|
|
111
|
+
await writeFile(
|
|
112
|
+
path.join(consumerDir, 'index.ts'),
|
|
113
|
+
"import { createSweBenchAdapter, executePreparedPierCandidate, FilePierCandidateTrialController, resolveAdapter, runBenchmarks, runStagedJudge, StagedJudgeError, type BenchmarkAdapter, type JudgeArtifactReceipt, type PierCandidateTrialController, type PierCandidateTrialHandle, type PierDockerConnection, type StagedPierCandidateExecution } from '@tangle-network/agent-bench'\n\nconst adapter: BenchmarkAdapter = resolveAdapter('swe-bench')\nconst captureAdapter: BenchmarkAdapter = createSweBenchAdapter({ captureEvaluatorArtifacts: ({ taskId, attemptSequence }) => ({ destination: `/tmp/${taskId}/${attemptSequence}` }) })\nconst receipt = undefined as JudgeArtifactReceipt | undefined\nconst staged = undefined as StagedPierCandidateExecution | undefined\nconst trial = undefined as PierCandidateTrialHandle | undefined\nconst controller = undefined as PierCandidateTrialController | undefined\nconst dockerConnection = undefined as PierDockerConnection | undefined\nvoid adapter\nvoid captureAdapter\nvoid receipt\nvoid staged\nvoid trial\nvoid controller\nvoid dockerConnection\nvoid executePreparedPierCandidate\nvoid FilePierCandidateTrialController\nvoid runBenchmarks\nvoid runStagedJudge\nvoid StagedJudgeError\n",
|
|
114
|
+
)
|
|
115
|
+
await writeFile(
|
|
116
|
+
path.join(consumerDir, 'tsconfig.json'),
|
|
117
|
+
`${JSON.stringify(
|
|
118
|
+
{
|
|
119
|
+
compilerOptions: publicTsconfig.compilerOptions,
|
|
120
|
+
files: ['index.ts'],
|
|
121
|
+
},
|
|
122
|
+
null,
|
|
123
|
+
2,
|
|
124
|
+
)}\n`,
|
|
125
|
+
)
|
|
126
|
+
await writeFile(
|
|
127
|
+
path.join(consumerDir, 'verify_pier_payload.py'),
|
|
128
|
+
`import py_compile
|
|
129
|
+
from pathlib import Path
|
|
130
|
+
|
|
131
|
+
import pier_agents
|
|
132
|
+
from pier_agents import candidate_contract
|
|
133
|
+
|
|
134
|
+
root = Path(pier_agents.__file__).parent
|
|
135
|
+
expected = {
|
|
136
|
+
"__init__.py",
|
|
137
|
+
"candidate_contract.py",
|
|
138
|
+
"process_boundary.py",
|
|
139
|
+
"tangle_candidate.py",
|
|
140
|
+
"workspace_boundary.py",
|
|
141
|
+
}
|
|
142
|
+
observed = {path.name for path in root.glob("*.py")}
|
|
143
|
+
assert observed == expected, (observed, expected)
|
|
144
|
+
assert candidate_contract.PreparedCandidateContract.__module__ == "pier_agents.candidate_contract"
|
|
145
|
+
for name in sorted(expected):
|
|
146
|
+
py_compile.compile(root / name, doraise=True)
|
|
147
|
+
`,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
// Intentionally resolve the declared ranges like a brand-new registry consumer.
|
|
151
|
+
// The preceding frozen install + public typecheck cover the exact bench lockfile.
|
|
152
|
+
await run(
|
|
153
|
+
'npm',
|
|
154
|
+
['install', '--ignore-scripts', '--no-audit', '--no-fund', '--package-lock=false'],
|
|
155
|
+
consumerDir,
|
|
156
|
+
)
|
|
157
|
+
await run('npm', ['exec', '--', 'tsc', '-p', 'tsconfig.json'], consumerDir)
|
|
158
|
+
const typescript5 = await run('npm', ['exec', '--', 'tsc', '--version'], consumerDir)
|
|
159
|
+
if (typescript5.stdout.trim() !== `Version ${TYPESCRIPT_5}`) {
|
|
160
|
+
throw new Error(`expected TypeScript ${TYPESCRIPT_5}, received ${typescript5.stdout.trim()}`)
|
|
161
|
+
}
|
|
162
|
+
await run(
|
|
163
|
+
'npm',
|
|
164
|
+
[
|
|
165
|
+
'install',
|
|
166
|
+
'--ignore-scripts',
|
|
167
|
+
'--no-audit',
|
|
168
|
+
'--no-fund',
|
|
169
|
+
'--no-save',
|
|
170
|
+
'--package-lock=false',
|
|
171
|
+
`typescript@${TYPESCRIPT_6}`,
|
|
172
|
+
],
|
|
173
|
+
consumerDir,
|
|
174
|
+
)
|
|
175
|
+
await run('npm', ['exec', '--', 'tsc', '-p', 'tsconfig.json'], consumerDir)
|
|
176
|
+
const typescript6 = await run('npm', ['exec', '--', 'tsc', '--version'], consumerDir)
|
|
177
|
+
if (typescript6.stdout.trim() !== `Version ${TYPESCRIPT_6}`) {
|
|
178
|
+
throw new Error(`expected TypeScript ${TYPESCRIPT_6}, received ${typescript6.stdout.trim()}`)
|
|
179
|
+
}
|
|
180
|
+
await run('npm', ['exec', '--', 'tsx', 'index.ts'], consumerDir)
|
|
181
|
+
const installedPackage = path.join(consumerDir, 'node_modules', '@tangle-network', 'agent-bench')
|
|
182
|
+
const prepared = await run(
|
|
183
|
+
'npm',
|
|
184
|
+
[
|
|
185
|
+
'exec',
|
|
186
|
+
'--',
|
|
187
|
+
'tsx',
|
|
188
|
+
path.join(installedPackage, 'scripts', 'verify-pier-agent.mts'),
|
|
189
|
+
],
|
|
190
|
+
consumerDir,
|
|
191
|
+
{ ...process.env, PIER_PREPARE_ONLY: '1', PIER_PROOF_ARM: 'failure' },
|
|
192
|
+
)
|
|
193
|
+
const prepareProof = JSON.parse(prepared.stdout)
|
|
194
|
+
if (
|
|
195
|
+
prepareProof.prepared !== true ||
|
|
196
|
+
prepareProof.disposed !== true ||
|
|
197
|
+
!/^sha256:[a-f0-9]{64}$/.test(prepareProof.executionPlanDigest) ||
|
|
198
|
+
!/^sha256:[a-f0-9]{64}$/.test(prepareProof.graderDigest)
|
|
199
|
+
) {
|
|
200
|
+
throw new Error(`packed consumer did not prepare a real candidate: ${prepared.stdout}`)
|
|
201
|
+
}
|
|
202
|
+
await run('python3', ['verify_pier_payload.py'], consumerDir, {
|
|
203
|
+
...process.env,
|
|
204
|
+
PYTHONPATH: installedPackage,
|
|
205
|
+
})
|
|
206
|
+
let runtimeManifest
|
|
207
|
+
try {
|
|
208
|
+
runtimeManifest = JSON.parse(
|
|
209
|
+
await readFile(
|
|
210
|
+
path.join(consumerDir, 'node_modules/@tangle-network/agent-runtime/package.json'),
|
|
211
|
+
'utf8',
|
|
212
|
+
),
|
|
213
|
+
)
|
|
214
|
+
} catch (error) {
|
|
215
|
+
throw new Error('packed consumer did not install @tangle-network/agent-runtime', {
|
|
216
|
+
cause: error,
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
console.log(
|
|
220
|
+
`packed consumer verified: ${manifest.name}@${manifest.version} with @tangle-network/agent-runtime@${runtimeManifest.version}, TypeScript ${TYPESCRIPT_5} and ${TYPESCRIPT_6}; prepared ${prepareProof.executionPlanDigest}`,
|
|
221
|
+
)
|
|
222
|
+
} finally {
|
|
223
|
+
await rm(scratch, { recursive: true, force: true })
|
|
224
|
+
}
|