This repo orchestrates lm-evaluation-harness runs against vLLM via YAML workloads in workloads/. lib/run.sh parses a workload, brings up vLLM in Docker, and dispatches each task to a helper in lib/. Real runs need GPUs and are exercised on Buildkite.
Whenever a change touches the workload schema, repo layout, run command, or anything else a new user reads on day one, update README.md in the same change. The README is the entry point — if it lies, people waste time. This applies to schema renames (e.g. moving fields under a new top-level key), new files in lib/ or workloads/, changed local-run prerequisites, and additions to the Buildkite pipeline. If you're not sure whether a change deserves a README update, default to updating it.
If an AI agent (Claude Code, Cursor, Copilot, etc.) wrote, edited, or substantially shaped any code or config in a change, the change must say so. Concretely:
- PR description — include a line like
This PR was authored with assistance from <tool>(or "AI-assisted") in the body. Don't bury it; reviewers should see it before they read the diff. - Commit messages — include a
Co-Authored-By:trailer naming the model, e.g.Co-Authored-By: Claude <noreply@anthropic.com>. Claude Code adds this by default; don't strip it. - Code comments — not required. Don't sprinkle "// AI-generated" through the source; the PR + commit metadata is the durable record.
This applies to non-trivial changes (new code, refactors, design decisions). Pure mechanical edits the agent ran on the user's behalf — formatter runs, find-and-replace, bumping a version — don't need a callout, but err on the side of disclosing if unsure.
You cannot run a real eval locally — it needs a GPU host with Docker, vLLM, and lm-eval installed. What you can run locally:
Parser smoke test — exec the parser with a stubbed lm_eval registry to verify TSV output and validation behavior. The stub avoids needing lm-eval installed; populate all_tasks with the names referenced by the YAML under test:
python3 -c "
import sys, types
m = types.ModuleType('lm_eval'); t = types.ModuleType('lm_eval.tasks')
class TM: all_tasks = ['gsm8k', 'aime25']
t.TaskManager = TM
sys.modules['lm_eval'] = m; sys.modules['lm_eval.tasks'] = t
sys.argv = ['parse_workload.py', 'workloads/qwen3_5_h200.yaml']
exec(open('lib/parse_workload.py').read())
"Shell syntax — catches typos in the orchestrator and helpers without executing them:
bash -n lib/run.sh && bash -n lib/server.sh && bash -n lib/run_lm_eval.shIngestion auth and pipeline generation:
python3 -m unittest discover -s tests -p 'test_ingest_auth.py'
python3 .buildkite/test_generate_pipeline.pyIf you actually need real validation (parser hitting lm-eval's task registry rather than a stub), pip install 'lm-eval[api]' pyyaml first. Without it the parser exits with cannot validate task names: lm_eval not importable — that's intentional, never silently skip validation.
Use either the Buildkite MCP tools or an authenticated bk CLI. Prefer MCP
when it is available because its responses are already structured; bk is a
supported alternative for triggering, watching, and inspecting builds. Never
make raw Buildkite API calls with curl.
Pipeline metadata:
- org:
vllm - pipeline:
perf-eval - repo:
github.com/vllm-project/perf-eval - default branch:
main - what it runs: a dynamic pipeline. A bootstrap step runs
.buildkite/generate_pipeline.pyand generates per-workload steps using each workload's GPU profile. WhenWORKLOADSis set, it runs exactly those workload paths or stems. Otherwise it discovers allworkloads/*.yamlwithnightly: true.
-
Make sure the commit is on the remote. Buildkite clones from GitHub; unpushed commits will fail to resolve. If the user has local changes you've been working on, ask before pushing; once pushed, capture the SHA from
git rev-parse <branch>. -
Trigger the build with either MCP or
bk.With
mcp__claude_ai_Buildkite__create_build:org_slug: "vllm"pipeline_slug: "perf-eval"commit: "<full SHA>"branch: "<branch name>"(use the actual branch, notmain, when testing a feature branch)message: "<short description of what this tests>"— match the existing convention: short, action-oriented (e.g. "Add gpqa diamond", "Writable HF_HOME for lm_eval datasets cache"). No emoji unless the user asks.environment: always pass bothVLLM_COMMIT(the vLLM SHA being tested) andVLLM_IMAGE(the full Docker image URI). When CUDA and ROCm are unrelated artifacts, passVLLM_IMAGE_CUDA/VLLM_IMAGE_ROCMinstead — each overrides all other image selection for its platform, and a platform with neither its own pin norVLLM_IMAGEhas its workloads skipped. Optionally passWORKLOADSfor an explicit workload list; omit it to run allnightly: trueworkloads.
With
bk(runbk auth statusfirst):bk build create \ --yes \ --pipeline vllm/perf-eval \ --commit "<full perf-eval SHA>" \ --branch "<branch name>" \ --message "<short description of what this tests>" \ --env "VLLM_COMMIT=<vLLM SHA>" \ --env "VLLM_IMAGE=<full Docker image URI>" \ --env "WORKLOADS=<optional workload list>"
Omit the final
WORKLOADSargument to run everynightly: trueworkload. -
Report the build URL back to the user immediately so they can follow along. MCP returns it as
web_url;bk build createprints it in the build summary.
mcp__claude_ai_Buildkite__get_buildwithjob_state: "failed,broken,canceled"to check for failures without pulling full logs.mcp__claude_ai_Buildkite__tail_logswith thejob_idfor the most recent log lines — start here for failure diagnosis, it's far cheaper thanread_logs.mcp__claude_ai_Buildkite__search_logswith patterns like"error|failed|exception|Traceback"iftail_logsdoesn't show the failure.
With bk, watch the build, inspect its structured summary to identify failed
job IDs, and then pull only the relevant job log:
bk build watch <build-number> --pipeline perf-eval
bk build view <build-number> --pipeline perf-eval --json
bk job log <job-id> --pipeline perf-eval --build-number <build-number>A typical build takes ~30–90 minutes (the step has a 120-min hard timeout) — it downloads model weights into the workload GPU profile's HF cache, then runs every task in the workload. GPU queues are shared with other vLLM pipelines, so don't trigger duplicate builds for the same commit unless asked.
- Don't push to
mainor trigger builds without the user asking. Triggering a build is visible to the team and consumes GPU minutes. - Don't
--no-verifypast failing pre-commit hooks just to get a build out. Fix the hook failure first. - Don't hard-code GPU queues in
.buildkite/pipeline.yamlorgenerate_pipeline.py; add or update entries inlib/gpu_profiles.yamlinstead.