tea - testing and evaluation for agents
Test and evaluation tool for agentic workflows. Run an agent against a task, assert what it did and measure how it performed.
Getting started
Requirements
- Python 3.11+
- Docker
Install
pip install teaval
To install from source instead:
pip install -e .
Or with uv:
uv sync
Write a test
from tea import (
Agents,
check_diff_added,
check_file_changed,
check_run_score,
check_skill_used,
iterations,
)
@iterations(3, max_failures=1)
def test_add_minus_operation():
run = (
Agents.ClaudeCode.prompt("Add 'minus' operation support to the calculator")
.workdir("examples/demo_calculator")
.run()
)
check_diff_added(run, "calculator.py", "def minus")
check_diff_added(run, "test_calculator.py", "def test_minus")
check_file_changed(run, "README.md")
check_skill_used(run, "add-new-operation")
check_run_score(
run,
(
"Does this diff correctly implement a subtraction operation "
"without breaking the existing structure? "
"Score 1.0 if the implementation is clean and complete, "
"0.5 if it works but has style issues, "
"0.0 if it is broken or missing."
),
min_score=0.7,
)
Run tests
# Run all tests in a folder
tea examples/demo_calculator/agent-tests
# Run one file, repeated 5 times to measure pass rate
tea examples/demo_calculator/agent-tests/test_add_new_operation.py -n 5
# Run a single test function
tea examples/demo_calculator/agent-tests/test_add_new_operation.py::test_add_minus_operation
See examples/demo_calculator/agent-tests for runnable examples.
Metrics baselines
Export per-test metrics (average token count, step count, duration, and success
rate) to JSON, then compare later runs against it to catch regressions.
--baseline is warn-only — it flags any metric that worsened by more than 10%
but never changes the exit code.
tea examples/demo_calculator/agent-tests -n 3 --summary-export baseline.json
tea examples/demo_calculator/agent-tests -n 3 --baseline baseline.json
Checkers
Checkers are the assertions of a tea test: each one inspects an agent run, records a pass or a fail, and returns its result so you can build on it.
| Checker | Checks that | Returns |
|---|---|---|
check(label, condition) |
condition is truthy |
bool |
check_skill_used(run, skill) |
the agent used skill |
bool |
check_skill_not_used(run, skill) |
the agent did not use skill |
bool |
check_tool_used(run, tool) |
the agent called tool |
bool |
check_tool_not_used(run, tool) |
the agent did not call tool |
bool |
check_file_changed(run, file) |
file was created or modified |
bool |
check_file_not_changed(run, file) |
file was left untouched |
bool |
check_diff_added(run, file, code) |
code appears in file's added lines |
bool |
check_diff_removed(run, file, code) |
code appears in file's removed lines |
bool |
check_output_score(output_to_evaluate, judge_prompt, min_score=0.5, judge=None) |
the score assigned to the output using an LLM-as-a-Judge is higher than min_score |
the score, as float |
check_run_score(run, judge_prompt, min_score=0.5, judge=None) |
the score assigned to the agent run using an LLM-as-a-Judge is higher than min_score |
the score, as float |
Agents
ClaudeCode
Agents.ClaudeCode
.prompt("Add 'minus' operation support to the calculator")
.workdir("examples/demo_calculator")
.run()
Setup ANTHROPIC_API_KEY (API-key auth, recommended). A long-term token
(CLAUDE_CODE_OAUTH_TOKEN, generated with claude setup-token) is also
supported.
To pick a model, pass its identifier to with_model(); the default is
claude-haiku-4-5-20251001:
Agents.ClaudeCode.with_model("claude-opus-4-7").prompt("...").run()
Pi
Pi requires a PiConfig:
from tea.agent import PiConfig
Agents.Pi.with_model(PiConfig(
model="ollama/qwen2.5-coder:1.5b", # required
api_key_env="MY_API_KEY", # host env var forwarded into container
base_url="http://host.docker.internal:11434/v1", # OpenAI-compatible endpoint
))
| Field | Required | Purpose |
|---|---|---|
model |
Yes | Model identifier |
api_key_env |
No | Name of a host env var to forward into the container as the API key |
base_url |
No | OpenAI-compatible endpoint for custom or local providers |
base_url is used verbatim inside the container, so a provider running on
your machine must be addressed as http://host.docker.internal:<port> — not
localhost, which resolves to the container itself. Setting base_url makes
tea declare host_network_access, which is what makes that name resolve; the
address is still yours to write.
Skills
Tea discovers project skills automatically from conventional, in-workdir paths and copies them into the agent's native skill directory in the container.
| Agent | Default sources (searched in order) |
|---|---|
| ClaudeCode | .claude/skills, .agents/skills |
| Pi | .pi/skills, .agents/skills |
.agents/skills is the cross-agent convention — put a skill there and every
agent picks it up. Missing default paths are silently skipped. If the same
skill name appears in multiple sources, the earlier (agent-specific) source
wins.
Override the defaults with skills_sources:
# One or more custom source paths (relative to the run's workdir).
Agents(skills_sources=["eval-fixtures/skills"])
# Per-run override.
Agents.ClaudeCode.prompt("...").workdir(".").skills_from(["custom/skills"]).run()
# Explicit opt-out: load no skills at all.
Agents(skills_sources=[])
Each skill is a directory containing a SKILL.md file with frontmatter:
---
name: add-new-operation
description: How to add a new arithmetic operation to the calculator.
---
Step-by-step instructions live in the body of the file.
Development
uv sync --group dev
uv run pre-commit install
Run checks manually:
uv run pre-commit run --all-files
uv run pytest tests/unit -q
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 teaval-0.0.1.tar.gz.
File metadata
- Download URL: teaval-0.0.1.tar.gz
- Upload date:
- Size: 38.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5b7fc7e3eda8052900c13f82a776e65b4dcfa1ac74d6e8e8536358d43b4afb7
|
|
| MD5 |
23f5e32e6511daeb65ad0e254820fa47
|
|
| BLAKE2b-256 |
173f6c9e6cec4c325783dda1522608ca647de80271e7db5a8950ff0bd1da9b30
|
Provenance
The following attestation bundles were made for teaval-0.0.1.tar.gz:
Publisher:
publish.yml on rsn491/tea
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
teaval-0.0.1.tar.gz -
Subject digest:
a5b7fc7e3eda8052900c13f82a776e65b4dcfa1ac74d6e8e8536358d43b4afb7 - Sigstore transparency entry: 2336682070
- Sigstore integration time:
-
Permalink:
rsn491/tea@e215e76a4c5ede8546ca168f27a355484c4bbc6f -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/rsn491
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e215e76a4c5ede8546ca168f27a355484c4bbc6f -
Trigger Event:
push
-
Statement type:
File details
Details for the file teaval-0.0.1-py3-none-any.whl.
File metadata
- Download URL: teaval-0.0.1-py3-none-any.whl
- Upload date:
- Size: 48.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
152ab15994139059c67df3f6d6dafb5f7363bd4c23e2a2b218e0d0d9d8a75bab
|
|
| MD5 |
594ae724f90dca28b14ac0de4e543b35
|
|
| BLAKE2b-256 |
8909faeb8fa9aa553bd0290d3375c8ace28b6e99a083c1ef528922c1f4095ac8
|
Provenance
The following attestation bundles were made for teaval-0.0.1-py3-none-any.whl:
Publisher:
publish.yml on rsn491/tea
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
teaval-0.0.1-py3-none-any.whl -
Subject digest:
152ab15994139059c67df3f6d6dafb5f7363bd4c23e2a2b218e0d0d9d8a75bab - Sigstore transparency entry: 2336682106
- Sigstore integration time:
-
Permalink:
rsn491/tea@e215e76a4c5ede8546ca168f27a355484c4bbc6f -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/rsn491
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e215e76a4c5ede8546ca168f27a355484c4bbc6f -
Trigger Event:
push
-
Statement type: