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
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
| API | Description |
|---|---|
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_t | Monotonic 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. |
TurnRecorder | Builds 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.