End-to-end LLM evaluation pipeline built on vLLM and lm-evaluation-harness. Each part is small, self-contained, and honest about what it measures.
| Part | Scope | Dir |
|---|---|---|
| A | vLLM server launcher + async Python client (streaming, concurrency demo) | serve/ |
| B | Harness-compatible model wrapper, two official tasks + one custom (custom_qa), prompt cache |
eval_runner/ |
| C | Async load generator, percentile metrics, optional nvidia-smi sampler, analysis notebook |
perf/ |
| D | JSON-schema / regex output validation, determinism verification | guardrails/ |
| E | HellaSwag ablation with paired-bootstrap CIs, improvement + result logs | improve/ |
This repo is the reference implementation for the LLM Systems & Evaluation problem statement. The design goals are, in order:
- Correctness — the code does what it says (guardrails, determinism, paired-bootstrap CIs, cached scoring).
- Reproducibility — every run writes to
results/result-log.*anddocs/improvement-log.mdwith commit SHA, seed, and config, so past numbers can be re-derived from the repo alone. - Legibility — every module has a README that says what's in it and
why, and every non-trivial choice has an ADR or a paragraph in
docs/enterprise-standards.md. - No hidden state — no hardcoded URLs / models / paths / hyperparams;
everything flows through
common.config.Settings(pydantic-settings readingLLMEVAL_*env vars).
No GPU? Need real numbers for an assignment? Open
notebooks/colab_run.ipynb in Google Colab's
free T4 tier, set your REPO_URL, click Run all. ~45 min later the
last cell downloads a tarball with every Part A–E artefact populated
(result log, improvement log, report, metrics.csv, perf summary,
determinism report). Unpack into your repo, commit, submit.
See docs/cloud-setup.md for the full walkthrough
plus paid alternatives (RunPod, Lambda Labs, AWS g5, Codespaces GPU).
Local dev loop:
# First-time setup (Python 3.11 + uv + optional extras)
make bootstrap
# The fast loop, against the mock backend (no GPU needed):
make lint typecheck test
bash scripts/smoke.sh
# Real run on a GPU machine:
uv sync --extra serve --extra eval --extra perf --extra improve
make serve # in one terminal
make eval # full lm-eval on MMLU-STEM + HellaSwag + custom
make perf # load-test the server
bash improve/eval.sh # Part E ablation ladderEvery Makefile target is documented — make help prints the list.
Two central, human-readable logs are updated automatically by every eval / improve run:
results/result-log.md(+ authoritativeresult-log.csv) — one row per run with task, method, accuracy, delta vs baseline, 95% CI, p-value, wall time, commit SHA, seed.docs/improvement-log.md— chronological journal of Part E changes with hypothesis, exact config, result, significance verdict, decision.
These make the before/after story trivially skimmable after the fact and
are the primary artefacts (alongside improve/report.md) of the Part E
work.
Every tunable is an LLMEVAL_-prefixed env var (see .env.example):
- Model + vLLM:
LLMEVAL_MODEL_NAME,LLMEVAL_VLLM_HOST,LLMEVAL_VLLM_PORT,LLMEVAL_VLLM_MAX_MODEL_LEN,LLMEVAL_VLLM_DTYPE,LLMEVAL_VLLM_GPU_MEMORY_UTILIZATION,LLMEVAL_VLLM_TENSOR_PARALLEL_SIZE, … - Paths:
LLMEVAL_CACHE_DIR,LLMEVAL_RESULTS_DIR - Determinism:
LLMEVAL_SEED - Logging:
LLMEVAL_LOG_LEVEL,LLMEVAL_LOG_FORMAT(console/json) - Offline dev:
LLMEVAL_MOCK_BACKEND=true—VLLMClientreturns canned responses andserve.serveexits cleanly without launching vLLM, so the whole pipeline is runnable without a GPU.
common/ shared library (config, logging, cache, vllm client, stats, result log)
serve/ Part A — vLLM server + async client + demos
eval_runner/ Part B — harness wrapper + custom task (50 examples) + cached runner
perf/ Part C — load gen + metrics + gpu sampler + analysis.ipynb
guardrails/ Part D — schemas + validate.py + verify_determinism
improve/ Part E — prompt variants + retriever + paired-bootstrap ablation
docs/ problem statement, enterprise standards, ADRs, improvement log
results/ result-log.csv + result-log.md + per-run artefacts
scripts/ bootstrap.sh, smoke.sh
tests/ mirrors source tree; 142 tests as of this commit
Balanced-strict setup:
rufffor lint + formatmypywithdisallow_untyped_defs=trueoncommon/and every Partpytest+pytest-asyncio; coverage reported (not gated)pre-commit(ruff + mypy +detect-secrets)structlogfor structured logging (timeddecorator around perf-critical paths)- Custom exception hierarchy in
common/errors.py; no bareException - Conventional commits
The full 30-rule ruleset is in AGENTS.md and is read
automatically by Devin, Claude, Cursor, and Codex.
From PLAN.md; all checks run in this environment via
bash scripts/smoke.sh.
-
make installon a fresh clone succeeds. -
make lint && make typecheck && make testgreen. -
make smoke(bootstrap → mock inference → one CLI + demo + eval) green. -
python -m eval_runner.run_eval --task custom_qaproducesresults/custom_qa.json+results/summary.md+ one row inresult-log.csv. -
python -m perf.load_testproduces a well-formedmetrics.csvandperf.metricssummarises it with no NaNs. -
bash improve/eval.shproducesimprove/report.mdwith per-variant numbers, CIs, and one row per variant inresults/result-log.csvanddocs/improvement-log.md. -
python -m guardrails.validate determinism "prompt"reportsidentical=trueon the mock backend withtemperature=0. -
detect-secrets,ruff,mypyall green.
As of the latest commit:
142 tests pass in ~5s
common/ line coverage: 83.7%
(common/vllm_client 61% — the bulk of the uncovered branches are live HTTP
paths; tests use httpx mock transports and the mock backend.)
MIT.