eval-agentic-launch
SkillAI & modelsLaunch agentic Harbor evals through the OT-Agent unified eval listener (eval/unified_eval_listener.py) on any cluster: select models (query_unevaled_models.py / priority lists), wire the pinggy served-model tunnel, submit with the right preset + flags in tmux, then VERIFY the launch actually works via the 15-min infra sanity check (pinggy auth, Daytona→cluster api_base, vLLM POSTs, trial progression — catches "RUNNING but silently dead" jobs). Cluster-AGNOSTIC: per-cluster particulars (sbatch script, gpu-mem ceiling, concurrency, cert/tunnel, conda env, paths, Daytona key, pre-download) live in `.agents/ops/<cluster>/`. Use when asked to launch/relaunch agentic evals, or eval a model on a benchmark (terminal_bench_2 / dev_set_v2 / swebench / bfcl / aider).
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the eval-agentic-launch skill
What this skill tells your AI
The instructions your AI receives, as published by open-thoughts/openthoughts-agent in .agents/skills/eval-agentic-launch/SKILL.md and read by ahel’s review.
Launch agentic Harbor evals via the unified eval listener (eval/unified_eval_listener.py). Cluster-agnostic; read .agents/ops/<cluster>/ops.md first for the cluster's sbatch script, gpu-mem ceiling, concurrency, cert/tunnel, conda env, paths, Daytona eval-org key, and whether --pre-download is needed.
Front door:
python -m hpc.launch --job_type eval_listener …. Runs the listener'smain()in-process after the launcher preamble (detect_hpc+set_environment→DCFT/EXPERIMENTS_DIR/PYTHONPATH+ hosted-vllm/Supabase keys +chdirto repo root), so no manualsource hpc/dotenv/<cluster>.env/export PYTHONPATH/cdis needed. Forwards the listener's ~50 flags verbatim (strips only--job_type eval_listener). The rawpython eval/unified_eval_listener.py …fallback still works (same public API) but you own the preamble — if you ever seeFATAL: WORKDIR=... is not the OpenThoughts-Agent repo root, you used the raw script from the wrong place; switch to the front door.
⚠ Secrets from
$DC_AGENT_SECRET_ENV, never hardcoded in a script/config/commit. The eval sbatch sources it (~/secrets.env; TACC$SCRATCH/keys.env) and reads the two Daytona eval-org keys:DAYTONA_API_KEY(org1) +DAYTONA_DATA_API_KEY(org2), 3:1-weighted (3/4 org2). Fails loudly (:?) if either is unset. A literaldtn_…key committed anywhere is a leak — rotate/revoke it, don't just fix-forward.
1. Select the models
- Priority list (default): a file in
eval/lists/(models_8b_*.txt,models_32b.txt,models_131k.txt). Launch with--require-priority-list --priority-file eval/lists/<file>. - Find unevaled models —
scripts/database/query_unevaled_models.py(resolves benchmark families via the Supabaseduplicate_offield, e.g.dev_set_v2⊇DCAgent_dev_set_v2/dev_set_v2_2.0x/openthoughts-tblite):python scripts/database/query_unevaled_models.py --benchmark <fam> --size <8|32> -o eval/lists/<file>.txt -v # needs SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY
--require-priority-list is LOAD-BEARING
--priority-file alone only changes sort order; the filter "skip models not in the list" lives behind --require-priority-list (unified_eval_listener.py ~L978). Without it the listener submits evals for every unevaled model in the lookback window (routinely 700+). Always pass both. If launched without it by accident: kill the listener before it leaves pre-download (submission is after Pre-downloading…), then confirm via squeue/sacct --starttime=now-Nmin. The listener python is a child of the sshd: …@notty session and survives the local ssh client dying — pkill -9 -f unified_eval_listener.py on the cluster to stop it.
Benchmark = a --preset (one, not both with --datasets)
tb2=terminal_bench_2, v2=dev_set_v2, dev=dev_set_71_tasks, swebench, bfcl, aider. --preset swebench is the random-100 subset (DCAgent/swebench_verified_eval_set → swebench-verified-random-100-folders, n_concurrent 32), not the full set.
"ID evals" — launch all three legs
Each leg is a separate listener invocation (different n_concurrent/harbor-config, don't combine into one --datasets):
| leg | --preset | dataset (post-alias) | n_concurrent |
|---|---|---|---|
| SWE-bench-verified random-100 | swebench | swebench-verified-random-100-folders | 32 |
| dev_set_v2 | v2 | DCAgent/dev_set_v2 | 128 |
| terminal_bench_2 | tb2 | DCAgent2/terminal_bench_2 | 64 |
"Run the ID evals" = fire one listener per leg (§4) + the §5 infra check on each. (Full SWE-bench-verified and other benchmarks are OOD.) Scoring side (crud-otagent-supabase) uses the same 3-member set; dev_set_v2 is partial-credit → counts toward the ID mean but excluded from the ID SE and model-vs-model ranking.
Re-eval / parity test → --force-eval
By default the listener Skips any model with a Finished+metrics row (reason=job finished) — correct for cohort fill, but blocks a deliberate re-run. --force-eval bypasses that dedup and submits a fresh sandbox_jobs row (doesn't touch the existing row → no metrics-clearing, works across users). Pair with --require-priority-list + a single-model --priority-file so only the intended model is forced. --stale-started-hours does NOT override a Finished row (only re-ages Started). Distinct from --force-reeval (resume-path flag, see eval-agentic-cleanup check 4).
2. Harbor config + timeout multiplier (config-by-size — usually nothing to do)
Do NOT pass --harbor-config for standard terminus-2 evals. The listener selects the canonical config by model size and sets EVAL_HARBOR_CONFIG per-model:
| model size | selected config | timeout multiplier |
|---|---|---|
| 8B-class (≤ ~14B; 1.5B/7B/14B) | hpc/harbor_yaml/eval/dcagent_eval_defaults.yaml | 2× |
32B-class (~28–42B; incl. MoE 30b-a3b) | hpc/harbor_yaml/eval/dcagent_eval_defaults_32b.yaml | 16× |
| out-of-band (70B/80B) or no size token | base default | 2× + logged note |
Size is read from the largest \dB token in the HF name. The multiplier flows as EVAL_TIMEOUT_MULTIPLIER and is recorded in the Pending row so dedup matches what ran. The deprecated eval_ctx*_non_it* / ctx32k_non_it_16x_eval_.yaml configs carry stale *-drop-ei metrics → JobConfig ValidationError.
Resolution order (first wins): (1) explicit --harbor-config / preset harbor_config — overrides size selection for every model (use for 131k context / openhands_* installed-harness); (2) per-model timeout_multiplier: in the registry (for names with no size token, e.g. a Qwen3-8B named laion/GLM-4_7-swesmith-…); (3) size-based table above. For a one-off harbor jobs start, point --config at the 8B/32B file.
3. Pinggy tunnel — installed-harness ONLY (not terminus-2)
Skip for the default terminus-2 agent (every eval_ctx*/*_non_it* config; all --presets). Do NOT pass --pinggy_* / consume a pair.
Installed harnesses (opencode / openhands_*) run in the Daytona sandbox and call back out to the served model over a public pinggy tunnel. The full recipe for the opencode installed-harness + pinggy ID-eval — the config (eval_opencode_ctx32k.yaml), config-delivery mechanism (--config-yaml on TACC vs --harbor-config), the --pinggy_persistent_url/--pinggy_token flags + pairs 8/9/10, the sbatch installed-agent tunnel/routing branch, the vllm/ provider, -Thinking- model specifics, and the pinggy infra checks — lives in .agents/projects/harbor/ops.md → "Agentic ID-eval via the opencode (installed) harness + pinggy". The privileged URL/token bank is in .agents/secret.md / notes/ot-agent/pinggy_bank.md (never inline it). Resume of an installed-harness eval also needs the tunnel — see eval-agentic-cleanup check 4.
4. Launch (in tmux — listener is long-running)
Concurrent-submit guard: ONE listener enqueues many legs; do NOT fire N concurrent
--onceprocesses. A multi-leg refill is one invocation that submits each leg internally with a 1ssubmission_delay(unified_eval_listener.pyL3204–3205). Firing N listener processes near-simultaneously on the login node races conda's lazily-imported plugin registry → a circular-import at activation. If multiple listener processes are truly required (incompatible n_concurrent), stagger them ~30–45s apart — never&them together. (Per-jobconda activateinside the sbatch runs on independent compute nodes and never races.)
# inside tmux. The front door does the preamble (no manual source/PYTHONPATH).
python -m hpc.launch --job_type eval_listener \
--cluster-config <cluster-name> \
--preset <preset> \
--require-priority-list --priority-file eval/lists/<file>.txt \
--config-yaml dcagent_eval_config_no_override.yaml \
[--agent-kwarg 'extra_body={"chat_template_kwargs":{"enable_thinking":true}}'] [--agent-parser json] [--max-output-tokens 16384] \
[--pre-download] [--force-reeval] [--pinggy_persistent_url <URL> --pinggy_token <TOKEN>] \
--once --verbose 2>&1 | tee eval/<cluster>/logs/<preset>_listener_$(date +%Y%m%d_%H%M%S).log
# Raw-script fallback (you own the preamble): from repo root, export PYTHONPATH="$PWD:${PYTHONPATH:-}", run python eval/unified_eval_listener.py … with the same flags.
--cluster-configtakes a bare cluster name (leonardo,tacc) resolved fromhpc.hpc'seval_cluster_view(a.yamlpath still works as back-compat). Supplies sbatch_script/hardware/conda_envs/paths — so you no longer pass--sbatch-script/--n-concurrent/--gpu-memory-util.- Per-model serve config (
conda_env,tensor_parallel_size,data_parallel_size,max_model_len,limit_mm_per_prompt,max_output_tokens) comes from the shared registry by default — no flag. The cluster yaml'shardware_profile:(e.g.gh200) selects the per-cluster recipe; a per-cluster intrinsic delta isname@<profile>, a hardware delta isvariants: {<profile>: {…}}. Confirm it loaded: listener logsModel-config registry ENABLED+Loaded model registry: N model config(s)+Using conda env '<env>' for <model>.--baseline-model-configsis deprecated (opt-out of the registry). - Edit per-model serve config in
model_config/<org>/<slug>.yaml, NOT the generatedeval/configs/model_configs.yaml(auto-generated, carries a# do NOT hand-editbanner). Regenerate withpython scripts/generate_eval_registry.py(drift gate:--check). - Thinking is per-model authoritative (sourced from the registry via
agent_kwargs: [extra_body={…enable_thinking:true}]); presets never carry thinking; there is no--enable-thinkingflag. Override with--agent-kwarg 'extra_body={"chat_template_kwargs":{"enable_thinking":true}}'(precedence: CLI > registry > preset).
5. VERIFY the launch — 15-min infra sanity check (do NOT trust "RUNNING")
A job can report RUNNING while nothing happens (pinggy locked, launcher missing --pinggy_*, dead vLLM engine). After launching, schedule a 15-min (ScheduleWakeup delaySeconds: 900) check and re-arm each pass until the eval terminates / you have a verdict.
Checks 1–2 are pinggy-path (installed-harness) ONLY — skip for terminus-2. For terminus-2, served-model reachability is proven by check 3. Checks 3–4 apply to every launch.
- Pinggy tunnel (installed-harness) — pinggy auth
You are authenticated as …+ a growingRB:/SB:/TC:traffic counter. Full checks (auth, sandboxapi_base= public*.a.pinggy.link/v1not10.*, relaunch-on-lock) →.agents/projects/harbor/ops.md§ opencode + pinggy. - Daytona → cluster (installed-harness) — a trial's
config.jsonapi_baseMUST behttps://*.a.pinggy.link/v1, NOT10.*.*.*(see the harbor ops.md opencode+pinggy section). - vLLM serving —
POST /v1/chat/completionscount grows ≥ a few/min,200 OKdominates.400ratio > 15% → context overflow (VLLMValidationError: input tokens …→ lowermax_input_tokens/max_output_tokensin the harbor yaml). - Trial progression — count trials with
agent/populated (active) andresult.json(done). 30+ min with zeroagent/command-0/(OpenHands) → setup stalled. Completions withn_output_tokens: Noneandagent_execution.finished_at ≈ started_at(instant-fail) = tunnel not carrying traffic despite a healthy-looking job.
Quick liveness (≈15 min after submit): ssh <cluster> "squeue -u $USER --format='%.18i %.50j %.8T %.10M'" then tail the newest log — vLLM health-check pass, (Leonardo) SSH tunnel up, trial/reward lines, no OOM/repeated DaytonaErrors.
6. Trial directory layout
<run_tag>/<task>__<trial_id>/: config.json (mtime≈start, has api_base), trial.log, result.json (timestamps + verifier_result.rewards.reward + exception_info), exception.txt, agent/trajectory.json, verifier/{reward.txt,detailed_scores.json}. Eval cleanup + manual DB register + trace upload → eval-agentic-cleanup.
Other gotchas
-
PermissionError: [Errno 13]atharbor/job.py … job_dir.mkdir()= ajobs_dirin the harbor config that another user owns. The canonical configs ship nojobs_dir;eval_harbor.sbatchpasses--jobs-dir "$EVAL_JOBS_DIR"(per-user…/ot-baf/eval_jobs) which overrides the config. If you see this, confirm the sbatch has the--jobs-dirline; a hand-rolledharbor jobs startwill reintroduce it. Resume is unaffected (takes-p $RUN_DIR). -
A crashed eval leaves a non-terminal DB row blocking resubmission for 24h (
reason=job in progress). After a crash the row staysstarted; the listener only resubmitsstartedrows older than--stale-started-hours(default 24h,EVAL_LISTENER_STALE_HOURS). Pass a small value (e.g.--stale-started-hours 0.05= 3 min) to force resubmit of the just-crashed attempt. Pending rows use--stale-pending-hours(default 6h, auto-cancels the stale SLURM job). -
Jupiter: pass
--reservation reformoor eval jobs starve behind RL (the reservation holds ~128 nodes while the general booster pool is empty).eval/jupiter/eval_harbor.sbatchsets--account reformobut no#SBATCH --reservation. Checkscontrol show reservationfor the live name/expiry before relying on it (the flag errors if the reservation is dead). Rescue already-PENDING jobs:scontrol update jobid=<j> reservation=reformo. -
hosted_vllm/<org>/<model>evals need harbor commit0f5a6e9e(allows 2-slash org-qualified names —validate_hosted_vllm_model_configinllms/utils.py) andmodel_infosupplied via--agent-kwarg({"max_input_tokens":…,"max_output_tokens":…,"input_cost_per_token":0,"output_cost_per_token":0}; token limits from the served vLLMmax_model_len, costs 0 = self-hosted). Both are wired by default into theeval_harbor.sbatchfiles viaEVAL_VLLM_MAX_MODEL_LEN(default 32768) +EVAL_MAX_OUTPUT_TOKENS(default 16384) (OT-Agent commitd0064011). If org-model evals fast-fail (~9 min, 0 POST 200s, 0 trajectories, all N trials raise identically), confirm those commits are in the cluster's harbor clone.
Signals
- GitHub stars
- 291
- Forks
- 41
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
eval-agentic-launch- Source
- github.com/open-thoughts/openthoughts-agent