
Kami Memadam 5,000 Baris Kod Audio — dan Transkrip Mula Muncul Dua Kali
GeekBye v1.6 mencabut dua transkriber Swift atas peranti dan menggantikannya dengan satu saluran paip bersatu — pemadaman bersih lebih 5,000 baris, dilakukan sewaktu orang berada di tengah-tengah mesyuarat dalam apl. Kemudian transkrip mula muncul dua kali, kerana pepijat itu berpindah ke satu lapisan yang masih menyangka ada dua enjin.
Ada satu jenis kegerunan tertentu dalam me-refactor kod yang sedang berjalan sekarang ini, untuk orang yang sedang di tengah-tengah mesyuarat. Anda tidak boleh menutup ciri itu untuk penyelenggaraan — seseorang bergantung padanya untuk mentranskripsi perbualan yang tidak boleh mereka ulang. GeekBye v1.6 melakukan tepat begitu pada laluan paling kritikal dalam apl: saluran paip audio. Ini kisah tentang apa yang kami padam, apa yang rosak, dan dua pengajaran kebolehpercayaan yang terhasil daripadanya.
Dua enjin, salah satunya dalam Swift
Sebelum v1.6, pertuturan-ke-teks GeekBye berjalan atas peranti dalam binari Swift. Ada dua daripadanya: satu membalut rangka kerja Speech Apple, dan satu lagi yang lebih besar (lebih 1,500 baris) yang menangkap audio berkembar — mikrofon anda tambah audio sistem — melalui ScreenCaptureKit dan membuka WebSocket-nya sendiri terus ke backend kami untuk mengalirkan ke Deepgram. Dua transkriber, dua laluan tangkapan, dua cara bercakap dengan rangkaian, semuanya dalam bahasa terkompil yang bermakna setiap perubahan memerlukan pengkompilan semula sebuah binari.
v1.6.0 meruntuhkan kesemuanya menjadi satu saluran paip. Commit penyatu tunggal itu memadam transkriber Apple Speech (~1,145 baris), penangkap Swift Deepgram (~1,523 baris), kedua-dua jambatan TypeScript, orkestrator lama, dan pengendali IPC audio berkembar — pemadaman bersih lebih 5,200 baris. Sebagai gantinya: renderer menangkap audio melalui Web APIs standard, menghantarnya sebagai PCM melalui IPC, dan proses utama Electron memiliki satu laluan WebSocket ke backend, dengan penyedia dipilih oleh konfigurasi backend. Satu laluan tangkapan, satu pemilik soket, satu tempat untuk berfikir.
Memadam lima ribu baris terasa hebat. Selama kira-kira sehari.
Pepijat itu berpindah ke lapisan yang masih menyangka ada dua
Kemudian transkrip mula muncul dua kali. Satu ayat yang dituturkan akan disimpan sebagai dua entri yang sama — kadangkala lebih teruk.
Punca asasnya ialah perkara paling mengajar dalam keseluruhan keluaran ini, kerana ia adalah hukum umum penyatuan. Kami telah menyatukan penyedia dalam proses utama — satu soket, satu pengendali. Tetapi lapisan React belum menerima memo itu: ia masih memasang dua cangkuk peristiwa khusus penyedia serentak, satu dibina untuk Deepgram dan satu untuk ElevenLabs. Kedua-duanya mendengar. Kedua-duanya menyimpan. Setiap transkrip dikekalkan oleh mana-mana cangkuk yang hidup — dan kedua-duanya hidup.
Dan ada penggandaan kedua yang lebih licik di bawahnya: ElevenLabs memancarkan setiap baris muktamad dua kali — sekali sebagai peristiwa committed dan sekali lagi sebagai committed_with_timestamps. Jadi walaupun dengan satu cangkuk, satu ayat boleh tiba sebagai dua peristiwa.
Pembaikannya (dalam barisan v1.6) ialah dwibahagian yang kemas yang memetakan tepat pada dua punca: pagar setiap cangkuk peristiwa di sebalik bendera enabled yang dipacu oleh konfigurasi penyedia aktif backend, supaya hanya seorang pendengar sahaja pernah hidup; dan tambah penyahduaan teks bagi setiap sumber (ingat baris terakhir yang disimpan untuk mikrofon dan untuk audio sistem) untuk menelan pancaran berganda ElevenLabs.
Pengajarannya lebih besar daripada pepijat ini: apabila anda menyatukan dua backend di sebalik satu antara muka, penduaan tidak lenyap — ia berhijrah ke lapisan yang masih menganggap ada dua. Kami meruntuhkan saluran paip dalam proses utama dan kebocorannya timbul dua lapisan di atas, dalam pendawaian peristiwa UI. Menyatukan sebuah saluran paip belum selesai apabila pengeluar telah disatukan; ia selesai apabila setiap pengguna telah berhenti mempercayai bentuk lama.
Sambungan semula pertama: "menyerah selepas lima cubaan"
v1.6.0 juga menghantar sambungan semula automatik WebSocket pertama GeekBye dengan exponential backoff — dan ia berbaloi ditunjukkan justeru kerana ia adalah nenek moyang primitif kepada versi kalis peluru yang kami jalankan hari ini.
Yang asalnya terhad. Lima cubaan, dengan kelewatan 1s, 2s, 4s, 8s, 16s — dan kemudian ia menyerah, memancarkan ralat maut: "Sambungan terputus — sila mula semula transkripsi." Pada masa itu ia terasa bertanggungjawab: jangan cuba selama-lamanya, beritahu pengguna dengan jujur. Pada amalannya ia adalah pepijat UX yang menyamar. Gangguan rangkaian enam belas saat — pertukaran Wi-Fi, sambungan semula VPN, sebuah terowong — bersifat sementara, tetapi cubaan semula terhad menganggapnya sebagai muktamad. Pengguna tidak melakukan apa-apa salah dan disuruh mula semula mesyuarat yang sedang berlangsung.
Itulah tepat kegagalan yang dihapuskan oleh reka bentuk semasa. Hari ini GeekBye menyambung semula dengan backoff tanpa had dan malah menggilir penyedia, dan hanya berhenti atas sebab yang benar-benar maut — auth, kuota, pengebilan. Keseluruhan perjalanan daripada "menyerah selepas lima" kepada "jangan sekali-kali menyerah pada ralat sementara" diceritakan dalam mengapa pencatat nota AI anda berhenti pada Wi-Fi yang buruk. v1.6 ialah tempat jalan itu bermula, dengan versi naif yang perlu wujud dahulu.
Sambungan basi menghantui anda melainkan ia mempunyai nama
Satu lagi pengajaran kebolehpercayaan mendarat dalam v1.6.3, membaiki satu kelas pepijat halus yang lambat-laun akan ditemui sesiapa yang membuat sambungan semula atas backend berkeadaan.
Apabila anda menyambung semula — atau menukar bahasa transkripsi di tengah sesi, yang menyambung semula di sebalik tabir — sambungan lama tidak selalu mati dengan senyap. Mesej sakaratnya (CONNECTION_LOST, disconnected) boleh tiba selepas sambungan baharu sudah sihat, dan meruntuhkan pengganti yang sempurna baik itu. Pembaikannya memberikan setiap percubaan sambungan satu identiti — ID sesi bagi setiap sambungan yang dicap pada URL WebSocket — tambah satu tingkap tempoh belas kasihan ringkas selepas sambungan semula yang ketika itu mesej pemutusan basi daripada percubaan terdahulu diabaikan. Ia juga menghantar mesej stop yang jelas sebelum menutup soket supaya backend boleh meruntuhkan sesi lama dengan bersih.
Prinsipnya: sebarang logik sambungan semula atas backend berkeadaan perlu menandakan setiap percubaan dengan identiti dan menganggap mesej lewat daripada percubaan terdahulu sebagai hingar — jika tidak, sambungan semula yang berjaya akan dibunuh oleh nafas terakhir pendahulunya.
Tiga perkara yang diajar v1.6
- Penyatuan menempatkan semula pepijat; ia tidak memadamnya. Runtuhkan dua enjin menjadi satu dan audit setiap pengguna yang masih menyangka ada dua. Milik kami ialah UI, yang memasang dua cangkuk peristiwa selepas saluran paipnya sudah bergabung.
- Sambungan semula pertama anda akan terhad, dan terhad itu salah untuk strim langsung. "Menyerah selepas N cubaan" menukar gangguan sementara menjadi kegagalan muktamad yang penggunanya dipersalahkan. Bezakan maut (auth, kuota) daripada sementara (soket yang terputus) daripada versi pertama sekali.
- Sambungan semula memerlukan identiti. Tandakan setiap percubaan; abaikan mesej lewat daripada percubaan yang mati. Sambungan tanpa nama boleh dibunuh oleh hantunya sendiri.
Ini ialah bab kedua kisah kebolehpercayaan yang menjadi GeekBye v2. Untuk yang pertama, lihat saga kemas kini automatik yang mengambil enam keluaran dalam empat hari (v1.5.x); untuk ke mana kerja sambungan semula akhirnya tiba, mengapa pencatat nota AI anda berhenti pada Wi-Fi yang buruk; dan untuk keseluruhan lengkoknya, anatomi mengeluarkan perisian hingga ke kesempurnaan.