Skip to content

Repository files navigation

Rerun

Lose it once. Not twice.

Drop in a recording of a sales appointment you lost. Rerun finds the exchange where the deal died, then puts you back in that moment against a buyer built from that transcript — same objection, same wording, same stubbornness — until you can get past it.

Built for field reps: solar, HVAC, roofing, windows.

What it does

  1. Drop in a conversation. Audio or a pasted transcript.
  2. Find the cut. The pivotal exchange, with both sides quoted verbatim.
  3. Take the drill. Voice roleplay against a buyer seeded from that transcript.
  4. See the delta. Your attempt scored against your original handling.
  5. Run it again. Attempts stack under the cut.

Running it

Node 20 or newer.

npm install
cp .env.example .env      # add a Groq API key
npm run seed
npm run dev

The database is one SQLite file at ./data/rerun.db. No server, no container, no account.

API keys

One key in GROQ_API_KEY is enough. The free tier caps at 8,000 tokens per minute per key, which is the limit you feel during corpus generation, so extra keys can be listed as GROQ_API_KEY_2, GROQ_API_KEY_3 and so on. Calls are spread across whichever keys are present, and a key that hits a limit is parked until it recovers.

Scripts

Command What it does
npm run generate-corpus Builds the 24-conversation training corpus with ground-truth labels
npm run inspect-corpus Checks the corpus against the distribution and realism it is meant to have
npm run relabel-corpus Re-locates pivotal turns whose label looks wrong
npm run diversify-labels Spreads objection types across the corpus
npm run voice-corpus Voices four transcripts through Piper into stitched WAVs
npm run ingest-corpus Runs voiced audio back through the full ingest path and scores it
npm run check-copy Checks UI copy and the README against the banned-words list
npm run eval Scores the detector against the ground truth and prints the number
npm run cache-stats Tokens banked on disk, which a rerun does not spend again
npm run seed Loads finished conversations, diagnoses and one scored drill

Every model response is cached to .cache/llm by prompt hash, so re-running any script costs nothing. Deleting that directory means paying for it all again.

Layout

lib/models.ts     every model call, the key pool, the cache, JSON validation
lib/types.ts      shared types and the objection taxonomy
lib/db.ts         SQLite schema
lib/tts.ts        Piper speech synthesis and WAV handling
lib/stt.ts        speech to text, with chunking and its own cache
lib/pitch.ts      acoustic speaker separation
lib/ingest.ts     audio or text in, normalised turns out
scripts/          corpus generation, voicing, ingest, evaluation, seeding
corpus/           generated transcripts and their ground-truth plans
specs/            the build spec and a numbered note per milestone

Audio

Speech synthesis runs locally through Piper. It is not part of npm install, because the voices are about 260MB and only the four demo conversations need them. Python 3.9 or newer:

python -m venv piper/venv
piper/venv/Scripts/python -m pip install piper-tts        # bin/python on macOS and Linux
piper/venv/Scripts/python -m piper.download_voices --download-dir piper/voices \
  en_US-ryan-medium en_US-amy-medium fr_FR-siwis-medium fr_FR-upmc-medium
npm run voice-corpus

Speech to text runs on Groq's free Whisper tier and needs no extra setup. Synthesised turns are cached in .cache/tts and transcriptions in .cache/stt, so re-running either script is fast and costs nothing.

What the audio numbers do and do not cover

Every accuracy figure in this repo is measured on synthesized speech: two distinct Piper voices, no room noise, no overlapping microphones. Real recordings are harder, and speaker separation is the part that will suffer first — one phone on a kitchen table gives both people the same channel and similar volume. Treat the diarisation numbers as an upper bound, not a promise.

Two known limits, with what would fix them:

  • Confidence on a diagnosis is not yet meaningful. It scores 0.81 on correct answers and 0.79 on wrong ones, so it cannot be used to decide when to ask the rep for help. The fix is self-consistency — run the diagnosis three times at a higher temperature and use cross-run agreement as the score, which turns "confidently wrong" into "disagrees with itself". It is not built because it triples the cost of the run.
  • Speaker labels come from a text pass plus a pitch check, and the pitch refinement stands down when two voices sit close together. Turns it is unsure about are reported so they can be corrected by hand.

The corpus

There is no public corpus of in-home sales conversations, so this repo generates one. Each conversation is planned before it is written: vertical, outcome, buyer, and for the lost ones a designated pivotal turn with the objection type and a note on how the rep mishandled it. The dialogue is then written to realise that plan.

The plan is never shown to the detector. It exists so npm run eval can report how often the detector picks the same pivotal turn as the plan, within a tolerance of two turns.

About

Drill tool for field sales reps. Find the moment a deal died, then run it again.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages