Skip to main content

Offline turn-taking analysis and regression evidence for dual-channel voice-agent recordings, MIT.

Project description

hotato: find where your voice agent talks over callers, and pin the fix to a CI-gated contract

hotato

The open-source flight recorder for production voice agents.

Find where your voice agent talks over callers, and pin the failure to a portable contract with audio, timing, traces, trust checks, human labels, and CI gates. Recapture the call against your changed agent, and the same contract shows whether the fresh recapture passes it without regressing the paired hold guard. MIT.

PyPI version PyPI monthly downloads License: MIT Python 3.9 to 3.13 offline: yes runtime deps: zero tests

Start here (no account, no keys, no network)

uvx hotato start --demo

That sweeps two bundled recorded calls a provider's default agent failed, writes the dashboard, and turns one real missed-interruption candidate into a demo failure contract it immediately verifies:

[start] demo: swept 2 bundled calls, 5 candidate moments;
        wrote hotato-sweep.json, hotato-sweep.html, hotato-no-single-threshold.svg
        wrote contracts/demo-missed-interruption.hotato; verified contract: FAIL as expected

Open hotato-sweep.html for the ranked candidate moments with a hear-the-bug playhead. One of them is the failure below: the agent missed a real interruption in one call and false-stopped on a backchannel in another, so no single sensitivity dial fixes both. That is the card start --demo renders for you:

Hotato threshold-funnel card: no single threshold can fix this. Missed a real interruption; false-stopped on a backchannel. One sensitivity dial cannot satisfy both axes at once.

A real failure became a candidate, became a portable .hotato contract, and contract verify catches it. Hotato prints the exact next commands to promote it into a permanent fixture, run it in CI, and re-verify.

Choose your path

You want to Run this
Try the full loop, no credentials uvx hotato start --demo
Sweep the bundled demo calls uvx hotato sweep --demo
Sweep recent calls from a real stack hotato connect vapi then hotato sweep --stack vapi --since 7d
Add Hotato to an existing repo, CI gate included hotato init starter --stack vapi --out . (docs/STARTER.md)
Turn a confirmed failure into a portable contract hotato contract create --from-candidate hotato-sweep.json#1 --expect yield --id refund-cutoff-001 --out contracts (docs/CONTRACTS.md)
Verify contracts in CI hotato contract verify contracts/ --junit contracts-junit.xml
Attach observability traces to a contract hotato trace attach contracts/refund-cutoff-001.hotato --trace voice_trace.jsonl (docs/TRACE.md)
Test a candidate fix, before/after, fail-closed hotato fix trial patch.json --name staging-x --before before/ --after after/ (docs/FIX-TRIAL.md)
Share a finding in a PR or slide hotato card hotato-sweep.json#1 --out finding.svg
Drive it from a coding agent uvx --from "hotato[mcp]" hotato-mcp (one tool, voice_eval_run; configs in docs/MCP.md)

Every command above takes a two-channel recording (caller on one channel, agent on the other). A mono file or a bad export is marked NOT SCORABLE, never turned into a confident but meaningless verdict.

contract verify and a promoted fixture in CI are two different guarantees, depending on which recording goes in:

On the frozen recording (every push) On a fresh recapture (by hand, see docs/RECAPTURE.md)
Proves The evidence, policy, and scorer are intact The CURRENT agent's behavior still matches the label
Does not prove That the deployed agent hasn't changed --

A contract bundle contains call audio. Do not commit a raw customer contract to a public repository; use sanitized fixtures for anything public. See docs/CONTRACTS.md.

The loop

Catch it, confirm it, gate it, then prove it holds:

  1. Sweep surfaces candidate talk-over and false-stop moments across your recent calls, ranked by how far the timing missed.
  2. You label one (yield = stop for the caller, hold = keep talking through a backchannel) and fixture promote saves it as a permanent regression test.
  3. CI runs that fixture on every change and exits non-zero if the recorded evidence stops matching your policy -- a change to the evidence, thresholds, or scorer is caught on every push. Catching the AGENT itself regressing needs a fresh recapture through the same fixture: see docs/RECAPTURE.md.

Hotato measures whether the agent stopped talking when the caller started, how many seconds that took, and how many seconds both were talking at once. It reports what it measured, never a guess at intent.

Connect a production stack

The demo needs nothing. To point Hotato at real calls, connect once, then sweep on a schedule:

hotato connect vapi                                             # credentials stored 0600, local only
hotato sweep --stack vapi --since 7d --out hotato-sweep.html    # cron, CI, wherever

Run sweep on a timer and it becomes a scheduled batch scanner. Your audio stays on your machine unless you explicitly pull it from your stack. Full guide: docs/SET-AND-FORGET.md · runnable examples/set-and-forget/.

What Hotato is not

  • Not a full QA platform. It does not grade the whole conversation, task success, or content. See docs/COMPARE.md for where Hamming, Cekura, Coval, Bluejay, Roark, Vapi, and Retell fit instead.
  • Not transcript scoring. It measures audio timing, not what was said.
  • Not speaker ID. Channels are anonymous; nothing identifies who a person is.
  • Not semantic intent detection. It produces candidate timing evidence. Humans label intent. CI enforces confirmed contracts.
  • Not a hand on production config. It never sits in the live audio path and never changes a running agent.

Install

uvx hotato runs any command with zero install. To add it to a project:

pip install hotato                 # core: stdlib-only, zero dependencies
pip install 'hotato[neural]'       # optional Silero VAD cross-check
pip install 'hotato[livekit]'      # LiveKit live capture
pip install 'hotato[pipecat]'      # Pipecat live capture

Depth

Why "hotato": good turn-taking is a game of hot potato. Speak, then pass the turn the moment the caller wants it. MIT licensed (LICENSE); the open core stays open.

mcp-name: io.github.attenlabs/hotato

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hotato-0.10.0.tar.gz (7.6 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hotato-0.10.0-py3-none-any.whl (3.1 MB view details)

Uploaded Python 3

File details

Details for the file hotato-0.10.0.tar.gz.

File metadata

  • Download URL: hotato-0.10.0.tar.gz
  • Upload date:
  • Size: 7.6 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for hotato-0.10.0.tar.gz
Algorithm Hash digest
SHA256 39c99c371ec0b0a98f2ed0492d0b6d7f638d4f495ace02f70095924d88272206
MD5 d878a7ce7db55ea6493a91c18a37695b
BLAKE2b-256 258da1cdb7214a0b253532aa386816bfed0a5c9d1a4f7ee0fcd86d3a675b2005

See more details on using hashes here.

File details

Details for the file hotato-0.10.0-py3-none-any.whl.

File metadata

  • Download URL: hotato-0.10.0-py3-none-any.whl
  • Upload date:
  • Size: 3.1 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for hotato-0.10.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1f96847791304b33dd6ffaeafe5cb8d6fd58858b6333102e9dfe52cacfa7ef1b
MD5 533286682eae9c5e67899acb271183c0
BLAKE2b-256 edb3eb77d3f4e42c2f9b24e26a600e20cff820fe3b692828a48e19bfa41412d9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page