PromptDrift
CI regression testing for LLM prompts.
Catch AI behavior regressions before they reach production.
PromptDrift turns the behavior you need from a prompt into explicit, reviewable contracts. When a prompt changes, it runs those contracts in CI and tells you exactly what broke — without treating every wording change as a regression.
prompt change → PromptDrift → behavioral contracts → PR check
↳ PASS · WARN · FAIL
Why PromptDrift?
git diff tells you what changed in the prompt text. PromptDrift tells you whether the AI behavior changed in a way that matters.
| Problem | PromptDrift's answer |
|---|---|
| "We changed a prompt and broke 3 customer flows" | Behavioral contracts catch regressions before merge |
| "Every LLM output is different — how do I test that?" | Test structure and constraints, not exact wording |
| "Our prompt tests are flaky because they diff raw output" | Contracts pass/fail deterministically on semantics |
| "We have no idea if a prompt change is safe to deploy" | CI gives a clear PASS/FAIL on every PR |
Quick Start
pip install promptdrift-ci
promptdrift init
promptdrift test
init creates a fully local example using the deterministic mock provider. No API key needed — validate the workflow first, then connect OpenAI or Ollama.
Capture approved behavior:
promptdrift baseline
git add promptdrift.baseline.json
Define Behavioral Contracts
promptdrift.yaml stays small, readable, and version-controlled:
version: 1
provider:
type: openai
model: gpt-4.1-mini
api_key_env: OPENAI_API_KEY
tests:
- id: refund_request
prompt: prompts/support.txt
variables:
policy: Refunds are available within 30 days.
question: Can I get a refund for my order?
assertions:
- type: contains
value: 30 days
- type: not_contains
value: guaranteed
- type: max_length
value: 600
❌ FAIL refund_request
Assertion: contains
Expected: 30 days
Actual: You may be eligible for a refund.
Reason: Required text does not appear in the output.
Contracts, Not Cosmetic Diffs
| PromptDrift fails when… | PromptDrift does not fail just because… |
|---|---|
| Required text disappears | Wording changes |
| Forbidden content appears | Sentence order changes |
| JSON is invalid or violates its schema | An otherwise-valid answer is phrased differently |
| A length, token, latency, or cost limit is exceeded | Output hashes differ but contracts still pass |
Built-in Assertions
exact_match · contains · not_contains · regex · not_regex · json_valid · json_schema · min_length · max_length · max_tokens · latency_ms · cost_usd
Each assertion supports severity: warn for non-blocking warnings.
Run in CI
name: PromptDrift
on:
pull_request:
paths:
- "prompts/**"
- "promptdrift.yaml"
permissions:
contents: read
jobs:
promptdrift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: tanveer-arch/promptdrift/action@v1
with:
config: promptdrift.yaml
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
The Action writes a Step Summary, uploads a JSON report, and fails only after reports are available. For PR comments, add comment: 'true' and grant pull-requests: write.
Security: Use
pull_requestfor untrusted forks. Never expose provider secrets withpull_request_targetwhile executing code from a pull request.
Commands
| Command | What it does |
|---|---|
promptdrift init |
Creates an offline starter suite |
promptdrift test |
Runs contracts; auto-compares against baseline |
promptdrift baseline |
Captures approved behavior (requires --force to replace) |
promptdrift diff |
Runs and compares current output with baseline |
promptdrift report |
Generates an offline HTML report |
promptdrift doctor |
Checks config, files, keys, and baseline health |
promptdrift version |
Prints the installed version |
All commands support --json for machine-readable output. Exit codes: 0 success, 1 behavioral failure, 2 config/usage error, 3 provider/runtime error.
Architecture
promptdrift.yaml ─→ Config ─→ Template ─→ Provider ─→ Evaluator ─→ Report
Loader Renderer Adapter Engine Formatter
│
Baseline ←── Git
Comparison
- Providers are isolated adapters (OpenAI, Ollama, Mock) — adding one is a single file
- Assertions are pure, deterministic functions — same input always produces the same result
- Baselines store hashes and metrics, never raw outputs or secrets
- Reports output to terminal, JSON, HTML, or GitHub PR comments
Privacy by Default
- No telemetry, accounts, or hosted service
- Prompt content goes only to the provider you configure
- API keys are environment variables — never config values
- Baselines contain hashes and metrics, not raw outputs
- Local SQLite history suppresses prompts and outputs by default
Documentation
| Guide | Description |
|---|---|
| Getting Started | Install, first run, and connecting a provider |
| Configuration | Full promptdrift.yaml reference |
| Assertions | All contract types with examples |
| Providers | OpenAI, Ollama, and Mock setup |
| Baselines | Create, compare, and update baselines |
| GitHub Action | CI setup, secrets, and fork security |
| Architecture | Module design and data flow |
| FAQ | Common questions and troubleshooting |
| Contributing | Dev setup, testing, and PR process |
Roadmap
- Semantic similarity assertions (embedding-based)
- LLM-as-judge evaluator
- Multi-provider comparison (same prompt, different models)
- Parallel test execution
- PyPI trusted publisher release
- Cost tracking dashboard
License
Released under the MIT License.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file promptdrift_ci-0.1.0.tar.gz.
File metadata
- Download URL: promptdrift_ci-0.1.0.tar.gz
- Upload date:
- Size: 43.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d4a53d7f4a8e4f7ce9c4a1158576f9639ebcbccea7bc1d5fa982c9c9161571fb
|
|
| MD5 |
540a67ca7e1fae6efb93875bc179dfb9
|
|
| BLAKE2b-256 |
010043e5aa6a7c50297c5083cfa122469b327463f8a122d72e8eabfb6f6df6a7
|
File details
Details for the file promptdrift_ci-0.1.0-py3-none-any.whl.
File metadata
- Download URL: promptdrift_ci-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4d3acf805a8ef8fedbe5268d00f0ad9ae8d908d1845aef1585648219f359643d
|
|
| MD5 |
70380f7f12179343df75f95a3eb5ec5a
|
|
| BLAKE2b-256 |
5a55e3be2aa68565a05ee277c48964352d2023bdd873e3fc1ef238bb9d23ae3b
|