Skip to content

webex_byova.media

BYOVA gRPC media server — async handler API for voice virtual agent streaming.

Optional install

This module requires the media extra:

pip install webex-byova[media]

Overview

webex_byova.media

BYOVA gRPC media server — developer-facing API (requires webex-byova[media]).

MediaServerConfig

Bases: BaseModel

Validated server-wide settings for the BYOVA media server.

chunk_bytes property

chunk_bytes: int

Raw μ-law bytes per outbound chunk.

from_env classmethod

from_env() -> MediaServerConfig

Build configuration from WEBEX_MEDIA_* environment variables.

AudioInputEvent

Bases: BaseModel

Inbound caller audio chunk.

BargeInEvent

Bases: BaseModel

Caller interrupted bot playback.

DtmfInputEvent

Bases: BaseModel

Inbound DTMF digits.

ErrorEvent

Bases: BaseModel

Recoverable or fatal handler/server error.

NoInputEvent

Bases: BaseModel

Caller did not provide input within the timeout.

SessionEndEvent

Bases: BaseModel

Session terminated.

SessionStartEvent

Bases: BaseModel

Fired when a new call session begins.

TurnEndedEvent

Bases: BaseModel

Turn stream closed.

TurnStartedEvent

Bases: BaseModel

A new turn stream opened.

DuplicateTurnStreamError

Bases: MediaServerError

Raised when a duplicate concurrent turn stream is rejected.

MediaConfigError

Bases: MediaServerError

Raised when MediaServerConfig validation fails.

MediaServerError

Bases: WebexBYOVAError

Base exception for media server errors.

PromptValidationError

Bases: MediaServerError

Raised when prompt audio format or content is invalid.

ProxyBufferOverflowError

Bases: MediaServerError

Raised when the proxy buffer exceeds configured limits.

ProxyConnectionError

Bases: MediaServerError

Raised when the WebSocket proxy backend is unreachable.

PromptRequest

Bases: BaseModel

Developer request to play audio or TTS to the caller.

PromptResponse

Bases: BaseModel

Result of a play_prompt operation.

DefaultProxyAdapter

JSON message mapping per websocket-proxy contract.

ProxyAdapter

Bases: Protocol

Map media events to backend messages and vice versa.

WebSocketProxyConnector

Bridge media sessions to an external WebSocket voice AI backend.

attach

attach(server: BYOVAMediaServer) -> None

Attach to a media server instance.

forward_event async

forward_event(event: MediaEvent, session: MediaSession) -> None

Forward SDK event to backend WebSocket.

disconnect_session async

disconnect_session(conversation_id: str) -> None

Close backend connection for a session.

BYOVAMediaServer

Developer-hosted BYOVA gRPC media server.

from_env classmethod

from_env() -> BYOVAMediaServer

Construct server from WEBEX_MEDIA_* environment variables.

on

on(event: str) -> Callable[[Handler], Handler]

Decorator to register an async or sync event handler.

handler

handler(event: str) -> Callable[[Handler], Handler]

Alias for :meth:on.

use_proxy

use_proxy(connector: Any) -> None

Attach a WebSocket proxy connector.

start async

start() -> None

Bind and start the gRPC server.

stop async

stop(grace: float = 5.0) -> None

Gracefully stop the server.

serve async

serve() -> None

Start the server and wait until interrupted.

MediaSession

Long-lived call state keyed by conversation_id.

active_turn property

active_turn: TurnContext | None

Currently active turn, if any.

bind_turn

bind_turn(turn: TurnContext) -> None

Attach active turn context.

play_prompt async

play_prompt(*, text: str | None = None, audio: bytes | None = None, ssml: str | None = None, barge_in: bool | None = None) -> PromptResponse

Play prompt on the active turn.

collect_input async

collect_input(*, mode: str | None = None, timeout: float | None = None) -> AudioInputEvent | DtmfInputEvent

Await caller input on the active turn.

end_session async

end_session(reason: str = 'completed') -> None

Mark session ending and release resources.

TurnContext

Per-turn view during one bidirectional stream.

turn_id property

turn_id: str

Unique turn identifier.

turn_number property

turn_number: int

1-based turn index within the session.

is_active property

is_active: bool

Whether the turn stream is still open.

is_final property

is_final: bool

Whether the turn closed with RESPONSE_FINAL.

play_prompt async

play_prompt(*, text: str | None = None, audio: bytes | None = None, ssml: str | None = None, barge_in: bool | None = None) -> PromptResponse

Encode, chunk, and send outbound prompt audio.

collect_input async

collect_input(*, mode: str | None = None, timeout: float | None = None) -> AudioInputEvent | DtmfInputEvent

Wait for normalized caller input.

end_turn async

end_turn() -> None

Send RESPONSE_FINAL and close the turn stream.

BYOVAMediaServer

webex_byova.media.server.BYOVAMediaServer

Developer-hosted BYOVA gRPC media server.

config instance-attribute

config = config or MediaServerConfig()

_handlers instance-attribute

_handlers: dict[str, list[Handler]] = defaultdict(list)

_grpc_server instance-attribute

_grpc_server: Server | None = None

_conversation_store instance-attribute

_conversation_store = ConversationStore(ttl_seconds=max_session_duration or 3600.0)

_proxy instance-attribute

_proxy = None

_running instance-attribute

_running = False

__init__

__init__(config: MediaServerConfig | None = None) -> None

from_env classmethod

from_env() -> BYOVAMediaServer

Construct server from WEBEX_MEDIA_* environment variables.

on

on(event: str) -> Callable[[Handler], Handler]

Decorator to register an async or sync event handler.

handler

handler(event: str) -> Callable[[Handler], Handler]

Alias for :meth:on.

use_proxy

use_proxy(connector: Any) -> None

Attach a WebSocket proxy connector.

start async

start() -> None

Bind and start the gRPC server.

stop async

stop(grace: float = 5.0) -> None

Gracefully stop the server.

serve async

serve() -> None

Start the server and wait until interrupted.

__aenter__ async

__aenter__() -> BYOVAMediaServer

__aexit__ async

__aexit__(*args: object) -> None

_dispatch_event async

_dispatch_event(event_name: str, event: MediaEvent, session: MediaSession, turn: TurnContext | None) -> None

_invoke_handler async

_invoke_handler(fn: Handler, event: MediaEvent, session: MediaSession, turn: TurnContext | None) -> None

_handle_handler_error async

_handle_handler_error(exc: Exception, session: MediaSession, turn: TurnContext | None) -> None

_release_session async

_release_session(conversation_id: str, reason: str) -> None

MediaServerConfig

webex_byova.media.config.MediaServerConfig

Bases: BaseModel

Validated server-wide settings for the BYOVA media server.

host class-attribute instance-attribute

host: str = '0.0.0.0'

port class-attribute instance-attribute

port: int = Field(default=50051, ge=0, le=65535)

tls_cert class-attribute instance-attribute

tls_cert: str | None = None

tls_key class-attribute instance-attribute

tls_key: str | None = None

audio_mode class-attribute instance-attribute

audio_mode: AudioMode = 'chunked'

encoding class-attribute instance-attribute

encoding: Literal['mulaw'] = 'mulaw'

sample_rate class-attribute instance-attribute

sample_rate: Literal[8000, 16000] = 8000

channels class-attribute instance-attribute

channels: int = Field(default=1, ge=1, le=1)

chunk_size_ms class-attribute instance-attribute

chunk_size_ms: int = Field(default=20, ge=10, le=100)

input_mode class-attribute instance-attribute

input_mode: InputMode = 'voice'

barge_in_enabled class-attribute instance-attribute

barge_in_enabled: bool = False

no_input_timeout class-attribute instance-attribute

no_input_timeout: float = Field(default=5.0, gt=0)

turn_disconnect_timeout class-attribute instance-attribute

turn_disconnect_timeout: float | None = None

end_of_input_silence_ms class-attribute instance-attribute

end_of_input_silence_ms: int = Field(default=500, ge=0)

max_session_duration class-attribute instance-attribute

max_session_duration: float | None = Field(default=3600.0, gt=0)

verify_tokens class-attribute instance-attribute

verify_tokens: bool = True

proxy_buffer_limit class-attribute instance-attribute

proxy_buffer_limit: int = Field(default=65536, gt=0)

proxy_overflow_policy class-attribute instance-attribute

proxy_overflow_policy: OverflowPolicy = 'disconnect'

log_level class-attribute instance-attribute

log_level: str = 'INFO'

chunk_bytes property

chunk_bytes: int

Raw μ-law bytes per outbound chunk.

_validate_tls_pair

_validate_tls_pair() -> MediaServerConfig

from_env classmethod

from_env() -> MediaServerConfig

Build configuration from WEBEX_MEDIA_* environment variables.

MediaSession

webex_byova.media.session.MediaSession

Long-lived call state keyed by conversation_id.

conversation_id instance-attribute

conversation_id = conversation_id

session_id instance-attribute

session_id = str(uuid4())

metadata instance-attribute

metadata = metadata or {}

started_at instance-attribute

started_at = now(utc)

state instance-attribute

state = ACTIVE

turn_count instance-attribute

turn_count = 0

_config instance-attribute

_config = config

_server instance-attribute

_server = server

_active_turn instance-attribute

_active_turn: TurnContext | None = None

_input_futures instance-attribute

_input_futures: list[Future[AudioInputEvent | DtmfInputEvent]] = []

active_turn property

active_turn: TurnContext | None

Currently active turn, if any.

__init__

__init__(*, conversation_id: str, config: MediaServerConfig, server: BYOVAMediaServer, metadata: dict[str, Any] | None = None) -> None

bind_turn

bind_turn(turn: TurnContext) -> None

Attach active turn context.

play_prompt async

play_prompt(*, text: str | None = None, audio: bytes | None = None, ssml: str | None = None, barge_in: bool | None = None) -> PromptResponse

Play prompt on the active turn.

collect_input async

collect_input(*, mode: str | None = None, timeout: float | None = None) -> AudioInputEvent | DtmfInputEvent

Await caller input on the active turn.

end_session async

end_session(reason: str = 'completed') -> None

Mark session ending and release resources.

_resolve_input

_resolve_input(event: AudioInputEvent | DtmfInputEvent) -> None

_wait_for_input async

_wait_for_input(*, timeout: float | None) -> AudioInputEvent | DtmfInputEvent

TurnContext

webex_byova.media.session.TurnContext

Per-turn view during one bidirectional stream.

session instance-attribute

session = session

_config instance-attribute

_config = config

_send_response instance-attribute

_send_response = send_response

_close_stream_cb instance-attribute

_close_stream_cb = close_stream

_runtime instance-attribute

_runtime = TurnRuntime(turn_number=turn_number)

turn_id property

turn_id: str

Unique turn identifier.

turn_number property

turn_number: int

1-based turn index within the session.

is_active property

is_active: bool

Whether the turn stream is still open.

is_final property

is_final: bool

Whether the turn closed with RESPONSE_FINAL.

__init__

__init__(*, session: MediaSession, config: MediaServerConfig, send_response: Callable[[Any], Coroutine[Any, Any, None]], close_stream: Callable[[], Coroutine[Any, Any, None]], turn_number: int) -> None

play_prompt async

play_prompt(*, text: str | None = None, audio: bytes | None = None, ssml: str | None = None, barge_in: bool | None = None) -> PromptResponse

Encode, chunk, and send outbound prompt audio.

collect_input async

collect_input(*, mode: str | None = None, timeout: float | None = None) -> AudioInputEvent | DtmfInputEvent

Wait for normalized caller input.

end_turn async

end_turn() -> None

Send RESPONSE_FINAL and close the turn stream.

_emit_turn_ended async

_emit_turn_ended(reason: str) -> None

_send_platform async

_send_platform(response: Any) -> None

_close_stream async

_close_stream() -> None

Events

webex_byova.media.events.SessionStartEvent

Bases: BaseModel

Fired when a new call session begins.

webex_byova.media.events.AudioInputEvent

Bases: BaseModel

Inbound caller audio chunk.

webex_byova.media.events.DtmfInputEvent

Bases: BaseModel

Inbound DTMF digits.

webex_byova.media.events.BargeInEvent

Bases: BaseModel

Caller interrupted bot playback.

webex_byova.media.events.NoInputEvent

Bases: BaseModel

Caller did not provide input within the timeout.

webex_byova.media.events.TurnStartedEvent

Bases: BaseModel

A new turn stream opened.

webex_byova.media.events.TurnEndedEvent

Bases: BaseModel

Turn stream closed.

webex_byova.media.events.SessionEndEvent

Bases: BaseModel

Session terminated.

webex_byova.media.events.ErrorEvent

Bases: BaseModel

Recoverable or fatal handler/server error.

WebSocket proxy

webex_byova.media.proxy.connector.WebSocketProxyConnector

Bridge media sessions to an external WebSocket voice AI backend.

url instance-attribute

url = url

adapter instance-attribute

adapter = adapter or DefaultProxyAdapter()

reconnect instance-attribute

reconnect = reconnect

connect_timeout instance-attribute

connect_timeout = connect_timeout

_server instance-attribute

_server: BYOVAMediaServer | None = None

_connections instance-attribute

_connections: dict[str, Any] = {}

_buffers instance-attribute

_buffers: dict[str, bytearray] = {}

__init__

__init__(url: str, adapter: ProxyAdapter | None = None, *, reconnect: bool = False, connect_timeout: float = 10.0) -> None

attach

attach(server: BYOVAMediaServer) -> None

Attach to a media server instance.

forward_event async

forward_event(event: MediaEvent, session: MediaSession) -> None

Forward SDK event to backend WebSocket.

_read_loop async

_read_loop(ws: Any, session: MediaSession) -> None

disconnect_session async

disconnect_session(conversation_id: str) -> None

Close backend connection for a session.

webex_byova.media.proxy.adapter.ProxyAdapter

Bases: Protocol

Map media events to backend messages and vice versa.

to_backend

to_backend(event: MediaEvent, session: MediaSession) -> str | bytes

from_backend

from_backend(message: str | bytes, session: MediaSession) -> PromptRequest | None

webex_byova.media.proxy.adapter.DefaultProxyAdapter

JSON message mapping per websocket-proxy contract.

to_backend

to_backend(event: MediaEvent, session: MediaSession) -> str

from_backend

from_backend(message: str | bytes, session: MediaSession) -> PromptRequest | None

Prompts

webex_byova.media.prompts.PromptRequest

Bases: BaseModel

Developer request to play audio or TTS to the caller.

webex_byova.media.prompts.PromptResponse

Bases: BaseModel

Result of a play_prompt operation.

Exceptions

webex_byova.media.exceptions.MediaServerError

Bases: WebexBYOVAError

Base exception for media server errors.

webex_byova.media.exceptions.MediaConfigError

Bases: MediaServerError

Raised when MediaServerConfig validation fails.

webex_byova.media.exceptions.DuplicateTurnStreamError

Bases: MediaServerError

Raised when a duplicate concurrent turn stream is rejected.

webex_byova.media.exceptions.PromptValidationError

Bases: MediaServerError

Raised when prompt audio format or content is invalid.

webex_byova.media.exceptions.ProxyConnectionError

Bases: MediaServerError

Raised when the WebSocket proxy backend is unreachable.

webex_byova.media.exceptions.ProxyBufferOverflowError

Bases: MediaServerError

Raised when the proxy buffer exceeds configured limits.

Guides