
हमने ऑडियो कोड की 5,000 लाइनें हटाईं — और ट्रांसक्रिप्ट दो-दो बार दिखने लगे
GeekBye v1.6 ने डिवाइस पर चलने वाले दो Swift ट्रांसक्राइबर उखाड़ फेंके और उनकी जगह एक एकीकृत पाइपलाइन लगा दी — शुद्ध रूप से 5,000 से ज़्यादा लाइनों की कटौती, वह भी तब जब लोग ऐप पर मीटिंग के बीच में थे। फिर ट्रांसक्रिप्ट दो-दो बार दिखने लगे, क्योंकि बग उस एक परत तक खिसक गया जो अब भी मानती थी कि दो इंजन हैं।
अभी इसी वक़्त चल रहे कोड को, उन लोगों के लिए जो मीटिंग के बीच में हैं, रीफ़ैक्टर करने में एक खास किस्म की दहशत होती है। आप फ़ीचर को मेंटेनेंस के लिए बंद नहीं कर सकते — कोई उस पर भरोसा कर रहा है कि वह एक ऐसी बातचीत ट्रांसक्राइब करेगा जिसे वह दोहरा नहीं सकता। GeekBye v1.6 ने ऐप के सबसे नाज़ुक रास्ते के साथ ठीक यही किया: ऑडियो पाइपलाइन। यह कहानी है कि हमने क्या हटाया, क्या टूटा, और उससे निकले विश्वसनीयता के दो सबक।
दो इंजन, उनमें से एक Swift में
v1.6 से पहले, GeekBye की स्पीच-टू-टेक्स्ट डिवाइस पर Swift बाइनरी में चलती थी। उनमें से दो थीं: एक Apple के Speech फ़्रेमवर्क को लपेटती हुई, और एक बड़ी (1,500 से ज़्यादा लाइनें) जो दोहरा ऑडियो कैप्चर करती थी — आपका माइक और सिस्टम ऑडियो — ScreenCaptureKit के ज़रिए, और अपना खुद का WebSocket सीधे हमारे बैकएंड तक खोलकर Deepgram पर स्ट्रीम करती थी। दो ट्रांसक्राइबर, दो कैप्चर रास्ते, नेटवर्क से बात करने के दो तरीके, सब एक कम्पाइल की जाने वाली भाषा में जिसका मतलब था हर बदलाव पर एक बाइनरी दोबारा कम्पाइल करना।
v1.6.0 ने यह सब समेटकर एक पाइपलाइन बना दिया। एक ही एकीकरण करने वाले कमिट ने Apple Speech ट्रांसक्राइबर (~1,145 लाइनें), Deepgram Swift कैप्चरर (~1,523 लाइनें), दोनों TypeScript ब्रिज, पुराना ऑर्केस्ट्रेटर, और दोहरे-ऑडियो IPC हैंडलर हटा दिए — शुद्ध रूप से 5,200 से ज़्यादा लाइनों की कटौती। उनकी जगह: रेंडरर मानक Web APIs के ज़रिए ऑडियो कैप्चर करता है, उसे IPC पर PCM के रूप में भेजता है, और Electron मेन प्रोसेस बैकएंड तक एक अकेले WebSocket रास्ते का मालिक होता है, जहाँ प्रोवाइडर बैकएंड कॉन्फ़िग से चुना जाता है। एक कैप्चर रास्ता, एक सॉकेट मालिक, सोचने के लिए एक ही जगह।
पाँच हज़ार लाइनें हटाना शानदार लगता है। करीब एक दिन के लिए।
बग उस परत तक खिसक गया जो अब भी मानती थी कि दो हैं
फिर ट्रांसक्रिप्ट दो-दो बार दिखने लगे। एक बोला गया वाक्य दो एक जैसी एंट्री के रूप में सहेजा जाता — कभी-कभी उससे भी बुरा।
मूल कारण इस पूरी रिलीज़ में सबसे शिक्षाप्रद चीज़ है, क्योंकि यह एकीकरण का एक आम नियम है। हमने प्रोवाइडर को मेन प्रोसेस में एक कर दिया था — एक सॉकेट, एक हैंडलर। पर React परत को यह संदेश नहीं मिला था: वह अब भी एक साथ दो प्रोवाइडर-विशिष्ट इवेंट हुक माउंट कर रही थी, एक Deepgram के लिए बना और एक ElevenLabs के लिए। दोनों सुन रहे थे। दोनों सहेज रहे थे। हर ट्रांसक्रिप्ट जो भी हुक ज़िंदा होता उसके ज़रिए सहेजा जाता — और दोनों ज़िंदा थे।
और उसके नीचे एक दूसरा, ज़्यादा चालाक दोहराव था: ElevenLabs हर अंतिम की गई लाइन दो बार भेजता है — एक बार committed इवेंट के रूप में और फिर committed_with_timestamps के रूप में। तो एक अकेले हुक के साथ भी, एक वाक्य दो इवेंट के रूप में आ सकता था।
फ़िक्स (v1.6 लाइन में) एक सुथरा दो-हिस्सों वाला है जो ठीक-ठीक दोनों कारणों पर बैठता है: हर इवेंट हुक को बैकएंड की सक्रिय-प्रोवाइडर कॉन्फ़िग से चलने वाले एक enabled फ़्लैग के पीछे रखो, ताकि किसी भी समय सिर्फ़ एक ही लिसनर ज़िंदा रहे; और प्रति-स्रोत टेक्स्ट डी-डुप्लिकेशन जोड़ो (माइक और सिस्टम ऑडियो के लिए आखिरी सहेजी गई लाइन याद रखो) ताकि ElevenLabs के दोहरे भेजने को निगल लिया जाए।
सबक इस बग से बड़ा है: जब आप दो बैकएंड को एक इंटरफ़ेस के पीछे एक करते हैं, तो दोहराव गायब नहीं होता — वह उस परत तक खिसक जाता है जो अब भी मानती है कि दो हैं। हमने मेन प्रोसेस में प्लंबिंग समेट दी और रिसाव दो परत ऊपर, UI की इवेंट वायरिंग में उभरा। एक पाइपलाइन का एकीकरण तब पूरा नहीं होता जब प्रोड्यूसर एक हो जाए; वह तब पूरा होता है जब हर कंज़्यूमर ने पुराने आकार पर यक़ीन करना बंद कर दिया हो।
पहली रीकनेक्ट: "पाँच कोशिशों के बाद हार मान लो"
v1.6.0 ने एक्सपोनेंशियल बैकऑफ़ के साथ GeekBye की बिल्कुल पहली WebSocket ऑटो-रीकनेक्ट भी शिप की — और यह दिखाना इसलिए ज़रूरी है क्योंकि यह उस बुलेटप्रूफ़ वर्शन का आदिम पूर्वज है जो हम आज चलाते हैं।
मूल वर्शन बंधा हुआ था। पाँच प्रयास, 1s, 2s, 4s, 8s, 16s की देरी — और फिर यह हथियार डाल देता, एक फ़ैटल त्रुटि भेजते हुए: "कनेक्शन खो गया — कृपया ट्रांसक्रिप्शन रीस्टार्ट करें।" उस वक़्त यह ज़िम्मेदाराना लगता था: हमेशा के लिए रीट्राई मत करो, यूज़र को ईमानदारी से बता दो। व्यवहार में यह भेस बदले एक UX बग था। सोलह सेकंड का नेटवर्क झपका — एक Wi-Fi हैंडऑफ़, एक VPN रीकनेक्ट, एक टनल — क्षणिक है, पर एक बंधा हुआ रीट्राई उसे अंतिम मान लेता है। यूज़र ने कुछ गलत नहीं किया और उसे एक लाइव मीटिंग रीस्टार्ट करने को कह दिया गया।
यह ठीक वही विफलता है जिसे मौजूदा डिज़ाइन ने ख़त्म कर दिया। आज GeekBye बिना बंधन वाले बैकऑफ़ के साथ रीकनेक्ट करता है और यहाँ तक कि प्रोवाइडर भी बदलता रहता है, और सिर्फ़ किसी सचमुच फ़ैटल कारण पर ही रुकता है — auth, कोटा, बिलिंग। "पाँच के बाद हार मान लो" से "किसी क्षणिक त्रुटि पर कभी हार मत मानो" तक का पूरा सफ़र खराब Wi-Fi पर आपका AI नोटटेकर क्यों रुक जाता है में बताया गया है। v1.6 वहीं है जहाँ वह रास्ता शुरू हुआ, उस भोले वर्शन के साथ जिसका पहले होना ज़रूरी था।
बासी कनेक्शन आपका पीछा करते हैं, बशर्ते उनके नाम न हों
एक और विश्वसनीयता सबक v1.6.3 में उतरा, जिसने बग की एक सूक्ष्म श्रेणी को ठीक किया जिससे किसी स्टेटफ़ुल बैकएंड पर रीकनेक्शन करने वाला हर कोई आख़िरकार मिलेगा।
जब आप रीकनेक्ट करते हैं — या सेशन के बीच में ट्रांसक्रिप्शन भाषा बदलते हैं, जो पर्दे के पीछे रीकनेक्ट करती है — तो पुराना कनेक्शन हमेशा चुपचाप नहीं मरता। उसके मौत की गुहार वाले संदेश (CONNECTION_LOST, disconnected) नए कनेक्शन के पहले से स्वस्थ हो जाने के बाद आ सकते हैं, और उस बिल्कुल ठीक-ठाक उत्तराधिकारी को गिरा सकते हैं। फ़िक्स हर कनेक्शन प्रयास को एक पहचान देता है — WebSocket URL पर छापी गई एक प्रति-कनेक्शन सेशन ID — और रीकनेक्ट के बाद एक छोटी ग्रेस विंडो जिसके दौरान किसी पिछले प्रयास के बासी डिस्कनेक्ट संदेशों को अनदेखा किया जाता है। यह किसी सॉकेट को बंद करने से पहले एक स्पष्ट stop संदेश भी भेजता है ताकि बैकएंड पुराने सेशन को साफ़-सुथरे तरीके से गिरा सके।
सिद्धांत: किसी स्टेटफ़ुल बैकएंड पर किसी भी रीकनेक्ट लॉजिक को हर प्रयास को एक पहचान से टैग करना होगा और पिछले प्रयासों के देर से आए संदेशों को शोर मानना होगा — वरना एक सफल रीकनेक्ट को उसके पूर्ववर्ती की मरती साँस मार डालती है।
तीन बातें जो v1.6 ने सिखाईं
- एकीकरण बग को हटाता नहीं, उनकी जगह बदल देता है। दो इंजनों को एक में समेटो और हर उस कंज़्यूमर की जाँच करो जो अब भी मानता है कि दो हैं। हमारा UI था, जो प्लंबिंग के पहले ही मिल जाने के बाद भी दो इवेंट हुक माउंट कर रहा था।
- आपकी पहली रीकनेक्ट बंधी हुई होगी, और लाइव स्ट्रीम के लिए बंधी हुई गलत है। "N कोशिशों के बाद हार मान लो" एक क्षणिक झपके को एक अंतिम विफलता में बदल देता है जिसके लिए यूज़र को दोष दिया जाता है। पहले ही वर्शन से फ़ैटल (auth, कोटा) को क्षणिक (एक गिरा सॉकेट) से अलग करो।
- रीकनेक्शन को पहचान चाहिए। हर प्रयास को टैग करो; मरे हुए प्रयासों के देर से आए संदेशों को अनदेखा करो। बिना नाम वाला कनेक्शन अपने ही भूत के हाथों मारा जा सकता है।
यह उस विश्वसनीयता की कहानी का दूसरा अध्याय है जो GeekBye v2 बनती है। पहले अध्याय के लिए, देखें वह ऑटो-अपडेट गाथा जिसमें चार दिनों में छह रिलीज़ लगीं (v1.5.x); जहाँ रीकनेक्ट का काम आख़िरकार पहुँचा उसके लिए, खराब Wi-Fi पर आपका AI नोटटेकर क्यों रुक जाता है; और पूरे चाप के लिए, सॉफ़्टवेयर को पूर्णता तक शिप करने की शारीरिक-रचना।