guardmeter 0.3.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.
- guardmeter-0.3.0/LICENSE +21 -0
- guardmeter-0.3.0/PKG-INFO +237 -0
- guardmeter-0.3.0/README.md +194 -0
- guardmeter-0.3.0/guardmeter/__init__.py +3 -0
- guardmeter-0.3.0/guardmeter/cli/__init__.py +1 -0
- guardmeter-0.3.0/guardmeter/cli/main.py +441 -0
- guardmeter-0.3.0/guardmeter/core/__init__.py +1 -0
- guardmeter-0.3.0/guardmeter/core/guard.py +34 -0
- guardmeter-0.3.0/guardmeter/core/io_utils.py +59 -0
- guardmeter-0.3.0/guardmeter/core/registry.py +53 -0
- guardmeter-0.3.0/guardmeter/core/text_norm.py +44 -0
- guardmeter-0.3.0/guardmeter/data/__init__.py +1 -0
- guardmeter-0.3.0/guardmeter/data/augmentor.py +97 -0
- guardmeter-0.3.0/guardmeter/data/builtin/__init__.py +1 -0
- guardmeter-0.3.0/guardmeter/data/builtin/sample.csv +159 -0
- guardmeter-0.3.0/guardmeter/data/builtin/sample_10.jsonl +10 -0
- guardmeter-0.3.0/guardmeter/data/loader.py +99 -0
- guardmeter-0.3.0/guardmeter/data/schema.py +18 -0
- guardmeter-0.3.0/guardmeter/engine/__init__.py +1 -0
- guardmeter-0.3.0/guardmeter/engine/evaluator.py +174 -0
- guardmeter-0.3.0/guardmeter/engine/metrics.py +154 -0
- guardmeter-0.3.0/guardmeter/engine/results.py +149 -0
- guardmeter-0.3.0/guardmeter/engine/significance.py +42 -0
- guardmeter-0.3.0/guardmeter/gate/__init__.py +1 -0
- guardmeter-0.3.0/guardmeter/gate/checker.py +159 -0
- guardmeter-0.3.0/guardmeter/gate/schema.py +45 -0
- guardmeter-0.3.0/guardmeter/gate/summary.py +76 -0
- guardmeter-0.3.0/guardmeter/guards/__init__.py +1 -0
- guardmeter-0.3.0/guardmeter/guards/llamaguard.py +106 -0
- guardmeter-0.3.0/guardmeter/guards/openai_moderation.py +80 -0
- guardmeter-0.3.0/guardmeter/guards/regex_guard.py +216 -0
- guardmeter-0.3.0/guardmeter/judge/__init__.py +7 -0
- guardmeter-0.3.0/guardmeter/judge/base.py +27 -0
- guardmeter-0.3.0/guardmeter/judge/consensus.py +60 -0
- guardmeter-0.3.0/guardmeter/judge/llm_judge.py +168 -0
- guardmeter-0.3.0/guardmeter/judge/prompts.py +26 -0
- guardmeter-0.3.0/guardmeter/py.typed +0 -0
- guardmeter-0.3.0/guardmeter/report/__init__.py +1 -0
- guardmeter-0.3.0/guardmeter/report/charts.py +57 -0
- guardmeter-0.3.0/guardmeter/report/generator.py +313 -0
- guardmeter-0.3.0/guardmeter/report/templates/dashboard.html +575 -0
- guardmeter-0.3.0/guardmeter/report/templates/report.html +311 -0
- guardmeter-0.3.0/guardmeter/store/__init__.py +1 -0
- guardmeter-0.3.0/guardmeter/store/base.py +37 -0
- guardmeter-0.3.0/guardmeter/store/json_store.py +86 -0
- guardmeter-0.3.0/guardmeter/store/sqlite.py +239 -0
- guardmeter-0.3.0/guardmeter.egg-info/PKG-INFO +237 -0
- guardmeter-0.3.0/guardmeter.egg-info/SOURCES.txt +52 -0
- guardmeter-0.3.0/guardmeter.egg-info/dependency_links.txt +1 -0
- guardmeter-0.3.0/guardmeter.egg-info/entry_points.txt +2 -0
- guardmeter-0.3.0/guardmeter.egg-info/requires.txt +23 -0
- guardmeter-0.3.0/guardmeter.egg-info/top_level.txt +1 -0
- guardmeter-0.3.0/pyproject.toml +64 -0
- guardmeter-0.3.0/setup.cfg +4 -0
guardmeter-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Sam Vardani
|
|
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,237 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: guardmeter
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Benchmark, compare, and gate AI safety guards
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/samvardani/guardmeter
|
|
7
|
+
Project-URL: Repository, https://github.com/samvardani/guardmeter
|
|
8
|
+
Project-URL: Changelog, https://github.com/samvardani/guardmeter/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Issues, https://github.com/samvardani/guardmeter/issues
|
|
10
|
+
Keywords: ai-safety,guardrails,evaluation,benchmark,llm
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Testing
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: click>=8.0
|
|
23
|
+
Requires-Dist: jinja2>=3.0
|
|
24
|
+
Requires-Dist: pyyaml>=6.0
|
|
25
|
+
Requires-Dist: pandas>=1.5
|
|
26
|
+
Requires-Dist: scikit-learn>=1.1
|
|
27
|
+
Requires-Dist: matplotlib>=3.5
|
|
28
|
+
Requires-Dist: pydantic>=2.0
|
|
29
|
+
Provides-Extra: llm
|
|
30
|
+
Requires-Dist: openai; extra == "llm"
|
|
31
|
+
Requires-Dist: anthropic; extra == "llm"
|
|
32
|
+
Provides-Extra: hf
|
|
33
|
+
Requires-Dist: datasets; extra == "hf"
|
|
34
|
+
Requires-Dist: transformers; extra == "hf"
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
38
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
39
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
40
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
41
|
+
Requires-Dist: twine>=4.0; extra == "dev"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
<p align="center"><img src="https://raw.githubusercontent.com/samvardani/guardmeter/main/branding/guardmeter-wordmark.svg" alt="GuardMeter" width="440"/></p>
|
|
45
|
+
|
|
46
|
+
<p align="center"><img src="https://raw.githubusercontent.com/samvardani/guardmeter/main/docs/images/dashboard.png" alt="GuardMeter dashboard" width="800"/></p>
|
|
47
|
+
|
|
48
|
+
# GuardMeter — AI Safety Guard Evaluation Framework
|
|
49
|
+
|
|
50
|
+
[](https://github.com/samvardani/guardmeter/actions)
|
|
51
|
+
[](https://pypi.org/project/guardmeter/)
|
|
52
|
+
[](https://www.python.org/)
|
|
53
|
+
[](LICENSE)
|
|
54
|
+
|
|
55
|
+
GuardMeter compares two content-safety guards — a **baseline** and a **candidate** — on a labeled dataset and produces per-slice metrics, an HTML report, an interactive dashboard, and a pass/fail CI gate. It's for developers and ML engineers who ship a safety classifier and need to catch regressions — per category, language, and attack type — before they merge.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 30-second demo
|
|
60
|
+
|
|
61
|
+
On a fresh `pip install guardmeter`, these commands run verbatim:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
guardmeter init
|
|
65
|
+
guardmeter compare --baseline regex-baseline --candidate regex-enhanced --dataset dataset/sample.csv
|
|
66
|
+
guardmeter gate --config gate.json --run latest
|
|
67
|
+
guardmeter dashboard --open
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`init` writes `gate.json` and `dataset/sample.csv` into the current directory. `compare` evaluates both guards and stores the run. `gate` checks the latest run against `gate.json` and exits non-zero on failure. `dashboard` builds `report/dashboard.html` (`--open` launches your browser; omit it or pass `--no-open` in CI).
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## How it works
|
|
75
|
+
|
|
76
|
+
**Baseline vs candidate.** You give GuardMeter two guards. The baseline is your current behavior; the candidate is the change you're evaluating. Every metric is reported for both so you can see whether the candidate actually improved things.
|
|
77
|
+
|
|
78
|
+
**Strict vs lenient policy.** Each dataset row is labeled `benign`, `borderline`, or `unsafe`. Under the **strict** policy a `borderline` row counts as something the guard *should* flag (positive); under the **lenient** policy `borderline` counts as benign (negative). Both policies are always computed; the dashboard has a toggle, and the gate/McNemar test use strict by default.
|
|
79
|
+
|
|
80
|
+
**Slices.** Aggregate numbers hide regressions. GuardMeter computes recall, FPR, precision, F1 and latency for every `(category × language)` slice, and separately for every `attack_type` slice, so a drop confined to (say) Farsi violence or leetspeak-obfuscated prompts is visible.
|
|
81
|
+
|
|
82
|
+
**Significance and confidence.** A McNemar test on the paired predictions tells you whether the baseline↔candidate difference is real or noise. Recall and FPR come with Wilson score confidence intervals so small slices aren't over-interpreted.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## CI gate
|
|
87
|
+
|
|
88
|
+
`gate.json` is a machine-readable safety policy you check into version control. `guardmeter gate` loads a stored run and fails the build if any threshold is breached.
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"mode": "strict",
|
|
93
|
+
"global_thresholds": {
|
|
94
|
+
"min_recall": 0.55,
|
|
95
|
+
"min_f1": 0.80,
|
|
96
|
+
"max_fpr": 0.01,
|
|
97
|
+
"max_latency_p99_ms": 20
|
|
98
|
+
},
|
|
99
|
+
"slices": {
|
|
100
|
+
"self_harm/en": { "min_recall": 0.44, "min_f1": 0.60 },
|
|
101
|
+
"crime/en": { "min_recall": 0.44, "min_f1": 0.60 },
|
|
102
|
+
"malware/en": { "min_recall": 0.44 },
|
|
103
|
+
"pii/en": { "min_f1": 0.65 }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Fields:
|
|
109
|
+
|
|
110
|
+
- **`mode`** — `strict` or `lenient`; selects which policy's metrics the gate checks.
|
|
111
|
+
- **`global_thresholds`** — applied to the overall candidate metrics and, by default, to every `(category × language)` slice:
|
|
112
|
+
- `min_recall` — minimum recall (skipped for slices with no positive examples).
|
|
113
|
+
- `min_f1` — minimum F1 (default `0.80`; set `0.0` to disable).
|
|
114
|
+
- `max_fpr` — maximum false-positive rate (skipped for slices with no negatives).
|
|
115
|
+
- `max_latency_p99_ms` — maximum p99 latency in milliseconds.
|
|
116
|
+
- **`slices`** — per-slice overrides. Keys are fnmatch globs. A `"category/language"` key (e.g. `"self_harm/en"`, `"*/fa"`) targets the category×language family; an `"attack:<glob>"` key (e.g. `"attack:leetspeak"`) targets the attack-type family. Only the fields you set are overridden; the rest fall back to `global_thresholds`. Attack-type slices are opt-in — they're gated only where an `attack:` key matches.
|
|
117
|
+
- **`comparison`** *(optional)* — regression limits versus the previous stored run: `max_recall_regression`, `max_fpr_increase`.
|
|
118
|
+
- **`on_failure`** — `block` (fail the gate) or `warn` (report but pass).
|
|
119
|
+
|
|
120
|
+
The per-slice overrides in the shipped `gate.json` reflect the known limits of the built-in regex demo guard; tighten or remove them for your own guard.
|
|
121
|
+
|
|
122
|
+
GitHub Actions:
|
|
123
|
+
|
|
124
|
+
```yaml
|
|
125
|
+
- name: Install guardmeter
|
|
126
|
+
run: pip install guardmeter
|
|
127
|
+
- name: Evaluate
|
|
128
|
+
run: guardmeter compare --baseline regex-baseline --candidate ${{ env.CANDIDATE_GUARD }} --dataset dataset/sample.csv
|
|
129
|
+
- name: Report
|
|
130
|
+
run: guardmeter report --run latest
|
|
131
|
+
- name: Safety gate
|
|
132
|
+
run: guardmeter gate --config gate.json --run latest # exits 1 on regression
|
|
133
|
+
- name: Upload report
|
|
134
|
+
uses: actions/upload-artifact@v4
|
|
135
|
+
with:
|
|
136
|
+
name: safety-report
|
|
137
|
+
path: report/
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Built-in guards
|
|
143
|
+
|
|
144
|
+
| Name | Requirements | Notes |
|
|
145
|
+
|------|--------------|-------|
|
|
146
|
+
| `regex-baseline` | built-in | Simple keyword-matching profile — the weak baseline to compare against |
|
|
147
|
+
| `regex-enhanced` | built-in | Expanded patterns, obfuscation detection, Farsi coverage |
|
|
148
|
+
| `regex` | built-in | Alias of `regex-enhanced` (kept for backward compatibility) |
|
|
149
|
+
| `openai` | `pip install guardmeter[llm]` + `OPENAI_API_KEY` | OpenAI Moderation API (experimental — see below) |
|
|
150
|
+
| `llamaguard` | HuggingFace `transformers` or an HTTP endpoint | Llama Guard 3, local pipeline or hosted API (experimental — see below) |
|
|
151
|
+
|
|
152
|
+
### Write your own guard
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from guardmeter.core.guard import Guard, GuardResult
|
|
156
|
+
from guardmeter.core.registry import register
|
|
157
|
+
|
|
158
|
+
class MyGuard(Guard):
|
|
159
|
+
name = "my-guard"
|
|
160
|
+
version = "1.0.0"
|
|
161
|
+
|
|
162
|
+
def predict(self, text: str, **meta) -> GuardResult:
|
|
163
|
+
is_unsafe = "bomb" in text.lower()
|
|
164
|
+
return GuardResult(prediction="flag" if is_unsafe else "pass",
|
|
165
|
+
score=0.9 if is_unsafe else 0.1, latency_ms=5)
|
|
166
|
+
|
|
167
|
+
register("my-guard", MyGuard) # now usable as --candidate my-guard
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Dashboard & report
|
|
173
|
+
|
|
174
|
+
`guardmeter report --run latest` writes an HTML report for a single run (baseline vs candidate cards with Wilson CIs, category×language and attack-type slice tables, a real candidate threshold-sweep chart, and per-sample latency charts). It also mentions an informational regulatory mapping — see the note under *Experimental*.
|
|
175
|
+
|
|
176
|
+
`guardmeter dashboard` builds an interactive multi-run dashboard (`report/dashboard.html`), also auto-rebuilt on every `report`. Four tabs:
|
|
177
|
+
|
|
178
|
+
- **Overview** — run history table with F1, recall, FPR, McNemar p-value and gate badges. Click a row to drill in.
|
|
179
|
+
- **Run Detail** — baseline vs candidate metric cards, category×language and attack-type slice tables, and a sample-results table (first 200 rows). Strict/Lenient toggle.
|
|
180
|
+
- **Trends** — recall, F1, FPR and McNemar p-value over all runs (p-value on a log scale with a p=0.05 reference line).
|
|
181
|
+
- **Compare** — pick any two runs and see a per-metric delta table with improvement/regression arrows.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Experimental
|
|
186
|
+
|
|
187
|
+
These features work but require API keys or extra dependencies and have limited automated test coverage. Treat them as advisory:
|
|
188
|
+
|
|
189
|
+
- **LLM-as-judge** (`guardmeter/judge/`) — uses Claude or an OpenAI model as a second opinion on predictions. Available through the Python API only (no CLI subcommand); needs a provider API key.
|
|
190
|
+
- **`openai` guard** — calls the OpenAI Moderation API; needs `guardmeter[llm]` and `OPENAI_API_KEY`.
|
|
191
|
+
- **`llamaguard` guard** — runs Llama Guard 3 via a local `transformers` pipeline or an HTTP endpoint; needs `guardmeter[hf]` or a hosted endpoint and key.
|
|
192
|
+
- **Regulatory mapping (informational).** The HTML report includes a table mapping a run's metrics to regulatory themes (e.g. EU AI Act articles, NIST AI RMF). It is an informational aid for your own documentation, **not** a compliance certification or legal assessment.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Development Setup
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
python3.13 -m venv .venv
|
|
200
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
201
|
+
pip install -e ".[dev]"
|
|
202
|
+
ruff check guardmeter tests
|
|
203
|
+
mypy guardmeter
|
|
204
|
+
pytest tests/guardmeter/ -q
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
> **Note:** macOS users with Homebrew Python must use a virtual environment (Homebrew enforces PEP 668).
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Not affiliated with
|
|
212
|
+
|
|
213
|
+
This project is unrelated to the JRC "GuardBench" toxicity-benchmark library at [github.com/AmenRa/guardbench](https://github.com/AmenRa/guardbench). Same name, different project.
|
|
214
|
+
|
|
215
|
+
Formerly published as `sea-guard` (versions 0.1–0.2, import name `guardbench`). Renamed in 0.3.0 to avoid confusion with the unrelated JRC GuardBench benchmark.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Contributing
|
|
220
|
+
|
|
221
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Contributions welcome — new guard adapters, dataset/language coverage, and report improvements especially.
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
MIT — see [LICENSE](LICENSE).
|
|
226
|
+
|
|
227
|
+
## Branding
|
|
228
|
+
|
|
229
|
+
Logo assets are in the `branding/` directory.
|
|
230
|
+
|
|
231
|
+
- `guardmeter-logo.svg` — shield mark (favicon, PyPI, GitHub avatar)
|
|
232
|
+
- `guardmeter-wordmark.svg` — full lockup with tagline
|
|
233
|
+
- `guardmeter-social-card.svg` — 1280×640 OG image for GitHub social preview
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
*Built by [SeaTechOne LLC](https://seatechone.com) · Seattle, WA*
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<p align="center"><img src="https://raw.githubusercontent.com/samvardani/guardmeter/main/branding/guardmeter-wordmark.svg" alt="GuardMeter" width="440"/></p>
|
|
2
|
+
|
|
3
|
+
<p align="center"><img src="https://raw.githubusercontent.com/samvardani/guardmeter/main/docs/images/dashboard.png" alt="GuardMeter dashboard" width="800"/></p>
|
|
4
|
+
|
|
5
|
+
# GuardMeter — AI Safety Guard Evaluation Framework
|
|
6
|
+
|
|
7
|
+
[](https://github.com/samvardani/guardmeter/actions)
|
|
8
|
+
[](https://pypi.org/project/guardmeter/)
|
|
9
|
+
[](https://www.python.org/)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
GuardMeter compares two content-safety guards — a **baseline** and a **candidate** — on a labeled dataset and produces per-slice metrics, an HTML report, an interactive dashboard, and a pass/fail CI gate. It's for developers and ML engineers who ship a safety classifier and need to catch regressions — per category, language, and attack type — before they merge.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 30-second demo
|
|
17
|
+
|
|
18
|
+
On a fresh `pip install guardmeter`, these commands run verbatim:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
guardmeter init
|
|
22
|
+
guardmeter compare --baseline regex-baseline --candidate regex-enhanced --dataset dataset/sample.csv
|
|
23
|
+
guardmeter gate --config gate.json --run latest
|
|
24
|
+
guardmeter dashboard --open
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`init` writes `gate.json` and `dataset/sample.csv` into the current directory. `compare` evaluates both guards and stores the run. `gate` checks the latest run against `gate.json` and exits non-zero on failure. `dashboard` builds `report/dashboard.html` (`--open` launches your browser; omit it or pass `--no-open` in CI).
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## How it works
|
|
32
|
+
|
|
33
|
+
**Baseline vs candidate.** You give GuardMeter two guards. The baseline is your current behavior; the candidate is the change you're evaluating. Every metric is reported for both so you can see whether the candidate actually improved things.
|
|
34
|
+
|
|
35
|
+
**Strict vs lenient policy.** Each dataset row is labeled `benign`, `borderline`, or `unsafe`. Under the **strict** policy a `borderline` row counts as something the guard *should* flag (positive); under the **lenient** policy `borderline` counts as benign (negative). Both policies are always computed; the dashboard has a toggle, and the gate/McNemar test use strict by default.
|
|
36
|
+
|
|
37
|
+
**Slices.** Aggregate numbers hide regressions. GuardMeter computes recall, FPR, precision, F1 and latency for every `(category × language)` slice, and separately for every `attack_type` slice, so a drop confined to (say) Farsi violence or leetspeak-obfuscated prompts is visible.
|
|
38
|
+
|
|
39
|
+
**Significance and confidence.** A McNemar test on the paired predictions tells you whether the baseline↔candidate difference is real or noise. Recall and FPR come with Wilson score confidence intervals so small slices aren't over-interpreted.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## CI gate
|
|
44
|
+
|
|
45
|
+
`gate.json` is a machine-readable safety policy you check into version control. `guardmeter gate` loads a stored run and fails the build if any threshold is breached.
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"mode": "strict",
|
|
50
|
+
"global_thresholds": {
|
|
51
|
+
"min_recall": 0.55,
|
|
52
|
+
"min_f1": 0.80,
|
|
53
|
+
"max_fpr": 0.01,
|
|
54
|
+
"max_latency_p99_ms": 20
|
|
55
|
+
},
|
|
56
|
+
"slices": {
|
|
57
|
+
"self_harm/en": { "min_recall": 0.44, "min_f1": 0.60 },
|
|
58
|
+
"crime/en": { "min_recall": 0.44, "min_f1": 0.60 },
|
|
59
|
+
"malware/en": { "min_recall": 0.44 },
|
|
60
|
+
"pii/en": { "min_f1": 0.65 }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Fields:
|
|
66
|
+
|
|
67
|
+
- **`mode`** — `strict` or `lenient`; selects which policy's metrics the gate checks.
|
|
68
|
+
- **`global_thresholds`** — applied to the overall candidate metrics and, by default, to every `(category × language)` slice:
|
|
69
|
+
- `min_recall` — minimum recall (skipped for slices with no positive examples).
|
|
70
|
+
- `min_f1` — minimum F1 (default `0.80`; set `0.0` to disable).
|
|
71
|
+
- `max_fpr` — maximum false-positive rate (skipped for slices with no negatives).
|
|
72
|
+
- `max_latency_p99_ms` — maximum p99 latency in milliseconds.
|
|
73
|
+
- **`slices`** — per-slice overrides. Keys are fnmatch globs. A `"category/language"` key (e.g. `"self_harm/en"`, `"*/fa"`) targets the category×language family; an `"attack:<glob>"` key (e.g. `"attack:leetspeak"`) targets the attack-type family. Only the fields you set are overridden; the rest fall back to `global_thresholds`. Attack-type slices are opt-in — they're gated only where an `attack:` key matches.
|
|
74
|
+
- **`comparison`** *(optional)* — regression limits versus the previous stored run: `max_recall_regression`, `max_fpr_increase`.
|
|
75
|
+
- **`on_failure`** — `block` (fail the gate) or `warn` (report but pass).
|
|
76
|
+
|
|
77
|
+
The per-slice overrides in the shipped `gate.json` reflect the known limits of the built-in regex demo guard; tighten or remove them for your own guard.
|
|
78
|
+
|
|
79
|
+
GitHub Actions:
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
- name: Install guardmeter
|
|
83
|
+
run: pip install guardmeter
|
|
84
|
+
- name: Evaluate
|
|
85
|
+
run: guardmeter compare --baseline regex-baseline --candidate ${{ env.CANDIDATE_GUARD }} --dataset dataset/sample.csv
|
|
86
|
+
- name: Report
|
|
87
|
+
run: guardmeter report --run latest
|
|
88
|
+
- name: Safety gate
|
|
89
|
+
run: guardmeter gate --config gate.json --run latest # exits 1 on regression
|
|
90
|
+
- name: Upload report
|
|
91
|
+
uses: actions/upload-artifact@v4
|
|
92
|
+
with:
|
|
93
|
+
name: safety-report
|
|
94
|
+
path: report/
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Built-in guards
|
|
100
|
+
|
|
101
|
+
| Name | Requirements | Notes |
|
|
102
|
+
|------|--------------|-------|
|
|
103
|
+
| `regex-baseline` | built-in | Simple keyword-matching profile — the weak baseline to compare against |
|
|
104
|
+
| `regex-enhanced` | built-in | Expanded patterns, obfuscation detection, Farsi coverage |
|
|
105
|
+
| `regex` | built-in | Alias of `regex-enhanced` (kept for backward compatibility) |
|
|
106
|
+
| `openai` | `pip install guardmeter[llm]` + `OPENAI_API_KEY` | OpenAI Moderation API (experimental — see below) |
|
|
107
|
+
| `llamaguard` | HuggingFace `transformers` or an HTTP endpoint | Llama Guard 3, local pipeline or hosted API (experimental — see below) |
|
|
108
|
+
|
|
109
|
+
### Write your own guard
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from guardmeter.core.guard import Guard, GuardResult
|
|
113
|
+
from guardmeter.core.registry import register
|
|
114
|
+
|
|
115
|
+
class MyGuard(Guard):
|
|
116
|
+
name = "my-guard"
|
|
117
|
+
version = "1.0.0"
|
|
118
|
+
|
|
119
|
+
def predict(self, text: str, **meta) -> GuardResult:
|
|
120
|
+
is_unsafe = "bomb" in text.lower()
|
|
121
|
+
return GuardResult(prediction="flag" if is_unsafe else "pass",
|
|
122
|
+
score=0.9 if is_unsafe else 0.1, latency_ms=5)
|
|
123
|
+
|
|
124
|
+
register("my-guard", MyGuard) # now usable as --candidate my-guard
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Dashboard & report
|
|
130
|
+
|
|
131
|
+
`guardmeter report --run latest` writes an HTML report for a single run (baseline vs candidate cards with Wilson CIs, category×language and attack-type slice tables, a real candidate threshold-sweep chart, and per-sample latency charts). It also mentions an informational regulatory mapping — see the note under *Experimental*.
|
|
132
|
+
|
|
133
|
+
`guardmeter dashboard` builds an interactive multi-run dashboard (`report/dashboard.html`), also auto-rebuilt on every `report`. Four tabs:
|
|
134
|
+
|
|
135
|
+
- **Overview** — run history table with F1, recall, FPR, McNemar p-value and gate badges. Click a row to drill in.
|
|
136
|
+
- **Run Detail** — baseline vs candidate metric cards, category×language and attack-type slice tables, and a sample-results table (first 200 rows). Strict/Lenient toggle.
|
|
137
|
+
- **Trends** — recall, F1, FPR and McNemar p-value over all runs (p-value on a log scale with a p=0.05 reference line).
|
|
138
|
+
- **Compare** — pick any two runs and see a per-metric delta table with improvement/regression arrows.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Experimental
|
|
143
|
+
|
|
144
|
+
These features work but require API keys or extra dependencies and have limited automated test coverage. Treat them as advisory:
|
|
145
|
+
|
|
146
|
+
- **LLM-as-judge** (`guardmeter/judge/`) — uses Claude or an OpenAI model as a second opinion on predictions. Available through the Python API only (no CLI subcommand); needs a provider API key.
|
|
147
|
+
- **`openai` guard** — calls the OpenAI Moderation API; needs `guardmeter[llm]` and `OPENAI_API_KEY`.
|
|
148
|
+
- **`llamaguard` guard** — runs Llama Guard 3 via a local `transformers` pipeline or an HTTP endpoint; needs `guardmeter[hf]` or a hosted endpoint and key.
|
|
149
|
+
- **Regulatory mapping (informational).** The HTML report includes a table mapping a run's metrics to regulatory themes (e.g. EU AI Act articles, NIST AI RMF). It is an informational aid for your own documentation, **not** a compliance certification or legal assessment.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Development Setup
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
python3.13 -m venv .venv
|
|
157
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
158
|
+
pip install -e ".[dev]"
|
|
159
|
+
ruff check guardmeter tests
|
|
160
|
+
mypy guardmeter
|
|
161
|
+
pytest tests/guardmeter/ -q
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
> **Note:** macOS users with Homebrew Python must use a virtual environment (Homebrew enforces PEP 668).
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Not affiliated with
|
|
169
|
+
|
|
170
|
+
This project is unrelated to the JRC "GuardBench" toxicity-benchmark library at [github.com/AmenRa/guardbench](https://github.com/AmenRa/guardbench). Same name, different project.
|
|
171
|
+
|
|
172
|
+
Formerly published as `sea-guard` (versions 0.1–0.2, import name `guardbench`). Renamed in 0.3.0 to avoid confusion with the unrelated JRC GuardBench benchmark.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Contributing
|
|
177
|
+
|
|
178
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Contributions welcome — new guard adapters, dataset/language coverage, and report improvements especially.
|
|
179
|
+
|
|
180
|
+
## License
|
|
181
|
+
|
|
182
|
+
MIT — see [LICENSE](LICENSE).
|
|
183
|
+
|
|
184
|
+
## Branding
|
|
185
|
+
|
|
186
|
+
Logo assets are in the `branding/` directory.
|
|
187
|
+
|
|
188
|
+
- `guardmeter-logo.svg` — shield mark (favicon, PyPI, GitHub avatar)
|
|
189
|
+
- `guardmeter-wordmark.svg` — full lockup with tagline
|
|
190
|
+
- `guardmeter-social-card.svg` — 1280×640 OG image for GitHub social preview
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
*Built by [SeaTechOne LLC](https://seatechone.com) · Seattle, WA*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI entry points."""
|