transport

WebSocket server transport for the eval harness.

A subclass of SingleClientWebsocketServerTransport that adds eval-only behavior driven by per-connection query flags the harness sets:

  • ?skip_tts=true silences the bot’s output for the session (text mode), including any on-connect greeting. This is pushed as an LLMConfigureOutputFrame before on_client_connected fires: pipecat processes frames in order, and a bot that greets in on_client_connected queues its greeting there, so a config sent afterwards (as a client message) would arrive too late.

  • ?capture_bot_audio=true makes the serializer forward the bot’s synthesized audio to the harness (for tts_response transcription).

  • ?record=<path> records the conversation audio (user + bot) to <path>. The recorder is an AudioBufferProcessor placed after the real output transport (so both input and output audio flow through it); output() returns that composite. Recording starts on connect and is written on disconnect, before the bot’s on_client_disconnected handler fires (a bot that cancels its pipeline there may exit right after, so the write must land first). Recording is eval-only — the generic transport is untouched.

The input side runs a virtual microphone (EvalMicrophone), enabled per connection by ?user_audio=true (audio-mode scenarios): the harness sends each user utterance as a few large raw-audio messages, and the input transport plays them into the pipeline at real-time cadence (~20ms frames) with locally generated silence in between — so VADs, turn models, and streaming STTs see exactly what a live client’s mic would produce, without a continuous frame stream crossing the wire. Text-mode scenarios leave the mic off, so no silence is ever fed into the bot’s STT.

Client disconnects behave as on any transport: the bot’s on_client_disconnected handler fires normally, and whether the pipeline survives the disconnect is the application’s choice. The server itself keeps running either way, so a bot that opts not to cancel can serve several sequential eval connections.

class pipecat.evals.transport.EvalMicrophone(push: Callable[[bytes, int], Awaitable[None]])[source]

Bases: object

Plays harness-sent utterances into the pipeline at real-time cadence.

The harness sends each user utterance as a few large raw-audio messages (cheap on the wire — no continuous frame stream to encode and ship). A real microphone, though, delivers small frames at real-time pace, and timing-sensitive consumers rely on that: VAD start windows, Krisp IP/turn models, and turn-detecting STTs all break if a whole utterance floods the pipeline at once. This class is the eval transport’s virtual microphone: every ~20ms tick it pushes one frame — utterance audio when queued, locally generated silence otherwise — so the bot hears exactly what a live client would produce, including the silence that lets its VAD end each turn.

Speech that falls behind after a late wake-up is sent back-to-back until caught up (the utterance content must stay gap-free); silence never catches up (the end-of-turn gap must stay honest).

__init__(push: Callable[[bytes, int], Awaitable[None]])[source]

Initialize the microphone.

Parameters:

push – Async callable (pcm: bytes, sample_rate: int) invoked with each ~20ms mic frame.

add_audio(pcm: bytes, sample_rate: int) None[source]

Queue one utterance (or a piece of one) for real-time playout.

reset() None[source]

Drop queued and in-progress utterance audio (a new eval client starts fresh).

async run(silence_sample_rate: int) None[source]

Emit one mic frame per ~20ms tick, forever (cancel to stop).

Parameters:

silence_sample_rate – Sample rate for the generated silence frames (the transport’s input rate).

class pipecat.evals.transport.EvalTransportParams(*, audio_out_enabled: bool = False, audio_out_sample_rate: int | None = None, audio_out_channels: int = 1, audio_out_bitrate: int = 96000, audio_out_10ms_chunks: int = 4, audio_out_mixer: Mapping[str | None, ~pipecat.audio.mixers.base_audio_mixer.BaseAudioMixer] | None=None, audio_out_destinations: list[str] = <factory>, audio_out_end_silence_secs: int = 2, audio_out_auto_silence: bool = True, audio_in_enabled: bool = False, audio_in_sample_rate: int | None = None, audio_in_channels: int = 1, audio_in_filter: BaseAudioFilter | None = None, audio_in_stream_on_start: bool = True, audio_in_passthrough: bool = True, video_in_enabled: bool = False, video_out_enabled: bool = False, video_out_is_live: bool = False, video_out_width: int = 1024, video_out_height: int = 768, video_out_bitrate: int | None = None, video_out_framerate: int = 30, video_out_color_format: str = 'RGB', video_out_codec: str | None = None, video_out_destinations: list[str] = <factory>, add_wav_header: bool = False, serializer: FrameSerializer | None = None, session_timeout: int | None = None, allowed_origins: list[str] = <factory>)[source]

Bases: SingleClientWebsocketServerParams

Transport parameters for the eval harness.

A thin subclass of SingleClientWebsocketServerParams that gives the eval transport its own parameter type. Bots configure the "eval" entry of transport_params with this class so the eval setup reads as eval-specific rather than leaking the underlying WebSocket server transport.

add_wav_header: bool
serializer: FrameSerializer | None
session_timeout: int | None
allowed_origins: list[str]
class pipecat.evals.transport.EvalInputTransport(*args, **kwargs)[source]

Bases: SingleClientWebsocketServerInputTransport

Input transport that plays a virtual mic and serves the harness’s images.

Audio: the harness sends each user utterance as a few large raw-audio messages; instead of letting them flood the VAD path at once, they are queued on an EvalMicrophone that plays them into the pipeline at real-time cadence with locally generated silence in between — a virtual microphone. The mic is enabled per connection by the harness’s ?user_audio=true flag (audio-mode scenarios only — a text-mode scenario must not feed silence into the bot’s STT, which costs real streaming-STT minutes) and starts when the client signals ready (the client-readyInputTransportStartAudioStreamingFrame path).

Images: a function-calling-video bot pushes a UserImageRequestFrame upstream when it needs the user’s camera image. There is no camera under eval, so we serve the image the harness registered for the turn (an eval-image message, stored on the serializer) as a UserImageRawFrame — mirroring daily/transport.py but sourcing the image from the serializer instead of a live video frame.

__init__(*args, **kwargs)[source]

Initialize the input transport and its virtual mic.

async configure_mic(enabled: bool) None[source]

Configure the virtual mic for a new eval client.

Drops any utterance audio a previous client left queued, and enables or disables the mic for this connection (the harness sets ?user_audio=true for audio-mode scenarios). Disabling stops a mic a previous audio-mode client left running, so a following text-mode scenario doesn’t stream silence into the bot’s STT.

Parameters:

enabled – Whether this connection’s scenario sends user audio.

async process_frame(frame: Frame, direction: FrameDirection)[source]

Route utterance audio to the virtual mic; serve image requests.

async stop(frame: EndFrame)[source]

Stop the virtual mic, then the transport.

async cancel(frame: CancelFrame)[source]

Cancel the virtual mic, then the transport.

class pipecat.evals.transport.EvalOutputTransport(transport: BaseTransport, params: SingleClientWebsocketServerParams, **kwargs)[source]

Bases: SingleClientWebsocketServerOutputTransport

Output transport used by the eval harness.

The eval harness sends the bot’s output over the same WebSocket connection as any client, so this currently adds no behavior beyond SingleClientWebsocketServerOutputTransport. It exists for naming symmetry with EvalInputTransport and as a hook for any future eval-specific output behavior.

class pipecat.evals.transport.EvalTransport(*args, **kwargs)[source]

Bases: SingleClientWebsocketServerTransport

WebSocket server transport used by the eval harness (see the module docstring).

__init__(*args, **kwargs)[source]

Initialize the transport and the (lazily built) recording composite.

input() SingleClientWebsocketServerInputTransport[source]

Return an input transport that can serve harness-provided images.

output()[source]

Return the output as a recorder composite: [real_output, AudioBufferProcessor].

self._output stays the real output transport, so the transport’s set_client_connection reaches it directly (no proxy). The buffer sits after it, where both input and output audio flow.