Metadata-Version: 2.4
Name: trustlens-gate
Version: 0.2.0
Summary: A safety gate for high-stakes ML: models abstain when unsafe, with a certified error budget.
Author: Le Vu Anh Tin
License: MIT
Project-URL: Homepage, https://github.com/tin-31/trustlens
Project-URL: Issues, https://github.com/tin-31/trustlens/issues
Keywords: machine-learning,validation,conformal-prediction,selective-prediction,out-of-distribution,calibration,trustworthy-ai,mlops
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.23
Requires-Dist: scipy>=1.9
Requires-Dist: scikit-learn>=1.1
Requires-Dist: pandas>=1.4
Requires-Dist: joblib>=1.1
Provides-Extra: dashboard
Requires-Dist: streamlit>=1.20; extra == "dashboard"
Requires-Dist: matplotlib>=3.5; extra == "dashboard"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Dynamic: license-file

# TrustLens 🔎

**A safety gate for high-stakes ML: your model refuses to predict when it isn't safe to — with a certified error budget.**

Most validation tools *test* and *monitor* your model. TrustLens does that too — then it does the thing they don't: at inference time it **abstains** on inputs that are out-of-distribution or not confident enough to meet a guarantee you set. Every prediction it *does* serve comes with a distribution-free error bound.

> Born from a real failure. A pathology model I built scored **AUC 0.998** in-house and collapsed to **0.48** on independent data — while staying *confident*. The signal most people use for uncertainty (softmax / entropy) scored **0.31 AUROC** at catching this: worse than a coin flip. TrustLens is the tooling I built so a model has to admit that itself.

---

## Quickstart (10 seconds, no data needed)

```bash
pip install trustlens-gate      # the import name stays `trustlens`
python -m trustlens.demo        # 3 scenarios on sklearn breast_cancer; flags only real faults
```

Expected output — it stays silent on clean data and only fires on the real fault in each case:

| scenario | leakage | neg-control | shortcut | calibration | domain | OVERALL |
|----------|:-:|:-:|:-:|:-:|:-:|:-:|
| A. good model, clean data | ✓ | ✓ | ✓ | ✓ | ✓ | **PASS** |
| B. leakage + label-leak + shift | ✗ | ✓ | ! | ✓ | ✗ | **FAIL** |
| C. over-confident model | ✓ | ✓ | ! | ! | ✓ | **WARN** |

---

## The core idea: `SafeClassifier`

```python
from trustlens import SafeClassifier

# Wrap any model with predict_proba. Calibrate on in-domain labeled data.
safe = SafeClassifier.fit(model, X_train, X_calib, y_calib, alpha=0.05)

safe.predict(X_new)
# -> [{"decision": "PREDICT",  "label": 1, "proba": 0.97, "reason": "in_domain_guaranteed"},
#     {"decision": "ABSTAIN",  "proba": None,            "reason": "out_of_domain"},
#     {"decision": "ABSTAIN",  "proba": 0.61,            "reason": "low_confidence_no_guarantee"}]
```

Two layers, both provable:
1. **OOD gate** — input outside the calibrated domain → `ABSTAIN` (the guarantee's exchangeability assumption is violated, so we don't pretend).
2. **Risk-controlled selector** — set an error budget `alpha`; among served inputs the true error is bounded `≤ alpha` with probability `≥ 1 − delta`, via an **exact Clopper–Pearson binomial bound** on a threshold grid (Learn-then-Test; Angelopoulos, Bates, Candès, Jordan 2021). Below threshold → `ABSTAIN`.

On the pathology case that started this: at 100%-coverage the error was **13.1%**; after the guarantee it served **81%** at **7.7% error ≤ 10% budget**, handing the rest to a human.

---

## The audit (6 checks for silent failures)

```python
from trustlens import audit, render
results = audit(model, X_train, y_train, X_test, y_test, X_external, y_external)
render(results, "report.html")
print(results["summary"]["overall"])   # PASS / WARN / FAIL
```

| Check | Catches |
|-------|---------|
| Data leakage | exact / near-duplicate rows across train–test |
| Negative control | shuffle labels → still high AUC = pipeline leak |
| Shortcut | one feature ≈ whole model = trivial/leaked signal |
| Calibration | over-confident probabilities (ECE, Brier) |
| Uncertainty | AUC with 95% CI (bootstrap + DeLong) |
| Domain shift | test/external drifts from train (OOD guard) |

## In your CI (block bad models from shipping)

```bash
trustlens audit --model model.pkl --train tr.csv --test te.csv \
    --label-col target --external ext.csv --report report.html
# exit code: 0 = PASS, 1 = WARN, 2 = FAIL
```

## Dashboard (optional, from a clone of this repo)

```bash
pip install "trustlens-gate[dashboard]"
streamlit run trustlens/dashboard.py
```

---

## Honest benchmark (6 datasets × 3 shift types)

| Detector | OOD-AUROC (mean ± sd) | FPR@95 |
|----------|----------------------:|-------:|
| kNN | 0.986 ± 0.025 | 0.046 |
| IsolationForest | 0.982 ± 0.028 | 0.063 |
| **TrustLens Guard** | **0.981 ± 0.034** | 0.051 |
| Mahalanobis | 0.964 ± 0.053 | 0.091 |
| Softmax / Entropy | 0.311 | 0.97 |

TrustLens Guard is **not #1** — it ties kNN / IsolationForest within standard error. A validation tool that inflates its own numbers is a contradiction, so it doesn't. Its value is the **integration**: self-calibrated threshold + the guarantee layer wired into one gate. Reproduce: `python -m trustlens.benchmark_suite`.

The durable finding: **softmax/entropy — what most people ship as "uncertainty" — is worse than random under domain shift.** Don't trust it as a safety signal.

---

## When to use this
- You deploy a classifier where a **confident wrong answer is expensive** (clinical, fraud, safety, compliance).
- You need the model to **defer to a human** on inputs it shouldn't touch — and to prove which those are.
- You want a **certified error budget**, not a vibe. (Useful as evidence toward EU AI Act Art. 15 accuracy/robustness/human-oversight requirements.)

## What it is not
- Not a validated medical device. It measures validity; it doesn't make your model correct — it tells you when **not to trust it**.
- Binary classifiers with `predict_proba` today (sklearn, XGBoost, LightGBM, or any wrapper).

## License
MIT. Built in public by a 17-year-old. Tell me where it's wrong — I learn more from that than from a star.
