AI डेमोज सर्वत्र आहेत. एक साधा Python स्क्रिप्ट एखाद्या स्थिर फोटोमधील चेहरा बदलू शकतो आणि त्याचे परिणाम एखाद्या जादूसारखे वाटतात. पण खऱ्या लोकांनी ज्यावर प्रत्यक्षात डेटा अपलोड करू शकेल, काम सोडून जाऊ शकेल आणि नंतर पुन्हा येऊ शकेल असे काहीतरी तयार करणे? ते पूर्णपणे वेगळे काम आहे. मी अलीकडेच एक वेब टूल बनवले आहे जे एक ॲनिमेटेड GIF आणि एक संदर्भ चेहरा (reference face) घेते आणि प्रत्येक फ्रेममध्ये चेहरा बदलून तेच ॲनिमेशन परत देते. मॉडेल मुख्य व्हिज्युअल काम (visual heavy lifting) करते, तरीही खरा इंजिनिअरिंगचा प्रयत्न त्याच्या सभोवतालच्या आर्किटेक्चरमध्ये खर्च झाला: कनेक्शन्स जिवंत ठेवणे, पेज रिफ्रेश झाल्यावरही प्रक्रिया सुरू ठेवणे आणि दोन मिनिटांचे इन्फरन्स (inference) जॉब 504 Gateway Timeout मध्ये नाहीसे होणार नाही याची खात्री करणे.
हा प्रोटोटाइप आणि उत्पादन (product) यांच्यातील फरक आहे. इंजिनिअर्सना डिफ्यूजन आर्किटेक्चर आणि इन्फरन्स पॅरामीटर्सबद्दल बोलायला आवडते. पण जेव्हा एखादा वापरकर्ता दोनशे फ्रेम्स असलेला मोठा GIF अपलोड करतो, तेव्हा जर ब्राउझर टॅब तीस सेकंदांनंतर बंद पडला, तर तुमच्या मॉडेलची कोणालाही पर्वा नसते. इन्फरन्सच्या सभोवतालचे इन्फ्रास्ट्रक्चर देखील इन्फरन्सइतकेच महत्त्वाचे असते.
वाट पाहण्यातील समस्या
मानक वेब आर्किटेक्चर जलद प्रतिसादाची अपेक्षा करते. वापरकर्ता बटण क्लिक करतो, सर्व्हर उत्तर देतो आणि पेज अपडेट होते. डझनावारी GIF फ्रेम्समध्ये चेहरा बदलण्याची प्रक्रिया ही अपेक्षा त्वरित मोडीत काढते. मॉडेल Replicate च्या GPUs वर चालते, माझ्या सर्व्हरवर नाही, आणि मोठ्या GIF ला प्रोसेस करण्यासाठी सहज एक मिनिट किंवा त्यापेक्षा जास्त वेळ लागू शकतो. जर तुम्ही इतका वेळ HTTP रिक्वेस्ट उघडी ठेवण्याचा प्रयत्न केला, तर तुम्ही स्वतःहून अडचणीत येता. लोड बॅलन्सर (Load balancers) निष्क्रिय कनेक्शन्स तोडतात. ब्राउझर नेटवर्क फेल झाले असे मानून पुन्हा प्रयत्न करतात. वापरकर्ते स्क्रीनवर फिरणारे लोडिंग स्पिनर पाहून ॲप खराब झाले आहे असे समजतात.
मी सबमिशन आणि रिझल्ट यांचे विलगीकरण (decoupling) करून ही समस्या पूर्णपणे टाळली. जेव्हा वापरकर्ता GIF आणि चेहऱ्याचा फोटो अपलोड करतो, तेव्हा माझे Next.js बॅकएंड Replicate वर प्रेडिक्शन सुरू करते आणि त्वरित एक job ID परत करते. वापरकर्त्याला त्वरित कन्फर्मेशन मिळते. GPU चे काम पूर्ण झाल्यावर वेबहुक (webhook) द्वारे प्रत्यक्ष रिझल्ट नंतर प्राप्त होतो. ही पद्धत काहीतरी नवीन किंवा विचित्र नाही, परंतु दीर्घकाळ चालणाऱ्या मीडिया जॉब्ससाठी ती अत्यंत आवश्यक आहे. यामुळे अनिश्चित प्रतीक्षा ही एका खात्रीशीर प्रक्रियेत बदलते: जॉब स्वीकारला गेला आहे आणि तो पूर्ण झाल्यावर तुम्हाला सूचित केले जाईल.
विश्वासार्ह स्टेट मशीन (State Machine) तयार करणे
एकदा तुम्ही असिंक्रोनस (asynchronous) पद्धत स्वीकारली की, तुम्हाला दृश्यमानता (visibility) हवी असते. वापरकर्ते पेज रिफ्रेश करतील. ते टॅब बंद करतील आणि पुन्हा उघडतील. ते लिंक कॉपी करून सहकाऱ्याला पाठवतील जो दोन तासांनंतर ती तपासेल. काय घडले याचा कायमस्वरूपी रेकॉर्ड नसेल, तर गोंधळ निर्माण होईल.
मी 'सिंगल सोर्स ऑफ ट्रुथ' (single source of truth) म्हणून Supabase चा वापर केला. प्रत्येक अपलोडमुळे एका युनिक job ID सह एक रो (row) तयार होतो आणि तो रो विशिष्ट अवस्थांमधून (states) जातो: queued, processing, succeeded, failed, किंवा expired. जेव्हा वापरकर्ता पहिल्यांदा सबमिट करतो, तेव्हा तो रो 'queued' स्थितीत असतो. ज्या क्षणी Replicate प्रेडिक्शन स्वीकारते, तेव्हा तो 'processing' मध्ये बदलतो. वेबहुक त्याला 'succeeded' किंवा 'failed' मध्ये पाठवतो. ज्या जॉब्सना कॉलबॅक न मिळाल्याने खूप वेळ लागतो, त्यांच्यासाठी मी 'expired' ही स्थिती जोडली आहे, जेणेकरून सिस्टम अनंतकाळ रिकाम्या शोधात राहणार नाही.
TypeScript मध्ये बनवलेले फ्रंटएंड ठराविक अंतराने Supabase कडून माहिती घेते (polls) आणि सध्याची स्थिती काय आहे ते दर्शवते. ही 'पोलिंग' पद्धत प्राथमिक वाटू शकते, परंतु ती रिफ्रेशची समस्या पूर्णपणे सोडवते. वापरकर्ता आपला लॅपटॉप बंद करू शकतो, उद्या उघडू शकतो आणि गोष्टी नेमक्या कुठे आहेत ते पाहू शकतो, कारण प्रगतीचा (progress) ताबा ब्राउझर मेमरीकडे नसून डेटाबेसकडे असतो. Supabase क्रेडिट्स देखील ट्रॅक करते, त्यामुळे स्टेट ट्रॅक करणाऱ्या त्याच जॉब रेकॉर्डशी हिशोब जोडलेला असतो. सर्व काही एकाच ठिकाणी असते.
ब्राउझरवर ताण न देता फाइल्स हाताळणे
GIF हे लोकांच्या कल्पनेपेक्षा जास्त जड असतात. AI पाइपलाइनसाठी योग्य आकाराची फाईल देखील काही मेगाबाइट्सची असू शकते. ब्राउझरमध्ये त्याचे लूपिंग प्रिव्ह्यू रेंडर केल्यास परफॉर्मन्स खराब होईल, विशेषतः कमी क्षमतेच्या उपकरणांवर. मला दोन पूर्णपणे वेगळ्या फाईल पाइपलाइनची गरज होती: एक AI साठी आणि दुसरी युजर इंटरफेससाठी.
प्रिव्ह्यूसाठी, मी WebAssembly मध्ये कंपाईल केलेल्या FFmpeg चा वापर करून मोठ्या GIF ला ॲनिमेटेड WebP मध्ये रूपांतरित करतो. हे पूर्णपणे ब्राउझरमध्ये क्लायंट-साइड चालते. याचा परिणाम म्हणजे एक हलका (lightweight) प्रिव्ह्यू मिळतो जो मूळ फाईलला स्पर्श न करता इंटरफेस वेगवान ठेवतो. Replicate ला पाठवलेला GIF तसाच राहतो. हे विलगीकरण महत्त्वाचे आहे. तुम्हाला प्रिव्ह्यूमधील कॉम्प्रेशन आर्टिफॅक्ट्स (compression artifacts) तुमच्या ट्रेनिंग डेटा किंवा फायनल रेंडरमध्ये मिसळलेले नको आहेत, आणि AI विचार करत असताना युजर इंटरफेस मोठा डेटा लोड करताना अडकू नये असेही तुम्हाला वाटते.
फाईल ब्राउझरमधून अपलोड होण्यापूर्वी FFmpeg WASM इतर GIF कार्ये देखील हाताळते. मी याचा वापर फ्रेम काउंट तपासण्यासाठी, डायमेन्शन्स (dimensions) व्हॅलिडेट करण्यासाठी आणि खराब (corrupted) फाईल्स लवकर ओळखण्यासाठी करतो. GPU क्रेडिट्स खर्च होण्यापूर्वी समस्या ओळखल्यामुळे पैसे आणि वापरकर्त्याचा संयम दोन्ही वाचतात.
डेमोचे रूपांतर लोकांच्या विश्वासातील सॉफ्टवेअरमध्ये करणे
येथे एक व्यापक धडा आहे जो बाजारातील जवळजवळ प्रत्येक जनरेटिव्ह AI टूलला लागू होतो. मॉडेल स्वतः कामाचा केवळ तीस टक्के भाग असू शकते. उरलेले सत्तर टक्के म्हणजे ते असं ग्लॅमरस नसलेले तांत्रिक पायाभूत काम (plumbing) आहे ज्याबद्दल कोणीही ट्विट करत नाही: स्टेट रिकव्हरी, वेबहुक सिग्नेचर्स, फाईल कन्व्हर्जन, क्रेडिट ट्रॅकिंग आणि ग्रॅसफुल फेल्युअर.
माझा स्टॅक साधा आणि विचारपूर्वक निवडलेला आहे. Next.js आणि TypeScript इंटरफेस आणि API रूट्स हाताळतात. Replicate मॉडेल्स चालवते. Supabase स्टेट्स, डेटा आणि क्रेडिट्स व्यवस्थापित करते. FFmpeg WASM क्लायंट-साइड मीडिया काम हाताळते. Animated WebP UI वेगवान ठेवते. प्रत्येक भागाचे एक विशिष्ट काम आहे आणि ते नाजूक किंवा दीर्घकाळ चालणाऱ्या रिक्वेस्ट्सऐवजी स्पष्ट स्टेट ट्रान्झिशन्सद्वारे एकमेकांशी जोडलेले आहेत.
जेव्हा एखादा वापरकर्ता फेस स्वॅपसाठी क्रेडिट्स वापरतो, तेव्हा त्यांना विश्वासार्हता हवी असते, रिसर्च पेपर नाही. जर एखादे प्रेडिक्शन फेल झाले, तर सिस्टमला ते माहित असावे आणि तसे स्पष्टपणे सांगावे. जर वापरकर्त्याला प्रतीक्षा करावी लागली, तर त्यांच्याकडे पाहण्यासाठी एक हलका (lightweight) प्रिव्ह्यू असावा आणि ब्राउझर रीस्टार्ट होऊनही त्यांचा स्टेटस टिकून राहिला पाहिजे. जेव्हा हे तपशील व्यवस्थित काम करतात तेव्हा ते अदृश्य असतात, पण जेव्हा ते काम करत नाहीत, तेव्हा ते घातक ठरतात.
फेस स्वॅप हे स्वतःमध्ये एक छान तंत्र आहे. पण हे टूल तेव्हाच वास्तववादी वाटते कारण वापरकर्ता फाईल अपलोड करून, काम सोडून जाऊन, पुन्हा आल्यावर जिथे सोडले होते तिथेच आपले काम सुरू ठेवू शकतो. यामुळेच एका API कॉलचे रूपांतर अशा सॉफ्टवेअरमध्ये होते ज्यावर लोक खरोखर विश्वास ठेवतात.
