comparison 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
comparison
equal deleted inserted replaced
279:b3b547563ec7 280:49e9e591c9bb
52 def on_open() -> None: 52 def on_open() -> None:
53 self.send("ready", sessionId=self.session_id) 53 self.send("ready", sessionId=self.session_id)
54 54
55 @channel.on("message") 55 @channel.on("message")
56 def on_message(message: Any) -> None: 56 def on_message(message: Any) -> None:
57 if message == "stop": 57 if message in {"commit", "pause", "stop"}:
58 self.stopping = True 58 if message == "stop":
59 self.stopping = True
60 if self.partial_task and not self.partial_task.done():
61 self.partial_task.cancel()
59 event = self.segmenter.flush() 62 event = self.segmenter.flush()
60 if event and event.samples is not None: 63 if event and event.samples is not None:
61 self._start_final(event.samples) 64 self._start_final(event.samples)
62 else: 65 else:
63 self.send("speech.ended") 66 self.send("speech.ended")
153 {"type": event_type, **payload}, 156 {"type": event_type, **payload},
154 separators=(",", ":"), 157 separators=(",", ":"),
155 ) 158 )
156 ) 159 )
157 160
161 async def _cancel_pending_ice_transactions(self) -> None:
162 # aioice 0.10.2 can leave STUN retry timers armed after its datagram
163 # transport closes. Cancel their futures first so Transaction.run()
164 # clears each timer before RTCPeerConnection.close() drops sockets.
165 ice_transports = getattr(
166 self.peer,
167 "_RTCPeerConnection__iceTransports",
168 (),
169 )
170 for ice_transport in ice_transports:
171 connection = getattr(ice_transport, "_connection", None)
172 for protocol in getattr(connection, "_protocols", ()):
173 for transaction in tuple(
174 getattr(protocol, "transactions", {}).values()
175 ):
176 future = getattr(transaction, "_Transaction__future", None)
177 if future is not None and not future.done():
178 future.cancel()
179 await asyncio.sleep(0)
180
158 async def close(self) -> None: 181 async def close(self) -> None:
159 if self.closed: 182 if self.closed:
160 return 183 return
161 self.closed = True 184 self.closed = True
162 tasks = [ 185 tasks = [
172 task.cancel() 195 task.cancel()
173 if tasks: 196 if tasks:
174 await asyncio.gather(*tasks, return_exceptions=True) 197 await asyncio.gather(*tasks, return_exceptions=True)
175 if self.channel and self.channel.readyState != "closed": 198 if self.channel and self.channel.readyState != "closed":
176 self.channel.close() 199 self.channel.close()
200 await self._cancel_pending_ice_transactions()
177 await self.peer.close() 201 await self.peer.close()