Python quickstart
Build a voice CLI with `cosmo-ai-sdk` in five minutes.
Prerequisites
- Python 3.10+
- A Cosmo account — sign up at platform.askcosmo.ai
Set up
The recommended route is the CLI — it signs you in, stores the key, and equips your coding agent in one command:
curl -fsSL https://platform.askcosmo.ai/docs/install.sh | sh
cosmo initYour browser opens, you pick a workspace, and the CLI stores a key at ~/.cosmo/credentials. That's the whole setup: the SDK reads the same file, so a client built with no arguments — RealtimeClient() — finds the credential and the backend it was issued for on its own. Nothing to export, and no key in your source. See Set up with the CLI for what else it does.
Run cosmo whoami any time to see which workspace you're pointed at.
Install the SDK
pip install cosmo-ai-sdkThe WebRTC transport and OS microphone/speaker I/O are included. On Linux, speaker playback also needs the system PortAudio library (apt install libportaudio2). Update later with pip install -U cosmo-ai-sdk.
Prefer to manage the key yourself? Create one in the dashboard (API keys) and export COSMO_API_KEY=cosmo_... instead — the SDK checks that first, before the credentials file. Either way the key scopes your workspace server-side, so there is no project ID to pass.
Write the script
RealtimeClient is the client; client.agent(...) builds a reusable agent; agent.start() opens one live session as an async context manager, resolving once the agent is ready, so every session method works immediately. The session is an async iterator of typed events.
import asyncio
import sys
from cosmo_ai import (
RealtimeClient,
ErrorEvent,
ReadyEvent,
RealtimeSession,
SessionEndedEvent,
TranscriptDeltaEvent,
)
async def print_events(session: RealtimeSession) -> None:
async for event in session:
if isinstance(event, ReadyEvent):
print("Ready — speak into your microphone. Press Enter to end.")
elif isinstance(event, TranscriptDeltaEvent):
marker = "»" if event.is_final else "…"
print(f"[{event.role.value}] {event.text}{marker}", flush=True)
elif isinstance(event, ErrorEvent):
print(f"[error] {event.code.value}: {event.message}", file=sys.stderr)
elif isinstance(event, SessionEndedEvent):
print(f"Session ended: {event.reason}")
async def main() -> None:
async with RealtimeClient() as client:
agent = client.agent(
instructions="You are a terse voice assistant.",
greeting="Hi — how can I help?",
)
async with agent.start() as session:
print(f"Connected — session_id={session.session_id}")
printer = asyncio.create_task(print_events(session))
await session.set_microphone_enabled(True)
await session.set_speaker_enabled(True)
loop = asyncio.get_event_loop()
try:
await loop.run_in_executor(None, sys.stdin.readline)
except (KeyboardInterrupt, EOFError):
pass
await session.end()
await printer
if __name__ == "__main__":
asyncio.run(main())SessionEndedEvent is the stream's terminal event, so the printer task finishes on its own once session.end() runs. Leaving the async with block also ends the session.
Run it
python quickstart.pyWhat you should see
Connected — session_id=3f2b8c1e-9a4d-4e7f-8b21-06d5c9a1f4e2
Ready — speak into your microphone. Press Enter to end.
[assistant] Hi — how can I help?»
[user] what's the tallest mountain…
[user] what's the tallest mountain in the world»
[assistant] Mount Everest, at 8,849 meters.»
Session ended: client endedNo microphone handy? Drive the session over the text channel instead: await session.send_text("Hello!") sends a text turn. hello_realtime.py in the examples repo is a complete text-only version, and examples/python/voice_cli/ is the full voice CLI this page is based on.
Call session methods
| Method | What it does |
|---|---|
send_text(content) | Send a text turn the agent answers. |
send_context(content) | Give the agent context without asking it anything — no turn, no speech. |
set_microphone_enabled(enabled) | Publish or unpublish the OS microphone. |
set_speaker_enabled(enabled) | Play or stop the agent's audio on the OS speaker. |
end() | End the session gracefully (also runs on async with exit). |
Troubleshooting
CredentialsError (code NO_CREDENTIAL) before the session starts.
Cause: nothing resolved — no COSMO_API_KEY in this shell and no credentials file.
Solution: run cosmo login. If you signed in under a named profile, set COSMO_PROFILE to match.
SessionStartError with an auth code.
Cause: the key is expired, revoked, or lacks the realtime:start scope.
Solution: run cosmo whoami — it names the workspace and says whether the credential can start sessions. For a dashboard key, confirm its scopes on the API keys page.
No audio in or out.
Cause: no input device is available, or the playback dependency is missing.
Solution: check that the default input device is the one you expect and that the process is allowed to use it; for playback on Linux, install PortAudio (apt install libportaudio2). See Audio.
Next steps
- Clients, agents, and sessions — the three-tier model in depth
- Events — every typed event the session yields
- Tools —
@tool-decorated Python functions the agent can call - Hooks — intercept session start, tool use, and speech timeouts
- Telephony — put the same agent on a phone call