Mercurial
comparison dictation/webrtc_smoke.py @ 275:78699f810817
Add Qwen3-VL and WebRTC dictation services
Add Bazel targets for the CUDA-backed Qwen3-VL server and a local WebRTC faster-whisper dictation service.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: e3d8cb06-6c95-4ae0-9757-651d3796ab00
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 10:58:47 -0700 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 274:c9be578316a6 | 275:78699f810817 |
|---|---|
| 1 from __future__ import annotations | |
| 2 | |
| 3 import argparse | |
| 4 import asyncio | |
| 5 import json | |
| 6 | |
| 7 from aiortc import RTCPeerConnection, RTCSessionDescription | |
| 8 from aiortc.contrib.media import MediaPlayer | |
| 9 import httpx | |
| 10 | |
| 11 | |
| 12 async def run(server_url: str, audio_path: str, timeout: float) -> None: | |
| 13 peer = RTCPeerConnection() | |
| 14 channel = peer.createDataChannel("transcripts") | |
| 15 player = MediaPlayer(audio_path) | |
| 16 if player.audio is None: | |
| 17 raise RuntimeError("Input file does not contain an audio track") | |
| 18 peer.addTrack(player.audio) | |
| 19 final_received = asyncio.Event() | |
| 20 session_id = None | |
| 21 | |
| 22 @channel.on("message") | |
| 23 def on_message(message): | |
| 24 event = json.loads(message) | |
| 25 print(json.dumps(event, ensure_ascii=False), flush=True) | |
| 26 if event.get("type") == "transcript.final": | |
| 27 final_received.set() | |
| 28 | |
| 29 try: | |
| 30 offer = await peer.createOffer() | |
| 31 await peer.setLocalDescription(offer) | |
| 32 async with httpx.AsyncClient(timeout=timeout) as client: | |
| 33 response = await client.post( | |
| 34 f"{server_url}/api/webrtc/offer", | |
| 35 json={ | |
| 36 "sdp": peer.localDescription.sdp, | |
| 37 "type": peer.localDescription.type, | |
| 38 }, | |
| 39 ) | |
| 40 response.raise_for_status() | |
| 41 answer = response.json() | |
| 42 session_id = answer["sessionId"] | |
| 43 await peer.setRemoteDescription( | |
| 44 RTCSessionDescription( | |
| 45 sdp=answer["sdp"], | |
| 46 type=answer["type"], | |
| 47 ) | |
| 48 ) | |
| 49 await asyncio.wait_for(final_received.wait(), timeout=timeout) | |
| 50 await client.post( | |
| 51 f"{server_url}/api/webrtc/session/{session_id}/close" | |
| 52 ) | |
| 53 finally: | |
| 54 await peer.close() | |
| 55 | |
| 56 | |
| 57 def main() -> None: | |
| 58 parser = argparse.ArgumentParser() | |
| 59 parser.add_argument("audio") | |
| 60 parser.add_argument( | |
| 61 "--server", | |
| 62 default="http://127.0.0.1:8090", | |
| 63 ) | |
| 64 parser.add_argument("--timeout", type=float, default=60.0) | |
| 65 args = parser.parse_args() | |
| 66 asyncio.run(run(args.server.rstrip("/"), args.audio, args.timeout)) | |
| 67 | |
| 68 | |
| 69 if __name__ == "__main__": | |
| 70 main() |