diff dictation/session.py @ 280:49e9e591c9bb

Add persistent dictation, prewarmed WebRTC speech input, Copilot SDK routing, animated conversation lifecycle controls, parking, and architecture coverage.
author MrJuneJune <me@mrjunejune.com>
date Tue, 18 Aug 2026 19:14:53 -0700
parents 78699f810817
children
line wrap: on
line diff
--- a/dictation/session.py	Mon Aug 17 22:22:36 2026 -0700
+++ b/dictation/session.py	Tue Aug 18 19:14:53 2026 -0700
@@ -54,8 +54,11 @@
 
         @channel.on("message")
         def on_message(message: Any) -> None:
-            if message == "stop":
-                self.stopping = True
+            if message in {"commit", "pause", "stop"}:
+                if message == "stop":
+                    self.stopping = True
+                if self.partial_task and not self.partial_task.done():
+                    self.partial_task.cancel()
                 event = self.segmenter.flush()
                 if event and event.samples is not None:
                     self._start_final(event.samples)
@@ -155,6 +158,26 @@
             )
         )
 
+    async def _cancel_pending_ice_transactions(self) -> None:
+        # aioice 0.10.2 can leave STUN retry timers armed after its datagram
+        # transport closes. Cancel their futures first so Transaction.run()
+        # clears each timer before RTCPeerConnection.close() drops sockets.
+        ice_transports = getattr(
+            self.peer,
+            "_RTCPeerConnection__iceTransports",
+            (),
+        )
+        for ice_transport in ice_transports:
+            connection = getattr(ice_transport, "_connection", None)
+            for protocol in getattr(connection, "_protocols", ()):
+                for transaction in tuple(
+                    getattr(protocol, "transactions", {}).values()
+                ):
+                    future = getattr(transaction, "_Transaction__future", None)
+                    if future is not None and not future.done():
+                        future.cancel()
+        await asyncio.sleep(0)
+
     async def close(self) -> None:
         if self.closed:
             return
@@ -174,4 +197,5 @@
             await asyncio.gather(*tasks, return_exceptions=True)
         if self.channel and self.channel.readyState != "closed":
             self.channel.close()
+        await self._cancel_pending_ice_transactions()
         await self.peer.close()