credonlabs 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.
- credonlabs-0.1.0/LICENSE +23 -0
- credonlabs-0.1.0/PKG-INFO +435 -0
- credonlabs-0.1.0/README.md +392 -0
- credonlabs-0.1.0/pyproject.toml +72 -0
- credonlabs-0.1.0/setup.cfg +4 -0
- credonlabs-0.1.0/src/credonlabs/__init__.py +253 -0
- credonlabs-0.1.0/src/credonlabs/accumulators.py +1376 -0
- credonlabs-0.1.0/src/credonlabs/adapters.py +517 -0
- credonlabs-0.1.0/src/credonlabs/business_logic.py +495 -0
- credonlabs-0.1.0/src/credonlabs/cli.py +561 -0
- credonlabs-0.1.0/src/credonlabs/client.py +470 -0
- credonlabs-0.1.0/src/credonlabs/counterfactual.py +273 -0
- credonlabs-0.1.0/src/credonlabs/demo.py +351 -0
- credonlabs-0.1.0/src/credonlabs/drift.py +613 -0
- credonlabs-0.1.0/src/credonlabs/engine.py +745 -0
- credonlabs-0.1.0/src/credonlabs/exceptions.py +157 -0
- credonlabs-0.1.0/src/credonlabs/explain.py +682 -0
- credonlabs-0.1.0/src/credonlabs/fairness.py +538 -0
- credonlabs-0.1.0/src/credonlabs/freeai.py +395 -0
- credonlabs-0.1.0/src/credonlabs/groups.py +336 -0
- credonlabs-0.1.0/src/credonlabs/inspector.py +1080 -0
- credonlabs-0.1.0/src/credonlabs/model_card.py +770 -0
- credonlabs-0.1.0/src/credonlabs/pii.py +341 -0
- credonlabs-0.1.0/src/credonlabs/publish.py +698 -0
- credonlabs-0.1.0/src/credonlabs/readers.py +425 -0
- credonlabs-0.1.0/src/credonlabs/reasons.py +414 -0
- credonlabs-0.1.0/src/credonlabs/registry.py +808 -0
- credonlabs-0.1.0/src/credonlabs/runner.py +1450 -0
- credonlabs-0.1.0/src/credonlabs/scan.py +1079 -0
- credonlabs-0.1.0/src/credonlabs/stability.py +423 -0
- credonlabs-0.1.0/src/credonlabs/statistical.py +735 -0
- credonlabs-0.1.0/src/credonlabs/suite.py +976 -0
- credonlabs-0.1.0/src/credonlabs/tasks.py +590 -0
- credonlabs-0.1.0/src/credonlabs.egg-info/PKG-INFO +435 -0
- credonlabs-0.1.0/src/credonlabs.egg-info/SOURCES.txt +46 -0
- credonlabs-0.1.0/src/credonlabs.egg-info/dependency_links.txt +1 -0
- credonlabs-0.1.0/src/credonlabs.egg-info/entry_points.txt +2 -0
- credonlabs-0.1.0/src/credonlabs.egg-info/requires.txt +28 -0
- credonlabs-0.1.0/src/credonlabs.egg-info/top_level.txt +1 -0
- credonlabs-0.1.0/tests/test_drift_and_suite.py +390 -0
- credonlabs-0.1.0/tests/test_fairness_and_explain.py +513 -0
- credonlabs-0.1.0/tests/test_inspector_and_run.py +627 -0
- credonlabs-0.1.0/tests/test_pii.py +186 -0
- credonlabs-0.1.0/tests/test_publish.py +677 -0
- credonlabs-0.1.0/tests/test_registry.py +372 -0
- credonlabs-0.1.0/tests/test_statistical.py +210 -0
- credonlabs-0.1.0/tests/test_streaming.py +904 -0
- credonlabs-0.1.0/tests/test_validation_tests.py +312 -0
credonlabs-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Copyright (c) 2026 Credon Labs. All rights reserved.
|
|
2
|
+
|
|
3
|
+
This software and its source code are proprietary and confidential.
|
|
4
|
+
|
|
5
|
+
Use is permitted only by parties holding a valid written licence
|
|
6
|
+
agreement with Credon Labs, and only within the scope of that
|
|
7
|
+
agreement. No right is granted to copy, modify, distribute,
|
|
8
|
+
sublicense, reverse engineer, or create derivative works, except
|
|
9
|
+
where such a right cannot lawfully be excluded.
|
|
10
|
+
|
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
12
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
13
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
14
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
15
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
16
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
17
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
18
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
19
|
+
|
|
20
|
+
Model Risk Management outputs produced by this software are
|
|
21
|
+
decision-support material. They do not constitute regulatory
|
|
22
|
+
approval, and responsibility for any lending decision remains
|
|
23
|
+
with the licensee.
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: credonlabs
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Credon Labs Model Risk Management SDK — validation, drift, explainability, fairness and RBI FREE-AI reporting for credit models.
|
|
5
|
+
Author: Credon Labs Engineering
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
|
+
Project-URL: Homepage, https://credonlabs.com
|
|
8
|
+
Project-URL: Documentation, https://credonlabs.com
|
|
9
|
+
Project-URL: Issues, https://credonlabs.com
|
|
10
|
+
Keywords: model-risk-management,mrm,credit-risk,free-ai,rbi,explainability,drift,fairness
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numpy>=1.24
|
|
21
|
+
Requires-Dist: pandas>=2.0
|
|
22
|
+
Requires-Dist: scikit-learn>=1.3
|
|
23
|
+
Requires-Dist: scipy>=1.10
|
|
24
|
+
Provides-Extra: shap
|
|
25
|
+
Requires-Dist: shap>=0.44; extra == "shap"
|
|
26
|
+
Provides-Extra: http
|
|
27
|
+
Requires-Dist: requests>=2.31; extra == "http"
|
|
28
|
+
Provides-Extra: parquet
|
|
29
|
+
Requires-Dist: pyarrow>=14.0; extra == "parquet"
|
|
30
|
+
Provides-Extra: onnx
|
|
31
|
+
Requires-Dist: onnxruntime>=1.16; extra == "onnx"
|
|
32
|
+
Provides-Extra: all
|
|
33
|
+
Requires-Dist: shap>=0.44; extra == "all"
|
|
34
|
+
Requires-Dist: requests>=2.31; extra == "all"
|
|
35
|
+
Requires-Dist: pyarrow>=14.0; extra == "all"
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: shap>=0.44; extra == "dev"
|
|
38
|
+
Requires-Dist: requests>=2.31; extra == "dev"
|
|
39
|
+
Requires-Dist: pyarrow>=14.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
41
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
# credonlabs
|
|
45
|
+
|
|
46
|
+
Model Risk Management SDK for Indian lenders — validation, drift monitoring, explainability, fairness testing and RBI FREE-AI reporting for credit models.
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install credonlabs
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Everything runs locally. The HTTP client is optional and is only needed to push results to the hosted platform.
|
|
54
|
+
|
|
55
|
+
**Handles any model type and any dataset size.** Binary, multiclass and regression; scikit-learn, XGBoost, LightGBM, CatBoost, ONNX, PyTorch, or any callable. Datasets stream in constant memory — measured at **11.2 MB peak whether the input is 100 thousand rows or 10 million**.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Two entry points
|
|
60
|
+
|
|
61
|
+
| | `run()` | `scan()` |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| Data | fits in memory | any size, streamed |
|
|
64
|
+
| Passes | one, in memory | one, batched |
|
|
65
|
+
| Model | required | optional |
|
|
66
|
+
| Stages | all 12 | metrics, drift, fairness exact; explainability on a sample |
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
credonlabs.run(model="model.joblib", data="validation.csv", target="default_flag")
|
|
70
|
+
|
|
71
|
+
credonlabs.scan(data="scores/*.parquet", target="default_flag", scores="score")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`scan()` with `scores=` loads **no model at all** — metrics, drift, calibration and fairness need only what the model produced, not the model itself. That is what makes a 100GB model workable: your scoring layer already wrote the scores, so the SDK reads them.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Quick start
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
import credonlabs
|
|
82
|
+
|
|
83
|
+
report = credonlabs.validate(
|
|
84
|
+
model=model, # any fitted sklearn-compatible estimator
|
|
85
|
+
X=X_val, # validation features (DataFrame)
|
|
86
|
+
y=y_val, # observed outcomes
|
|
87
|
+
model_name="personal_loan_pd",
|
|
88
|
+
version="1.0.0",
|
|
89
|
+
owner="Risk Team",
|
|
90
|
+
business_purpose="Unsecured personal loan underwriting",
|
|
91
|
+
criticality="tier_1",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
print(credonlabs.summarise(report))
|
|
95
|
+
print(report["verdict"]) # approved | approved_with_conditions | not_approved
|
|
96
|
+
print(report["model_card"]) # RBI-inspection-ready Markdown
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
One call runs every stage. Each is independent: a stage whose inputs are missing reports itself as unavailable rather than aborting the run, so a partial validation still produces usable evidence.
|
|
100
|
+
|
|
101
|
+
### See it work on generated data
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
credonlabs demo
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Generates a labelled dataset with real signal, trains a model, and runs the whole suite. Writes only inside `./credonlabs_demo`.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## What it does
|
|
112
|
+
|
|
113
|
+
| Stage | Requirement | Output |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| **PII boundary** | NFR-013 | Rejects PAN, Aadhaar, phone, email, IFSC, voter ID |
|
|
116
|
+
| **Registry** | FR-MRM-001…004 | Immutable versioning, criticality tiers, approval workflow |
|
|
117
|
+
| **Statistical battery** | FR-MRM-010 | AUC, KS, Gini, Brier, PSI + bootstrap CIs + calibration |
|
|
118
|
+
| **Business-logic tests** | FR-MRM-011 | Monotonicity, sign and bound constraints |
|
|
119
|
+
| **Stability** | FR-MRM-012 | Perturbation, feature dropout, adversarial noise |
|
|
120
|
+
| **Validation report** | FR-MRM-013 | Signed, inspection-format Markdown |
|
|
121
|
+
| **Data drift** | FR-MRM-020 | Per-feature PSI, alerts at 0.10 / 0.25 |
|
|
122
|
+
| **Concept drift** | FR-MRM-021 | AUC/KS decay, 5-point revalidation trigger |
|
|
123
|
+
| **Alerting** | FR-MRM-022 | Per-model thresholds and routing |
|
|
124
|
+
| **Explainability** | FR-MRM-030/031 | TreeSHAP / KernelSHAP, seven feature groups |
|
|
125
|
+
| **Counterfactuals** | FR-MRM-032 | "If X were Y, the score would change by Z" |
|
|
126
|
+
| **Bias & fairness** | FR-MRM-040/041 | Demographic parity, equal opportunity, average odds, incidents |
|
|
127
|
+
| **Model card** | FR-MRM-050 | Purpose, performance, bias, limitations, history |
|
|
128
|
+
| **FREE-AI mapping** | FR-MRM-051 | 7 Sutras × 6 Pillars, with honest coverage gaps |
|
|
129
|
+
| **Plain-language reasons** | FR-MRM-060 | Jargon-free borrower explanations |
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Big data and big models
|
|
134
|
+
|
|
135
|
+
### Stream a sharded dataset
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
report = credonlabs.scan(
|
|
139
|
+
data="s3_export/scores/", # CSV, Parquet, directory, or glob
|
|
140
|
+
target="default_flag",
|
|
141
|
+
scores="score", # no model loaded
|
|
142
|
+
baseline="baseline.json", # enables drift
|
|
143
|
+
protected=["age_band"],
|
|
144
|
+
batch_rows=100_000,
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Only the columns a stage needs are read. On a wide Parquet table that alone removes most of the bytes on disk.
|
|
149
|
+
|
|
150
|
+
### Reference profiles
|
|
151
|
+
|
|
152
|
+
Do not re-read the training set every day. Summarise it once:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
credonlabs.profile(data="train/", target="default_flag", out="baseline.json")
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
30,000 rows becomes a **4.8 KB** file of bin edges and counts. Daily monitoring compares against that:
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
credonlabs.compare(data="today/", baseline="baseline.json")
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### How the streaming metrics stay exact
|
|
165
|
+
|
|
166
|
+
Every metric decomposes into a single pass:
|
|
167
|
+
|
|
168
|
+
| Metric | Accumulated state | Memory |
|
|
169
|
+
|---|---|---|
|
|
170
|
+
| AUC, KS, Gini | score histogram × {pos, neg} | O(bins) |
|
|
171
|
+
| Brier | running Σ(p−y)² | O(1) |
|
|
172
|
+
| Calibration | per-bin Σp, Σy, n | O(bins) |
|
|
173
|
+
| PSI | bin counts vs stored edges | O(features × bins) |
|
|
174
|
+
| Bias | counters per (group, decision, outcome) | O(groups) |
|
|
175
|
+
| Regression | running sums of error and y² | O(1) |
|
|
176
|
+
|
|
177
|
+
Brier and the regression metrics are **exact**. AUC from a 10,000-bin histogram matches `sklearn.roc_auc_score` to **six decimal places** — verified in `tests/test_streaming.py` against the same data. The bin count is recorded in every report so a reviewer can see the resolution.
|
|
178
|
+
|
|
179
|
+
Feeding data in different batch sizes produces bit-identical results, and partial histograms `merge()`, so parallel readers are safe.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Model types
|
|
184
|
+
|
|
185
|
+
| Task | Metrics | Fairness |
|
|
186
|
+
|---|---|---|
|
|
187
|
+
| **Binary** | AUC, KS, Gini, Brier, calibration | selection-rate parity, equal opportunity, average odds |
|
|
188
|
+
| **Multiclass** | macro AUC (OvR), accuracy, log loss, confusion | selection-rate parity |
|
|
189
|
+
| **Regression** | RMSE, MAE, R², MAPE, residuals | prediction parity, group error gaps |
|
|
190
|
+
|
|
191
|
+
The task is detected from the model and the outcome column; the outcome wins, because a classifier scored against a continuous target is a mistake to catch, not a configuration to honour. Override with `task="regression"`.
|
|
192
|
+
|
|
193
|
+
Fairness changes shape per task deliberately. Selection-rate parity is meaningless for a regression — there the question is whether groups receive systematically different predictions and whether the model is systematically less accurate for one of them.
|
|
194
|
+
|
|
195
|
+
## Model adapters
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from credonlabs import SklearnAdapter, CallableAdapter, ScoreColumnAdapter, OnnxAdapter, TorchAdapter
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
| Adapter | For |
|
|
202
|
+
|---|---|
|
|
203
|
+
| `SklearnAdapter` | sklearn, XGBoost, LightGBM, CatBoost wrappers |
|
|
204
|
+
| `ScoreColumnAdapter` | no model — scores already in the data |
|
|
205
|
+
| `CallableAdapter` | any function, a queue, a remote endpoint |
|
|
206
|
+
| `OnnxAdapter` | ONNX Runtime |
|
|
207
|
+
| `TorchAdapter` | PyTorch, eval mode, batched, device-aware |
|
|
208
|
+
|
|
209
|
+
Chosen automatically; pass one explicitly to override. A score column always wins over a model, because re-running a large model to get numbers you already have is the expensive mistake.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## The pieces individually
|
|
214
|
+
|
|
215
|
+
### Statistical battery
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
from credonlabs import statistical
|
|
219
|
+
|
|
220
|
+
result = statistical.run_battery(
|
|
221
|
+
y_true=y_val,
|
|
222
|
+
y_score=scores,
|
|
223
|
+
baseline_scores=training_scores, # enables PSI
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
result["auc"] # 0.9810
|
|
227
|
+
result["confidence_intervals"]["auc"] # {'lower': 0.97, 'upper': 0.99, ...}
|
|
228
|
+
result["calibration"]["mean_absolute_calibration_error"]
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Business-logic constraints
|
|
232
|
+
|
|
233
|
+
Catches the defect a purely statistical battery cannot see: a model can hold an AUC of 0.85 while still *lowering* creditworthiness as income rises.
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
from credonlabs import MonotonicConstraint, SignConstraint, BoundConstraint
|
|
237
|
+
|
|
238
|
+
constraints = [
|
|
239
|
+
MonotonicConstraint(feature="verified_monthly_income", direction="decreasing"),
|
|
240
|
+
SignConstraint(feature="emi_bounces_12m", expected_sign="positive"),
|
|
241
|
+
BoundConstraint(feature="credit_score", minimum=300, maximum=900),
|
|
242
|
+
]
|
|
243
|
+
|
|
244
|
+
report = credonlabs.validate(..., constraints=constraints)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
`direction="decreasing"` means higher income must not increase the probability of default. Violations come back with severity and the exact inputs that triggered them.
|
|
248
|
+
|
|
249
|
+
### Bias audit
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
report = credonlabs.validate(
|
|
253
|
+
...,
|
|
254
|
+
protected_attributes={"age_band": validation["age_band"]},
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
report["fairness"]["attributes"]["age_band"]["disparate_impact_ratio"] # 0.254
|
|
258
|
+
report["fairness"]["incidents"] # incident records
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Ratios outside **0.80–1.25** (the four-fifths rule) raise an incident with affected groups, sample sizes, and the FR-MRM-041 SLA.
|
|
262
|
+
|
|
263
|
+
Protected attributes must arrive already banded — `age_band`, not a date of birth.
|
|
264
|
+
|
|
265
|
+
### Explainability and borrower reasons
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
local = credonlabs.explain_local(model, X_val, row=0)
|
|
269
|
+
|
|
270
|
+
reason = credonlabs.generate_reason(
|
|
271
|
+
group_attribution=local["group_attribution"],
|
|
272
|
+
decision="declined",
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
print(reason["text"])
|
|
276
|
+
print(reason["jargon_check"]) # {'clean': True, 'banned_terms_found': []}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
> We were not able to approve this application. The main reasons were that there were missed or late repayments in your recent credit history and you are already repaying a large amount each month relative to what you earn. In your favour, your income was steady and at a comfortable level. You can ask us to look at this decision again if you think something has been recorded incorrectly.
|
|
280
|
+
|
|
281
|
+
Every generated sentence is checked against a banned-terms list — no "SHAP", "percentile", "model", "probability". Templates are data, not code: register a translated set with `reasons.register_locale()` so the lender's legal team owns the wording.
|
|
282
|
+
|
|
283
|
+
### Counterfactuals
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
result = credonlabs.generate_counterfactuals(model, row=0, X=X_val, top_n=3)
|
|
287
|
+
|
|
288
|
+
for entry in result["counterfactuals"]:
|
|
289
|
+
print(entry["statement"])
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Searches single-feature, policy-compliant changes only. Age band, pincode tier, gender and employment category are **never** suggested, whatever the caller passes.
|
|
293
|
+
|
|
294
|
+
### Monitoring
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
report = credonlabs.run_monitoring(
|
|
298
|
+
baseline_features=X_train,
|
|
299
|
+
current_features=X_production,
|
|
300
|
+
baseline_scores=scores_train,
|
|
301
|
+
current_scores=scores_production,
|
|
302
|
+
current_outcomes=y_production, # once outcomes arrive
|
|
303
|
+
baseline_metrics={"auc": 0.98, "ks": 0.89},
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
report["alert_level"] # ok | warning | critical
|
|
307
|
+
report["data_drift"]["drifted_features"]
|
|
308
|
+
report["concept_drift"]["revalidation_required"]
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
The three signals become available at different times — features first, scores next, outcomes last — so each section runs only when its inputs are present.
|
|
312
|
+
|
|
313
|
+
### Registry
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
store = credonlabs.ModelRegistry("registry.json")
|
|
317
|
+
|
|
318
|
+
store.register(
|
|
319
|
+
model_name="personal_loan_pd",
|
|
320
|
+
version="1.0.0",
|
|
321
|
+
owner="Risk Team",
|
|
322
|
+
criticality="tier_1",
|
|
323
|
+
business_purpose="Underwriting",
|
|
324
|
+
artifact_path="model.joblib", # records SHA-256 + size
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
store.approve("personal_loan_pd", "1.0.0", "priya")
|
|
328
|
+
store.approve("personal_loan_pd", "1.0.0", "ravi") # tier_1 needs two distinct approvers
|
|
329
|
+
|
|
330
|
+
store.diff("personal_loan_pd", "1.0.0", "1.1.0") # what changed between versions
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Versions are immutable — re-registering the same version is refused. Tiers can be escalated but never silently downgraded. Deprecation never deletes.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Command line
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
credonlabs validate --model model.joblib --data validation.csv \
|
|
341
|
+
--target default_flag --model-name personal_loan_pd --version 1.0.0 \
|
|
342
|
+
--owner "Risk Team" --purpose "Underwriting" \
|
|
343
|
+
--protected age_band --card model_card.md --out report.json
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Exits **non-zero** when the verdict is `not_approved`, so it gates a CI pipeline directly.
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
credonlabs monitor --model model.joblib --baseline train.csv \
|
|
350
|
+
--current production.csv --target default_flag
|
|
351
|
+
|
|
352
|
+
credonlabs registry --registry registry.json --csv inventory.csv
|
|
353
|
+
|
|
354
|
+
credonlabs demo
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## Platform client (optional)
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
pip install credonlabs[http]
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
```python
|
|
366
|
+
from credonlabs import CredonClient, ClientConfig
|
|
367
|
+
|
|
368
|
+
client = CredonClient(ClientConfig(
|
|
369
|
+
api_key="...",
|
|
370
|
+
lender_tenant_id="nbfc_alpha",
|
|
371
|
+
))
|
|
372
|
+
|
|
373
|
+
client.register_model(record.to_dict())
|
|
374
|
+
client.submit_validation(model_id, report)
|
|
375
|
+
client.score(features={...}, product_code="personal_loan_unsecured")
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
`client.score()` runs the PII scrubber **before** the payload leaves the process. Platform errors come back as the same exception classes the SDK raises locally, so you handle one taxonomy:
|
|
379
|
+
|
|
380
|
+
| Exception | Code | HTTP |
|
|
381
|
+
|---|---|---|
|
|
382
|
+
| `SchemaValidationError` | `SCHEMA_VALIDATION_ERROR` | 400 |
|
|
383
|
+
| `PIIDetectedError` | `PII_DETECTED` | 400 |
|
|
384
|
+
| `UnauthorizedError` | `UNAUTHORIZED` | 401 |
|
|
385
|
+
| `TenantAccessDeniedError` | `TENANT_ACCESS_DENIED` | 403 |
|
|
386
|
+
| `IdempotencyConflictError` | `IDEMPOTENCY_CONFLICT` | 409 |
|
|
387
|
+
| `CoverageTooLowError` | `COVERAGE_TOO_LOW` | 422 |
|
|
388
|
+
| `RateLimitedError` | `RATE_LIMITED` | 429 |
|
|
389
|
+
| `ModelUnavailableError` | `MODEL_UNAVAILABLE` | 503 |
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## Design notes
|
|
394
|
+
|
|
395
|
+
**No PII, ever.** `pii.assert_clean()` rejects a feature vector before it leaves your process. Errors name the *field*, never the value — an error payload can't itself become a leak. Measured floats are exempt from the numeric patterns: the digits of `-0.1321048632913019` contain a ten-digit run that looks like a mobile number, and flagging it would make the scanner useless.
|
|
396
|
+
|
|
397
|
+
**Explainability-first.** The SDK never returns a score with no attribution. Without `shap` it falls back to native importances or standardised coefficients and marks the result `degraded` with the reason — it doesn't silently return nothing.
|
|
398
|
+
|
|
399
|
+
**Honest reporting.** `freeai.coverage_for_report()` marks an obligation as evidenced only if the stage that evidences it actually ran. A validation that skipped the bias audit reports S4 as *not evidenced*, so a lender never claims coverage it hasn't demonstrated. The model card's Limitations section is populated from real findings, not boilerplate.
|
|
400
|
+
|
|
401
|
+
**Verdicts are blocking.** PII in the data, a high-severity business-logic violation, a high-severity bias incident, or no attribution at all all produce `not_approved` for a tier-1 model — not a warning.
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Publishing reports to Credon
|
|
406
|
+
|
|
407
|
+
```python
|
|
408
|
+
credonlabs.run(..., api_key="mrm_...") # or set CREDONLABS_API_KEY
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Reports post to `https://api.credonlabs.com` — the endpoint ships with the package, so no integrator's code names a URL. Override with `base_url=` or `CREDONLABS_BASE_URL` for staging or a self-hosted engine.
|
|
412
|
+
|
|
413
|
+
The key is an **MRM key**, created from the dashboard's API keys page or `POST /mrm/keys`. It is separate from the credit-model scoring key: leaking the scoring key must not expose validation reports.
|
|
414
|
+
|
|
415
|
+
Nothing is sent unless a key is supplied.
|
|
416
|
+
|
|
417
|
+
## Install options
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
pip install credonlabs # core: numpy, pandas, scikit-learn, scipy
|
|
421
|
+
pip install credonlabs[shap] # + SHAP attributions
|
|
422
|
+
pip install credonlabs[parquet] # + Parquet streaming (pyarrow)
|
|
423
|
+
pip install credonlabs[onnx] # + ONNX Runtime scoring
|
|
424
|
+
pip install credonlabs[http] # + platform client
|
|
425
|
+
pip install credonlabs[all]
|
|
426
|
+
pip install credonlabs[dev] # + pytest, build
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Requires Python 3.10+.
|
|
430
|
+
|
|
431
|
+
## Tests
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
pytest tests/ -q
|
|
435
|
+
```
|