coval-agent-sdk. Your application runs the agent; Coval records and evaluates the conversation. The SDK opens an outbound WebSocket, so your agent does not need a public endpoint.
Install
Requires Python 3.10 or newer:coval_agent_sdk. See PyPI for versions and release status. To manage resources through the API, use the separate API SDKs.
Before you run
Select these resources from the same Coval workspace:- An organization API key with access to launch simulations and read their results.
- The workspace ID.
- An agent configured for the SDK WebSocket connection type.
- A voice persona and a test set containing your scenarios.
Check the connection
Pass your API key and workspace ID toCovalVoiceClient, and the agent, persona, and test-set IDs to RunConfig. This example reads those values from environment variables.
The handler below echoes the caller’s audio to check the connection. In an agent integration, the handler connects that audio stream to your voice pipeline.
https://api.coval.dev/v1. Set api_base_url to use another API root, with credentials and resource IDs from that environment. agent_id identifies the agent configuration in Coval.
Connect your voice pipeline
Pass an async handler torun(config, on_connection=...). The SDK calls it with a ready AudioConnection for each conversation. The handler bridges your pipeline and the connection:
- Consume caller audio through
receive_audio()and send agent speech with awaitedsend_audio()calls. - Keep input and output independent so the agent can speak before receiving caller audio.
- Keep the handler active through voice completion, then finish conversation tasks and spans before returning.
finally blocks can finish. Handlers may run concurrently; keep conversation state separate.
Audio connection reference
Audio format and limits
Both directions use 16 kHz, 16-bit (2 bytes per sample), mono, little-endian PCM. A 20 ms frame contains 640 bytes. Convert your pipeline’s input and output formats at its transport boundary; the SDK does not resample. Await every send. Each chunk must be even-lengthbytes, at most 2 MiB. The SDK slices and paces output across calls and serializes concurrent writes. A write interrupted by normal completion returns False, even if some audio was sent.
Unread input is limited to 64 KiB; overflow fails the connection. Drain input promptly and bound application queues. The SDK does not send relay clear or barge-in controls.
Launch a run or connect to one conversation
run() creates a run, invokes your handler for each ready connection, and waits for completion, including evaluation processing. RunConfig defaults to one iteration, concurrency one, and a 3,600-second deadline; the example uses 600 seconds.
client.connect(simulation_id) attaches to one existing simulation without launching a run. Its async context yields a ready connection:
connection.result after the context exits successfully. Each connection has its own simulation ID; multiple iterations of a test case produce separate conversations.
Local timeout or cancellation stops local work without cancelling the hosted run. Inspect that run before retrying. The SDK does not automatically repeat run creation or reconnect a consumed connection.
Results and errors
run() returns an immutable RunResult with:
run_id: the hosted run identifier.status: the hosted completion status.conversations: a tuple of completedConversationResultvalues.
simulation_id, local voice status, received_audio_bytes, sent_audio_bytes, and optional trace delivery information. With tracing disabled, trace is None.
A run result reports execution completion; a conversation result reports voice completion. Review evaluation results in Coval to check whether the agent met its quality criteria.
Catch CovalError for SDK failures. After launch, RunError.result retains the run ID, completed conversations, and last observed status. Application exceptions and cancellation propagate unchanged.
To recover the run ID after a failure, use this pattern inside your async application code:
Argument validation
Missing arguments and unknown keywords raiseTypeError. Invalid RunConfig limits or empty IDs raise ValueError. Credentials and resource access are checked when requests reach the API. See the API contract for details.
Optional OpenTelemetry traces
Install the extra:CovalTraceExporter with your OpenTelemetry provider and pass it to the client’s trace_exporter argument. This example creates a provider and a handler with a conversation span:
conversation root with conversation.id set to the simulation ID. Finish child spans before the handler returns, and shut down tracing after SDK cleanup.
The SDK uploads traces for each completed conversation. Check ConversationResult.trace.uploaded_spans and trace.error for delivery results. Trace delivery failures do not rerun the conversation.
Spans may contain prompts, transcripts, and model output. Export only data you intend to send to Coval. See the trace lifecycle for buffering and cleanup details.