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.

Catch where it talks over callers, pin the failure to a portable contract, and re-check it in CI.
Offline by default · MIT · zero runtime dependencies.

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

Quickstart -- no account, no keys, no network

uvx hotato start --demo

It sweeps two bundled recorded calls a provider's default agent failed, writes the candidate dashboard, and turns one 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. This is the actual output -- the ranked candidate moments, each with a hear-the-bug playhead:

The hotato candidate dashboard: 5 candidate moments ranked by salience across 2 calls, each card showing the caller and agent timeline, an embedded hear-the-bug audio playhead, and promote-as-yield / promote-as-hold / ignore buttons.

hotato-sweep.html · candidate moments ranked by salience, each with a playhead that sweeps the timeline in sync with the embedded audio. Candidates you review and label, never a decided verdict.

start --demo promoted one of these candidates into a portable .hotato contract and ran contract verify on it (FAIL, as expected). It then prints the exact next commands to save the candidate as a permanent fixture, gate it in CI, and re-check it.

Point it at your own recording to walk the same loop end to end:

hotato start --stereo my-call.wav          # trust -> scan -> review -> label -> contract

Every command 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.

How the loop works

Catch a moment, confirm what it should have done, gate it in CI, then re-check today's agent.

1. Catch -- surface the candidate moments

sweep ranks the talk-over and false-stop moments across your recent calls by how far the timing missed. Two candidate types, straight from the open scorer, no accuracy score:

Level 1 candidate card: 0.32s of overlap while the agent was talking, at t=2s in the recording. Hotato reports timing candidates, not intent. Level 1 candidate card: 0.46s of silence after the agent stopped with no caller nearby, at t=1.28s in the recording. Hotato reports timing candidates, not intent.
Level 1 -- candidate. Hotato reports what it measured (0.32s of overlap; 0.46s of trailing silence), never a guess at intent. Each is a timing moment worth review, not a bug yet.

2. Confirm -- you label yield or hold

You decide the expected behavior for a candidate: yield (stop for the caller) or hold (keep talking through a backchannel). No single sensitivity dial decides it for you -- a threshold that stops missing interruptions starts false-stopping on backchannels, so when both fail in one run diagnose refuses to name one threshold:

No single threshold can fix this: one sensitivity dial cannot both avoid missing a real interruption and avoid false-stopping on a backchannel. Hotato refused threshold tuning; fix class engagement-control.

Level 2 -- human-labeled failure. A reviewer confirms the recording broke an explicit yield-or-hold expectation. When a missed interruption and a false stop collide, the fix is the engagement-control class, not one dial.

3. Gate -- pin it to a CI contract

fixture promote saves your labeled call as a permanent regression test. On every push, CI re-scores that recording under the pinned thresholds and scorer, and exits non-zero if the stored evidence stops matching your policy:

The scored hotato HTML report: 0 of 1 events pass with a REGRESSION verdict, an analytics panel (time to yield, talk-over histogram, failure clusters by fix class), and the per-event timeline with caller and agent channels, the measured metrics, and the fix note.

Level 3 -- stored-evidence check. One recording, the pinned scorer, a FAIL against the labeled yield expectation. The same audio and config reproduce every number in this report.

4. Prove -- re-check the current agent

The frozen recording catches the evidence, thresholds, or scorer drifting. To check that the CURRENT agent still behaves, recapture the same scenario and score a NEW contract under the same policy:

# place the same call against today's agent, capture dual-channel, then:
hotato contract create --stereo fresh-call.wav --onset 41.90 --expect yield \
    --id refund-cutoff-001-recapture --out contracts
hotato contract verify contracts/refund-cutoff-001-recapture.hotato

Level 4 -- fresh-recapture comparison. A newly captured call meets the same labelled policy and no submitted paired guard regressed. This is the claim the frozen-recording gate cannot make. Walkthrough: docs/RECAPTURE.md.

Five levels of evidence

Hotato never calls the weakest level a verdict. Every card, report, and CLI result names its level; the public tier is the weakest one its inputs support, never a blended score.

Level Name What it means
1 Candidate A timing moment worth human review. Not a bug yet.
2 Human-labeled failure A reviewer confirmed this recording broke an explicit yield-or-hold expectation.
3 Stored-evidence check The historical audio still produces the expected result under the pinned policy and scorer.
4 Fresh-recapture comparison A newly captured call passed the same contract, and no submitted paired guard regressed.
5 External proof An independent team confirmed a caught regression or a fresh recapture. Not yet published.

A before/after experiment (hotato fix trial, and the fleet loop) re-derives every verdict from the on-disk audio under one pinned trial manifest. It refuses a proof built from an edited verdict, a re-encoded old call, a dropped fixture, or unrelated audio. The number comes from re-scoring the recordings every time, never from a stored field.

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/.

Fleet -- private, self-hosted

hotato fleet runs the loop across every agent from one local workspace: ingest calls, surface candidates, label them, and run a before/after experiment that recomputes both sides from audio under a pinned manifest. It recommends a change; it never deploys one. Local mode is stdlib-only (SQLite plus a content-addressed store) with no account and no hosted dependency, and no product limit on how many agents you register.

hotato fleet init    -w acme
hotato fleet agent add -w acme --name support-bot --stack vapi --assistant-id asst_123
hotato fleet ingest  -w acme --agent support-bot call.wav
hotato fleet discover -w acme --agent support-bot call.wav
hotato fleet review   -w acme

A before/after experiment refuses a proof built from an edited verdict, a re-encoded old call, a dropped fixture, or unrelated audio: the number comes from re-scoring the recordings, under one pinned policy, every time. Full guide: docs/GUARDIAN-FLEET.md.

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 (the voice_eval_run scorer + eight fleet tools; configs in docs/MCP.md)

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.

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

What Hotato is not

  • Not a full QA platform. It does not grade the whole conversation, task success, or content -- it isolates turn-taking timing and pins it to reproducible evidence. See docs/COMPARE.md for how it fits alongside broader voice-agent testing tools.
  • 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.

Docs

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-1.1.0.tar.gz (7.7 MB view details)

Uploaded Source

Built Distribution

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

hotato-1.1.0-py3-none-any.whl (3.2 MB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for hotato-1.1.0.tar.gz
Algorithm Hash digest
SHA256 cf27c5434123617c4f1c1cb712002c06b258b88c9464ef9eb16ca4cd0fed2a5c
MD5 17c2c6448f2d1702660cc6a37be53b29
BLAKE2b-256 d215d8a413651d64b6c0f7215ba705eec77989cc41a65ef3901e0e0207dbf9ba

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for hotato-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 10e9b380a65a7a7edf05174044e4fef1ddd1b106d9e29acf096a49645957b6b4
MD5 1c81ec1005c369e4f6698584027cf7d9
BLAKE2b-256 b656732dcaf461545b4de91be92f3702983413f3a42ea67f5862e90214a6ddf4

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