{ "refundDays": 90 }
Structurally valid. Factually wrong when the supplied policy says 30 days.
× ships the bugDefine what your AI must do. Test it against real cases. Catch prompt, model, tool, and RAG regressions before release.
// One contract. A real output. Evidence you can act on.
const result = await evaluate(supportContract, {
input: { query: 'Can I return this?' },
context: 'Returns are accepted within 30 days.',
output: modelResponse,
});
expect(result.passed).toBe(true);
// failures include a code, path, explanation, and evidence
Ordinary tests can verify deterministic code. AI systems also need explicit checks for meaning, grounding, refusal behavior, and change over time.
{ "refundDays": 90 }
Structurally valid. Factually wrong when the supplied policy says 30 days.
× ships the bugFACT_CONTRADICTION
expected: 30 days
observed: 90 days
The contract fails with a stable code and the evidence behind it.
✓ blocks the regressionInitialize a working suite and agent instructions, then customize the generated requirements for output produced by any model or agent stack.
$ npm install llm-contract && npx llm-contract init
import { z } from 'zod';
import {
defineContract, evaluate, zodAdapter,
mustPreserveFacts, mustNotInvent,
} from 'llm-contract';
const supportContract = defineContract({
name: 'support-answer',
schema: zodAdapter(z.object({
answer: z.string(),
needsHuman: z.boolean(),
})),
invariants: [
mustPreserveFacts({ threshold: 1 }),
mustNotInvent({ mode: 'strict' }),
],
});
const result = await evaluate(supportContract, {
input: 'What is the return window?',
context: 'Returns are accepted within 30 days.',
output: modelResponse,
});
A contract combines normalization, structure, hard invariants, and weighted assertions under one reusable name.
Trim whitespace or unwrap code fences without silently rewriting meaning.
normalization
Validate structured output through Zod, Valibot, or the built-in JSON Schema subset.
schema
Invariants fail the contract when a non-negotiable behavior is broken.
invariants[]
Weighted assertions contribute to a score without obscuring deterministic failures.
assertions[]
Each stage answers a different question, so a passing parser cannot mask a broken business rule.
Conservative cleanup only.
raw → normalizedCan the expected representation be parsed?
PARSE_ERRORDoes the output satisfy the declared shape?
SCHEMA_VIOLATIONAre values, enums, ranges, and rules allowed?
NUMERIC_OUT_OF_BOUNDSAre identifiable claims supported by supplied context?
UNSUPPORTED_CLAIMDid the system clarify, refuse, and cover required topics?
UNCERTAINTY_VIOLATIONEvery failure can carry a stable code, exact path, human explanation, and supporting evidence for reports and CI.
SCHEMA_VIOLATIONwrong shape or field typeUNSUPPORTED_CLAIMclaim absent from supplied contextFACT_CONTRADICTIONoutput conflicts with contextREQUIRED_TOPIC_MISSINGmandatory concept omittedUNEXPECTED_REFUSALbenign request incorrectly refusedUNCERTAINTY_VIOLATIONambiguous input answered without clarificationRun a dataset through your generation function, compare it with historical outcomes, and repeat cases to expose nondeterminism.
Policies translate suite evidence into a deterministic exit code for any CI provider.
import {
runSuite, evaluatePolicy, standardCIPolicy,
} from 'llm-contract';
const suite = await runSuite(
'support-v2', cases, generate,
{ runsPerCase: 3, concurrency: 4 },
);
const policy = evaluatePolicy(suite, standardCIPolicy);
process.exit(policy.exitCode);
$ npx llm-contract run cases.json --contract ./contract.js --runs 3
Machine-readable automation and stored baselines.
Review-friendly summaries for pull requests.
Portable reports with readable failure detail.
Fast local output with policy status.
--contract performs a non-empty-output smoke check. Supply a contract module for behavioral validation.The core stays provider-agnostic. Optional peers unlock familiar validation, while generation remains an ordinary callback.
llm-contract/adapters/zodfull Zod validationllm-contract/adapters/valibotValibot safe parsingllm-contract/adapters/json-schemabuilt-in deterministic subsetdefineContract(...)business-specific checksworks around OpenAI · Anthropic · Gemini · Ollama · LangChain · custom agents through your callback
Grounding checks compare output against context you supply. They do not verify every fact in the world.
Repeated runs measure and expose flakiness; they cannot make probabilistic models deterministic.
The built-in adapter intentionally covers a deterministic subset. Use Zod, Valibot, or custom logic for more.
Probabilistic judge signals can be added separately without overwriting hard failures.
It turns expected AI behavior into reusable checks, runs those checks across datasets, compares results with a baseline, measures stability, and produces evidence CI can enforce.
No. Evaluate any output string directly or pass your own generation function to a suite. This keeps credentials, provider SDKs, tracing, and retry behavior under your control.
Yes. Contracts can compare claims against supplied retrieval context and check behaviors such as required topics, clarification, refusal, citations, forbidden phrases, or custom business rules.
Version 0.9.0 includes a typed API, CLI, suite runner, policies, reporters, examples, and CI across Node 18, 20, 22, and 24. As a pre-1.0 release, review version changes before upgrading.
Open source. MIT licensed. Install from npm and run your first real case.