llm-drift-bench 0.1.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.
- llm_drift_bench-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +34 -0
- llm_drift_bench-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
- llm_drift_bench-0.1.0/.github/workflows/ci.yml +83 -0
- llm_drift_bench-0.1.0/.github/workflows/publish.yml +36 -0
- llm_drift_bench-0.1.0/.gitignore +74 -0
- llm_drift_bench-0.1.0/CONTRIBUTING.md +78 -0
- llm_drift_bench-0.1.0/LICENSE +21 -0
- llm_drift_bench-0.1.0/PKG-INFO +273 -0
- llm_drift_bench-0.1.0/README.md +236 -0
- llm_drift_bench-0.1.0/docs/architecture.md +175 -0
- llm_drift_bench-0.1.0/examples/demo_suite.json +14 -0
- llm_drift_bench-0.1.0/pyproject.toml +93 -0
- llm_drift_bench-0.1.0/src/llm_drift_bench/__init__.py +98 -0
- llm_drift_bench-0.1.0/src/llm_drift_bench/__main__.py +10 -0
- llm_drift_bench-0.1.0/src/llm_drift_bench/_matching.py +37 -0
- llm_drift_bench-0.1.0/src/llm_drift_bench/cli.py +460 -0
- llm_drift_bench-0.1.0/src/llm_drift_bench/engine.py +426 -0
- llm_drift_bench-0.1.0/src/llm_drift_bench/evaluator.py +326 -0
- llm_drift_bench-0.1.0/src/llm_drift_bench/metrics.py +269 -0
- llm_drift_bench-0.1.0/src/llm_drift_bench/models.py +362 -0
- llm_drift_bench-0.1.0/src/llm_drift_bench/py.typed +0 -0
- llm_drift_bench-0.1.0/src/llm_drift_bench/reporter.py +175 -0
- llm_drift_bench-0.1.0/src/llm_drift_bench/validator.py +371 -0
- llm_drift_bench-0.1.0/tests/__init__.py +1 -0
- llm_drift_bench-0.1.0/tests/conftest.py +139 -0
- llm_drift_bench-0.1.0/tests/test_cli.py +271 -0
- llm_drift_bench-0.1.0/tests/test_engine.py +172 -0
- llm_drift_bench-0.1.0/tests/test_evaluator.py +264 -0
- llm_drift_bench-0.1.0/tests/test_matching.py +116 -0
- llm_drift_bench-0.1.0/tests/test_metrics.py +187 -0
- llm_drift_bench-0.1.0/tests/test_models.py +216 -0
- llm_drift_bench-0.1.0/tests/test_reporter.py +106 -0
- llm_drift_bench-0.1.0/tests/test_validator.py +240 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug Report
|
|
3
|
+
about: Report a bug, unexpected drift score, or schema validation issue in llm-drift-bench
|
|
4
|
+
title: "[BUG] "
|
|
5
|
+
labels: ["bug", "triage"]
|
|
6
|
+
assignees: ""
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Bug Description
|
|
10
|
+
A clear and concise description of what the bug is.
|
|
11
|
+
|
|
12
|
+
## Steps to Reproduce
|
|
13
|
+
1. Run command or Python snippet:
|
|
14
|
+
```bash
|
|
15
|
+
llm-drift-bench run -b ... -c ...
|
|
16
|
+
```
|
|
17
|
+
2. Provide minimal sample input (`prompts.json` / `.jsonl` snippet):
|
|
18
|
+
```json
|
|
19
|
+
{"prompt_id": "example_1", "output": "{...}"}
|
|
20
|
+
```
|
|
21
|
+
3. See error or unexpected output.
|
|
22
|
+
|
|
23
|
+
## Expected Behavior
|
|
24
|
+
A clear and concise description of what you expected to happen.
|
|
25
|
+
|
|
26
|
+
## Actual Output / Traceback
|
|
27
|
+
```text
|
|
28
|
+
Paste terminal output or stack trace here
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Environment
|
|
32
|
+
- **`llm-drift-bench` version**: (e.g., `0.1.0`)
|
|
33
|
+
- **Python version**: (e.g., `3.11.8`)
|
|
34
|
+
- **Operating System**: (e.g., `Ubuntu 22.04`, `macOS 14`, `Windows 11`)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature Request
|
|
3
|
+
about: Suggest a new drift metric, schema rule, or CLI capability for llm-drift-bench
|
|
4
|
+
title: "[FEAT] "
|
|
5
|
+
labels: ["enhancement"]
|
|
6
|
+
assignees: ""
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem Statement
|
|
10
|
+
Is your feature request related to a specific LLM evaluation or quantization drift challenge? Please describe.
|
|
11
|
+
|
|
12
|
+
## Proposed Solution
|
|
13
|
+
Describe the CLI flag, Python API, or drift metric you would like to see added.
|
|
14
|
+
|
|
15
|
+
### Example Usage
|
|
16
|
+
```bash
|
|
17
|
+
# Example CLI or Python usage
|
|
18
|
+
llm-drift-bench run --baseline fp16.jsonl --candidate int4.jsonl
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Alternatives Considered
|
|
22
|
+
Describe any alternative solutions, workarounds, or external libraries you've considered.
|
|
23
|
+
|
|
24
|
+
## Additional Context
|
|
25
|
+
Add any other context, academic references, or benchmark examples about the feature request here.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["main"]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint-and-typecheck:
|
|
14
|
+
name: Lint (Ruff) & Type-Check (Mypy)
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout repository
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python 3.11
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.11"
|
|
24
|
+
cache: "pip"
|
|
25
|
+
|
|
26
|
+
- name: Install package and dev dependencies
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install --upgrade pip
|
|
29
|
+
pip install -e ".[dev]"
|
|
30
|
+
|
|
31
|
+
- name: Run Ruff linter
|
|
32
|
+
run: ruff check src tests
|
|
33
|
+
|
|
34
|
+
- name: Run Mypy strict type checker
|
|
35
|
+
run: mypy src/llm_drift_bench
|
|
36
|
+
|
|
37
|
+
test:
|
|
38
|
+
name: Test Suite (Python ${{ matrix.python-version }})
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
strategy:
|
|
41
|
+
fail-fast: false
|
|
42
|
+
matrix:
|
|
43
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
44
|
+
steps:
|
|
45
|
+
- name: Checkout repository
|
|
46
|
+
uses: actions/checkout@v4
|
|
47
|
+
|
|
48
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
49
|
+
uses: actions/setup-python@v5
|
|
50
|
+
with:
|
|
51
|
+
python-version: ${{ matrix.python-version }}
|
|
52
|
+
cache: "pip"
|
|
53
|
+
|
|
54
|
+
- name: Install package and test dependencies
|
|
55
|
+
run: |
|
|
56
|
+
python -m pip install --upgrade pip
|
|
57
|
+
pip install -e ".[dev]"
|
|
58
|
+
|
|
59
|
+
- name: Run pytest with coverage enforcement (>85%)
|
|
60
|
+
run: pytest --cov=llm_drift_bench --cov-report=term-missing --cov-fail-under=85
|
|
61
|
+
|
|
62
|
+
build:
|
|
63
|
+
name: Package Build Check
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
needs: [lint-and-typecheck, test]
|
|
66
|
+
steps:
|
|
67
|
+
- name: Checkout repository
|
|
68
|
+
uses: actions/checkout@v4
|
|
69
|
+
|
|
70
|
+
- name: Set up Python 3.12
|
|
71
|
+
uses: actions/setup-python@v5
|
|
72
|
+
with:
|
|
73
|
+
python-version: "3.12"
|
|
74
|
+
|
|
75
|
+
- name: Install build tooling
|
|
76
|
+
run: |
|
|
77
|
+
python -m pip install --upgrade pip build twine
|
|
78
|
+
|
|
79
|
+
- name: Build wheel and sdist
|
|
80
|
+
run: python -m build
|
|
81
|
+
|
|
82
|
+
- name: Verify distribution metadata with twine
|
|
83
|
+
run: twine check dist/*
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
pypi-publish:
|
|
13
|
+
name: Build & Publish to PyPI
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
environment:
|
|
16
|
+
name: pypi
|
|
17
|
+
url: https://pypi.org/p/llm-drift-bench
|
|
18
|
+
permissions:
|
|
19
|
+
id-token: write
|
|
20
|
+
steps:
|
|
21
|
+
- name: Check out the repository
|
|
22
|
+
uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Set up Python 3.12
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.12"
|
|
28
|
+
|
|
29
|
+
- name: Install build tooling
|
|
30
|
+
run: pip install --upgrade pip build
|
|
31
|
+
|
|
32
|
+
- name: Build source distribution and binary wheel
|
|
33
|
+
run: python -m build
|
|
34
|
+
|
|
35
|
+
- name: Publish distribution to PyPI
|
|
36
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
*.manifest
|
|
31
|
+
*.spec
|
|
32
|
+
|
|
33
|
+
# Unit test / coverage reports
|
|
34
|
+
htmlcov/
|
|
35
|
+
.tox/
|
|
36
|
+
.nox/
|
|
37
|
+
.coverage
|
|
38
|
+
.coverage.*
|
|
39
|
+
.cache
|
|
40
|
+
nosetests.xml
|
|
41
|
+
coverage.xml
|
|
42
|
+
*.cover
|
|
43
|
+
*.py,cover
|
|
44
|
+
.hypothesis/
|
|
45
|
+
.pytest_cache/
|
|
46
|
+
cover/
|
|
47
|
+
|
|
48
|
+
# Environments
|
|
49
|
+
.env
|
|
50
|
+
.venv
|
|
51
|
+
env/
|
|
52
|
+
venv/
|
|
53
|
+
ENV/
|
|
54
|
+
env.bak/
|
|
55
|
+
venv.bak/
|
|
56
|
+
|
|
57
|
+
# Type checkers & linters
|
|
58
|
+
.mypy_cache/
|
|
59
|
+
.dmypy.json
|
|
60
|
+
dmypy.json
|
|
61
|
+
.pyre/
|
|
62
|
+
.pytype/
|
|
63
|
+
.ruff_cache/
|
|
64
|
+
|
|
65
|
+
# IDEs & Editors
|
|
66
|
+
.idea/
|
|
67
|
+
.vscode/
|
|
68
|
+
*.swp
|
|
69
|
+
*.swo
|
|
70
|
+
*~
|
|
71
|
+
|
|
72
|
+
# OS generated files
|
|
73
|
+
.DS_Store
|
|
74
|
+
Thumbs.db
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Contributing to `llm-drift-bench`
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to **`llm-drift-bench`**! We aim to keep this package fast, modular, strictly typed, and zero-bloat.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Development Environment Setup
|
|
8
|
+
|
|
9
|
+
### Prerequisites
|
|
10
|
+
- Python **3.10+**
|
|
11
|
+
- `git`
|
|
12
|
+
|
|
13
|
+
### Clone and Install in Editable Mode
|
|
14
|
+
```bash
|
|
15
|
+
git clone https://github.com/anant-goel/llm-drift-bench.git
|
|
16
|
+
cd llm-drift-bench
|
|
17
|
+
python -m venv .venv
|
|
18
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
19
|
+
pip install -e ".[dev]"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 2. Running Tests, Linting & Type Checking
|
|
25
|
+
|
|
26
|
+
Before opening a Pull Request, verify that all checks pass locally:
|
|
27
|
+
|
|
28
|
+
### Run the Test Suite with Coverage
|
|
29
|
+
```bash
|
|
30
|
+
pytest --cov=llm_drift_bench --cov-report=term-missing --cov-fail-under=85
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Run the complete test suite with pytest after installing `.[dev]`.
|
|
34
|
+
|
|
35
|
+
The standard-library unittest runner does not execute the pytest-style
|
|
36
|
+
function tests, and importing this package still requires its runtime
|
|
37
|
+
dependencies.
|
|
38
|
+
|
|
39
|
+
### Run Ruff Linter
|
|
40
|
+
```bash
|
|
41
|
+
ruff check src tests
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Run Mypy Strict Type Checking
|
|
45
|
+
```bash
|
|
46
|
+
mypy src/llm_drift_bench
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 3. Pull Request & Commit Guidelines
|
|
52
|
+
|
|
53
|
+
1. **Zero-Bloat Philosophy**: Do not add third-party runtime dependencies to `[project].dependencies` unless strictly necessary; prefer standard library implementations or optional extras.
|
|
54
|
+
2. **Atomic Conventional Commits**: Every logical change (schema addition, metric function, CLI flag, test file, or documentation update) should have an atomic commit following [Conventional Commits](https://www.conventionalcommits.org/):
|
|
55
|
+
- `feat(metrics): ...`
|
|
56
|
+
- `fix(validator): ...`
|
|
57
|
+
- `test(engine): ...`
|
|
58
|
+
- `docs: ...`
|
|
59
|
+
- `ci: ...`
|
|
60
|
+
3. **Test Coverage**: New features and bug fixes must include unit tests in `tests/` maintaining at least 85% total coverage.
|
|
61
|
+
4. **Strict Typing**: All functions and methods in `src/llm_drift_bench/` must include full Python type annotations (`from __future__ import annotations`) and pass `mypy --strict`.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 4. `good first issue` Roadmap
|
|
66
|
+
|
|
67
|
+
Looking for a place to start contributing? Here is our curated list of `good first issue` enhancements:
|
|
68
|
+
|
|
69
|
+
- [ ] **`[good first issue]` Support `uniqueItems` and `multipleOf` in `validator.py`**
|
|
70
|
+
- Extend `validate_against_schema` in `src/llm_drift_bench/validator.py` to validate JSON Schema `uniqueItems: true` on arrays and `multipleOf` on numeric fields, with corresponding unit tests in `tests/test_validator.py`.
|
|
71
|
+
- [ ] **Render the canonical benchmark report directly as Markdown.**
|
|
72
|
+
- Preserve thresholds, failure reasons, optional measurements, and scores without recomputing metrics or inventing missing values.
|
|
73
|
+
- [ ] **`[good first issue]` Per-Category Drift Breakdown in `DriftBenchmarkReport`**
|
|
74
|
+
- Group `SampleDriftResult` items by `category` and include per-category mean drift and conformity rates in `DriftBenchmarkReport.to_dict()`.
|
|
75
|
+
- [ ] **`[good first issue]` CSV / TSV Export Option for Sample Results**
|
|
76
|
+
- Add `--csv <path>` to `llm-drift-bench run` using Python's standard library `csv` module to export per-prompt drift scores for spreadsheet analysis.
|
|
77
|
+
- [ ] **`[good first issue]` JSON Schema `anyOf` / `oneOf` Combinator Support**
|
|
78
|
+
- Implement recursive support for `anyOf` and `oneOf` schema combinators in `src/llm_drift_bench/validator.py`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 llm-drift-bench Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: llm-drift-bench
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight, zero-bloat CLI and Python library to benchmark, validate JSON conformity, and detect output drift across LLM updates and quantizations.
|
|
5
|
+
Project-URL: Homepage, https://github.com/anant-goel/llm-drift-bench
|
|
6
|
+
Project-URL: Repository, https://github.com/anant-goel/llm-drift-bench
|
|
7
|
+
Project-URL: Issues, https://github.com/anant-goel/llm-drift-bench/issues
|
|
8
|
+
Author: Anant Goel
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: benchmark,drift-detection,evaluation,json-validation,llm,mlops,quantization
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
24
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
25
|
+
Classifier: Topic :: Software Development :: Testing
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Requires-Dist: pydantic>=2.0.0
|
|
29
|
+
Requires-Dist: rich>=13.0.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: build>=1.2.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: mypy>=1.10.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: ruff>=0.5.0; extra == 'dev'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# llm-drift-bench
|
|
39
|
+
|
|
40
|
+
[](https://github.com/anant-goel/llm-drift-bench/actions/workflows/ci.yml)
|
|
41
|
+
[](https://www.python.org/downloads/)
|
|
42
|
+
[](LICENSE)
|
|
43
|
+
[](pyproject.toml)
|
|
44
|
+
[](https://github.com/astral-sh/ruff)
|
|
45
|
+
[](http://mypy-lang.org/)
|
|
46
|
+
|
|
47
|
+
**`llm-drift-bench`** is a Python CLI and library for comparing captured LLM outputs across model updates, prompt revisions, and quantizations. It measures lexical differences, JSON structure and field agreement, numeric changes, and recorded latency.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Why `llm-drift-bench`?
|
|
52
|
+
|
|
53
|
+
When upgrading model checkpoints or quantizing weights for production inference, subtle regressions frequently slip past perplexity benchmarks:
|
|
54
|
+
- **Broken JSON Conformity**: Markdown fence artifacts or missing required keys in structured outputs.
|
|
55
|
+
- **Structural Key-Path Drift**: Schema-valid payloads that drop optional nested fields or alter value types.
|
|
56
|
+
- **Numeric Field Drift**: Extracted financial or confidence numbers drifting beyond acceptable tolerances.
|
|
57
|
+
- **Tail Latency Regressions**: P95 latency spikes across specific prompt categories.
|
|
58
|
+
|
|
59
|
+
The runtime uses Pydantic and Rich, together with their transitive dependencies.
|
|
60
|
+
|
|
61
|
+
Similarity measures agreement with a baseline, not factual correctness or semantic equivalence. The validator implements a JSON Schema subset and rejects unsupported keywords before checking the response. It is not a complete JSON Schema implementation.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Quickstart after publication
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install llm-drift-bench
|
|
69
|
+
llm-drift-bench init-sample --dir ./demo
|
|
70
|
+
llm-drift-bench run -b ./demo/baseline_fp16.jsonl -c ./demo/candidate_int4.jsonl -p ./demo/prompts.json
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Visual Terminal Output
|
|
76
|
+
|
|
77
|
+
### 1. Drift Benchmark Comparison (`llm-drift-bench run`)
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
==============================================================================
|
|
81
|
+
LLM DRIFT BENCHMARK REPORT [PASSED]
|
|
82
|
+
==============================================================================
|
|
83
|
+
Baseline Model : llama-3-8b-fp16
|
|
84
|
+
Candidate Model : llama-3-8b-q4_k_m
|
|
85
|
+
Total Samples : 2 (Drifted: 0 / 0.0%)
|
|
86
|
+
------------------------------------------------------------------------------
|
|
87
|
+
Metric | Baseline | Candidate / Value
|
|
88
|
+
------------------------------------------------------------------------------
|
|
89
|
+
JSON Conformity Rate | 100.0% | 100.0%
|
|
90
|
+
Mean Composite Drift | 0.0000 (ref) | 0.0426
|
|
91
|
+
Max Composite Drift | 0.0000 (ref) | 0.0852
|
|
92
|
+
Mean Token Jaccard Similarity | 1.0000 (ref) | 0.9412
|
|
93
|
+
Mean Levenshtein Similarity | 1.0000 (ref) | 0.9930
|
|
94
|
+
Mean JSON Structural Similarity | 1.0000 (ref) | 1.0000
|
|
95
|
+
Mean JSON Numeric Relative Drift | 0.0000 (ref) | 0.0160
|
|
96
|
+
P50 Latency (ms) | 130.2 | 82.8
|
|
97
|
+
P95 Latency (ms) | 141.3 | 88.6
|
|
98
|
+
------------------------------------------------------------------------------
|
|
99
|
+
Prompt ID | Category | JSON OK | Struct Sim | Drift | State
|
|
100
|
+
------------------------------------------------------------------------------
|
|
101
|
+
extract_invoice_01 | extraction | YES | 1.0000 | 0.0000 | OK
|
|
102
|
+
classify_ticket_02 | classificati | YES | 1.0000 | 0.0852 | OK
|
|
103
|
+
==============================================================================
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 2. Standalone JSON Conformity Validation (`llm-drift-bench validate`)
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
llm-drift-bench validate -i ./demo/candidate_int4.jsonl -p ./demo/prompts.json --strict
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
========================================================================
|
|
114
|
+
JSON CONFORMITY VALIDATION [2/2 passed (100.0%)]
|
|
115
|
+
========================================================================
|
|
116
|
+
[PASS] extract_invoice_01 | Conformant
|
|
117
|
+
[PASS] classify_ticket_02 | Conformant
|
|
118
|
+
========================================================================
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Input & Configuration Formats
|
|
124
|
+
|
|
125
|
+
### Prompt Suite with JSON Schemas (`prompts.json`)
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
[
|
|
129
|
+
{
|
|
130
|
+
"prompt_id": "extract_invoice_01",
|
|
131
|
+
"category": "extraction",
|
|
132
|
+
"prompt": "Extract invoice fields as JSON.",
|
|
133
|
+
"schema": {
|
|
134
|
+
"type": "object",
|
|
135
|
+
"required": ["invoice_id", "total_usd", "status"],
|
|
136
|
+
"properties": {
|
|
137
|
+
"invoice_id": {"type": "string", "minLength": 3, "pattern": "^INV-"},
|
|
138
|
+
"total_usd": {"type": "number", "minimum": 0},
|
|
139
|
+
"status": {"type": "string", "enum": ["paid", "pending", "overdue"]}
|
|
140
|
+
},
|
|
141
|
+
"additionalProperties": false
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Model Outputs (`baseline_fp16.jsonl` / `candidate_int4.jsonl`)
|
|
148
|
+
|
|
149
|
+
```jsonl
|
|
150
|
+
{"prompt_id": "extract_invoice_01", "model_name": "llama-3-8b-fp16", "latency_ms": 142.5, "output": "{\"invoice_id\": \"INV-2026-09\", \"total_usd\": 1250.00, \"status\": \"paid\"}"}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## CLI Reference & CI Gating
|
|
156
|
+
|
|
157
|
+
Gate your CI/CD pipeline on model drift and JSON conformity thresholds using `--fail-on-drift`:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
llm-drift-bench run \
|
|
161
|
+
--baseline ./baseline_fp16.jsonl \
|
|
162
|
+
--candidate ./candidate_int4.jsonl \
|
|
163
|
+
--prompts ./prompts.json \
|
|
164
|
+
--max-drift 0.15 \
|
|
165
|
+
--min-conformity 0.98 \
|
|
166
|
+
--max-numeric-drift 0.05 \
|
|
167
|
+
--max-latency-regression 25.0 \
|
|
168
|
+
--output ./artifacts/drift_report.json \
|
|
169
|
+
--fail-on-drift
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
| Flag | Default | Description |
|
|
173
|
+
| :--- | :--- | :--- |
|
|
174
|
+
| `-b`, `--baseline` | *(Required)* | Path to baseline model outputs (`.json` or `.jsonl`). |
|
|
175
|
+
| `-c`, `--candidate` | *(Required)* | Path to candidate model outputs (`.json` or `.jsonl`). |
|
|
176
|
+
| `-p`, `--prompts` | `None` | Optional prompt definitions with per-prompt JSON Schemas. |
|
|
177
|
+
| `--max-drift` | `0.25` | Maximum allowed mean/per-sample composite drift score `[0.0, 1.0]`. |
|
|
178
|
+
| `--min-conformity` | `0.95` | Minimum required candidate JSON Schema conformity rate `[0.0, 1.0]`. |
|
|
179
|
+
| `--max-numeric-drift` | `0.15` | Maximum allowed relative numeric drift on shared JSON number fields. |
|
|
180
|
+
| `--max-latency-regression` | `50.0` | Maximum allowed P95 latency regression percentage (`%`). |
|
|
181
|
+
| `-o`, `--output` | `None` | File path to save the structured JSON benchmark report. |
|
|
182
|
+
| `--format` | `table` | Output format on stdout (`table`, `rich`, `json`, or `markdown`). |
|
|
183
|
+
| `--fail-on-drift` | `False` | Exit with code `1` if any threshold is violated or sample drifts. |
|
|
184
|
+
|
|
185
|
+
### Current behavior of the unreleased revision
|
|
186
|
+
|
|
187
|
+
Levenshtein similarity always compares characters (Unicode code points),
|
|
188
|
+
including whitespace and case. Each response is limited to 16,384 characters
|
|
189
|
+
for this metric. Longer responses raise an error (CLI exit code `2`), rather
|
|
190
|
+
than being truncated or scored using a different unit. Unicode normalization
|
|
191
|
+
is not applied.
|
|
192
|
+
|
|
193
|
+
Commands are `run`, `report`, `validate`, and `init-sample`.
|
|
194
|
+
`--suite` and `--prompts` are aliases for the suite path.
|
|
195
|
+
|
|
196
|
+
Use existing captured-output files for both comparison targets.
|
|
197
|
+
Missing files, directory paths, and model identifiers are input errors
|
|
198
|
+
(CLI exit code `2`). The tool does not call model endpoints or generate
|
|
199
|
+
responses. The library also accepts explicit sequences of `ModelSample`.
|
|
200
|
+
`init-sample` creates illustrative fixtures, not measured model outputs.
|
|
201
|
+
|
|
202
|
+
Baseline and candidate must contain exactly the same non-empty prompt IDs,
|
|
203
|
+
with no duplicates. An explicitly supplied suite must cover exactly those
|
|
204
|
+
IDs and cannot be empty. The `validate --prompts` command applies the same
|
|
205
|
+
suite-coverage rule. Matching errors return CLI exit code `2`.
|
|
206
|
+
Comparison results are ordered by prompt ID, independently of input order.
|
|
207
|
+
For library callers, `evaluate_suite([], baseline, candidate)` still means
|
|
208
|
+
infer the prompts from the complete matching output sets; it never selects
|
|
209
|
+
only their intersection.
|
|
210
|
+
|
|
211
|
+
Table and JSON output use the benchmark engine. Rich and Markdown output
|
|
212
|
+
currently recompute scores through a second evaluator with different
|
|
213
|
+
weights. Until these paths are unified, their scores are not interchangeable.
|
|
214
|
+
|
|
215
|
+
Missing measurements appear as `n/a` in reports and `null` in JSON.
|
|
216
|
+
Throughput requires an explicit `tokens_generated` count and positive measured
|
|
217
|
+
`latency_ms`; text token estimates are not substituted. Zero latency remains
|
|
218
|
+
a recorded value, but cannot produce throughput or a baseline-relative
|
|
219
|
+
percentage regression.
|
|
220
|
+
|
|
221
|
+
Suite means and latency percentiles require complete measurements for that
|
|
222
|
+
side of the comparison. If either side lacks timings, the latency regression
|
|
223
|
+
check is skipped; a passing result does not certify latency performance.
|
|
224
|
+
Other drift and conformity thresholds still apply.
|
|
225
|
+
|
|
226
|
+
Saved engine reports lose some information when converted by `report`.
|
|
227
|
+
Unavailable latency means and throughput remain `n/a`; percentiles are not
|
|
228
|
+
substituted for means or individual observations.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Python Library API
|
|
233
|
+
|
|
234
|
+
Use `llm-drift-bench` directly inside your evaluation pipelines or pytest suites:
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
from llm_drift_bench import (
|
|
238
|
+
DriftBenchmarkEngine,
|
|
239
|
+
DriftThresholds,
|
|
240
|
+
ModelSample,
|
|
241
|
+
PromptCase,
|
|
242
|
+
format_report_table,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
prompts = [
|
|
246
|
+
PromptCase(
|
|
247
|
+
prompt_id="invoice_1",
|
|
248
|
+
prompt="Extract invoice total.",
|
|
249
|
+
schema={
|
|
250
|
+
"type": "object",
|
|
251
|
+
"required": ["total"],
|
|
252
|
+
"properties": {"total": {"type": "number", "minimum": 0}},
|
|
253
|
+
},
|
|
254
|
+
)
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
baseline = [ModelSample(prompt_id="invoice_1", model_name="fp16", output='{"total": 100.0}')]
|
|
258
|
+
candidate = [ModelSample(prompt_id="invoice_1", model_name="int4", output='{"total": 99.5}')]
|
|
259
|
+
|
|
260
|
+
engine = DriftBenchmarkEngine(thresholds=DriftThresholds(max_composite_drift=0.10))
|
|
261
|
+
report = engine.evaluate(baseline, candidate, prompts)
|
|
262
|
+
|
|
263
|
+
print(format_report_table(report))
|
|
264
|
+
assert report.passed
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Contributing & License
|
|
270
|
+
|
|
271
|
+
Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing instructions, and our `good first issue` roadmap.
|
|
272
|
+
|
|
273
|
+
Licensed under the [MIT License](LICENSE).
|