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.
Files changed (33) hide show
  1. llm_drift_bench-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +34 -0
  2. llm_drift_bench-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
  3. llm_drift_bench-0.1.0/.github/workflows/ci.yml +83 -0
  4. llm_drift_bench-0.1.0/.github/workflows/publish.yml +36 -0
  5. llm_drift_bench-0.1.0/.gitignore +74 -0
  6. llm_drift_bench-0.1.0/CONTRIBUTING.md +78 -0
  7. llm_drift_bench-0.1.0/LICENSE +21 -0
  8. llm_drift_bench-0.1.0/PKG-INFO +273 -0
  9. llm_drift_bench-0.1.0/README.md +236 -0
  10. llm_drift_bench-0.1.0/docs/architecture.md +175 -0
  11. llm_drift_bench-0.1.0/examples/demo_suite.json +14 -0
  12. llm_drift_bench-0.1.0/pyproject.toml +93 -0
  13. llm_drift_bench-0.1.0/src/llm_drift_bench/__init__.py +98 -0
  14. llm_drift_bench-0.1.0/src/llm_drift_bench/__main__.py +10 -0
  15. llm_drift_bench-0.1.0/src/llm_drift_bench/_matching.py +37 -0
  16. llm_drift_bench-0.1.0/src/llm_drift_bench/cli.py +460 -0
  17. llm_drift_bench-0.1.0/src/llm_drift_bench/engine.py +426 -0
  18. llm_drift_bench-0.1.0/src/llm_drift_bench/evaluator.py +326 -0
  19. llm_drift_bench-0.1.0/src/llm_drift_bench/metrics.py +269 -0
  20. llm_drift_bench-0.1.0/src/llm_drift_bench/models.py +362 -0
  21. llm_drift_bench-0.1.0/src/llm_drift_bench/py.typed +0 -0
  22. llm_drift_bench-0.1.0/src/llm_drift_bench/reporter.py +175 -0
  23. llm_drift_bench-0.1.0/src/llm_drift_bench/validator.py +371 -0
  24. llm_drift_bench-0.1.0/tests/__init__.py +1 -0
  25. llm_drift_bench-0.1.0/tests/conftest.py +139 -0
  26. llm_drift_bench-0.1.0/tests/test_cli.py +271 -0
  27. llm_drift_bench-0.1.0/tests/test_engine.py +172 -0
  28. llm_drift_bench-0.1.0/tests/test_evaluator.py +264 -0
  29. llm_drift_bench-0.1.0/tests/test_matching.py +116 -0
  30. llm_drift_bench-0.1.0/tests/test_metrics.py +187 -0
  31. llm_drift_bench-0.1.0/tests/test_models.py +216 -0
  32. llm_drift_bench-0.1.0/tests/test_reporter.py +106 -0
  33. 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
+ [![CI](https://github.com/anant-goel/llm-drift-bench/actions/workflows/ci.yml/badge.svg)](https://github.com/anant-goel/llm-drift-bench/actions/workflows/ci.yml)
41
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
42
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
43
+ [![Runtime dependencies](https://img.shields.io/badge/runtime-Pydantic%20%2B%20Rich-blue.svg)](pyproject.toml)
44
+ [![Code style: Ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
45
+ [![Typed: Mypy Strict](https://img.shields.io/badge/typing-mypy%20strict-blue.svg)](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).