bdp-model-gate 0.2.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.
- bdp_model_gate-0.2.0/LICENSE +21 -0
- bdp_model_gate-0.2.0/PKG-INFO +374 -0
- bdp_model_gate-0.2.0/README.md +332 -0
- bdp_model_gate-0.2.0/bdp_model_gate/__init__.py +67 -0
- bdp_model_gate-0.2.0/bdp_model_gate/_logging.py +27 -0
- bdp_model_gate-0.2.0/bdp_model_gate/cli.py +248 -0
- bdp_model_gate-0.2.0/bdp_model_gate/config.py +114 -0
- bdp_model_gate-0.2.0/bdp_model_gate/core/__init__.py +6 -0
- bdp_model_gate-0.2.0/bdp_model_gate/core/base.py +47 -0
- bdp_model_gate-0.2.0/bdp_model_gate/core/context.py +49 -0
- bdp_model_gate-0.2.0/bdp_model_gate/core/gate.py +111 -0
- bdp_model_gate-0.2.0/bdp_model_gate/core/report.py +101 -0
- bdp_model_gate-0.2.0/bdp_model_gate/core/validation.py +120 -0
- bdp_model_gate-0.2.0/bdp_model_gate/exceptions.py +21 -0
- bdp_model_gate-0.2.0/bdp_model_gate/metrics.py +217 -0
- bdp_model_gate-0.2.0/bdp_model_gate/py.typed +0 -0
- bdp_model_gate-0.2.0/bdp_model_gate/registry.py +59 -0
- bdp_model_gate-0.2.0/bdp_model_gate/structured/__init__.py +56 -0
- bdp_model_gate-0.2.0/bdp_model_gate/structured/compliance.py +106 -0
- bdp_model_gate-0.2.0/bdp_model_gate/structured/fairness.py +299 -0
- bdp_model_gate-0.2.0/bdp_model_gate/structured/performance.py +141 -0
- bdp_model_gate-0.2.0/bdp_model_gate/structured/security.py +219 -0
- bdp_model_gate-0.2.0/bdp_model_gate/unstructured/__init__.py +34 -0
- bdp_model_gate-0.2.0/bdp_model_gate.egg-info/PKG-INFO +374 -0
- bdp_model_gate-0.2.0/bdp_model_gate.egg-info/SOURCES.txt +39 -0
- bdp_model_gate-0.2.0/bdp_model_gate.egg-info/dependency_links.txt +1 -0
- bdp_model_gate-0.2.0/bdp_model_gate.egg-info/entry_points.txt +2 -0
- bdp_model_gate-0.2.0/bdp_model_gate.egg-info/requires.txt +26 -0
- bdp_model_gate-0.2.0/bdp_model_gate.egg-info/top_level.txt +1 -0
- bdp_model_gate-0.2.0/pyproject.toml +97 -0
- bdp_model_gate-0.2.0/setup.cfg +4 -0
- bdp_model_gate-0.2.0/tests/test_check_coverage.py +145 -0
- bdp_model_gate-0.2.0/tests/test_cli.py +363 -0
- bdp_model_gate-0.2.0/tests/test_edge_cases.py +178 -0
- bdp_model_gate-0.2.0/tests/test_metrics.py +179 -0
- bdp_model_gate-0.2.0/tests/test_package.py +27 -0
- bdp_model_gate-0.2.0/tests/test_registry.py +12 -0
- bdp_model_gate-0.2.0/tests/test_registry_plugins.py +71 -0
- bdp_model_gate-0.2.0/tests/test_structured_gate.py +83 -0
- bdp_model_gate-0.2.0/tests/test_unstructured.py +16 -0
- bdp_model_gate-0.2.0/tests/test_validation.py +114 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kera Health
|
|
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,374 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bdp-model-gate
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Automated pre-deployment ML model governance gate: fairness, performance, compliance, and security checks.
|
|
5
|
+
Author: Vangelis Oden
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/vanjy-eng/model-gate
|
|
8
|
+
Project-URL: Repository, https://github.com/vanjy-eng/model-gate
|
|
9
|
+
Project-URL: Changelog, https://github.com/vanjy-eng/model-gate/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Issues, https://github.com/vanjy-eng/model-gate/issues
|
|
11
|
+
Keywords: ml,governance,fairness,compliance,mlops,responsible-ai,data-science,predictive-modelling
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: numpy<3.0,>=1.23
|
|
22
|
+
Requires-Dist: pandas<3.0,>=1.5
|
|
23
|
+
Requires-Dist: joblib<2.0,>=1.2
|
|
24
|
+
Provides-Extra: structured
|
|
25
|
+
Requires-Dist: scikit-learn<2.0,>=1.2; extra == "structured"
|
|
26
|
+
Requires-Dist: fairlearn<0.13,>=0.9; extra == "structured"
|
|
27
|
+
Requires-Dist: shap<0.47,>=0.44; extra == "structured"
|
|
28
|
+
Provides-Extra: yaml
|
|
29
|
+
Requires-Dist: pyyaml<7.0,>=6.0; extra == "yaml"
|
|
30
|
+
Provides-Extra: toml
|
|
31
|
+
Requires-Dist: tomli<3.0,>=2.0; python_version < "3.11" and extra == "toml"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest<9.0,>=7.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov<6.0,>=4.1; extra == "dev"
|
|
35
|
+
Requires-Dist: build<2.0,>=1.0; extra == "dev"
|
|
36
|
+
Requires-Dist: mypy<2.0,>=1.8; extra == "dev"
|
|
37
|
+
Requires-Dist: ruff<1.0,>=0.4; extra == "dev"
|
|
38
|
+
Requires-Dist: pandas-stubs; extra == "dev"
|
|
39
|
+
Requires-Dist: pyyaml<7.0,>=6.0; extra == "dev"
|
|
40
|
+
Requires-Dist: types-PyYAML<7.0,>=6.0; extra == "dev"
|
|
41
|
+
Dynamic: license-file
|
|
42
|
+
|
|
43
|
+
# BDP Model Gate
|
|
44
|
+
|
|
45
|
+
Automated pre-deployment ML model governance: fairness, performance,
|
|
46
|
+
compliance, and security checks, run as a single gate that gives you a
|
|
47
|
+
`PASS` / `NEEDS_REVIEW` / `BLOCKED` status to wire into CI before a model
|
|
48
|
+
is promoted to production.
|
|
49
|
+
|
|
50
|
+
Currently covers **structured data models**. Unstructured (text, image,
|
|
51
|
+
audio) support is planned — see `bdp_model_gate.unstructured` for the reserved
|
|
52
|
+
interface and roadmap notes.
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# core (context/report/gate objects only — no check logic that needs ML libs)
|
|
58
|
+
pip install bdp-model-gate
|
|
59
|
+
|
|
60
|
+
# structured-data checks (fairlearn, shap, scikit-learn) — install this for real use
|
|
61
|
+
pip install bdp-model-gate[structured]
|
|
62
|
+
|
|
63
|
+
# for running the test suite
|
|
64
|
+
pip install bdp-model-gate[dev]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Compliance and security checks (model card validation, adversarial
|
|
68
|
+
robustness, PII scanning, prompt-injection testing) work with just the core
|
|
69
|
+
install. Fairness checks need `fairlearn`/`shap`, and every performance
|
|
70
|
+
metric except `accuracy` needs `scikit-learn` — install the `structured`
|
|
71
|
+
extra to get all of it. On a core-only install the default `metric="auto"`
|
|
72
|
+
falls back to `accuracy` and says so loudly; see
|
|
73
|
+
[Choosing the performance metric](#choosing-the-performance-metric).
|
|
74
|
+
|
|
75
|
+
## Quickstart
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from bdp_model_gate import StructuredGateContext, ModelGate
|
|
79
|
+
|
|
80
|
+
context = StructuredGateContext(
|
|
81
|
+
model=my_model,
|
|
82
|
+
X=X_val,
|
|
83
|
+
y_true=y_val,
|
|
84
|
+
y_pred=y_pred,
|
|
85
|
+
protected_df=protected_val, # optional — enables fairness checks
|
|
86
|
+
latencies_ms=benchmark_latencies, # optional — enables performance checks
|
|
87
|
+
cost_per_inference=0.0008, # optional
|
|
88
|
+
model_card=my_model_card, # optional — enables compliance checks
|
|
89
|
+
generate_fn=None, # optional — set if there's a generative side-car
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
report = ModelGate().run(context)
|
|
93
|
+
print(report.summary())
|
|
94
|
+
report.to_json("gate_report.json")
|
|
95
|
+
|
|
96
|
+
if report.gate_status == "BLOCKED":
|
|
97
|
+
raise SystemExit("Model failed governance gate — see gate_report.json")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Or the one-liner:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from bdp_model_gate import run_structured_gate
|
|
104
|
+
|
|
105
|
+
report = run_structured_gate(model, X_val, y_val, y_pred, protected_df=protected_val)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## What each category checks
|
|
109
|
+
|
|
110
|
+
**Fairness** (non-blocking by default — routes to `NEEDS_REVIEW`, since some
|
|
111
|
+
flags need human judgment)
|
|
112
|
+
- `ProxyCorrelationCheck` — input features that correlate with a protected attribute
|
|
113
|
+
- `DisparateImpactCheck` — outcome-level demographic parity
|
|
114
|
+
- `ShapSubgroupCheck` — features whose SHAP contribution differs across groups
|
|
115
|
+
- `CounterfactualFlipCheck` — prediction shift when a protected attribute is flipped
|
|
116
|
+
|
|
117
|
+
**Performance** (blocking)
|
|
118
|
+
- `PerformanceThresholdCheck` — model score on a metric you choose, p95
|
|
119
|
+
latency, cost-per-inference. See [Choosing the performance metric](#choosing-the-performance-metric).
|
|
120
|
+
|
|
121
|
+
**Compliance** (blocking)
|
|
122
|
+
- `ComplianceMappingCheck` — model card completeness, DPIA trigger for
|
|
123
|
+
high-risk use cases, explainability requirement for models affecting a person
|
|
124
|
+
|
|
125
|
+
**Security** (blocking)
|
|
126
|
+
- `AdversarialRobustnessCheck` — prediction flip rate under small feature perturbation
|
|
127
|
+
- `PIILeakageCheck` — regex scan of string columns for PII patterns
|
|
128
|
+
- `PromptInjectionCheck` — canned jailbreak prompts against any generative side-car
|
|
129
|
+
|
|
130
|
+
## Customizing thresholds
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from bdp_model_gate import GateConfig
|
|
134
|
+
from bdp_model_gate.structured import default_structured_checks
|
|
135
|
+
from bdp_model_gate import ModelGate
|
|
136
|
+
|
|
137
|
+
config = GateConfig()
|
|
138
|
+
config.performance.metric = "roc_auc"
|
|
139
|
+
config.performance.min_score = 0.85
|
|
140
|
+
config.fairness.disparity_threshold = 0.05
|
|
141
|
+
|
|
142
|
+
gate = ModelGate(checks=default_structured_checks(config))
|
|
143
|
+
report = gate.run(context)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Choosing the performance metric
|
|
147
|
+
|
|
148
|
+
`PerformanceConfig.metric` decides what the model is scored on, and
|
|
149
|
+
`min_score` is the threshold that score must clear. Set the two together —
|
|
150
|
+
`min_score` means nothing on its own.
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
config = GateConfig()
|
|
154
|
+
config.performance.metric = "f1" # what to measure
|
|
155
|
+
config.performance.min_score = 0.75 # what it has to beat
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Built-in names: `roc_auc`, `average_precision`, `accuracy`,
|
|
159
|
+
`balanced_accuracy`, `f1`, `precision`, `recall`. All except `accuracy`
|
|
160
|
+
require scikit-learn (the `structured` extra).
|
|
161
|
+
|
|
162
|
+
**Label-based metrics need hard classes.** `accuracy`, `balanced_accuracy`,
|
|
163
|
+
`f1`, `precision`, and `recall` binarize continuous `y_pred` at
|
|
164
|
+
`config.performance.decision_threshold` (default `0.5`). Predictions already
|
|
165
|
+
in `{0, 1}` are left alone. Ranking metrics (`roc_auc`,
|
|
166
|
+
`average_precision`) use the raw scores and ignore the threshold.
|
|
167
|
+
|
|
168
|
+
**Your own metric.** Any `fn(y_true, y_pred) -> float` works, and is called
|
|
169
|
+
with `y_pred` exactly as you supplied it — no thresholding, since only you
|
|
170
|
+
know what your metric expects:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from sklearn.metrics import fbeta_score
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def f2(y_true, y_pred):
|
|
177
|
+
return fbeta_score(y_true, (y_pred >= 0.3).astype(int), beta=2)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
config.performance.metric = f2 # reported under the name "f2"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**`"auto"` (the default)** uses `roc_auc` when scikit-learn is installed and
|
|
184
|
+
falls back to `accuracy` when it isn't. The fallback is never silent: it's
|
|
185
|
+
logged at `WARNING`, marked `metric_is_fallback: true` in the result
|
|
186
|
+
metadata, and spelled out in the check's detail string. A score is only
|
|
187
|
+
comparable to `min_score` if you know which metric produced it, so the
|
|
188
|
+
report always names it:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"gate_status": "PASS",
|
|
193
|
+
"model_metric": "roc_auc",
|
|
194
|
+
"model_score": 0.9132
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Naming a metric explicitly opts out of fallback entirely — if
|
|
199
|
+
`metric="roc_auc"` can't run, the gate reports a blocking `CHECK_ERROR`
|
|
200
|
+
rather than quietly scoring you on something else. A typo'd metric name
|
|
201
|
+
raises `GateConfigurationError` as soon as the check is constructed.
|
|
202
|
+
|
|
203
|
+
From the CLI, `--metric`, `--min-score`, and `--decision-threshold` do the
|
|
204
|
+
same thing, and take precedence over a `--config` file:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
bdp-model-gate --model model.joblib --data validation.csv --target-col label \
|
|
208
|
+
--metric f1 --min-score 0.75 --output gate_report.json
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
> **Migrating from 0.1.0:** `min_accuracy` is now `min_score`, and the old
|
|
212
|
+
> name was misleading — it was compared against ROC AUC whenever
|
|
213
|
+
> scikit-learn was installed, and accuracy otherwise. `min_accuracy` still
|
|
214
|
+
> works (in Python and in `--config` files) but emits a `DeprecationWarning`.
|
|
215
|
+
> Likewise `GateReport.model_auc` is superseded by `model_metric` /
|
|
216
|
+
> `model_score`, and now returns `None` unless the metric really was AUC.
|
|
217
|
+
|
|
218
|
+
## Writing your own check
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
from bdp_model_gate import BaseCheck, CheckResult
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class MyCustomCheck(BaseCheck):
|
|
225
|
+
name = "my_custom_check"
|
|
226
|
+
category = "compliance" # fairness | performance | compliance | security
|
|
227
|
+
blocking = True
|
|
228
|
+
|
|
229
|
+
def run(self, context):
|
|
230
|
+
# inspect context.model, context.X, context.model_card, etc.
|
|
231
|
+
return [CheckResult(self.name, self.category, "OK", "looks fine", self.blocking)]
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
gate = ModelGate(checks=[MyCustomCheck()])
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Using it as a pre-deployment CI/CD gate
|
|
238
|
+
|
|
239
|
+
Installing the package gives you an `bdp-model-gate` console script, meant to
|
|
240
|
+
run as a **pre-deployment step** — after a model is trained/built, before
|
|
241
|
+
it's promoted to a registry or prod endpoint. It is not intended to run on
|
|
242
|
+
every PR.
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
bdp-model-gate \
|
|
246
|
+
--model model.joblib \
|
|
247
|
+
--data validation.csv \
|
|
248
|
+
--target-col label \
|
|
249
|
+
--protected protected.csv \
|
|
250
|
+
--model-card model_card.json \
|
|
251
|
+
--cost-per-inference 0.0008 \
|
|
252
|
+
--output gate_report.json
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Exit codes are chosen so a pipeline can distinguish three outcomes:
|
|
256
|
+
|
|
257
|
+
| Exit code | Status | Pipeline behavior |
|
|
258
|
+
|---|---|---|
|
|
259
|
+
| `0` | `PASS` | proceed to deploy automatically |
|
|
260
|
+
| `2` | `NEEDS_REVIEW` | stop and require a human sign-off (fairness flags need judgment) |
|
|
261
|
+
| `1` | `BLOCKED` | hard fail — performance, compliance, or security check failed |
|
|
262
|
+
|
|
263
|
+
A ready-to-adapt **Azure Pipelines** example is in
|
|
264
|
+
[`ci_examples/azure-pipelines.model-gate.yml`](ci_examples/azure-pipelines.model-gate.yml),
|
|
265
|
+
and a **GitHub Actions** equivalent (a reusable `workflow_call` workflow) is in
|
|
266
|
+
[`ci_examples/github-actions.model-gate.yml`](ci_examples/github-actions.model-gate.yml).
|
|
267
|
+
Both structure this as three stages/jobs: run the gate, a manual-approval
|
|
268
|
+
step gated behind exit code `2` (GitHub Environments / Azure Environments
|
|
269
|
+
with required reviewers), and a deploy step that only runs if the gate
|
|
270
|
+
passed outright or was manually approved. Point them at wherever your
|
|
271
|
+
training pipeline publishes `model.joblib` / `validation.csv` /
|
|
272
|
+
`protected.csv` / `model_card.json` as a build artifact.
|
|
273
|
+
|
|
274
|
+
Config overrides for the CLI can be JSON, YAML, or TOML — pick whichever
|
|
275
|
+
matches your repo's conventions:
|
|
276
|
+
|
|
277
|
+
```yaml
|
|
278
|
+
# config.yaml
|
|
279
|
+
performance:
|
|
280
|
+
metric: f1
|
|
281
|
+
min_score: 0.85
|
|
282
|
+
decision_threshold: 0.5
|
|
283
|
+
fairness:
|
|
284
|
+
disparity_threshold: 0.05
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
bdp-model-gate --model model.joblib --data validation.csv --target-col label \
|
|
289
|
+
--config config.yaml --output gate_report.json
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
YAML configs need `pip install pyyaml` (or `bdp-model-gate[dev]`, which
|
|
293
|
+
already includes it); TOML needs `tomli` on Python < 3.11 (3.11+ has
|
|
294
|
+
`tomllib` built in).
|
|
295
|
+
|
|
296
|
+
Pass `-v`/`--verbose` for debug-level logging (per-check timing, which
|
|
297
|
+
checks ran/skipped and why) — the library uses the standard `logging`
|
|
298
|
+
module throughout, so it composes with whatever logging setup your
|
|
299
|
+
pipeline already has.
|
|
300
|
+
|
|
301
|
+
## Extending with plugins
|
|
302
|
+
|
|
303
|
+
Third-party packages can register additional checks without forking this
|
|
304
|
+
library, via the `bdp_model_gate.checks` entry-point group:
|
|
305
|
+
|
|
306
|
+
```toml
|
|
307
|
+
# in your plugin package's pyproject.toml
|
|
308
|
+
[project.entry-points."bdp_model_gate.checks"]
|
|
309
|
+
my_check = "my_package.checks:MyCustomCheck"
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Once installed alongside `bdp-model-gate`, `default_structured_checks()`
|
|
313
|
+
picks it up automatically (pass `include_plugins=False` to opt out). A
|
|
314
|
+
plugin that fails to import or isn't a `BaseCheck` subclass is logged and
|
|
315
|
+
skipped rather than crashing the gate.
|
|
316
|
+
|
|
317
|
+
## Error handling
|
|
318
|
+
|
|
319
|
+
Bad inputs fail fast with a clear message rather than a confusing
|
|
320
|
+
exception from deep inside a check:
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
from bdp_model_gate import ModelGate, StructuredGateContext
|
|
324
|
+
from bdp_model_gate.exceptions import GateValidationError
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
report = ModelGate().run(context)
|
|
328
|
+
except GateValidationError as exc:
|
|
329
|
+
print(f"Fix your inputs: {exc}")
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Validation covers: the model exposes `.predict()`, `X` is a non-empty
|
|
333
|
+
DataFrame, `y_true`/`y_pred`/`X` are aligned in length, `y_true` has at
|
|
334
|
+
least two classes, `protected_df` is row-aligned and has no all-NaN
|
|
335
|
+
columns, `model_card` is a dict, `generate_fn` is callable, and
|
|
336
|
+
`latencies_ms` has no negative values.
|
|
337
|
+
|
|
338
|
+
## Roadmap
|
|
339
|
+
|
|
340
|
+
- Unstructured data support (text/image/audio) — `bdp_model_gate.unstructured` reserves
|
|
341
|
+
the shape (`UnstructuredGateContext`, a matching check suite) but raises
|
|
342
|
+
`NotImplementedError` until it lands.
|
|
343
|
+
- HTML/Markdown report rendering alongside `to_json()`.
|
|
344
|
+
|
|
345
|
+
## Development
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
pip install -e ".[dev,structured]"
|
|
349
|
+
|
|
350
|
+
ruff check . # lint
|
|
351
|
+
ruff format . # format
|
|
352
|
+
mypy bdp_model_gate # type check
|
|
353
|
+
pytest -q # test (85% coverage floor enforced)
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
`.pre-commit-config.yaml` runs ruff, mypy, and basic hygiene checks on
|
|
357
|
+
every commit — install with `pip install pre-commit && pre-commit install`.
|
|
358
|
+
|
|
359
|
+
CI (`.github/workflows/ci.yml`) runs lint, type-check, and the test suite
|
|
360
|
+
across Python 3.9–3.12 on every push/PR, plus a **core-install job** with
|
|
361
|
+
no `structured` extra — that job is what keeps the graceful-degradation
|
|
362
|
+
paths (`NOT_APPLICABLE` results, metric fallback) honest. Tests that need
|
|
363
|
+
a real estimator `importorskip` on scikit-learn rather than failing there.
|
|
364
|
+
|
|
365
|
+
The 3.12 ceiling tracks the `shap<0.47` pin, which has no wheels beyond
|
|
366
|
+
3.12; raise both together. Note `[tool.mypy] python_version` is pinned to
|
|
367
|
+
3.12 for numpy's stubs, so 3.9 compatibility is enforced by ruff's
|
|
368
|
+
`target-version` and the 3.9 test job rather than by the type checker.
|
|
369
|
+
|
|
370
|
+
This is all separate from `ci_examples/`, which are pre-deployment gates
|
|
371
|
+
for models *built by* consumers of this library, not for the library's own
|
|
372
|
+
code.
|
|
373
|
+
|
|
374
|
+
See [`CHANGELOG.md`](CHANGELOG.md) for release history.
|