coval-agents-sdk
Connect Python voice agents to Coval simulations through an authenticated outbound WebSocket. Your process runs the agent; Coval supplies the simulated caller, records the conversation, and evaluates the result. The agent does not need a public URL or a listening port.
The distribution is coval-agent-sdk, the Python package is coval_agent_sdk, and the API is a Python library. Requires Python 3.10 or newer. The initial release candidate is 0.1.0; install from a checkout until it is published.
Install from source
From this repository's root, install into an existing Python environment with uv or pip:
uv pip install .
# Or: python -m pip install .
For SDK development, use the editable setup instead:
scripts/install
.venv/bin/python -c "import coval_agent_sdk"
See local development for building a wheel and installing it into another project. A source install does not publish a package. Maintainers can follow releasing to configure PyPI publishing.
Connect an existing agent
The SDK exposes an audio connection, not an agent class. Plug it into your framework's input and output transports. Your application owns agent construction, conversation state, provider clients, and pipeline cleanup.
Select existing Coval agent, persona, and test-set fixtures in the intended workspace. Configure credentials through your application environment; model-provider credentials stay in your application.
import asyncio
import os
import coval_agent_sdk
async def on_connection(connection: coval_agent_sdk.AudioConnection) -> None:
# Minimal audio passthrough to demonstrate the connection contract.
# A real integration feeds caller audio into its existing pipeline and
# independently sends the pipeline's speech with send_audio().
async for audio in connection.receive_audio():
await connection.send_audio(audio)
async def main() -> coval_agent_sdk.RunResult:
client = coval_agent_sdk.CovalVoiceClient(
api_key=os.environ["COVAL_API_KEY"],
workspace_id=os.environ["COVAL_WORKSPACE_ID"],
)
config = coval_agent_sdk.RunConfig(
agent_id=os.environ["COVAL_AGENT_ID"],
persona_id=os.environ["COVAL_PERSONA_ID"],
test_set_id=os.environ["COVAL_TEST_SET_ID"],
)
return await client.run(config, on_connection=on_connection)
if __name__ == "__main__":
asyncio.run(main())
on_connection is an async connection handler, not a factory: it receives a ready AudioConnection and returns nothing. It wires your application into the audio exchange, remains active until the conversation ends, then stops its tasks and finishes its spans before returning. Different conversations may invoke it concurrently; the application or framework owns isolation of conversation state. The SDK never constructs, subclasses, or closes your agent.
agent_id selects an existing hosted Coval fixture; it is not a Python object or class. The client defaults to https://api.coval.dev/v1; pass api_base_url to select another environment. Select the intended workspace explicitly when testing. The Coval API SDK stays private: applications use only this package's client, configuration, audio connection, results, and errors.
Attach to an existing simulation
connect() opens one existing simulation without creating a run. It is an async context manager that yields only after the relay is ready:
async def attach(
client: coval_agent_sdk.CovalVoiceClient, simulation_id: str
) -> coval_agent_sdk.ConversationResult | None:
async with client.connect(simulation_id) as connection:
await on_connection(connection)
return connection.result
The context closes transport and finalizes traces after application code exits. connection.result is available after successful exit; leaving before voice completion raises instead of reporting success. run() returns a RunResult containing these per-conversation outcomes. Neither method provides a CLI, logs, or console output.
Audio connection
| Member | Contract |
|---|---|
simulation_id |
Correlate application state and traces with this conversation. |
receive_audio() |
Async iterator of caller PCM; ends normally only on successful voice completion. Use one receive loop. |
send_audio(audio) |
Await a paced write. Returns True for a complete write, False when normal completion interrupts it; failures raise. |
wait_closed() |
Wait for voice completion without consuming input; failures raise. |
closed |
Whether the exchange has ended, including failure. |
result |
Finalized ConversationResult after successful context exit, otherwise None. |
Input and output are independent, so the pipeline may greet the caller before receiving any audio. Audio is PCM16 little-endian, mono, 16 kHz. A normal 20 ms frame contains 640 bytes. The SDK does not resample or implement relay interruption/clear controls. Framework integrations own conversion and interruption policy.
Drain incoming audio promptly into a bounded pipeline; unread input is limited to 64 KiB. Await output writes and bound framework queues rather than spawning unlimited send tasks. Chunks must be even-length bytes, at most 2 MiB. Output is serialized and paced across adjacent writes. Read runtime contracts for readiness, backpressure, deadlines, cancellation, and cleanup.
Results and failures
RunResult contains run_id, the hosted status, and a tuple of completed conversations. Each ConversationResult contains simulation_id, local voice status, received_audio_bytes, sent_audio_bytes, and an optional trace outcome. These immutable objects contain no relay credentials or transport objects.
A returned run has completed its execution; this does not mean the agent passed its quality criteria. A returned session describes completed voice transport, not hosted evaluation. With tracing disabled, session.trace is None. Otherwise, TraceResult.uploaded_spans reports delivered spans after a successful upload (None when a failure leaves the count unknown), and TraceResult.error contains an optional flush/upload failure. The calling application decides how to store or display those outcomes.
Catch CovalError for SDK-owned failures, or a specific subclass such as CovalHttpError, CovalConnectionError, RunError, or CovalTraceError. Adapter exceptions and asyncio.CancelledError propagate unchanged after local cleanup; catching CovalError does not catch them.
For constructor and input checks, see argument validation.
Failures raise instead of returning success. After a run is created, an SDK run failure raises RunError with a result snapshot containing its ID, last observed monitoring status (None before the first status response), and completed local conversations. Failed, cancelled, unfinished, or missed conversations do not appear as successful results. Local cancellation still propagates after cleanup and does not imply hosted cancellation.
Optional traces
Install the tracing extra from the checkout with python -m pip install ".[tracing]", or request [tracing] when installing a built wheel. The base installation does not install or import OpenTelemetry.
Import the optional exporter explicitly:
from coval_agent_sdk import tracing
exporter = tracing.CovalTraceExporter()
Register it with your pipeline's OpenTelemetry setup, and pass the same exporter to CovalVoiceClient(trace_exporter=...). Use a root span named conversation whose conversation.id attribute is the simulation ID available as connection.simulation_id. The SDK flushes ended spans and uploads each completed conversation separately.
Spans may contain prompts, transcripts, and model output. Enable upload only for data you intend to send to Coval. Failed-session traces are discarded after cleanup; a failed upload after a completed voice session is returned in ConversationResult.trace.error and can be retried through the retained exporter. See trace lifecycle for buffer limits and failure semantics.
Develop and contribute
scripts/install
scripts/lint
scripts/check
CONTRIBUTING.md covers setup, tests, and review. Python conventions define imports, initializers, Google-style docstrings, types, and formatting. Architecture maps module ownership. CHANGELOG.md records user-facing changes, and SECURITY.md explains private vulnerability reporting.
License
Copyright 2026 Coval, Inc. Licensed under the Apache License, Version 2.0. See NOTICE for attribution.
Metadata
Release files for coval-agent-sdk 0.1.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 | |
|---|---|---|---|---|
| coval_agent_sdk-0.1.0.tar.gz | 82.5 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| coval_agent_sdk-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 116.3 kB
Release files / coval_agent_sdk-0.1.0.tar.gz
| Download URL | coval_agent_sdk-0.1.0.tar.gz |
|---|---|
| Size | 82.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
89699587cfe513959fd39670ad2eccd961428a26ba79f22a1bdc0acdab6d77d1
|
|
BLAKE2b-256 checksum How to use checksums |
36c2962e00f881fb50e9728a2e505ae1c6bc1f57c1af5c4759b587ea7cfe9b25
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 20, 2026.
Transparency logRelease files / coval_agent_sdk-0.1.0-py3-none-any.whl
| Download URL | coval_agent_sdk-0.1.0-py3-none-any.whl |
|---|---|
| Size | 33.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ac63363e237d927195571a7810cd371a715d3cd9cf062a3fef70219e9ce3c890
|
|
BLAKE2b-256 checksum How to use checksums |
0404d008fe4d473e9a4029d46f600d81b8e6ca9965e093481366d9bff686916f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 20, 2026.
Transparency logSecurity for coval-agent-sdk 0.1.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 coval_agent_sdk-0.1.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 20, 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.