Skip to main content
Keido Labs

aipsy-bench

Open-source psychological-safety benchmark for conversational AI

Point a chatbot at a frozen battery of clinical scenarios, score the transcripts with a frozen panel of LLM judges, and ship a CI pass/fail gate + a clinician-grade diagnostic — which turns failed, why, and what to tune.

CI PyPI Python 3.12+ Code license: Apache 2.0 Data license: CC BY 4.0 Built on Inspect AI Status: Directional

Quickstart · Benchmark your app · Local judge · Run profiles · What a score means

Built and maintained by Keido Labs


It is a benchmark (fixed content, comparable scores), not a framework. The content — 20 scenarios + a 6-metric rubric + the judge prompt — is verbatim from our source research study (documented in the forthcoming methodology preprint). The engine is Inspect AI; we ship a Task, a Solver, and a Scorer, not a new runner.

[!IMPORTANT] ⚠ Directional — a recommendation, not a rubber-stamp. The premise is "the judge panel is validated against human clinical experts." That validation — a multi-rater human-vs-judge agreement study — is running in parallel and has not yet landed. So aipsy-bench ships now as a directional, methodology-transparent, reproducible reading — run it yourself to reproduce — not an authoritative, human-validated safety rating. The CI gate is functional: a metric fails the build against the thresholds you set (your policy, not a validated rating). But no validated per-metric agreement (α) number is claimed until the study lands — don't cite these as validated agreement. When it lands, the directional reading upgrades to validated authority.

Prerequisites

  1. uv — gives you uvx (run with zero install). (Prefer pip? You only need Python 3.12+.)
  2. One way to score transcripts — pick either:
    • 🖥️ Ollama — runs the default local judge, 100% offline, no API key (needs a 48 GB+ Mac or a 16 GB-VRAM + 64 GB-RAM Linux box).
    • 🔑 A provider API keyOpenAI, Anthropic, or Google — for the frontier judge lane (--judges gold/single; your key, your cost).

Quickstart

See what it catches — 10 seconds, nothing to install, fully offline (no API keys, no network, no setup):

uvx aipsy-bench demo

Replays two real recorded runs of the same coaching app on two different backing models — an older one and a newer one, scored by the same judge — and renders the head-to-head. On a crisis scenario, swapping the model under the app flips the safety gate from FAIL (AI-Trust 2.37) to PASS (4.02). Same product, swap the LLM, safety moves — the kind of regression aipsy-bench catches in CI. (Directional single-judge replay, so it's illustrative, not a comparable score.)

Then run the engine yourself (offline self-test: mock bot + mock judges):

uvx aipsy-bench run --target mock --quick

Install once — then it's just aipsy-bench … (no per-command prefixes):

uv tool install aipsy-bench             # one install — local judge + every provider, no extras to pick
aipsy-bench judge pull                  # one-time: fetch the local judge (needs Ollama)
aipsy-bench init --http-target http://localhost:3000/eval   # scaffold config for your bot
aipsy-bench run --quick                 # go

Frontier judges instead of the local default? Nothing extra to install — set your provider key (aipsy-bench keys set, stored in a gitignored .env) and pass --judges single (or gold). Prefer classic pip? pip install aipsy-bench is identical.

Each run writes result.json (citable), report.txt (with the remediation cards), and a share card.svg/card.png + badge.svg (skip with --no-card).

Benchmarking your own app? The only integration you write is a thin, stateless /eval endpoint — see the adapter guides: a hand-written cookbook, an AI-agent build prompt, or a Tier-2 callable.

Which judge? The local judge is the offline default; frontier (--judges gold/single, provider keys) is a different instrument — a local score compares to other local runs only, never to gold. Setup + hardware: docs/local-judge.md.

Run profiles

  • --judges local (default) — the offline fine-tuned judge (aipsy-judge-1.0) served by Ollama. No API key, no network (needs a 48 GB+ Mac or a 16 GB-VRAM/64 GB-RAM Linux box — see docs/local-judge.md). Its own comparability lane — comparable to other local runs, never to gold. Directional by construction and human-in-the-loop (strongest on crisis/empathy/boundary; advice is the lowest-confidence axis — treat advice flags as flag-for-review). See docs/local-judge.md.
  • --judges gold — the 3-judge frontier ensemble (the official/citable lane). Comparable, drives the frontier badge/leaderboard. Needs all three provider keys.
  • --judges single — the primary frontier judge only. Fast inner loop; NOT comparable to the gold or local lanes.
  • --quick — smoke subset (one scenario per domain + both crisis scenarios). Directional only; never feeds a card or the leaderboard.
  • --scenario s06,s07 — run a subset.
  • --baseline-prompt — inject the research baseline system prompt to reproduce the published frontier baseline. By default the target keeps its own system prompt (the bot as deployed); aipsy-bench sends only the scripted user turns.
  • --judge-override anthropic=claude-haiku-4-5 — swap a pinned judge for a cheaper one while iterating (repeatable). The judge pins are frozen, so this makes the run non-comparable — not the frozen instrument, not board/card eligible, loudly warned (§8). Never use it for a number you'll cite. (Set once in aipsy-bench.yaml via judge_overrides: {anthropic: claude-haiku-4-5}.)

Long / real runs (timeouts, interrupting, resuming)

A gold battery is ~800 calls — --dry-run first to see the estimate. Calls are bounded so a hung or rate-limited provider can't stall forever:

  • --timeout <seconds> (default 120) — per-call timeout; a stuck call fails and that scenario is reported as a run failure (never a low safety score).
  • --max-retries <n> (default 3) — bounds rate-limit backoff (which can otherwise look like a hang). A whole battery never aborts on one bad scenario — it's logged and the rest still score.
  • --max-connections <n> — cap concurrent calls per provider. Rate-limited? Lower it (e.g. 24) — fewer parallel calls means fewer 429s and a more complete run. (Running a model as both target and a judge doubles that provider's load, so it rate-limits first.)
  • Interrupt with Ctrl+C (bounded by --timeout, so it stops promptly), then --resume <run_id> continues without re-doing completed work. Live progress shows aipsy: judge calls / aipsy: scoring counters; if the terminal UI feels heavy, add --display plain.

The footer counters like openai 5/5 · anthropic 14/40 · google 0/20 are Inspect's per-provider in-flight / pool-size gauges (live concurrency), not call totals — unequal and fluctuating is normal. A rate-limited / target-doubling provider stays saturated while an idle one reads 0/n. All turns are still judged by every panel member.

API keys (your keys, your cost)

aipsy-bench uses your provider keys, read from the environment — it never stores, transmits, or proxies them, so the provider bills you directly. --target mock, the test suite, and the default local judge need no provider keys (the local judge runs offline on Ollama); the frontier panels need a key per judge.

Panel Keys needed
--judges local (default) nonejudge pull is token-free (public, ungated repo)
--judges single OPENAI_API_KEY
--judges gold OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY

A real target may still need its own provider key even with the local judge (only the judge is local). The local judge itself needs no key — judge pull fetches from a public, ungated repo.

Set them the friendly way (interactive, input hidden, written to a gitignored .env):

aipsy-bench keys set                # pick a provider, paste the key, done
aipsy-bench keys set --provider openai
aipsy-bench keys status             # which keys are present (never prints values)

…or do it by hand — a project .env (auto-loaded; gitignored) or exported shell vars (which take precedence):

cp .env.example .env       # then fill in your keys
# or:  export OPENAI_API_KEY=sk-...

aipsy-bench doctor --judges gold   # preflight: which keys are present, data SHA, config

keys set only writes to your local .env — aipsy-bench never stores, transmits, or proxies your keys.

doctor reads the same .env a real run will, so it tells you exactly what's set before you spend anything. (Enterprises can route the pinned judges through their own Azure / Vertex / gateway via base-URL overrides — a later iteration.)

What a score means

The only variable in a comparable score is the target bot — the scenarios, rubric, judge prompt, and judge model snapshots are all frozen and SHA-256 verified at runtime. The six metrics (empathy, tone_consistency, crisis_handling, advice_safety, boundary_safety, affective_complexity) roll up into a weighted AI-Trust composite over the applicable metrics. A target error / timeout / rate-limit / refusal is a run failure, never a low safety score.

Coverage limits (v1)

aipsy-bench v1 measures English, text, single-agent conversational safety on the frozen battery. Out of scope for v1 (named here, not silently mishandled):

  • Non-English — scenarios and judges are English-frozen; a non-English bot needs a future data/ version, not v1.
  • Voice / multimodal — text only; a voice bot needs a user-supplied speech-to-text shim ahead of the adapter (not shipped).
  • Agentic / tool-using / RAG-grounded bots — the transcript is user/assistant text only; tool calls and retrieved context are not modeled. Such bots run, but the score reflects the text exchange only.
  • Guardrail / safety-filter vendors want pass/fail on detection, not quality scoring — an adjacent product, not this tool.

Data residency

The benchmark drives synthetic, public scripted scenarios through your bot — the "user" turns are ours and carry no real end-user data. What differs by lane is where your bot's replies get scored:

  • Local judge (--judges local, the default) — fully in your boundary. Scoring runs on your own machine via Ollama; the transcript is never sent to any third-party provider and nothing leaves your environment. This is the lane for sensitive transcripts — real user data via the SDK, regulated / PHI content — where an API judge structurally cannot offer the same guarantee.
  • Any API judge (--judges single or --judges gold) — the transcript leaves your environment. Your bot's replies are sent to the judge provider(s) — OpenAI / Anthropic / Google, whichever that lane uses — for scoring, billed to your keys. In pure benchmark mode that's only your bot's answers to our public scripted prompts (still no real end-user data), but the target's outputs do go out.

So the local-judge default already runs the whole pipeline inside your own boundary — no enterprise routing required. (For teams that want the frontier lane in-boundary too, routing the pinned API judges through your own Azure / Vertex / gateway is a later enterprise feature.)

Development

uv run pytest          # full suite, offline, no API keys
uv run ruff check .    # lint

CI runs the suite offline with no provider keys — every test is deterministic against the mock target + mock judges.

Licenses

  • Code: Apache-2.0 (see LICENSE).
  • Data (data/v1/ — the scenarios, rubric, and judge prompt): CC BY 4.0 (see data/v1/DATA_LICENSE) — the standard content license, and the same one the forthcoming methodology preprint will carry.
  • Local judge model (keidolabs/aipsy-judge-1.0, on Hugging Face): Apache-2.0, inherited from its Gemma-4 base model's terms.

aipsy-bench — built and maintained by Keido Labs
Apache-2.0 (code) · CC BY 4.0 (data) · directional until the human-validation study lands

Release files for aipsy-bench 0.1.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aipsy-bench 0.1.5
File Size Uploaded
aipsy_bench-0.1.5.tar.gz 481.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aipsy-bench 0.1.5
File Interpreter ABI Platform
aipsy_bench-0.1.5-py3-none-any.whl Python 3 none any Details

Total release size:733.9 kB

Release files / aipsy_bench-0.1.5.tar.gz

Download URL aipsy_bench-0.1.5.tar.gz
Size 481.3 kB
Tags Source
SHA-256 checksum
How to use checksums
948ede32703bb1e0e37ad2988f214c18172926cb64507c88b002c53fd052acbb
BLAKE2b-256 checksum
How to use checksums
b0a0819d7e1014b6b7ffe65fa1343eafbe1387a349f9004bf2e82695359dea78
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / aipsy_bench-0.1.5-py3-none-any.whl

Download URL aipsy_bench-0.1.5-py3-none-any.whl
Size 252.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9eff769d3ca08c400f69beaeb3172e6ce4e8caa391ac908ff52df0fa21d47ea7
BLAKE2b-256 checksum
How to use checksums
2139f795423a646d8a73f0da4b18df0e11ee13fbc935f0f891940d7cdc17d94c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.1.5 This release

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page