Skip to main content

Discussion Helper

🇫🇷 · 🇬🇧

License: BSD-3-Clause Python Local-first

Discussion Helper belongs to a collection of libraries called AI Helpers developed for building Artificial Intelligence.

🌍 AI Helpers

Hold a spoken conversation at the microphone: listen, answer, speak — and stop the moment the user cuts in.

vocal-helper hears. speaker-helper speaks. A local language model answers. None of them knows whose turn it is. This package is the loop between the three, and the three questions it has to get right:

  • Who holds the floor. Exactly one party at a time. Most voice-agent failures are two parties believing they hold it — the agent talking over the user is the obvious one; both waiting in silence is the one nobody notices until a recording is played back.
  • What happens when the user cuts in. Under 150 ms from their first syllable to the agent's silence, and the audio already synthesised and not yet heard is discarded, not drained. Playing it out because it exists is what makes an agent feel deaf.
  • Where the latency went. A single total says a conversation feels slow and nothing about why. Every exchange reports its breakdown — endpointing, recognition, first token, first audio — and names the stage to fix.

The promise

Local-first. The microphone, the recogniser, the model and the voice all run on your machine. No audio leaves it, no account, no per-minute billing.

Testable without a room. The three parts are protocols, so the whole loop runs in CI with no microphone, no model and no sound card. That is not tidiness: interruption handling is exactly the behaviour a real room makes impossible to assert. You cannot hear whether the queued audio was discarded.

Install

pip install 'discussion-helper[all]'      # hearing, thinking, speaking, playing
speaker-helper install-voice --engine kokoro   # the voice's weights, once

On Linux and macOS the voice also needs espeak-ng (apt install espeak-ng, brew install espeak-ng).

Each half installs on its own if you only want some of it: [listen], [think], [speak], [play].

Try it with nothing installed

pip install discussion-helper
discussion-helper rehearse "bonjour" "comment ça va ?"

rehearse runs the same loop with the microphone, model and speaker replaced by fakes. When a voice agent misbehaves the first question is always "is it the loop or is it the room", and this answers it in a second:

# stage an interruption a quarter of a second into the reply
discussion-helper rehearse "dis-moi tout" --sentences 6 --interrupt-after 0.25 --grace 0
 you: dis-moi tout
  it: dis-moi tout dis-moi tout (2) (cut off)

Talk to it

discussion-helper talk --voice ff_siwis
discussion-helper talk --no-barge-in        # strict half-duplex
discussion-helper talk --json               # transcript + latency breakdown

As a library

import asyncio
from discussion_helper import Discussion, Policy
from discussion_helper.adapters import VocalListener, LocalResponder, SpeakerVoice


async def main():
    async with Discussion(
        VocalListener(language="fr"),
        LocalResponder(),
        SpeakerVoice(voice="ff_siwis"),
        Policy(barge_in_grace_s=0.35),
    ) as talk:
        conversation = await talk.run()

    for turn in conversation.utterances:
        print(turn.speaker, turn.text)
    print(conversation.timings[0].as_dict())


asyncio.run(main())

Bring your own parts by implementing Listener, Responder or Voice — a cloud recogniser, a scripted model for a demo, a phone line instead of a speaker. Each is one class, and the loop does not change.

Over HTTP, and over a socket

A conversation does not fit a request — it has no single answer and lasts minutes — so the HTTP surface is a session you create, a stream you follow and a button you press:

pip install 'discussion-helper[server]'
discussion-helper-mcp            # or: uvicorn discussion_helper.api:build --factory
Route What it is
GET /health whether each of the three parts could run here, and what is missing
POST /sessions start one; returns an id immediately
GET /sessions every conversation this process is holding
GET /sessions/{id} the transcript, the floor and the timings, as they stand
GET /sessions/{id}/events follow it — floor, turns, latency, as they happen
POST /sessions/{id}/say push a user turn in as text, for a remote session
POST /sessions/{id}/interrupt stop the agent, through the same path a barge-in uses
DELETE /sessions/{id} end it, and get what was said
WS /sessions/{id}/audio the microphone at the other end
POST /turn one exchange, no audio — the shape an agent can use
/mcp the same routes as agent tools

The WebSocket carries real audio both ways: the client sends PCM and control frames, the server sends events and WAV. That is how every hosted voice agent works, and it means the browser can hold the microphone while this end holds the model. Two duties fall on the client and are not optional — send {"type":"speaking"} on the first syllable so a barge-in can travel, and drop buffered audio on {"kind":"stop"}. A client that plays its buffer out reproduces, at the far end, exactly the deafness that stopping it here was meant to prevent.

In a container

docker compose up -d
curl -s localhost:8090/health

The image serves the remote transport — the caller owns the microphone and sends frames — and that is the honest half of this package a container can run. It does not open a microphone of its own: --transport local reads the machine's sound card, and a container has none. Passing one in is --device /dev/snd on Linux and impossible on macOS, which is a per-machine arrangement rather than an image, so the listen extra is deliberately left out and the refusal happens at install time instead of at the first call.

The port is bound to the loopback, and the reason is in discussion-helper-mcp --help as well: this server can open a microphone. One worker, also deliberately — the voice backend loads its ONNX graph once per process and a second worker pays for its own copy, and sessions are stateful besides.

Read this before tuning anything

KNOWLEDGE.md is the design notebook: the 800 ms latency budget and where it actually goes, why a silence threshold is not turn detection, the two mistakes everyone makes with barge-in, why replies are chunked at sentences and not tokens, and why acoustic echo is the problem that decides your defaults.

The short version: a voice agent has about 800 ms before the conversation feels broken, the largest line in that budget is endpointing rather than the model, and the agent's own voice reaching its own microphone looks exactly like the user speaking.

The rest of the documentation

File What is in it
EXAMPLES.md A cookbook. Every Python block runs on a bare install — no microphone, no model, no sound card — because the parts are protocols with shipped fakes.
KNOWLEDGE.md The design notebook, above.
LANDSCAPE.md / PAYSAGE.md Where this sits against LiveKit Agents, Pipecat, OpenAI's Realtime API and six others, with a positioning map. It scores ⭐ on telephony and scale, which is the honest answer.
TRIGGERS.md The exhaustive catalogue of what should, and should not, reach for this helper. Mirrored into the agent skill, with a gate that fails when the two drift apart.
CHANGELOG.md What changed, and why.

How this differs from its neighbours

You have Reach for
A recording somebody needs as text vocal-helper
Text somebody needs as speech speaker-helper
A live camera or microphone as frames capture-helper
A conversation to hold this one

What is deliberately not here

No wake word, no speaker diarization, no telephony, no acoustic echo cancellation, and no turn detection — that last one lives in vocal-helper, which ships Silero VAD and a semantic end-of-turn stage. Reimplementing it here would give it two owners. KNOWLEDGE.md §10 explains each.

Author

Warith HARCHAOUI — warithmetics@deraison.ai

License

BSD-3-Clause. See LICENSE.

Metadata

Release files for discussion-helper 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for discussion-helper 0.2.1
File Size Uploaded Provenance
discussion_helper-0.2.1.tar.gz 65.7 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for discussion-helper 0.2.1
File Interpreter ABI Platform
discussion_helper-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 118.2 kB

Release files / discussion_helper-0.2.1.tar.gz

Download URL discussion_helper-0.2.1.tar.gz
Size 65.7 kB
Tags Source
SHA-256 checksum
How to use checksums
33d659547e92bea0ece3a50fac546fe1a704ec7b5dbaa823bdf07e6f5ee45b36
BLAKE2b-256 checksum
How to use checksums
064431a8fd88bf9e222a8411e111ff9c6ef9ce3613505ad55fedd45cc0ea5275
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.13

Release files / discussion_helper-0.2.1-py3-none-any.whl

Download URL discussion_helper-0.2.1-py3-none-any.whl
Size 52.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e077117cd90e121b36107d65293a6dc29746f3667ff9bcf70688c3a1c01cc1a5
BLAKE2b-256 checksum
How to use checksums
f904607f36687f7b317e776da1f6b2210655665a7efbf73ad4d1e6cd0ee4123f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.13

Security for discussion-helper 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page