pipecat-dialt
pipecat-dialt is the author-maintained Dialt realtime voice provider for
Pipecat. One DialtLLMService handles the complete
speech-to-speech path: ASR, turn detection, endpointing, response generation, tool orchestration,
interruption, and TTS.
This is a community integration maintained by Dialt, not by the Pipecat maintainers. It follows Pipecat's external integration policy and is independently versioned under Apache-2.0.
Release status: source-complete alpha. This repository has not yet been published to PyPI or listed in the official Pipecat documentation.
Install
Requires Python 3.11 or newer.
uv add pipecat-dialt
Until the first public release, install from a checkout:
uv add --editable /path/to/pipecat-dialt
For the local microphone/speaker example, also install Pipecat's local audio dependency:
uv sync --extra local
Set DIALT_API_KEY in the environment. Do not put it in source code.
Foundational pipeline
uv run python examples/foundational.py
The important topology is:
from pipecat_dialt import DialtContextAggregatorPair, DialtLLMService
context = LLMContext()
llm = DialtLLMService(
api_key=os.environ["DIALT_API_KEY"],
initial_context=context,
mode=DialtMode(
instructions="You are a concise, helpful voice assistant.",
greeting="Hello! How can I help?",
),
)
user, assistant = DialtContextAggregatorPair(context)
pipeline = Pipeline(
[
transport.input(),
user,
llm, # Dialt owns ASR + turns + LLM + tools + TTS
transport.output(),
assistant,
]
)
There is intentionally no Pipecat VAD, Smart Turn analyzer, STT, second LLM, or TTS service. The
owned pair configures external turn strategies without constructing a default Smart Turn analyzer.
Its semantic turn proposals do not authorize interruption: Dialt makes that decision separately.
Use DialtContextAggregatorPair, not the stock pair, to keep the shared context consistent with
accepted transcript revisions and avoid duplicate legacy/canonical appends.
Startup and initial history
There are two explicit startup paths:
- Greeting-only (default): pass
initial_context=contextand optionallymode.greeting.StartFrameconnects immediately after forwarding startup downstream; loading history is silent and noLLMRunFrameis needed. Early SDK output cannot race pre-start aggregators. This is the foundational microphone example and cannot wait on a transcript to connect. - Framework startup run: set
startup_run=Trueand queueLLMRunFrame()after pipeline start (for example inon_pipeline_started). The user aggregator supplies the initialLLMContextFrame; the service disables automatic greeting before connecting and requests one response after SDK readiness, using a stable operation ID. Repeated run/context frames do not request a second startup response.reply_ackis admission/lifecycle, not speaker drain.
With startup_run=True, initial_context is optional. If omitted, connection waits for the first
context frame because Pipecat's StartFrame precedes it. Queue the startup run explicitly; never
wait for microphone speech to produce that first context. Audio arriving before it is not sent.
Supplying initial_context allows early connection with history while still requesting the startup
response only when the framework runs it.
The initial context imports user/assistant text and completed tool exchanges, correlated by call ID (not tool name or arrival order). Explicit service instructions come first, then framework system/developer messages in order, composed once. They are not history or synthetic caller text. Historical tools never execute. Pending tools, media and unsupported provider-specific messages fail explicitly. The SDK atomically enforces 256 items, 128 KiB total and 16 KiB per item before readiness or generation; the integration never truncates history.
Later context frames synchronize tools and return results for dispatched application calls only.
They never reimport the shared transcript or request responses. For a deliberately new typed caller
turn, use the public SDK llm.session.inject_context(text, role="user", reply=True); for a new
host-requested reply without inventing a caller turn, use request_reply(operation_id=...).
Application tools
Supply Pipecat tools through LLMContext. Handler-carrying schemas and direct async functions are
auto-registered by Pipecat:
from pipecat.services.llm_service import FunctionCallParams
async def lookup_order(params: FunctionCallParams, order_id: str):
"""Look up an order by ID."""
await params.result_callback({"order_id": order_id, "status": "shipped"})
context = LLMContext(tools=[lookup_order])
Dialt dispatches the call, Pipecat executes the explicitly supplied application handler, and the
service returns one correlated terminal result through dialt-sdk. Duplicate call IDs and repeated
context frames do not execute or return a tool twice. Intermediate streaming tool results are not
supported by Pipecat realtime services; the service emits a machine-readable error and waits for
the final result.
Ownership boundary
Dialt / dialt-sdk owns |
Pipecat / this integration owns |
|---|---|
| Protocol and authentication | Input/output media transport |
| Logical session lifecycle | PCM downmixing/resampling to 16 kHz mono |
| Reconnect, token rotation, and resume | Standard Pipecat frame projection |
| ASR and transcript revisions | Output-buffer interruption propagation |
| Turn detection and endpointing | Observers and context aggregation |
| Response and tool orchestration | Explicit Pipecat application tool execution |
| Interruption decisions and TTS | Best available playback discard accounting |
The integration never opens its own Dialt WebSocket and does not duplicate SDK retry or resume
logic. It also rejects settings.model: private serving provider/model identities are not part of
Dialt's public contract. Use the public DialtMode.brain setting ("fast" or "smart") when a
different conversational tier is required.
Configuration
Pass the published SDK's DialtMode to the service. instructions is required.
mode = DialtMode(
instructions="Help customers with orders. Keep spoken answers brief.",
voice="<public Dialt roster key>",
greeting=False,
brain="fast",
turn_detection="server",
)
llm = DialtLLMService(api_key=api_key, mode=mode, initial_context=context)
Constructor-only connection options are base_url, user, timezone, session_id,
connect_timeout_s, auto_reconnect, reconnect_base_s, reconnect_max_s, and
max_reconnect_attempts, initial_context, and startup_run.
Runtime DialtLLMSettings updates support system_instruction, voice,
tools, and tool_choice; the service delegates each change to the corresponding SDK method.
turn_detection="client" is available for applications that deliberately provide their own turn
boundaries. In that mode the service does not advertise external strategies and commits input on
UserStoppedSpeakingFrame. The recommended and example configuration is server mode.
Frames and observers
The service projects provider activity into standard frames:
- upstream
TranscriptionFramefor final user ASR; DialtSpeechActivityFrame(speaking, speech_id)promptly on detected speech start/stop; this observer indication is not a Pipecat speaking proposal and cannot trigger local barge-in;ProposedUserStartedSpeakingFrame/ProposedUserStoppedSpeakingFramefor provider-confirmed semantic turns around final ASR (not speech-duration measurements), with interruptions disabled;- one
LLMFullResponseStartFrame/LLMFullResponseEndFramepair per segmentturn_id; LLMTextFrame,TTSTextFrame, and 16 kHz monoTTSAudioRawFrameoutput;- standard Pipecat function-call lifecycle frames from
LLMService.
Pipecat 1.x has no standard correction or structured provider-error frame. This package exposes:
DialtTranscriptCorrectionFrame: canonical observer revision withitem_id,revision, role and text; replace the matching display item, including empty-text retraction, rather than append;DialtErrorFrame: a sanitized message plus stablecodeandretryablefields.
Interruption and playback accounting
An incoming Pipecat InterruptionFrame immediately closes local output and suppresses late events,
then calls SDK interrupt(response_id=...) for the stable logical response. Provider interruption
broadcasts a marked standard-interruption subclass to clear transport output; delayed echoes never
interrupt a newer response. Response cancellation does not cancel application tools: only Dialt's
separate tool_cancel event does so by call ID.
Segment done does not end the logical target during tool waits. response_done ends generation
only; it may arrive before final segment done and never drops that segment's delivered content.
Playback tracking survives both until transport drain or a host discard. Host discard calls public
SDK playback_stopped(response_id, discarded_ms), including after producer completion.
Pipecat 1.12 does not expose a generic device playback cursor or clear acknowledgement to upstream processors. Like Pipecat's built-in realtime providers, this integration estimates heard audio as the smaller of generated PCM duration and elapsed wall-clock playback time. At the first audio chunk of each new segment, it carries forward only the estimated unplayed residual and resets the clock, so a bridge/tool wait cannot consume newly queued final audio. This remains an estimate: transport buffering, gaps within a segment and device latency are not measured. A transport with a real device/client playback cursor should expose that receipt to replace this approximation.
Accepted conversation and revisions
session.conversation is a detached read-only {items, first_index} SDK projection. The owned pair
maps accepted item IDs to shared-context messages in order, updates revisions in place for both
roles, and removes empty retractions. It does not buffer provisional Dialt text as accepted history,
so a late aggregation flush cannot restore a corrected transcript. Resume snapshots reconcile the
retained window; first_index makes eviction explicit. Observers can also inspect upstream
conversation frames. Host system/developer instructions remain separate; internal prompts, hidden
reasoning and managed tools are never imported from Dialt. Customer tool result business fields
remain intact. This projection does not edit broker history.
Compatibility
| pipecat-dialt | Python | Pipecat tested | dialt-sdk tested |
|---|---|---|---|
| 0.1.x | 3.11–3.13 | 1.12.0 | 0.41.0 |
| 0.2.x | 3.11–3.13 | 1.12.0 | 0.45.0 |
Dependencies intentionally use bounded ranges (pipecat-ai>=1.12,<2,
dialt-sdk>=0.45.0,<0.46.0). Version 0.2 requires the coordinated broker capabilities
conversation_v1, request_reply_v1, targeted_interrupt_v1, and speech_activity_v1;
unsupported servers fail explicitly rather than silently falling back. Every release must update
this table with the exact versions exercised.
Development
uv sync --all-groups
uv lock --check
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run mypy
uv build
uv run twine check dist/*
See CONTRIBUTING.md, RELEASING.md, and CHANGELOG.md. Security reports should follow SECURITY.md.
Metadata
Release files for pipecat-dialt 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pipecat_dialt-0.2.0.tar.gz | 248.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pipecat_dialt-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 273.2 kB
Release files / pipecat_dialt-0.2.0.tar.gz
| Download URL | pipecat_dialt-0.2.0.tar.gz |
|---|---|
| Size | 248.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8c9b8b8cfd7b2dd98c29d0de80d748cc38db2cd956aef2732c322bb70202de3d
|
|
BLAKE2b-256 checksum How to use checksums |
949c97d5879982ab15622957bb175edb21eaca25eed0a8dea553a83a751b5b60
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logRelease files / pipecat_dialt-0.2.0-py3-none-any.whl
| Download URL | pipecat_dialt-0.2.0-py3-none-any.whl |
|---|---|
| Size | 24.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
caf31985c349640e2d8a639403b216ff5df1131dfdc76b87f474092cec5a6a02
|
|
BLAKE2b-256 checksum How to use checksums |
ead4fa5b190aae0919541655ba1bbdc6d7221b9430c762894d8ea0a3f3e83779
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logSecurity for pipecat-dialt 0.2.0
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for pipecat_dialt-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logA 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.