|
| 1 | +# Profile Eval (Python scripts via running PrismGuard) - Design |
| 2 | + |
| 3 | +**Date:** 2026-04-22 |
| 4 | + |
| 5 | +## Goal |
| 6 | + |
| 7 | +Provide a fast-to-iterate evaluation tool that: |
| 8 | + |
| 9 | +1. Accepts a moderation `profile` |
| 10 | +2. Reports local model artifacts for that profile (runtime + training marker files) |
| 11 | +3. Uses the **already running** PrismGuard Rust service to: |
| 12 | + - Open the profile database in read-only mode (server-side) |
| 13 | + - Run predictions/metrics for the profile's `local_model_type` |
| 14 | + - Produce F1 scores across multiple confidence thresholds |
| 15 | + |
| 16 | +This intentionally avoids compiling or testing the Rust codebase, and only depends on the existing binary/service. |
| 17 | + |
| 18 | +## Non-Goals |
| 19 | + |
| 20 | +- Reimplement local inference in Python (hashlinear/bow/fasttext) |
| 21 | +- Read/iterate `history.rocks` directly in Python |
| 22 | +- Add new Rust endpoints or change existing service behavior |
| 23 | + |
| 24 | +## Approach |
| 25 | + |
| 26 | +### Python tool layout |
| 27 | + |
| 28 | +- Directory: `py-scripts/` |
| 29 | +- Dependency management: `uv` virtualenv in `py-scripts/.venv` |
| 30 | +- Script: `py-scripts/eval_profile.py` |
| 31 | + |
| 32 | +### Service discovery |
| 33 | + |
| 34 | +The script determines the service base URL in this order: |
| 35 | + |
| 36 | +1. `--base-url` CLI override |
| 37 | +2. `PRISMGUARD_BASE_URL` or `BASE_URL` env var |
| 38 | +3. Repository root `.env` values: `HOST` + `PORT` |
| 39 | +4. Fallback: `http://127.0.0.1:8000` |
| 40 | + |
| 41 | +If `.env` binds to `0.0.0.0`, the client uses `127.0.0.1`. |
| 42 | + |
| 43 | +### Data & metrics retrieval |
| 44 | + |
| 45 | +The script calls: |
| 46 | + |
| 47 | +- `GET /debug/profile/<profile>` to retrieve: |
| 48 | + - `live_sample_count` (used for full-dataset evaluation size) |
| 49 | + - `history_rocks_path` (reported) |
| 50 | + - `training_status` (reported) |
| 51 | + - plus other debug metadata |
| 52 | + |
| 53 | +- `GET /debug/profile/<profile>/metrics?...` per threshold: |
| 54 | + - `sample_size=<live_sample_count>` |
| 55 | + - `sampling=latest_full|random_full|balanced` |
| 56 | + - `threshold=<t>` |
| 57 | + |
| 58 | +The service performs DB open/read-only behavior internally; the script reports the output as evidence. |
| 59 | + |
| 60 | +### Model selection |
| 61 | + |
| 62 | +Only the profile's configured `local_model_type` is evaluated. |
| 63 | + |
| 64 | +The script still reports missing/present runtime + marker files for that model type to explain failures quickly. |
| 65 | + |
| 66 | +### Output |
| 67 | + |
| 68 | +- Human-readable artifact report (exists/size/mtime) |
| 69 | +- Human-readable metrics table per threshold: |
| 70 | + - `precision`, `recall`, `f1`, `accuracy` |
| 71 | + - `tp`, `tn`, `fp`, `fn`, `evaluated` |
| 72 | + - elapsed seconds per request |
| 73 | +- Optional `--out-json` for machine-readable output |
| 74 | + |
| 75 | +## Risks / Notes |
| 76 | + |
| 77 | +- Full-dataset metrics may be slow (depends on sample count, model type, and server load). |
| 78 | +- The `/debug/profile/<profile>/metrics` route can be feature-gated; the tool should error clearly on non-200 responses. |
| 79 | + |
0 commit comments