experiments

LLM-Expect: Testing LLM Functions in Python

Why I built LLM-Expect, how to run a function against JSONL test cases, and how to choose checks that match the behavior you need.

Filed
Read
3 min
Author
Ref
DOC-A2D5
Revised
LLM-Expect: Testing LLM Functions in Python

I built LLM-Expect because I kept seeing teams recreate the same evaluation setup: a notebook, a dataset, a few scoring functions, and manual checks before a release.

I wanted a small Python SDK that could run those checks inside the codebase. Give it a function and a JSONL dataset, then call run_eval().

Why I built it

An LLM feature needs repeatable checks when a prompt, model, or integration changes. Printing a few answers is useful during development, but it makes regressions easy to miss.

A dedicated evaluation platform can be worthwhile. For a small function, I wanted less setup and results I could inspect alongside the code.

What it covers

LLM-Expect runs a Python function against test cases and scores the outputs. The test runner and reports are local; your function or an optional model-based judge may still call an external provider.

It does not build the agent, supply a representative dataset, or decide what quality means for your product. Those remain your responsibility.

Turn expectations into checks

Some outputs need an exact match. Others need valid JSON, required fields, particular content, or a judgment against a rubric. Choose the check that matches the requirement.

A schema check can confirm that an answer has the required structure. It cannot establish that the answer is true. A model-based judge needs its own calibration against examples you have reviewed.

A small example

Install the package:

pip install llm-expect

Save two cases in tests.jsonl:

{"id": "capital", "input": "Capital of France", "expected": {"reference": "Paris"}}
{"id": "addition", "input": "2+2", "expected": {"reference": "4"}}

This deliberately simple function lets you try the test runner without an API key:

from llm_expect import llm_expect

@llm_expect(dataset="tests.jsonl")
def answer(prompt: str) -> str:
    responses = {"Capital of France": "Paris", "2+2": "4"}
    return responses.get(prompt, "Unknown")

if __name__ == "__main__":
    results = answer.run_eval()
    if not results["passed"]:
        raise SystemExit(1)

You can download the exact Python script, test cases, and pinned dependency. Save them in one folder, install with pip install -r requirements.txt, and run python run_eval.py.

I checked this example with LLM-Expect 0.1.9: both cases pass. Changing the answer for Paris to an incorrect value makes one case fail and the script exit with status 1. This checks the local runner and failure signal; it is not a measurement of any model’s accuracy.

Replace the function body with your model call when you are ready. Keep the test inputs compatible with the function’s arguments and the expected values compatible with its return type.

How it works

The decorator associates the function with the dataset. run_eval() loads the cases, calls the function, selects checks from the expectations, and saves per-case results and a summary.

Choosing the checks

The expected keys describe what to test:

  • reference supplies an expected answer for an exact-match check.
  • contains requires specified words or phrases.
  • regex checks a pattern.
  • schema supplies a JSON Schema.
  • judge supplies a rubric for a configured model-based judge.

For example, a schema expectation can require a name and an integer age:

{"expected":{"schema":{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"integer"}},"required":["name","age"]}}}

Why JSONL

One case per line makes the dataset easy to review in Git. A change to an expectation is visible in the same review as a change to the prompt or function.

Build a useful dataset

Start with ordinary requests, then add examples of failures you have encountered. Include malformed input, ambiguous requests, and cases the function should decline if those are relevant to the feature.

Review individual failures before accepting an overall score. Passing a small dataset establishes performance on those cases, not general reliability.

Use the result before shipping

The example exits with a failure status when the configured checks fail, so it can run in CI. For tests that call a model, account for API cost, variability, and credentials separately from tests that run entirely locally.

Keep the setup small

The aim remains a short path from a product requirement to a repeatable check. Add more infrastructure when it solves an evaluation problem you actually have.

For evaluation that follows actions across a tool catalog, see how I evaluated an MCP server. Correct output is only one part of an agent’s behavior.

Code and documentation

See the LLM-Expect repository for the current API, examples, and supported checks, or read the documentation.