From 217c04f8d18d8b74310517e570fbb591043247cc Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Mon, 9 Feb 2026 20:23:33 +0000 Subject: [PATCH 01/12] make function public --- src/anam/_streaming.py | 15 +-------------- src/anam/client.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/anam/_streaming.py b/src/anam/_streaming.py index 7db9990..f582818 100644 --- a/src/anam/_streaming.py +++ b/src/anam/_streaming.py @@ -669,20 +669,7 @@ def send_user_audio( sample_rate: int, num_channels: int, ) -> None: - """Send raw user audio samples to Anam for processing. - - This method accepts 16-bit PCM samples and adds them to the audio buffer for transmission via WebRTC. - The audio track is created lazily when first audio arrives. - Audio is only added to the buffer after the connection is established, to avoid accumulating stale audio. - - Args: - audio_bytes: Raw audio data (16-bit PCM). - sample_rate: Sample rate of the input audio (Hz). - num_channels: Number of channels in the input audio (1=mono, 2=stereo). - - Raises: - RuntimeError: If peer connection is not initialized. - """ + """Send raw user audio samples to Anam for processing.""" if not self._peer_connection: raise RuntimeError("Peer connection not initialized. Call connect() first.") if num_channels != 1 and num_channels != 2: diff --git a/src/anam/client.py b/src/anam/client.py index 73075d3..235aba3 100644 --- a/src/anam/client.py +++ b/src/anam/client.py @@ -587,6 +587,35 @@ def create_agent_audio_input_stream( raise SessionError("Not connected") return self._client._streaming_client.create_agent_audio_input_stream(config) + def send_user_audio( + self, + audio_bytes: bytes, + sample_rate: int, + num_channels: int, + ) -> None: + """Send raw user audio samples to Anam for processing. + + This method accepts 16-bit PCM samples and adds them to the audio buffer + for transmission via WebRTC. The audio track is created lazily when first + audio arrives. Audio is only added to the buffer after the connection is + established, to avoid accumulating stale audio. + + Args: + audio_bytes: Raw audio data (16-bit PCM). + sample_rate: Sample rate of the input audio (Hz). + num_channels: Number of channels in the input audio (1=mono, 2=stereo). + + Raises: + SessionError: If not connected. + """ + if not self._client._streaming_client: + raise SessionError("Not connected") + self._client._streaming_client.send_user_audio( + audio_bytes=audio_bytes, + sample_rate=sample_rate, + num_channels=num_channels, + ) + def video_frames(self) -> AsyncIterator[VideoFrame]: """Get video frames as an async iterator. From 25ffc7d21c62ff9a8938418821878dd52142b855 Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 10 Feb 2026 12:22:29 +0000 Subject: [PATCH 02/12] proper session end handling --- src/anam/_streaming.py | 28 ++++++++++++++++++++-------- src/anam/client.py | 38 ++++++++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/anam/_streaming.py b/src/anam/_streaming.py index f582818..7b2d3db 100644 --- a/src/anam/_streaming.py +++ b/src/anam/_streaming.py @@ -22,7 +22,7 @@ from ._agent_audio_input_stream import AgentAudioInputStream from ._signalling import SignalAction, SignallingClient from ._user_audio_input_track import UserAudioInputTrack -from .types import AgentAudioInputConfig, SessionInfo +from .types import AgentAudioInputConfig, ConnectionClosedCode, SessionInfo logger = logging.getLogger(__name__) @@ -75,6 +75,7 @@ def __init__( self._agent_audio_input_stream: AgentAudioInputStream | None = None self._user_audio_input_track: UserAudioInputTrack | None = None self._audio_transceiver = None # Store transceiver for lazy track creation + self._closing = False # True when client initiated close (avoid duplicate/error notification) async def connect(self, timeout: float = 30.0) -> None: """Start the streaming connection. @@ -136,7 +137,7 @@ async def _handle_signal_message(self, message: dict[str, Any]) -> None: reason = payload if isinstance(payload, str) else "Session ended by server" logger.info("Session ended by server: %s", reason) if self._on_connection_closed: - await self._on_connection_closed("server_closed", reason) + await self._on_connection_closed(ConnectionClosedCode.SERVER_CLOSED.value, reason) await self.close() elif action_type == SignalAction.WARNING.value: @@ -282,7 +283,7 @@ def on_ice_connection_state_change() -> None: if not self._peer_connection: return state = self._peer_connection.iceConnectionState - logger.info("ICE connection state: %s", state) + logger.debug("ICE connection state: %s", state) if state in ("connected", "completed"): if not self._is_connected: self._is_connected = True @@ -297,8 +298,12 @@ def on_ice_connection_state_change() -> None: if hasattr(self, "_connection_ready"): self._connection_ready.set() elif state == "closed": - if self._on_connection_closed: - asyncio.create_task(self._on_connection_closed("connection_closed", None)) + if not self._closing and self._on_connection_closed: + asyncio.create_task( + self._on_connection_closed( + ConnectionClosedCode.WEBRTC_FAILURE.value, None + ) + ) @self._peer_connection.on("connectionstatechange") def on_connection_state_change() -> None: @@ -348,12 +353,12 @@ async def _setup_data_channel(self) -> None: @self._data_channel.on("open") def on_open() -> None: - logger.info("Data channel opened") + logger.debug("Data channel opened") self._data_channel_open = True @self._data_channel.on("close") def on_close() -> None: - logger.info("Data channel closed") + logger.debug("Data channel closed") self._data_channel_open = False @self._data_channel.on("message") @@ -629,6 +634,12 @@ def audio_track(self) -> MediaStreamTrack | None: async def close(self) -> None: """Close the streaming connection and clean up resources.""" + if self._closing: + return + if self._peer_connection is None and self._signalling_client is None: + return + self._closing = True + self._on_connection_closed = None logger.debug("Closing streaming client") # Close signalling @@ -660,8 +671,9 @@ async def close(self) -> None: finally: self._peer_connection = None + self._closing = False self._is_connected = False - logger.info("Streaming client closed") + logger.debug("Streaming client closed") def send_user_audio( self, diff --git a/src/anam/client.py b/src/anam/client.py index 235aba3..64fb13b 100644 --- a/src/anam/client.py +++ b/src/anam/client.py @@ -18,6 +18,7 @@ AgentAudioInputConfig, AnamEvent, ClientOptions, + ConnectionClosedCode, Message, MessageRole, MessageStreamEvent, @@ -343,7 +344,7 @@ async def _handle_connection_established(self) -> None: async def _handle_connection_closed(self, code: str, reason: str | None) -> None: """Handle connection closed.""" - logger.info("Connection closed: %s %s", code, reason) + logger.debug("Connection closed") self._is_streaming = False await self._emit(AnamEvent.CONNECTION_CLOSED, code, reason) @@ -365,15 +366,28 @@ def create_agent_audio_input_stream( raise SessionError("Failed to create agent audio input stream: session is not started") return self._streaming_client.create_agent_audio_input_stream(config) - async def close(self) -> None: - """Close the connection and clean up resources.""" + async def close( + self, + close_code: str | None = ConnectionClosedCode.NORMAL.value, + ) -> None: + """Close the connection and clean up resources. + + When close_code is set, emit CONNECTION_CLOSED before tearing down. + When close_code is None (listener cleaning up after already being notified), + only tear down to avoid recursion. + """ + if not self._streaming_client: + self._session_info = None + self._is_streaming = False + return + if close_code is not None: + await self._handle_connection_closed(close_code, None) if self._streaming_client: await self._streaming_client.close() self._streaming_client = None - + logger.info("Client closed") self._session_info = None self._is_streaming = False - logger.info("Client closed") @property def is_streaming(self) -> bool: @@ -532,6 +546,7 @@ async def interrupt(self) -> None: raise SessionError("Data channel did not open in time") streaming.send_interrupt() + streaming._agent_audio_input_stream.end_sequence() async def send_talk_stream( self, @@ -675,9 +690,16 @@ async def wait_until_closed(self) -> None: """ await self._close_event.wait() - async def close(self) -> None: - """Close the session.""" - await self._client.close() + async def close( + self, + close_code: str | None = ConnectionClosedCode.NORMAL.value, + ) -> None: + """Close the session. + + Pass close_code=None when the connection was already notified (e.g. + listener cleaning up after CONNECTION_CLOSED) to avoid double-notify. + """ + await self._client.close(close_code=close_code) self._closed = True self._close_event.set() From 3afd74249a5269374a76f3cfa7ca119b55d3cc71 Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 10 Feb 2026 12:58:47 +0000 Subject: [PATCH 03/12] clear message history on close to avoid leaks --- src/anam/client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/anam/client.py b/src/anam/client.py index 64fb13b..8e70208 100644 --- a/src/anam/client.py +++ b/src/anam/client.py @@ -379,6 +379,7 @@ async def close( if not self._streaming_client: self._session_info = None self._is_streaming = False + self._message_history.clear() return if close_code is not None: await self._handle_connection_closed(close_code, None) @@ -388,6 +389,7 @@ async def close( logger.info("Client closed") self._session_info = None self._is_streaming = False + self._message_history.clear() @property def is_streaming(self) -> bool: From 69d7d21afd010996560d13473349ce32055c5e38 Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 10 Feb 2026 14:01:57 +0000 Subject: [PATCH 04/12] interrupt should not send end_sequence, as we lack context of buffers --- src/anam/client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/anam/client.py b/src/anam/client.py index 8e70208..5bc7ee0 100644 --- a/src/anam/client.py +++ b/src/anam/client.py @@ -548,7 +548,6 @@ async def interrupt(self) -> None: raise SessionError("Data channel did not open in time") streaming.send_interrupt() - streaming._agent_audio_input_stream.end_sequence() async def send_talk_stream( self, From 53d2a3b30a172136f79a1e41c74afcc5be2d601c Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 10 Feb 2026 14:03:54 +0000 Subject: [PATCH 05/12] lint --- src/anam/_streaming.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/anam/_streaming.py b/src/anam/_streaming.py index 7b2d3db..bcbe519 100644 --- a/src/anam/_streaming.py +++ b/src/anam/_streaming.py @@ -75,7 +75,9 @@ def __init__( self._agent_audio_input_stream: AgentAudioInputStream | None = None self._user_audio_input_track: UserAudioInputTrack | None = None self._audio_transceiver = None # Store transceiver for lazy track creation - self._closing = False # True when client initiated close (avoid duplicate/error notification) + self._closing = ( + False # True when client initiated close (avoid duplicate/error notification) + ) async def connect(self, timeout: float = 30.0) -> None: """Start the streaming connection. @@ -300,9 +302,7 @@ def on_ice_connection_state_change() -> None: elif state == "closed": if not self._closing and self._on_connection_closed: asyncio.create_task( - self._on_connection_closed( - ConnectionClosedCode.WEBRTC_FAILURE.value, None - ) + self._on_connection_closed(ConnectionClosedCode.WEBRTC_FAILURE.value, None) ) @self._peer_connection.on("connectionstatechange") From d64572649d2bfbe374955211002bff9d2c769251 Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 10 Feb 2026 16:14:13 +0000 Subject: [PATCH 06/12] add callback handling for session ready --- src/anam/_streaming.py | 5 +++++ src/anam/client.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/anam/_streaming.py b/src/anam/_streaming.py index bcbe519..44ae747 100644 --- a/src/anam/_streaming.py +++ b/src/anam/_streaming.py @@ -41,6 +41,7 @@ def __init__( on_message: Callable[[dict[str, Any]], Awaitable[None]] | None = None, on_connection_established: Callable[[], Awaitable[None]] | None = None, on_connection_closed: Callable[[str, str | None], Awaitable[None]] | None = None, + on_session_ready: Callable[[], Awaitable[None]] | None = None, custom_ice_servers: list[dict[str, Any]] | None = None, ): """Initialize the streaming client. @@ -50,6 +51,7 @@ def __init__( on_message: Callback for data channel messages. on_connection_established: Callback when connected. on_connection_closed: Callback when disconnected. + on_session_ready: Callback when sessionready signal is received (ready to receive TTS). custom_ice_servers: Custom ICE servers (optional). """ self._session_info = session_info @@ -59,6 +61,7 @@ def __init__( self._on_message = on_message self._on_connection_established = on_connection_established self._on_connection_closed = on_connection_closed + self._on_session_ready = on_session_ready # Configuration self._ice_servers = custom_ice_servers or session_info.ice_servers @@ -147,6 +150,8 @@ async def _handle_signal_message(self, message: dict[str, Any]) -> None: elif action_type == SignalAction.SESSION_READY.value: logger.info("Session ready") + if self._on_session_ready: + await self._on_session_ready() elif action_type == SignalAction.TALK_STREAM_INTERRUPTED.value: correlation_id = payload.get("correlationId") if isinstance(payload, dict) else None diff --git a/src/anam/client.py b/src/anam/client.py index 5bc7ee0..7e4e1b4 100644 --- a/src/anam/client.py +++ b/src/anam/client.py @@ -246,6 +246,7 @@ async def connect_async(self) -> "Session": on_message=self._handle_data_message, on_connection_established=self._handle_connection_established, on_connection_closed=self._handle_connection_closed, + on_session_ready=self._handle_session_ready, custom_ice_servers=self._options.ice_servers, ) @@ -342,6 +343,10 @@ async def _handle_connection_established(self) -> None: logger.info("Connection established") await self._emit(AnamEvent.CONNECTION_ESTABLISHED) + async def _handle_session_ready(self) -> None: + """Handle session ready (signalling: ready to receive TTS).""" + await self._emit(AnamEvent.SESSION_READY) + async def _handle_connection_closed(self, code: str, reason: str | None) -> None: """Handle connection closed.""" logger.debug("Connection closed") From e8164c4df5016d2366c7ec392879f22385a1acc0 Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 10 Feb 2026 19:38:46 +0000 Subject: [PATCH 07/12] trigger callback on close, do not use connection states for close, call close when an error happens --- src/anam/_streaming.py | 36 ++++++++++++++++--------- src/anam/_user_audio_input_track.py | 4 --- src/anam/client.py | 41 +++++++---------------------- 3 files changed, 33 insertions(+), 48 deletions(-) diff --git a/src/anam/_streaming.py b/src/anam/_streaming.py index bcbe519..d050be1 100644 --- a/src/anam/_streaming.py +++ b/src/anam/_streaming.py @@ -75,9 +75,7 @@ def __init__( self._agent_audio_input_stream: AgentAudioInputStream | None = None self._user_audio_input_track: UserAudioInputTrack | None = None self._audio_transceiver = None # Store transceiver for lazy track creation - self._closing = ( - False # True when client initiated close (avoid duplicate/error notification) - ) + self._closing = False async def connect(self, timeout: float = 30.0) -> None: """Start the streaming connection. @@ -299,11 +297,6 @@ def on_ice_connection_state_change() -> None: ) if hasattr(self, "_connection_ready"): self._connection_ready.set() - elif state == "closed": - if not self._closing and self._on_connection_closed: - asyncio.create_task( - self._on_connection_closed(ConnectionClosedCode.WEBRTC_FAILURE.value, None) - ) @self._peer_connection.on("connectionstatechange") def on_connection_state_change() -> None: @@ -311,6 +304,15 @@ def on_connection_state_change() -> None: return state = self._peer_connection.connectionState logger.debug("Connection state: %s", state) + if state == "closed": + # Only emit CONNECTION_CLOSED when the connection was lost (e.g. network), + # not when we initiated close() (client calls on_connection_closed itself). + if not self._closing and self._on_connection_closed: + asyncio.create_task( + self._on_connection_closed( + ConnectionClosedCode.WEBRTC_FAILURE.value, None + ) + ) @self._peer_connection.on("track") def on_track(track: MediaStreamTrack) -> None: @@ -636,10 +638,7 @@ async def close(self) -> None: """Close the streaming connection and clean up resources.""" if self._closing: return - if self._peer_connection is None and self._signalling_client is None: - return self._closing = True - self._on_connection_closed = None logger.debug("Closing streaming client") # Close signalling @@ -681,7 +680,20 @@ def send_user_audio( sample_rate: int, num_channels: int, ) -> None: - """Send raw user audio samples to Anam for processing.""" + """Send raw user audio samples to Anam for processing. + + This method accepts 16-bit PCM samples and adds them to the audio buffer for transmission via WebRTC. + The audio track is created lazily when first audio arrives. + Audio is only added to the buffer after the connection is established, to avoid accumulating stale audio. + + Args: + audio_bytes: Raw audio data (16-bit PCM). + sample_rate: Sample rate of the input audio (Hz). + num_channels: Number of channels in the input audio (1=mono, 2=stereo). + + Raises: + RuntimeError: If peer connection is not initialized. + """ if not self._peer_connection: raise RuntimeError("Peer connection not initialized. Call connect() first.") if num_channels != 1 and num_channels != 2: diff --git a/src/anam/_user_audio_input_track.py b/src/anam/_user_audio_input_track.py index d8e52f0..9cd88df 100644 --- a/src/anam/_user_audio_input_track.py +++ b/src/anam/_user_audio_input_track.py @@ -53,10 +53,6 @@ def __init__(self, sample_rate: int, num_channels: int): # Flag to indicate if track is closed self._is_closed = False - # Flag to flush buffer on first recv() - handles audio that accumulated - # between track connection and WebRTC starting to pull frames - self._first_recv = True - # Lock for thread-safe buffer access self._lock = asyncio.Lock() diff --git a/src/anam/client.py b/src/anam/client.py index 5bc7ee0..bf6068c 100644 --- a/src/anam/client.py +++ b/src/anam/client.py @@ -366,30 +366,16 @@ def create_agent_audio_input_stream( raise SessionError("Failed to create agent audio input stream: session is not started") return self._streaming_client.create_agent_audio_input_stream(config) - async def close( - self, - close_code: str | None = ConnectionClosedCode.NORMAL.value, - ) -> None: - """Close the connection and clean up resources. - - When close_code is set, emit CONNECTION_CLOSED before tearing down. - When close_code is None (listener cleaning up after already being notified), - only tear down to avoid recursion. - """ - if not self._streaming_client: - self._session_info = None + async def close(self) -> None: + """Close the connection and clean up resources.""" + if self._streaming_client and self._is_streaming: self._is_streaming = False - self._message_history.clear() - return - if close_code is not None: - await self._handle_connection_closed(close_code, None) - if self._streaming_client: + await self._handle_connection_closed(ConnectionClosedCode.NORMAL.value, None) await self._streaming_client.close() self._streaming_client = None + self._session_info = None + self._message_history.clear() logger.info("Client closed") - self._session_info = None - self._is_streaming = False - self._message_history.clear() @property def is_streaming(self) -> bool: @@ -691,18 +677,9 @@ async def wait_until_closed(self) -> None: """ await self._close_event.wait() - async def close( - self, - close_code: str | None = ConnectionClosedCode.NORMAL.value, - ) -> None: - """Close the session. - - Pass close_code=None when the connection was already notified (e.g. - listener cleaning up after CONNECTION_CLOSED) to avoid double-notify. - """ - await self._client.close(close_code=close_code) - self._closed = True - self._close_event.set() + async def close(self) -> None: + """Close the session.""" + await self._client.close() @property def is_active(self) -> bool: From cccdb161e486e1001dcdf7bd8d5456204c7c5181 Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 10 Feb 2026 20:37:18 +0000 Subject: [PATCH 08/12] set _closed when the session is closed for interaction --- src/anam/client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/anam/client.py b/src/anam/client.py index bf6068c..37a06f5 100644 --- a/src/anam/client.py +++ b/src/anam/client.py @@ -679,6 +679,8 @@ async def wait_until_closed(self) -> None: async def close(self) -> None: """Close the session.""" + self._closed = True + self._close_event.set() await self._client.close() @property From 49cbf0e9c0acc1313adfe91d9960c407c66a4e4f Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 10 Feb 2026 21:14:40 +0000 Subject: [PATCH 09/12] set webrtc timeout to 2s --- src/anam/_signalling.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/anam/_signalling.py b/src/anam/_signalling.py index a8cabb7..f4ad319 100644 --- a/src/anam/_signalling.py +++ b/src/anam/_signalling.py @@ -106,7 +106,9 @@ async def connect(self) -> None: """ logger.debug("Connecting to signalling server: %s", self._ws_url) try: - self._ws = await websockets.asyncio.client.connect(self._ws_url) + self._ws = await websockets.asyncio.client.connect( + self._ws_url, close_timeout=2.0 + ) self._connection_attempts = 0 logger.info("WebSocket connection established") From 011bd34e13ae0b1362e2a9f9266c39c389ed4bf4 Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 10 Feb 2026 21:28:00 +0000 Subject: [PATCH 10/12] use is_streaming property --- src/anam/_streaming.py | 3 +-- src/anam/client.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/anam/_streaming.py b/src/anam/_streaming.py index d050be1..d815f22 100644 --- a/src/anam/_streaming.py +++ b/src/anam/_streaming.py @@ -305,8 +305,7 @@ def on_connection_state_change() -> None: state = self._peer_connection.connectionState logger.debug("Connection state: %s", state) if state == "closed": - # Only emit CONNECTION_CLOSED when the connection was lost (e.g. network), - # not when we initiated close() (client calls on_connection_closed itself). + # Only emit CONNECTION_CLOSED when the connection was lost (e.g. network) if not self._closing and self._on_connection_closed: asyncio.create_task( self._on_connection_closed( diff --git a/src/anam/client.py b/src/anam/client.py index 37a06f5..49bf421 100644 --- a/src/anam/client.py +++ b/src/anam/client.py @@ -225,7 +225,7 @@ async def connect_async(self) -> "Session": You must call session.close() when done. Prefer using `async with client.connect()` instead. """ - if self._is_streaming: + if self.is_streaming: raise SessionError("Already connected. Call close() first.") logger.info("Connecting to Anam...") @@ -368,7 +368,7 @@ def create_agent_audio_input_stream( async def close(self) -> None: """Close the connection and clean up resources.""" - if self._streaming_client and self._is_streaming: + if self._streaming_client and self.is_streaming: self._is_streaming = False await self._handle_connection_closed(ConnectionClosedCode.NORMAL.value, None) await self._streaming_client.close() From dd234cfefb7a9f31c715e830c5001ab8706cc38b Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Tue, 10 Feb 2026 22:06:19 +0000 Subject: [PATCH 11/12] linter --- src/anam/_signalling.py | 4 +--- src/anam/_streaming.py | 4 +--- uv.lock | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/anam/_signalling.py b/src/anam/_signalling.py index f4ad319..dca9048 100644 --- a/src/anam/_signalling.py +++ b/src/anam/_signalling.py @@ -106,9 +106,7 @@ async def connect(self) -> None: """ logger.debug("Connecting to signalling server: %s", self._ws_url) try: - self._ws = await websockets.asyncio.client.connect( - self._ws_url, close_timeout=2.0 - ) + self._ws = await websockets.asyncio.client.connect(self._ws_url, close_timeout=2.0) self._connection_attempts = 0 logger.info("WebSocket connection established") diff --git a/src/anam/_streaming.py b/src/anam/_streaming.py index 292d6a3..f153d19 100644 --- a/src/anam/_streaming.py +++ b/src/anam/_streaming.py @@ -308,9 +308,7 @@ def on_connection_state_change() -> None: # Only emit CONNECTION_CLOSED when the connection was lost (e.g. network) if not self._closing and self._on_connection_closed: asyncio.create_task( - self._on_connection_closed( - ConnectionClosedCode.WEBRTC_FAILURE.value, None - ) + self._on_connection_closed(ConnectionClosedCode.WEBRTC_FAILURE.value, None) ) @self._peer_connection.on("track") diff --git a/uv.lock b/uv.lock index 9d44fe9..4bc310a 100644 --- a/uv.lock +++ b/uv.lock @@ -188,7 +188,7 @@ wheels = [ [[package]] name = "anam" -version = "0.1.0" +version = "0.2.0a2" source = { editable = "." } dependencies = [ { name = "aiohttp" }, From 1455bbc99cbc9db79700b506a1ffac97a0952cf9 Mon Sep 17 00:00:00 2001 From: sebvanleuven Date: Wed, 11 Feb 2026 14:07:29 +0000 Subject: [PATCH 12/12] improve comment on _handle_session_ready --- src/anam/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/anam/client.py b/src/anam/client.py index 8e7c719..6b8e6ee 100644 --- a/src/anam/client.py +++ b/src/anam/client.py @@ -344,7 +344,7 @@ async def _handle_connection_established(self) -> None: await self._emit(AnamEvent.CONNECTION_ESTABLISHED) async def _handle_session_ready(self) -> None: - """Handle session ready (signalling: ready to receive TTS).""" + """Handle session ready (signalling: ready to receive user audio or TTS).""" await self._emit(AnamEvent.SESSION_READY) async def _handle_connection_closed(self, code: str, reason: str | None) -> None: