discussion_helper.api module

The HTTP surface: a conversation you can watch, interrupt and end.

Module summary

A spoken conversation does not fit a request. It has no single answer, it lasts minutes, and the interesting part is what happens during it. So this surface is not one endpoint that returns a reply — it is a session you create, a stream you follow, and a button you press.

Three ways in, and they are genuinely different products rather than three spellings of one:

  • A local session (POST /sessions) opens the microphone on this machine. The caller is a dashboard, a hotkey, an agent — something that starts a conversation it is not itself part of, and then watches.

  • A remote session (POST /sessions with transport="remote", then WS /sessions/{id}/audio) puts the microphone at the other end of the socket. The browser captures and plays; this end recognises, thinks and speaks. That is how every hosted voice agent works, and the audio really does cross the wire as PCM.

  • A single text turn (POST /turn) runs one exchange through the same loop with no audio at all. The honest use is testing — and it is what makes the MCP surface meaningful, since an agent has no microphone.

What this surface refuses to pretend

It does not pretend an HTTP request can hold a microphone. POST /sessions returns immediately with an id; everything live happens on the event stream or the socket. And POST /sessions/{id}/interrupt goes through the same Voice.stop() the microphone’s barge-in uses, because a button that stopped the audio some other way would stop it in some other way.

Author

Warith HARCHAOUI — https://linkedin.com/in/warith-harchaoui/

class discussion_helper.api.StartRequest(*, transport='fake', turns=<factory>, allow_barge_in=True, barge_in_grace_s=0.35, max_reply_sentences=0, silence_timeout_s=0.0, voice='', language='')[source]

Bases: BaseModel

Body for POST /sessions.

Parameters:
  • transport (str) – "local" opens the microphone on the machine running this server. "remote" waits for a client to connect to the session’s WebSocket and send audio or text. "fake" uses scripted parts — the same loop with no hardware, which is what makes this surface testable and what a caller should reach for first.

  • turns (list of str) – For "fake" only: what the user says.

  • allow_barge_in (bool) – The policy. See Policy.

  • barge_in_grace_s (float) – The policy. See Policy.

  • max_reply_sentences (int) – The policy. See Policy.

  • silence_timeout_s (float) – The policy. See Policy.

  • voice (str) – Passed to the voice.

  • language (str) – Passed to the voice.

allow_barge_in: bool
barge_in_grace_s: float
language: str
max_reply_sentences: int
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

silence_timeout_s: float
transport: str
turns: list[str]
voice: str
class discussion_helper.api.TurnRequest(*, text, history=<factory>, with_audio=False, voice='', language='')[source]

Bases: BaseModel

Body for POST /turn: one exchange, no audio.

Parameters:
history: list[dict[str, str]]
language: str
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

text: str
voice: str
with_audio: bool
discussion_helper.api.build()[source]

ASGI factory, for uvicorn discussion_helper.api:build --factory.

Return type:

fastapi.FastAPI

discussion_helper.api.create_app(sessions=None)[source]

Build the application.

Parameters:

sessions (Sessions or None) – Injectable so a test can hold the registry it asserts against, and so two apps in one process do not share conversations.

Return type:

fastapi.FastAPI