llm-d-benchmark adopts the following hierarchical technical governance structure:
- A community of contributors who file issues and submit pull requests
- A body of core maintainers who own
llm-d-benchmarkoverall and drive its development - A lead core maintainer who is the catch-all decision maker when consensus cannot be reached by core maintainers
All contributions are expected to follow llm-d-benchmark design principles and best practices, as enforced by core maintainers. While high-quality pull requests are appreciated and encouraged, all maintainers reserve the right to prioritize their own work over code reviews at-will, hence contributors should not expect their work to be reviewed promptly.
Contributors can maximize the chances of their work being accepted by maintainers by meeting a high quality bar before sending a PR to maintainers.
The core maintainers lead the development of llm-d-benchmark and define the benchmarking infrastructure and strategy for the broader llm-d project. Their responsibilities include:
- Proposing, implementing and reviewing load profiles, parameter configurations, run rules, data collections, and analysis of workloads to
llm-d - Enforcing code quality standards and adherence to core design principles
The core maintainers should publicly articulate their decision-making, and share the reasoning behind their decisions, vetoes, and dispute resolution.
List of core maintainers can be found in the OWNERS file.
When core maintainers cannot come to a consensus, a publicly declared lead maintainer is expected to settle the debate and make executive decisions.
The Lead Core Maintainer should publicly articulate their decision-making, and give a clear reasoning for their decisions.
The Lead Core Maintainer is also responsible for confirming or removing core maintainers.
We are committed to accepting functional bug fixes that meet our quality standards – and include minimized unit tests to avoid future regressions. Performance improvements generally fall under the same category, with the caveat that they may be rejected if the trade-off between usefulness and complexity is deemed unfavorable by core maintainers. Design changes that neither fix known functional nor performance issues are automatically considered controversial.
More controversial design changes (e.g., breaking changes to workload profiles, load generators, run rules or data collection and analisys tools) are evaluated on a case-by-case basis under the subjective judgment of core maintainers.
We welcome contributions to any aspect of llm-d-benchmark! If you have a bug fix, feature request, or improvement, please submit a pull request (PR) to the repository.
For every Pull Request submitted, ensure the following steps have been done:
- Sign your commits
- Make sure that the pre-commit hooks have been run and pass before you push the contents of your PR. See Local Development Checks below.
The repository ships a pre-commit configuration that runs the same checks CI runs on your PR, locally, before you push. Setting this up once means you catch regressions without waiting for CI and never push a PR that will fail the PR-benchmark or plan-rendering CI workflows.
./util/setup_precommit.shThat script:
- Delegates to
./install.sh(no-yflag — we deliberately want a virtualenv, not system Python) to create/reuse.venv/, install thellmdbenchmarkCLI andplanner(from llm-d-planner), and provision all required system tools (helm,helmfile,kubectl,skopeo,crane,helm-diff,jq,yq,kustomize). This is the same bootstrap CI uses (see.github/workflows/ci-pr-benchmark.yaml), with-yomitted so local development stays in.venv/instead of polluting your system Python. - Installs
pre-commit,pytest, anddetect-secretsfrom.pre-commit_requirements.txt. - Registers both the
pre-commitandpre-pushhook types.
You only need to run this once per clone. On subsequent invocations install.sh uses its ~/.llmdbench_dependencies_checked cache to skip dependencies that have already been verified.
| Hook | Stage | Command | Mirrors CI |
|---|---|---|---|
py-compile |
pre-commit, pre-push |
python -m compileall -q llmdbenchmark (only on changed llmdbenchmark/**.py) |
— (fast local-only syntax gate) |
pytest |
pre-commit, pre-push |
python -m pytest tests/ -x -q |
unit-tests job in ci-pr-benchmark.yaml |
render-validation-changed |
pre-commit, pre-push |
util/precommit_render_changed.py — detects which scenarios the commit actually touched and renders only those (falls back to cicd/kind-sim canary for shared-path changes) |
Scoped subset of ci-pr-plan-rendering-validation.yaml |
detect-secrets |
pre-commit |
ibm/detect-secrets against .secrets.baseline with --use-all-plugins |
— (local-only secrets scan) |
Stages explained:
pre-commitfires on everygit commit. Byte-compile, unit tests, a scoped render of whichever scenarios your diff actually touched, and a secrets scan. This is your "catch the typo and the scenario regression" layer.pre-pushfires on everygit push. Runs the same hooks again as a last-chance gate before the change leaves your machine. We intentionally do not run the full per-spec render loop here — if your diff didn't touch a spec, re-rendering every spec on every push is wasted work. CI still does the exhaustive per-spec render on the PR, so any shared-path regression a local run missed is caught there.
The helper script util/precommit_render_changed.py receives the list of staged files from pre-commit and resolves them to specs using four rules, in order:
config/specification/<path>.yaml.j2→ render<path>.config/scenarios/<path>.yaml→ render<path>(thescenarios/andspecification/trees are kept 1:1).- Any change under a shared render path (
config/templates/,llmdbenchmark/{parser,plan,executor,utilities,standup,run,teardown,smoketests}/,llmdbenchmark/cli.py,llmdbenchmark/config.py) → render thecicd/kind-simcanary. We do not expand shared-path changes into every scenario locally — that's what CI's full per-spec render job does on the PR. - Nothing resolved (docs-only commit, test-only commit, anything else that doesn't touch a scenario or shared render path) → render the
cicd/kind-simcanary as a baseline sanity check. Every commit proves the render path is healthy, even when the diff has nothing to do with scenarios.
The script always prints exactly which scenarios it is about to render and why, e.g.:
Rendering 2 scenarios:
- cicd/kind-sim [shared render path touched (llmdbenchmark/parser/version_resolver.py)]
- guides/tiered-prefix-cache [edited config/specification/guides/tiered-prefix-cache.yaml.j2]
Rendering: cicd/kind-sim
Rendering: guides/tiered-prefix-cache
Render results: 2 passed, 0 failed
So: edit a single spec → that spec renders. Edit three specs → all three render. Edit a parser file → the canary renders. Edit docs only → the canary still renders as a baseline. The hook is never a no-op on pre-commit, which is the point — every commit gets proof that something renders cleanly.
# Run the full pre-commit suite against every tracked file (what CI effectively does):
pre-commit run --all-files
# Run one specific hook by id:
pre-commit run pytest --all-files
pre-commit run render-validation-changed --all-files
# Run against only the files in your current diff (what `git commit` does):
pre-commit runIf a hook fails, the first line of output tells you which hook and exits with the underlying tool's output. Common cases:
pytestfailure — runpytest tests/ -x -qdirectly and fix the failing test. The CIunit-testsjob runs exactly this command, so a fix here is guaranteed to green CI.render-validation-changedfailure — the hook output prints which spec(s) it tried to render and their pass/fail status. Reproduce the failing spec interactively to see the full error:Render failures are usually caused by Jinja template changes or missing keys inllmdbenchmark --spec <the-failing-spec> --dry-run plan -p debug
config/scenarios/**/<name>.yaml. If the hook says it renderedcicd/kind-simand you were expecting a different spec, it means your change touched a shared render path and the hook fell back to the canary — the exhaustive per-spec render runs in CI on the PR.detect-secretsfailure — either your change added a secret (remove it) or added a new pattern the baseline doesn't know about. To update the baseline after reviewing the finding:detect-secrets scan --baseline .secrets.baseline --use-all-plugins detect-secrets audit .secrets.baseline
The hooks exist to catch regressions before they reach CI. Bypass only in genuine emergencies:
git commit --no-verify # skips pre-commit stage
git push --no-verify # skips pre-push stageIf you find yourself reaching for --no-verify to get around a legitimate bug, fix the bug instead — the same check will fail on CI when you open the PR.
The hooks are defined in .pre-commit-config.yaml. If you add a new hook there or change an existing one, re-run ./util/setup_precommit.sh (or just pre-commit install && pre-commit install --hook-type pre-push) so git picks up the change.