Local-first testing and observability for AI agents: trace, evaluate, test, and gate your LLM and voice agents on your own machine. Everything you use a hosted platform for, free and MIT at any scale, byte-reproducible, and CI-native. Nothing leaves your machine.
Project description
hotato
Local-first testing and observability for AI agents.
Your evals are green. Your agent still ships bugs they can't see: talk-over, dead air, a tool it swore it ran. hotato catches them on your machine and gates CI so they stay fixed.
Free, open-source, and deterministic: the same call scores the same way every run, so you can gate a build on it. Your traces and prompts never leave your machine, and there's no per-seat or per-event bill as you scale.
Your first catch in seconds. One command, no account:
$ uvx hotato start --demo
Conversation failed: Agent did not yield; measured talk-over was 2.66 s.
talk-over 2.66s the agent kept talking while the caller held the floor
That transcript passed every text eval. The timing did not. hotato pins the catch as a CI contract that reproduces byte for byte, so a fixed bug stays fixed. It measures timing and say-do, not intent.
The loop
Every production failure becomes a portable test, every candidate runs against it, and every release carries evidence. Five steps, one command each, nothing leaves your machine.
| Observe | traces, tokens, cost, and latency, from the OTel spans you already emit | hotato observe report traces/ |
| Catch | deterministic scoring finds what text evals miss: timing, say-do, policy | hotato investigate call.wav |
| Pin | your label turns the catch into a portable, content-addressed contract | hotato investigate label STATE#1 --expect yield |
| Test | simulate, stress, and drive candidates against everything you pinned | hotato gauntlet |
| Prove | every evidence lane composed into one fail-closed release proof, in CI | hotato prove --contracts contracts/ |
Deterministic. Byte-reproducible. Free, MIT. Agent-native over MCP. Every verdict carries its evidence across five dimensions (outcome, policy, conversation, speech, reliability), and production feeds the next loop: hotato production export-regression turns a live session back into a test.
The whole loop, command by command: docs/LIFECYCLE.md.
Why it is different
Same loop a hosted platform runs. Three things it cannot offer.
| hotato | Hosted platforms | |
|---|---|---|
| Observe, catch, pin, test, prove | yes | yes |
| Price at scale | free, MIT, any volume | metered per seat and per event |
| Verdicts | byte-for-byte reproducible, gate a build | vary run to run |
| Your traces and prompts | stay on your machine | live on their servers |
| Runs in CI, offline | yes | needs their service |
Full comparison: docs/COMPARE.md
From a bad call to a release proof
One recording in. The pinned failure becomes a gate that stays red until the agent stops failing that call, and every gate you have composes into one receipt:
$ hotato investigate ./call.wav
most likely failure: [1] the agent talked over the caller for 2.66s
next: hotato investigate label '.hotato/investigate-state.json#1' --expect yield
$ hotato investigate label '.hotato/investigate-state.json#1' --expect yield
created hotato contract: call-8s-yield
$ hotato prove --contracts contracts/
hotato prove: proof -- overall FAIL (exit 1)
lane verdict counts
contracts fail contracts=1 passed=0 failed=1 tampered=0 refused=0
content_id: sha256:2959c905ee6c1030...
proof: .hotato/proofs/proof/proof.json
A contract re-measures the captured failure under the pinned policy on every CI run, the same discipline a snapshot test gives you. hotato prove composes every lane you have (contracts, suites, before/after batteries, the stress suite) into one fail-closed verdict with a content-addressed receipt: pass only when every lane passed, and "could not tell" is never green.
Quickstart
# 1. catch a failure on two bundled calls (no account; exits 0)
uvx hotato start --demo
# 2. score your own recording (or a transcript: --transcript t.json)
hotato investigate ./call.wav
# 3. pin the caught moment as a regression contract
hotato investigate label '.hotato/investigate-state.json#1' --expect yield
# 4. compose every gate into one release proof, on every pull request
hotato prove --contracts contracts/
Keep it with pipx install hotato, drive it over MCP with uvx --from "hotato[mcp]" hotato-mcp, or walk the path in docs/GETTING-STARTED.md.
Wire it into CI
The step's exit code is the verdict: 0 pass, 1 fail, 2 refuse.
# .github/workflows/voice-qa.yml
on: [pull_request]
jobs:
hotato:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: attenlabs/hotato@v1.15.0
with:
contracts: contracts/
hotato-version: 1.15.0
Copy-paste workflow with a commit-SHA pin: docs/CI.md.
Feed it what you already have
Every onramp feeds the same offline scoring and the same 0 / 1 / 2 exit contract.
hotato pull --stack vapi --limit 10 # your stack's recorded calls
hotato trace ingest --otel traces.jsonl # the OTel spans you already log
hotato simulate demo.scenario.json --out ./sim # scripted fixtures, no production audio
Details: docs/CONNECT.md · docs/TRACE.md · docs/SIMULATE.md
Point your agent at it
Point Claude Code, Cursor, or any coding agent at this repo: it reads AGENTS.md and runs the loop end to end, offline, no key. The MCP server exposes the scorer plus read/verify/propose tools over local stdio (docs/MCP.md).
Nothing leaves your machine
hotato runs offline, on the machine that invokes it. The core is stdlib-only Python: no account, no key, no network call of its own. Your traces, prompts, and audio stay local, and the local-judge lane is opt-in and quality-gated, separate from the deterministic core.
Specifications
| Property | Value |
|---|---|
| Footprint | ~10 MiB installed, 0 runtime dependencies (stdlib-only) |
| Reproducibility | byte-for-byte, content-addressed contract |
| Exit contract | 0 pass · 1 fail · 2 refuse |
| Release integrity | OIDC Trusted Publishing + build-provenance attested |
| Runtime | offline, off the production data path |
Verify the measurement yourself
PYTHONPATH=src python3 -m hotato.benchmark \
--scenarios corpus/real/scenarios --audio corpus/real/audio
On 13 recorded AMI Meeting Corpus clips, the median error between measured caller-onset and the human word-alignment label is 20 ms. Provenance: corpus/real/README.md · method: METHODOLOGY.md.
Timing is measurable only when the two voices arrive on separate channels; a mono or mixed export is marked NOT SCORABLE and refused (hotato trust --stereo call.wav).
Contribute
Issues and PRs welcome: CONTRIBUTING.md · SECURITY.md · CHANGELOG · docs/
License
MIT (LICENSE)
mcp-name: io.github.attenlabs/hotato
Project details
Release history Release notifications | RSS feed
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 hotato-1.15.0.tar.gz.
File metadata
- Download URL: hotato-1.15.0.tar.gz
- Upload date:
- Size: 12.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
beda2e5253cc923e5d30b35eeeb0ff2d698e419e7bc5648f46ea88e4ce1bf240
|
|
| MD5 |
1a548390e8a6565239fccf5edf5af0f1
|
|
| BLAKE2b-256 |
0bfcddbb6986ca2c5793c6cad019db9ede4c37aa55b2380bad69603de90ff244
|
Provenance
The following attestation bundles were made for hotato-1.15.0.tar.gz:
Publisher:
publish-pypi-oidc.yml on attenlabs/hotato
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hotato-1.15.0.tar.gz -
Subject digest:
beda2e5253cc923e5d30b35eeeb0ff2d698e419e7bc5648f46ea88e4ce1bf240 - Sigstore transparency entry: 2218407323
- Sigstore integration time:
-
Permalink:
attenlabs/hotato@2a0443b45d075e944ea7877b7623925d8506fb53 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/attenlabs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi-oidc.yml@2a0443b45d075e944ea7877b7623925d8506fb53 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file hotato-1.15.0-py3-none-any.whl.
File metadata
- Download URL: hotato-1.15.0-py3-none-any.whl
- Upload date:
- Size: 6.0 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0cb0a6c52708e01d15b00eb78a366a8f2ef58627915136749118bb25e95d6e7c
|
|
| MD5 |
71dd212d3f9aa7e9aae80234145f87a2
|
|
| BLAKE2b-256 |
a76d3dbe7fc7a2fd25eb1ab14d40acf13ced86cef426e84eff96488c41bf2ced
|
Provenance
The following attestation bundles were made for hotato-1.15.0-py3-none-any.whl:
Publisher:
publish-pypi-oidc.yml on attenlabs/hotato
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hotato-1.15.0-py3-none-any.whl -
Subject digest:
0cb0a6c52708e01d15b00eb78a366a8f2ef58627915136749118bb25e95d6e7c - Sigstore transparency entry: 2218407340
- Sigstore integration time:
-
Permalink:
attenlabs/hotato@2a0443b45d075e944ea7877b7623925d8506fb53 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/attenlabs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi-oidc.yml@2a0443b45d075e944ea7877b7623925d8506fb53 -
Trigger Event:
workflow_dispatch
-
Statement type: