Skip to main content

aiexpect

Assertions for non-deterministic AI text. Drop into the tests you already have.

PyPI Downloads CI Python License: MIT

from aiexpect import expect

def test_refund_policy(bot):
    reply = bot.ask("What is your refund policy?")

    expect(reply).to_mean("you can return items within 30 days")   # semantic, no API key
    expect(reply).to_be_grounded_in(policy_doc)                    # no hallucination
    expect(reply).to_not_contain_pii().to_have_length(max=600)      # deterministic rules

Run pytest as usual. You get normal pass/fail plus a Trust Score and a self-contained HTML report:

================================= aiexpect =================================
Trust Score: 87/100
  Accuracy 92 · Groundedness 85 · Relevance 90 · Safety 100 · Consistency 80 · Format 75
  41/46 checks passed (89%)
  report: /your/project/aiexpect-report.html
aiexpect HTML report: Trust Score, six sub-scores, pass rate by assertion, score distribution

Open the sample report

pip install aiexpect

Zero dependencies. Works offline out of the box.


Why

Chatbot and LLM output changes every run. assert reply == "..." is useless, and most eval frameworks want you to adopt a whole new platform. aiexpect is just an assertion library: it slots into pytest next to your existing tests, and the results roll up into metrics a non-ML person can read.

Three tiers, free first

Tier Needs Assertions
1 · Rules nothing to_contain, to_contain_any, to_not_contain, to_match, to_not_match, to_have_length, to_be_json, to_match_schema, to_not_contain_pii, to_be_one_of, to_refuse, to_not_refuse, to_satisfy_fn
2 · Semantic nothing (pip install 'aiexpect[embeddings]' for a real local embedding model) to_mean, to_not_mean, to_be_similar_to, to_be_relevant_to, to_match_snapshot
3 · LLM judge any model you run or pay for: Ollama (free, local), Anthropic, OpenAI, or any OpenAI-compatible server to_be_grounded_in, to_answer, to_have_tone, to_satisfy(rubric), to_be_consistent_with, to_refuse (escalation)

aiexpect never proxies your traffic. You bring the key; you own the bill. Judge verdicts are cached on disk, so re-running an unchanged suite costs nothing. Is a free local model good enough? Measured answer in docs/judges.md: a 3B Ollama model got 43/44 probe verdicts right at ~2 s each.

Configure a judge (only needed for Tier 3)

# free, local (llama3.2 is 2 GB and fits an 8 GB laptop; use llama3.1 with 16 GB+)
ollama pull llama3.2
export AIEXPECT_JUDGE=ollama:llama3.2

# or a cloud model
export ANTHROPIC_API_KEY=...                        # auto-detected; uses claude-opus-5 at low effort
export AIEXPECT_JUDGE=anthropic:claude-haiku-4-5    # cheaper
export AIEXPECT_JUDGE=openai:gpt-4o-mini
export AIEXPECT_JUDGE=openai-compatible:qwen2.5@http://localhost:8000/v1   # vLLM, LM Studio, Groq...

or in conftest.py:

import aiexpect
aiexpect.configure(judge="ollama:llama3.2", judge_threshold=0.7)

Flaky by nature? Measure it.

import aiexpect

@aiexpect.consistent(samples=5, min_pass_rate=0.8)
def test_greeting(bot):
    expect(bot.ask("hi")).to_have_tone("friendly")

Runs the body 5 times and passes on the pass-rate, not a single coin flip. Feeds the Consistency sub-score.

Semantic snapshots

def test_refund_policy(bot):
    expect(bot.ask("refund policy?")).to_match_snapshot()

First run stores the reply in __aisnapshots__/. Later runs compare by meaning, so rewording passes and a real change in what the bot says fails. Refresh with pytest --aiexpect-update-snapshots; forbid silent creation in CI with --aiexpect-snapshot-mode=strict.

Hallucination probe pack

from aiexpect import probes

@pytest.mark.parametrize("probe", probes.all(), ids=lambda p: p.id)
def test_hallucination_probe(bot, probe):
    probes.check(bot.ask(probe.question), probe)

22 curated questions that reliably expose fabrication: false premises ("Name the current King of France"), true-but-surprising premises ("Are sharks older than trees?") and plain facts. Keyword answer keys work offline; with a judge configured, paraphrased corrections are recognised too.

The report

pytest writes aiexpect-report.html (and .json) every run:

  • Trust Score (0–100) = mean of six plain-English sub-scores: Accuracy, Groundedness, Relevance, Safety, Consistency, Format
  • pass rate per assertion type, score distribution, per-test table, Trust Score trend across runs
  • every check with the judge's reason, expandable, filterable (failed only / LLM-judged)
  • single file, no CDN, light and dark mode, colour-blind-safe palette

CI gate: pytest --aiexpect-min-trust=80 fails the run when the Trust Score drops below 80.

CLI

aiexpect check "Return within 30 days" --contain "30 days" --no-pii --mean "30-day returns"
aiexpect report aiexpect-report.json -o report.html
aiexpect summary aiexpect-report.json --min-trust 80
aiexpect judge     # which judge would be used?

Soft mode

e = expect(reply, soft=True).to_contain("30 days").to_not_contain_pii().to_be_json()
e.verify()   # raises once with every failure listed

How it compares

aiexpect DeepEval promptfoo Ragas
Fits into an existing pytest suite ✅ one import ✅ pytest-style ❌ YAML runner ❌ notebook/eval loop
Works with no API key ✅ Tier 1 + 2 ❌ judge required for most metrics partial
Zero dependencies ❌ (Node)
Local Ollama judge
Flakiness as a measured pass-rate @consistent repeat option
Semantic snapshot testing
Plain-English Trust Score + HTML report ✅ single file cloud dashboard web viewer
Built for QA / test engineers ML engineers prompt engineers RAG researchers

They are good tools with different centres of gravity. If you already run evals in one of them, keep doing so; aiexpect is for the tests next to your product code.

Roadmap

  • TypeScript port with Jest/Vitest matchers, Playwright fixture, Cypress commands
  • GitHub Action with PR comment + badge
  • Judge agreement benchmark across more models (see docs/judges.md for the first result)
  • More probe packs (multi-turn contradiction, instruction following)

Contributing

git clone https://github.com/dmsehgal87/aiexpect && cd aiexpect
uv venv && uv pip install -e ".[dev]" && pytest

The test suite is fully offline (fake judge, lexical embeddings). Adapters for other frameworks and new probe packs are the easiest first contributions — see CONTRIBUTING.md.

MIT © Deep Sehgal

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aiexpect-0.2.2.tar.gz (284.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aiexpect-0.2.2-py3-none-any.whl (41.0 kB view details)

Uploaded Python 3

File details

Details for the file aiexpect-0.2.2.tar.gz.

File metadata

  • Download URL: aiexpect-0.2.2.tar.gz
  • Upload date:
  • Size: 284.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for aiexpect-0.2.2.tar.gz
Algorithm Hash digest
SHA256 026db7f03692452183a5df7851c86582cc1142a5e5f5465492c6595463804dae
MD5 e0b2ad06f6a3ff585a8aa82b7ea0e72b
BLAKE2b-256 89a07fbc720819f396a70eeec8fc62b922e38101f86738e66375e9eb590fbd6e

See more details on using hashes here.

Provenance

The following attestation bundles were made for aiexpect-0.2.2.tar.gz:

Publisher: publish.yml on dmsehgal87/aiexpect

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file aiexpect-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: aiexpect-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 41.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for aiexpect-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 18b0cb5e0065033e281015c2e02c54b8feaee34836dfda6756f5ab83e6720f6b
MD5 617268171a31f899943bb6d30371d8aa
BLAKE2b-256 7ee9a1b8c11e89ea2f8709e82889234065a5f43e2bd9e50953798cba6dae8e84

See more details on using hashes here.

Provenance

The following attestation bundles were made for aiexpect-0.2.2-py3-none-any.whl:

Publisher: publish.yml on dmsehgal87/aiexpect

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.3.1

2 files

0.3.0

2 files

This release

0.2.2 This release

2 files

0.2.1

2 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