inference-doctor 0.4.0__tar.gz
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.
- inference_doctor-0.4.0/CHANGELOG.md +94 -0
- inference_doctor-0.4.0/CONTRIBUTING.md +53 -0
- inference_doctor-0.4.0/LICENSE +201 -0
- inference_doctor-0.4.0/MANIFEST.in +5 -0
- inference_doctor-0.4.0/PKG-INFO +228 -0
- inference_doctor-0.4.0/README.md +191 -0
- inference_doctor-0.4.0/STATUS.md +53 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/README.md +30 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/baseline/repetition-001.json +6 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/baseline/repetition-002.json +6 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/baseline/repetition-003.json +6 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/baseline/repetition-004.json +6 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/baseline/repetition-005.json +6 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/baseline/repetition-006.json +6 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/candidate/repetition-001.json +6 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/candidate/repetition-002.json +6 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/candidate/repetition-003.json +6 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/candidate/repetition-004.json +6 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/candidate/repetition-005.json +6 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/candidate/repetition-006.json +6 -0
- inference_doctor-0.4.0/examples/inference-ci-reliability-001/summary.json +12 -0
- inference_doctor-0.4.0/examples/quickstart/README.md +30 -0
- inference_doctor-0.4.0/examples/quickstart/baseline/repetition-001.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/baseline/repetition-002.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/baseline/repetition-003.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/baseline/repetition-004.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/baseline/repetition-005.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/candidate/repetition-001.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/candidate/repetition-002.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/candidate/repetition-003.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/candidate/repetition-004.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/candidate/repetition-005.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/regressed-candidate/repetition-001.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/regressed-candidate/repetition-002.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/regressed-candidate/repetition-003.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/regressed-candidate/repetition-004.json +22 -0
- inference_doctor-0.4.0/examples/quickstart/regressed-candidate/repetition-005.json +22 -0
- inference_doctor-0.4.0/examples/vllm-bench-serve/README.md +22 -0
- inference_doctor-0.4.0/examples/vllm-bench-serve/baseline/openai-chat-infqps-concurrency64-Llama-3.1-8B-Instruct-20261011-100000.json +34 -0
- inference_doctor-0.4.0/examples/vllm-bench-serve/baseline/openai-chat-infqps-concurrency64-Llama-3.1-8B-Instruct-20261011-110000.json +34 -0
- inference_doctor-0.4.0/examples/vllm-bench-serve/baseline/openai-chat-infqps-concurrency64-Llama-3.1-8B-Instruct-20261011-120000.json +34 -0
- inference_doctor-0.4.0/examples/vllm-bench-serve/baseline/openai-chat-infqps-concurrency64-Llama-3.1-8B-Instruct-20261011-130000.json +34 -0
- inference_doctor-0.4.0/examples/vllm-bench-serve/baseline/openai-chat-infqps-concurrency64-Llama-3.1-8B-Instruct-20261011-140000.json +34 -0
- inference_doctor-0.4.0/examples/vllm-bench-serve/candidate/openai-chat-infqps-concurrency64-Llama-3.1-8B-Instruct-20261011-100000.json +34 -0
- inference_doctor-0.4.0/examples/vllm-bench-serve/candidate/openai-chat-infqps-concurrency64-Llama-3.1-8B-Instruct-20261011-110000.json +34 -0
- inference_doctor-0.4.0/examples/vllm-bench-serve/candidate/openai-chat-infqps-concurrency64-Llama-3.1-8B-Instruct-20261011-120000.json +34 -0
- inference_doctor-0.4.0/examples/vllm-bench-serve/candidate/openai-chat-infqps-concurrency64-Llama-3.1-8B-Instruct-20261011-130000.json +34 -0
- inference_doctor-0.4.0/examples/vllm-bench-serve/candidate/openai-chat-infqps-concurrency64-Llama-3.1-8B-Instruct-20261011-140000.json +34 -0
- inference_doctor-0.4.0/pyproject.toml +89 -0
- inference_doctor-0.4.0/setup.cfg +4 -0
- inference_doctor-0.4.0/src/inference_doctor/__init__.py +3 -0
- inference_doctor-0.4.0/src/inference_doctor/__main__.py +3 -0
- inference_doctor-0.4.0/src/inference_doctor/adapters.py +564 -0
- inference_doctor-0.4.0/src/inference_doctor/benchmark_results.py +249 -0
- inference_doctor-0.4.0/src/inference_doctor/cli.py +377 -0
- inference_doctor-0.4.0/src/inference_doctor/collectors/__init__.py +0 -0
- inference_doctor-0.4.0/src/inference_doctor/collectors/prometheus.py +182 -0
- inference_doctor-0.4.0/src/inference_doctor/comparison.py +352 -0
- inference_doctor-0.4.0/src/inference_doctor/comparison_report.py +76 -0
- inference_doctor-0.4.0/src/inference_doctor/diagnostics.py +360 -0
- inference_doctor-0.4.0/src/inference_doctor/models.py +67 -0
- inference_doctor-0.4.0/src/inference_doctor/quality.py +573 -0
- inference_doctor-0.4.0/src/inference_doctor/quality_report.py +138 -0
- inference_doctor-0.4.0/src/inference_doctor/report.py +126 -0
- inference_doctor-0.4.0/src/inference_doctor/stats.py +139 -0
- inference_doctor-0.4.0/src/inference_doctor/telemetry.py +161 -0
- inference_doctor-0.4.0/src/inference_doctor.egg-info/PKG-INFO +228 -0
- inference_doctor-0.4.0/src/inference_doctor.egg-info/SOURCES.txt +85 -0
- inference_doctor-0.4.0/src/inference_doctor.egg-info/dependency_links.txt +1 -0
- inference_doctor-0.4.0/src/inference_doctor.egg-info/entry_points.txt +2 -0
- inference_doctor-0.4.0/src/inference_doctor.egg-info/requires.txt +8 -0
- inference_doctor-0.4.0/src/inference_doctor.egg-info/top_level.txt +1 -0
- inference_doctor-0.4.0/tests/fixtures/compare/baseline.json +36 -0
- inference_doctor-0.4.0/tests/fixtures/compare/candidate_28pct_regression.json +36 -0
- inference_doctor-0.4.0/tests/test_adapters.py +349 -0
- inference_doctor-0.4.0/tests/test_cli.py +77 -0
- inference_doctor-0.4.0/tests/test_compare.py +387 -0
- inference_doctor-0.4.0/tests/test_diagnostics.py +364 -0
- inference_doctor-0.4.0/tests/test_gate.py +147 -0
- inference_doctor-0.4.0/tests/test_inference_ci_reliability.py +146 -0
- inference_doctor-0.4.0/tests/test_inputs.py +106 -0
- inference_doctor-0.4.0/tests/test_multi_metric.py +80 -0
- inference_doctor-0.4.0/tests/test_quality.py +216 -0
- inference_doctor-0.4.0/tests/test_vllm_48035_harness.py +336 -0
- inference_doctor-0.4.0/tests/test_vllm_52630_harness.py +269 -0
- inference_doctor-0.4.0/tools/gate_simulation.py +120 -0
- inference_doctor-0.4.0/tools/render_readme_media.py +42 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.4.0 - 2026-10-11
|
|
6
|
+
|
|
7
|
+
### Changed: the compare-runs decision rule
|
|
8
|
+
|
|
9
|
+
- `compare-runs` decides with a confidence interval instead of a CV cut followed by a fixed threshold on the difference of means. It computes a Welch t interval on the log ratio of means (90% two-sided by default): `FAIL` when the whole interval for the regression is at or past `--regression-percent`, `PASS` when the whole interval is below it, and `INCONCLUSIVE` when it contains the threshold. Validity checks (environments, failed requests, missing metrics, repetition count) still come first.
|
|
10
|
+
- Why: simulating the old rule showed a healthy benchmark (true CV 4%, no change, 5 repetitions) was `INCONCLUSIVE` about a third of the time, and with 2 repetitions a true 12% regression passed about 13% of the time. The new rule's rates are published in `docs/gate-calibration.md`, generated by `tools/gate_simulation.py` and checked in `tests/test_gate.py`.
|
|
11
|
+
- The CV ceiling is now optional and off by default (`--stability-cv-percent`, Action input `stability-cv-percent`). Pass `--stability-cv-percent 5` to keep the old ceiling in addition to the interval.
|
|
12
|
+
- At least 3 repetitions per side are required (`--min-repetitions`, default 3, was 2). 5 or more is recommended.
|
|
13
|
+
- New options `--confidence` and `--min-repetitions`, and matching Action inputs. New Action outputs `regression-percent-low` and `regression-percent-high`.
|
|
14
|
+
- The JSON report is `schema_version: inference-ci-quality-2`. It adds `confidence_interval`, `lower_is_better`, `notes`, and `thresholds.confidence` and `thresholds.min_repetitions`; `measurement_quality` adds `enough_repetitions`, and `environment_valid` no longer depends on the wording of a reason.
|
|
15
|
+
- A candidate better than the baseline by more than the threshold gets a note asking to check that both sides measured the same workload.
|
|
16
|
+
- The quickstart has five repetitions per side and a `regressed-candidate` directory that demonstrates `FAIL`.
|
|
17
|
+
- The Action passes its inputs to the shell through environment variables instead of interpolating them into the script.
|
|
18
|
+
|
|
19
|
+
### Fixed: compare and diagnose
|
|
20
|
+
|
|
21
|
+
- `compare` no longer crashes with `UnboundLocalError` when a baseline value is 0; a rise from 0 in a latency, or a fall to 0 in a throughput, is a failure.
|
|
22
|
+
- `compare` returns `INCONCLUSIVE` (exit 2) instead of `PASS` when no latency or throughput metric is present in both files, and its overall status is `WARN` (still exit 0) when any row is in the warning band.
|
|
23
|
+
- `compare` is deprecated: it prints a notice on stderr pointing to `compare-runs`, and will be removed in a future release.
|
|
24
|
+
- `diagnose`: the queue mean is aggregated across series (`sum(rate(_sum)) / sum(rate(_count))`); with two replicas at 0.2 s it reported 0.4 s. The Prometheus client now refuses a query that returns more than one series instead of adding them up.
|
|
25
|
+
- `diagnose`: `QUEUE_PRESSURE` requires queue evidence (waiting requests, or a mean queue time of at least 20% of TTFT p95). TTFT over its SLO with a healthy TPOT used to be enough, which flagged prefill-heavy workloads as queueing.
|
|
26
|
+
- `diagnose`: a new `PREFILL_PRESSURE` finding covers TTFT over its SLO with no queue evidence and prefill time at least half of TTFT p95. Prefill time and prompt length were collected before but not used.
|
|
27
|
+
- `diagnose` exits 1 when it reports a critical finding; `--fail-on warning|never` changes the threshold. It always exited 0 before.
|
|
28
|
+
|
|
29
|
+
### Added: gate several metrics at once
|
|
30
|
+
|
|
31
|
+
- `--metric` can be repeated (Action: a comma-separated `metric` input). Each metric is compared in full; the combined result is `FAIL` if any metric fails, `INCONCLUSIVE` if none fails but one is inconclusive, and `PASS` only if all pass. The report notes that each extra metric adds a chance of a false alarm. With one metric the report is unchanged; with several it has `metrics` and `comparisons`. The Action's job summary has one row per metric, and a new `report` output holds the JSON report path.
|
|
32
|
+
|
|
33
|
+
### Added: read benchmark tools' own output
|
|
34
|
+
|
|
35
|
+
- `compare-runs` reads the result files of `vllm bench serve --save-result` (including `--append-result` JSON Lines), NVIDIA AIPerf and GenAI-Perf, GuideLLM `benchmarks.json`, and SGLang `bench_serving`, with no conversion step. `--format auto` (the default) detects the format per file; `--format` forces one. See `docs/input-formats.md`.
|
|
36
|
+
- `--gpu` and `--model` record values the result files do not carry; `--guidellm-benchmark` picks one load point of a GuideLLM sweep. Matching Action inputs `format`, `gpu`, `model` and `guidellm-benchmark`.
|
|
37
|
+
- p99 latencies, inter-token latency (`itl_*_ms`), end-to-end latency (`e2el_*_ms`) and total token throughput, in the normalized schema and as `--metric` choices. Every latency family has mean, median, p95 and p99.
|
|
38
|
+
- The report shows the input format, notes when the GPU or model was not recorded or a tool does not record failed requests, and lists skipped files under `input.ignored_files`.
|
|
39
|
+
- `examples/vllm-bench-serve` holds results in vLLM's own format, where throughput passes and mean TTFT fails.
|
|
40
|
+
- Normalized results under any file name are detected, as long as no file in the directory uses one of the standard names.
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- `inference-doctor --version`, and `python -m inference_doctor`.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- When no repeated-run files are found, the error names the accepted file patterns and lists the JSON files that were ignored. Files such as `run1.json` used to be skipped without a word.
|
|
49
|
+
- `compare-runs` explains `INCONCLUSIVE` accurately. A missing or non-finite metric, or a single repetition, used to be reported as "measurements exceeded the allowed stability threshold"; the stability reason now appears only when a CV was measured, and it states that CV.
|
|
50
|
+
- Identical repetitions, including all-zero values, have a CV of 0 instead of no CV. `--metric requests_failed` could never pass before.
|
|
51
|
+
- An integer `schema_version` (`1` instead of `"1"`) gets a message that says so.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- One exit-code scheme for every command: 0 PASS, 1 FAIL, 2 INCONCLUSIVE or UNSTABLE, 3 error with no decision. Before, `compare-runs` exited 2 (the INCONCLUSIVE code) for a missing directory or a mistyped option, `compare` exited 1 for UNSTABLE and 2 for invalid input, and `diagnose` exited 1 when Prometheus could not be queried.
|
|
56
|
+
- An internal error now prints one line and exits 3 instead of printing a traceback and exiting 1, which a CI job read as FAIL. Set `INFERENCE_DOCTOR_DEBUG=1` for the traceback.
|
|
57
|
+
- CI runs ruff, the formatter check and mypy, and tests on Python 3.11 and 3.14 on Linux plus 3.14 on macOS and Windows. Actions are pinned by commit SHA, the token is read-only, and checkouts do not keep credentials. The workflow is now `.github/workflows/ci.yml`.
|
|
58
|
+
- The package moved to `src/` and builds with setuptools. The version is defined once, in `inference_doctor/__init__.py`; it said 0.1.0 while the package metadata said 0.3.0.
|
|
59
|
+
|
|
60
|
+
## 0.3.0 - 2026-09-14
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
|
|
64
|
+
- Reusable GitHub Action for repeated-run inference regression gating.
|
|
65
|
+
- No-GPU quickstart fixtures for trying `compare-runs` locally in minutes.
|
|
66
|
+
- GitHub Actions documentation and PR smoke coverage.
|
|
67
|
+
- Measurement-quality gate with first-class `PASS`, `FAIL`, and `INCONCLUSIVE` outcomes.
|
|
68
|
+
- Repeated-run statistics including mean, median, sample standard deviation, CV, min/max, and descriptive outlier flags.
|
|
69
|
+
- Optional environment telemetry helpers for benchmark investigations.
|
|
70
|
+
- Experimental `inference-doctor compare` upgrade guard for normalized benchmark result files, with configurable regression thresholds, terminal and JSON output, and CI exit codes.
|
|
71
|
+
- Auditable historical regression and benchmark-reliability experiments that informed the measurement-quality design.
|
|
72
|
+
|
|
73
|
+
### Changed
|
|
74
|
+
|
|
75
|
+
- Repositioned the project around trustworthy inference performance CI: measurement quality is checked before a merge decision is made.
|
|
76
|
+
- `compare-runs` is now the primary documented workflow; Prometheus diagnosis remains supported as a secondary feature.
|
|
77
|
+
- Apparent outliers remain included in official decisions rather than being silently removed.
|
|
78
|
+
|
|
79
|
+
## 0.2.0 - 2026-09-13
|
|
80
|
+
|
|
81
|
+
### Added
|
|
82
|
+
|
|
83
|
+
- Queue mean, prefill p95, and prompt-token p95 collection and reporting.
|
|
84
|
+
- Regression coverage for non-finite Prometheus scalar values.
|
|
85
|
+
- Public integration reproduction notes and sanitized validation evidence.
|
|
86
|
+
|
|
87
|
+
### Changed
|
|
88
|
+
|
|
89
|
+
- Non-finite Prometheus scalar values now render as unavailable.
|
|
90
|
+
- Queue p95 remains visible but no longer raises queue-pressure confidence.
|
|
91
|
+
|
|
92
|
+
## 0.1.0
|
|
93
|
+
|
|
94
|
+
- Initial vertical slice with Prometheus collection, terminal and JSON reports, and queue, decode, and KV-cache diagnostic rules.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for helping improve Inference Doctor.
|
|
4
|
+
|
|
5
|
+
The primary product goal is narrow: **trustworthy performance regression testing for LLM inference**. Changes should make it easier to compare baseline vs candidate performance without giving false confidence when benchmark data is noisy.
|
|
6
|
+
|
|
7
|
+
The original read-only Prometheus diagnostic mode remains supported as a secondary feature.
|
|
8
|
+
|
|
9
|
+
## Issues
|
|
10
|
+
|
|
11
|
+
For inference-CI issues, include:
|
|
12
|
+
|
|
13
|
+
- selected metric
|
|
14
|
+
- baseline and candidate repetition counts
|
|
15
|
+
- relevant normalized benchmark metadata
|
|
16
|
+
- configured stability/regression thresholds
|
|
17
|
+
- redacted terminal or JSON output
|
|
18
|
+
|
|
19
|
+
For Prometheus diagnostic issues, include the vLLM version, relevant metric names, query window, supplied SLOs, and a redacted report.
|
|
20
|
+
|
|
21
|
+
Never include API tokens, credentials, customer prompts, private model data, or cloud access details.
|
|
22
|
+
|
|
23
|
+
## Pull requests
|
|
24
|
+
|
|
25
|
+
- Keep each change focused on one observable behavior or usability improvement.
|
|
26
|
+
- Preserve `PASS`, `FAIL`, and `INCONCLUSIVE` semantics unless the proposal includes evidence and tests.
|
|
27
|
+
- Do not silently remove statistical outliers from official decisions.
|
|
28
|
+
- Add or update regression tests for behavior changes.
|
|
29
|
+
- Update docs when schemas, thresholds, supported metrics, or exit codes change.
|
|
30
|
+
- Keep GPU/cloud provisioning outside the core comparison engine.
|
|
31
|
+
- Do not commit virtual environments, model caches, credentials, `.env` files, or cloud-provider artifacts.
|
|
32
|
+
|
|
33
|
+
Before opening a pull request:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python -m pip install -e ".[dev]"
|
|
37
|
+
python -m pytest -q
|
|
38
|
+
ruff check . && ruff format --check . && mypy
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
CI runs the same checks, with tests on Python 3.11 and 3.14 on Linux and on 3.14 on macOS and Windows.
|
|
42
|
+
|
|
43
|
+
## Releasing
|
|
44
|
+
|
|
45
|
+
1. Move the `## Unreleased` entries in `CHANGELOG.md` under `## X.Y.Z - date` and set
|
|
46
|
+
`__version__` in `src/inference_doctor/__init__.py` to `X.Y.Z`. Commit to `main`.
|
|
47
|
+
2. Once CI is green, tag and push: `git tag vX.Y.Z && git push origin vX.Y.Z`. The
|
|
48
|
+
release workflow checks the tag, builds, and publishes to PyPI through trusted
|
|
49
|
+
publishing.
|
|
50
|
+
3. Move the Action's major tag to the release when its inputs and outputs are compatible:
|
|
51
|
+
`git tag -f v1 vX.Y.Z && git push -f origin v1`.
|
|
52
|
+
|
|
53
|
+
Use clear commit messages and describe the validation performed in the pull request.
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: inference-doctor
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Performance regression gate for LLM inference benchmarks that checks measurement quality before it judges a regression.
|
|
5
|
+
Author-email: Dundy Pasupuleti <dundysm@gmail.com>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/dundysm/inference-doctor
|
|
8
|
+
Project-URL: Source, https://github.com/dundysm/inference-doctor
|
|
9
|
+
Project-URL: Issues, https://github.com/dundysm/inference-doctor/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/dundysm/inference-doctor/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: llm,inference,benchmark,performance-regression,vllm,continuous-integration,github-action,measurement-quality
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
24
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
25
|
+
Classifier: Topic :: Software Development :: Testing
|
|
26
|
+
Requires-Python: >=3.11
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: httpx>=0.27
|
|
30
|
+
Requires-Dist: rich>=13.7
|
|
31
|
+
Requires-Dist: typer>=0.12
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff==0.17.0; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<img src="https://raw.githubusercontent.com/dundysm/inference-doctor/main/docs/assets/banner.svg" alt="inference-doctor: did this change make LLM inference slower, and can your benchmark actually tell?" width="900">
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
<p align="center">
|
|
43
|
+
<a href="https://pypi.org/project/inference-doctor/"><img alt="PyPI" src="https://img.shields.io/pypi/v/inference-doctor?color=3776ab"></a>
|
|
44
|
+
<a href="https://github.com/dundysm/inference-doctor/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/dundysm/inference-doctor/actions/workflows/ci.yml/badge.svg"></a>
|
|
45
|
+
<img alt="Python 3.11+" src="https://img.shields.io/badge/python-3.11%2B-3776ab">
|
|
46
|
+
<img alt="Reads vLLM, AIPerf, GuideLLM and SGLang" src="https://img.shields.io/badge/reads-vLLM%2C%20AIPerf%2C%20GuideLLM%2C%20SGLang-1f6feb">
|
|
47
|
+
<a href="https://github.com/dundysm/inference-doctor/blob/main/LICENSE"><img alt="License: Apache-2.0" src="https://img.shields.io/badge/license-Apache--2.0-2ea043"></a>
|
|
48
|
+
<a href="https://github.com/dundysm/inference-doctor/blob/main/STATUS.md"><img alt="Status: early" src="https://img.shields.io/badge/status-early-d29922"></a>
|
|
49
|
+
<a href="https://github.com/dundysm/inference-doctor/blob/main/CONTRIBUTING.md"><img alt="PRs welcome" src="https://img.shields.io/badge/PRs-welcome-2ea043"></a>
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
A pull request can pass every test and still make inference slower. Putting a benchmark
|
|
53
|
+
in CI is the obvious fix, and it goes wrong in three ways:
|
|
54
|
+
|
|
55
|
+
* **false alarms**: GPU benchmarks are noisy, so a gate on two numbers fails changes that
|
|
56
|
+
did nothing, and the team turns it off,
|
|
57
|
+
* **missed regressions**: with two or three runs, a real 12% slowdown can land inside the
|
|
58
|
+
noise and pass,
|
|
59
|
+
* **apples to oranges**: the two sides ran on a different GPU, model or load, and the
|
|
60
|
+
difference is the setup, not the change.
|
|
61
|
+
|
|
62
|
+
inference-doctor compares repeated baseline and candidate runs and returns `PASS` or
|
|
63
|
+
`FAIL` only when the data can support it. Otherwise it returns `INCONCLUSIVE` and says
|
|
64
|
+
why. It reads the files `vllm bench serve`, AIPerf, GuideLLM and SGLang already write.
|
|
65
|
+
|
|
66
|
+
<p align="center">
|
|
67
|
+
<img src="https://raw.githubusercontent.com/dundysm/inference-doctor/main/docs/assets/terminal.svg" alt="Terminal output of inference-doctor compare-runs on a real H100 run: six runs per side, throughput 4.19% lower, 90% confidence interval for the regression -3.33% to +11.17%, which contains the 10% threshold, so the result is INCONCLUSIVE." width="860">
|
|
68
|
+
<br>
|
|
69
|
+
<sub>A real H100 run (<code>examples/inference-ci-reliability-001</code>), rendered from the CLI by
|
|
70
|
+
<code>tools/render_readme_media.py</code>. Throughput is 4.19% lower, but one noisy baseline block leaves an interval
|
|
71
|
+
from -3.33% to +11.17%. Six runs cannot rule out a 10% regression, so there is no merge recommendation.</sub>
|
|
72
|
+
</p>
|
|
73
|
+
|
|
74
|
+
## How it decides
|
|
75
|
+
|
|
76
|
+
<p align="center">
|
|
77
|
+
<img src="https://raw.githubusercontent.com/dundysm/inference-doctor/main/docs/assets/decision.svg" alt="Three 90% confidence intervals for the regression against a 10% threshold: the quickstart, +1.08% to +2.92%, PASS; a real H100 run, -3.33% to +11.17%, INCONCLUSIVE; a regressed candidate, +13.13% to +14.87%, FAIL." width="900">
|
|
78
|
+
</p>
|
|
79
|
+
|
|
80
|
+
1. **Is the comparison valid?** Same GPU, model and benchmark parameters on every run, no
|
|
81
|
+
failed requests, the metric present everywhere, and at least 3 runs per side. If not,
|
|
82
|
+
the result is `INCONCLUSIVE` and the reason names the check that failed.
|
|
83
|
+
2. **How big is the change, and how sure are we?** A 90% confidence interval for the
|
|
84
|
+
regression (positive is worse), from a Welch t interval on the log ratio of means.
|
|
85
|
+
3. **Verdict.** `FAIL` when the whole interval is at or past the threshold (default 10%),
|
|
86
|
+
`PASS` when the whole interval is below it, `INCONCLUSIVE` when it contains the
|
|
87
|
+
threshold. More runs narrow the interval.
|
|
88
|
+
|
|
89
|
+
Outliers are flagged but never removed: a bad run widens the interval instead of being
|
|
90
|
+
hidden. Details and references: [measurement quality](docs/measurement-quality.md).
|
|
91
|
+
|
|
92
|
+
## Quick start
|
|
93
|
+
|
|
94
|
+
Python 3.11 or newer. No GPU: the example results are checked in, so the clone is only for
|
|
95
|
+
them.
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install inference-doctor
|
|
99
|
+
git clone https://github.com/dundysm/inference-doctor.git && cd inference-doctor
|
|
100
|
+
|
|
101
|
+
inference-doctor compare-runs \
|
|
102
|
+
--baseline examples/quickstart/baseline \
|
|
103
|
+
--candidate examples/quickstart/candidate \
|
|
104
|
+
--metric texts_per_second
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`PASS`: the candidate is 2% slower, and the interval, +1.08% to +2.92%, is well below 10%.
|
|
108
|
+
Point `--candidate` at `examples/quickstart/regressed-candidate` to see a `FAIL`.
|
|
109
|
+
|
|
110
|
+
## Use your benchmark's own output
|
|
111
|
+
|
|
112
|
+
No converter needed. Put each side's runs in a directory and point `compare-runs` at it:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
inference-doctor compare-runs \
|
|
116
|
+
--baseline results/main \
|
|
117
|
+
--candidate results/my-branch \
|
|
118
|
+
--metric output_token_throughput --metric ttft_p99_ms
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Repeat `--metric` to gate on several at once: the result is `FAIL` if any metric fails,
|
|
122
|
+
`INCONCLUSIVE` if none fails but one cannot be decided, and `PASS` only when all pass.
|
|
123
|
+
|
|
124
|
+
| Tool | What it reads |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `vllm bench serve --save-result` | each result file, or each line with `--append-result` |
|
|
127
|
+
| NVIDIA AIPerf / GenAI-Perf | each `profile_export_aiperf.json` |
|
|
128
|
+
| GuideLLM | `benchmarks.json`, one load point of the sweep (`--guidellm-benchmark`) |
|
|
129
|
+
| SGLang `bench_serving` | each line of its `.jsonl` output |
|
|
130
|
+
| anything else | the small [normalized format](docs/benchmark-format.md) |
|
|
131
|
+
|
|
132
|
+
Metrics: output, total and request throughput, and TTFT, TPOT, ITL and end-to-end latency
|
|
133
|
+
at mean, median, p95 and p99. Most tools do not record the GPU; pass `--gpu` to label it,
|
|
134
|
+
or record it in the results (for vLLM, `--metadata gpu=...`). The
|
|
135
|
+
[input formats](docs/input-formats.md) page lists every field read from each tool.
|
|
136
|
+
|
|
137
|
+
[`examples/vllm-bench-serve`](examples/vllm-bench-serve/README.md) is a small example in
|
|
138
|
+
vLLM's format where throughput passes and TTFT fails, so gating on both catches what a
|
|
139
|
+
throughput-only gate would let through.
|
|
140
|
+
|
|
141
|
+
## In GitHub Actions
|
|
142
|
+
|
|
143
|
+
Run your benchmark on your own GPU runner, then gate on the results:
|
|
144
|
+
|
|
145
|
+
```yaml
|
|
146
|
+
- name: Gate inference performance
|
|
147
|
+
uses: dundysm/inference-doctor@v1
|
|
148
|
+
with:
|
|
149
|
+
baseline: results/main
|
|
150
|
+
candidate: results/my-branch
|
|
151
|
+
metric: output_token_throughput, ttft_p99_ms
|
|
152
|
+
min-repetitions: "5"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The Action writes a job summary with the verdict, the interval and the reason, and exposes
|
|
156
|
+
them as outputs. `FAIL`, `INCONCLUSIVE` and errors fail the step, so noisy evidence cannot
|
|
157
|
+
approve a change. inference-doctor does not provision GPUs or run the benchmark. See
|
|
158
|
+
[GitHub Actions](docs/github-actions.md).
|
|
159
|
+
|
|
160
|
+
## How much to trust it
|
|
161
|
+
|
|
162
|
+
Simulated with known truth, at default settings, from
|
|
163
|
+
[gate calibration](docs/gate-calibration.md) (4,000 trials per row):
|
|
164
|
+
|
|
165
|
+
| Runs per side, noise (CV) | No change | True 5% drop | True 15% drop | True 20% drop |
|
|
166
|
+
| --- | --- | --- | --- | --- |
|
|
167
|
+
| 3 runs, 4% | 78% PASS, 0% FAIL | 34% PASS, 0.1% FAIL | 0% PASS, 37% FAIL | 0% PASS, 85% FAIL |
|
|
168
|
+
| 5 runs, 4% | 98% PASS, 0% FAIL | 60% PASS, 0% FAIL | 0% PASS, 65% FAIL | 0% PASS, 99% FAIL |
|
|
169
|
+
| 10 runs, 4% | 100% PASS, 0% FAIL | 90% PASS, 0% FAIL | 0% PASS, 92% FAIL | 0% PASS, 100% FAIL |
|
|
170
|
+
|
|
171
|
+
The rest of each row is `INCONCLUSIVE`. Right at the 10% threshold, each wrong verdict
|
|
172
|
+
happens about 5% of the time; further from it, at most 0.5%. Noise costs decisions, not
|
|
173
|
+
correctness. Use 5 or more runs per side. These rates assume independent, roughly normal
|
|
174
|
+
runs; [interleave baseline and candidate runs](docs/measurement-quality.md#assumptions)
|
|
175
|
+
to stay close to that.
|
|
176
|
+
|
|
177
|
+
## Commands
|
|
178
|
+
|
|
179
|
+
| Command | What it does |
|
|
180
|
+
| --- | --- |
|
|
181
|
+
| `compare-runs` | The gate: repeated baseline and candidate runs, one or more metrics, `PASS`, `FAIL` or `INCONCLUSIVE`. |
|
|
182
|
+
| `compare` | Deprecated. One normalized file against another, every metric. A single run per side cannot measure noise; use `compare-runs`. See [upgrade guard](docs/upgrade-guard.md). |
|
|
183
|
+
| `diagnose` | Experimental. Reads a live vLLM server's Prometheus metrics and reports queue, prefill, decode and KV-cache pressure. Read-only. Exits 1 on a critical finding (`--fail-on` changes that). See [example reports](docs/example-reports.md). |
|
|
184
|
+
|
|
185
|
+
`compare-runs` options: `--metric` (repeatable), `--regression-percent` (10), `--confidence` (0.90),
|
|
186
|
+
`--min-repetitions` (3), `--stability-cv-percent` (an optional CV ceiling, off),
|
|
187
|
+
`--format` (auto), `--gpu`, `--model`, `--guidellm-benchmark` and `--json`.
|
|
188
|
+
|
|
189
|
+
**Exit codes**, the same for every command: `0` PASS, `1` FAIL (for `diagnose`, a
|
|
190
|
+
finding at or above `--fail-on`), `2` INCONCLUSIVE (or UNSTABLE for `compare`), `3` error
|
|
191
|
+
with no decision (bad arguments or input, an unreachable Prometheus, or an internal
|
|
192
|
+
error). `inference-doctor --version` prints the
|
|
193
|
+
version.
|
|
194
|
+
|
|
195
|
+
## What has been measured
|
|
196
|
+
|
|
197
|
+
* **The decision rule**: the calibration table above, regenerated by
|
|
198
|
+
`tools/gate_simulation.py` and checked by `tests/test_gate.py` on every CI run.
|
|
199
|
+
* **One real GPU run**: `examples/inference-ci-reliability-001`, six runs per side of
|
|
200
|
+
BAAI/bge-m3 on one H100 across two vLLM versions. The honest answer was `INCONCLUSIVE`,
|
|
201
|
+
and it still is.
|
|
202
|
+
* **The format readers**: tested against records built from each tool's source code. They
|
|
203
|
+
have not yet been run on a large set of real files from each tool, so please
|
|
204
|
+
[report](https://github.com/dundysm/inference-doctor/issues/new/choose) any file they
|
|
205
|
+
misread.
|
|
206
|
+
* **Not yet**: a gate running in another project's CI. See [STATUS.md](STATUS.md).
|
|
207
|
+
|
|
208
|
+
## Help test it
|
|
209
|
+
|
|
210
|
+
The most useful thing right now is a real change: run `compare-runs` on results from your
|
|
211
|
+
own benchmark and open a **Benchmark feedback** issue saying what it decided, whether that
|
|
212
|
+
was right, and what was confusing. Redact prompts, credentials, private model names and
|
|
213
|
+
customer data.
|
|
214
|
+
|
|
215
|
+
## Contributing
|
|
216
|
+
|
|
217
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). The changelog is [CHANGELOG.md](CHANGELOG.md).
|
|
218
|
+
The experiments that informed the design live under `experiments/`.
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
python -m pip install -e ".[dev]"
|
|
222
|
+
python -m pytest -q
|
|
223
|
+
ruff check . && ruff format --check . && mypy
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
Apache-2.0. See [LICENSE](LICENSE).
|