Dialt
Get API key

Developer guide

Python SDK

For server-side and headless integrations that already own audio capture and playback. The SDK handles the Dialt session protocol and audio conversion.

← Choose another integration

You provide the media layer. The Python SDK does not open a microphone, play audio or cancel echo.

Python SDK reference

The Python SDK is an asynchronous, headless session client. It converts audio frames and exposes events, but it does not open a microphone, play audio or cancel echo.

uv add dialt-sdk
Migrating to Dialt: replace converse-sdk with dialt-sdk and change imports from converse_sdk to dialt. The final converse-sdk release is a deprecated compatibility bridge.
import os
from dialt import DialtMode, DialtSession, ToolDefinition

lookup_tool: ToolDefinition = {
    'name': 'lookup_order',
    'description': 'Look up an order by ID.',
    'parameters': {'type': 'object', 'properties': {'order_id': {'type': 'string'}}},
    'read_only': True,
    'expected_duration': 'instant',
    'status_label': 'order lookup',
    'deferred': True,
    'deferred_timeout': 7200,
    'notify_on_complete': True,
}

async with await DialtSession.connect(
    'wss://dialt.com/ws',
    api_key=os.environ['DIALT_API_KEY'],
    mode=DialtMode(web_search=False, tools=[lookup_tool]),
) as session:
    await session.send_audio(processed_frame)
    async for event in session.events():
        if event.type == 'audio':
            play_or_store(event.audio)
        else:
            print(event.type, event.data)

For text, use the same client with mode=DialtMode(modality='text'), then call await session.send_text('Hello'). Text mode uses WebSocket only and emits the same transcript, tool and reply lifecycle events without audio.

DialtSession.connect

await DialtSession.connect(url, *, session_id=None, sr=16000, api_key=None, mode=None, user=None, timezone=None, capabilities=None, resume_token=None, connect_timeout_s=15.0)

Returns after ready. Raises DialtError for a server rejection before ready; inspect its code and retryable attributes rather than matching human-readable text. Raises TimeoutError when the connection deadline expires.

After a transport loss, save session.resume_token and pass it as resume_token=... to the replacement connection. Within the bounded broker resume window, conversation context and deferred jobs are restored and each job emits tool_deferred_resume.

Methods and helpers

APIDescription
send_audio(chunk)Sends bytes as PCM16 or converts a NumPy Float32 array to PCM16.
send_text(text)Commits one user turn in a DialtMode(modality='text') session; 1–20,000 characters.
stream_audio(audio, sr=16000, chunk_ms=100, realtime=True)Chunks and optionally paces a waveform; returns actual send timestamps.
events()Async iterator of SessionEvent(type, t_ms, data, audio). Every server event passes through, including working (data["active"]: Dialt is blocking on a tool result with nothing audible) and session_end_requested (the agent called end_call; data["farewell"] is the farewell it just spoke; stop automatic turn injection). A headless client that does its own playback can cover a tool wait the way the browser SDK's thinking sound does.
now_ms() / start_tMonotonic session timing relative to the start of connect().
reset()Clears server conversation context on the live connection.
inject_context(text, role='context', reply=False, message_id=None)Returns the broker's authoritative accepted/rejected acknowledgement; typed ASR echoes the message ID.
send_tool_result(id, content, outcome='unknown', verified=False)Resolves a client tool call; only outcome='succeeded' with verified=True authorizes success narration.
send_tool_progress(id, note)Adds progress to conversation context without resolving the call.
send_tool_deferred(id, handle, status_label=None)Releases the voice turn while retaining an eligible background job.
send_tool_partial_result(id, content, reply=False, interaction=None)Delivers a structured segment of the eventual answer; interaction={"id": ..., "prompt": ..., "options": [...]} raises a mid-call decision that is asked by voice and never silently dropped.
send_tool_interaction_update(id, interaction_id, state, note=None)Closes an open interaction (resolved/cancelled/superseded) without completing the call; returns the deterministic tool_interaction_update_ack payload.
narration_state(job_id) / interaction_state(interaction_id) / wait_for_narration_state(job_id, states, timeout=None)Track a raised interaction's tool_job_narration lifecycle by job or stable interaction id.
set_tool_choice(choice, one_shot=False)Restricts tool use mid-session ("auto"/"none"/"required"/{"allowed"}/{"tool"}); also available at session start as DialtMode(tool_choice=...).
send_tool_cancel(id)Cancels a client-owned tool call.
send_client_event(event, **fields)Sends playback lifecycle events such as playback_stopped.
close()Closes the session and its receive task.
TurnRecorderBuilds an assistant waveform from session events for recording or offline processing.

Pass message_id to correlate the returned inject_context_ack with a typed turn's final ASR. Typed ASR uses input_source='text'; spoken ASR uses input_source='voice'. Omitted message IDs are generated by the SDK.

DialtMode accepts modality ('voice', the default, or 'text'), voice, instructions, tools, tool_choice, web_search, end_call (declares the managed end_call(farewell) tool so the agent can end the session; off by default), flow, background_audio (a server-mixed background underscore; requires transport="webrtc") and greeting. ToolDefinition includes read_only, requires_permission, expected_duration, status_label, timeout, deferred, deferred_timeout and notify_on_complete.

Exported audio helpers are chunk_audio, float32_to_pcm16, pcm16_to_float32, f32le_to_float32 and to_ws_url.

Hosted evals

dialt.evals creates cases and starts runs on the Evals dashboard with your account API key. Cases are JSON files in your repository; upsert_case matches by name, so a re-push updates the hosted case instead of duplicating it. Field reference and checks: the evals guide.

from dialt import EvalsClient, load_cases

evals = EvalsClient(api_key=os.environ["DIALT_API_KEY"])
cases = evals.upsert_cases(load_cases("evals/"))       # one case per *.json file
run = evals.start_run([c["id"] for c in cases], modality="text")
print(evals.dashboard_url(run["id"]))
result = evals.wait(run["id"])                         # polls until the run is terminal

start_run also takes names=[...], repetitions and modality="voice". wait returns the run once its status is passed, failed, cancelled or error, with one entry per case in attempts. A rejected request raises EvalsError with the server's message.

WebRTC transport (experimental)

Experimental: the API is stable, but this transport is newly shipped and still being hardened on real networks; ws remains the default and recommended fallback.

Pass transport="webrtc" to carry the session over WebRTC (UDP) instead of the default WebSocket, useful when a headless client is bridging a caller on an unreliable network. Requires the optional aiortc dependency:

uv add "dialt-sdk[webrtc]"
session = await DialtSession.connect(
    "wss://dialt.com/ws",
    api_key=os.environ["DIALT_API_KEY"],
    mode=DialtMode(),
    transport="webrtc",
)

Most headless callers (services, telephony bridges) are fine on the default ws transport, since they usually don't sit behind the weak, lossy last-mile links WebRTC is built for.