Steven
Steven4 min lukuaika

Näin striimaat live-raportin ilman välkyntää

Kun palaverisi päättyy, GeekByen yhteenveto täyttyy nyt reaaliajassa sen sijaan, että tuijottaisit latausanimaatiota. Rauhallisen striimaavan käyttöliittymän saaminen nykivän sijaan vaati välkynnän ratkaisemista kahdesti — kerran rakenteisille kentille, kerran markdownille — ja toinen korjaus oli renderöijä, joka meillä oli jo valmiina.

Ohjelmistokehitys
Frontend
Striimaus
GeekBye-julkaisut
Näin striimaat live-raportin ilman välkyntää

Ennen tätä julkaisua GeekBye-palaverin lopettaminen tarkoitti latausanimaation tuijottamista. Sovellus lähetti koko transkriptisi analysoitavaksi, odotti koko yhteenvedon — pisteet, avainkohdat, toimenpiteet, kaikki — ja pudotti sen sitten ruudulle yhdellä kertaa. Toimivaa, mutta se tuntui hitaalta, koska tuijotit tyhjyyteen sen työskennellessä.

GeekBye v1.6.13–v1.6.15 muutti tuon raportiksi, joka striimaa sisään reaaliajassa, kenttä kentältä, sitä mukaa kun se syntyy. Mielenkiintoinen osa ei ole itse striimaus — vaan kaikki se, mitä meidän piti tehdä, jotta striimaus ei näyttäisi nykivältä. Rauhallinen striimaus ja töksähtelevä striimaus ovat sama ominaisuus hyvin erilaisella toteutuksella.

Välkyntä, osa yksi: älä anna jokaisen palan hakata Reactia

Yhteenveto ei ole yksi könttä — se on rakenteisia kenttiä (kokonaispisteet, avainkohdat, toimenpiteet ja niin edelleen), jotka backend lähettää yksi kerrallaan server-sent eventsin yli. Naiivi tapa renderöidä se on päivittää React-tila jokaisella saapuvalla tapahtumalla.

Tee niin, ja saat välkyntää. Jokainen saapuva kenttä laukaisee oman uudelleenrenderöintinsä; komponentit, jotka näyttivät "tyhjää", asennetaan, sitten puretaan, sitten asennetaan uudelleen "ladattuina"; asettelu nykii jokaisella paketilla. Raportti kokoaa itseään silmiesi edessä pahimmalla mahdollisella tavalla — näkyvästi, hermostuneesti.

Korjaus on kaksiosainen kuri. Ensin, kerää saapuvat kentät refiin — muuttuvaan säiliöön, joka ei laukaise renderöintejä — ja julkaise React-tilaan kerran per pala tuoreena kopiona, jotta puu päivittyy harkitusti eikä jokaisella mikrotapahtumalla. Toiseksi, renderöi yksi vakaa komponenttipuu koko ajan, olipa striimattaessa tai valmiina, sisäisillä paikanpitäjillä kentille, jotka eivät ole vielä saapuneet:

// accumulate in a ref, publish once per chunk
partialRef.current = { ...partialRef.current, [field]: value }
setPartial({ ...partialRef.current })

// one tree, never swapped; missing fields show a placeholder in place
const report = isStreaming ? partial : saved
<Score>{report.overallScore ?? '…'}</Score>

Komponentti, joka näyttää pisteet, ei koskaan purkaudu — se vain näyttää , kunnes numero saapuu, ja sitten numeron. Mikään ei nyki. Ja täysin koottu objekti tallentuu silti lopuksi paikalliseen tietokantaan välimuistiin, joten striimaava käyttöliittymä ja kestävä tallennus elävät rinnakkain taistelematta.

Välkyntä, osa kaksi: renderöijä, joka meillä oli jo

Toinen välkyntä on hienovaraisempi ja elää itse markdownissa. Raportti renderöi rikasta markdownia — otsikoita, lihavointia, listoja, koodilohkoja. Mutta kesken striimin saapuva markdown on minä tahansa hetkenä keskeneräistä. Lista, jossa on tähän mennessä yksi luoti. Koodiaita, joka on avattu mutta ei suljettu. Lihavointimerkki, jolla ei ole vielä paria.

Perinteinen markdown-renderöijä jäsentää koko merkkijonon uudelleen jokaisella tokenilla, ja tuo keskeneräinen tila jäsentyy joka kerta joksikin erilaiseksi — joten lista välkkyy, koodilohko välähtää auki ja kiinni, asettelu hyppää tokeneiden valmistuessa. Se on sama hermostunut kokoaminen kuin ennenkin, yksi kerros syvemmällä.

Korjaus oli lähes noloudessaan käsillä: käytimme striimaustietoista markdown-renderöijää jo live-tekoälychatissa. Renderöijä, joka on rakennettu sietämään keskeneräisiä tokeneita — renderöimään keskeneräistä markdownia vakaasti ja vakiinnuttamaan sen vasta, kun tokenit valmistuvat — sen sijaan että jäsentäisi joka kerta alusta. Raportin piti vain käyttää samaa. Olimme ratkaisseet tämän täsmälleen saman ongelman chatissa kuukausia aiemmin; raportin "korjaus" oli sen tajuaminen, että työkalu oli jo olemassa, ja sen suuntaaminen toiselle pinnalle. Uudelleenkäyttö voitti uudelleenrakentamisen.

Bonus: muotoiltu kopiointi on leikepöydän formaattikysymys

Kun raportti näytti hyvältä, ihmiset halusivat liittää sen — dokumenttiin, sähköpostiin — ja säilyttää muotoilun ja kuvakaappaukset. Vaisto on sarjallistaa raportti markdown-merkkijonoksi ja toivoa, että kohde renderöi sen. Yleensä se ei renderöi.

Todellinen vastaus on, että leikepöytä pitää useita esityksiä yhtä aikaa. Joten kirjoitamme kaksi: HTML-version, jossa muotoilu on tallella ja kuvakaappaukset upotettuina sisäisinä kuvina, ja pelkkä teksti -varajärjestelyn kohteille, jotka eivät ota HTML:ää vastaan. Liitä rikkaaseen editoriin ja saat muotoillun raportin kuvineen; liitä pelkkään tekstikenttään ja saat puhdasta tekstiä. "Muotoiltu kopiointi" ei ollut koskaan sarjallistamisongelma — se oli valitse-oikea-leikepöytäformaatti -ongelma.

Sama julkaisu antoi raportille myös chat-tyyliset Me / Them -nimikkeet kahden puhujan asettuessa vastakkaisille puolille, jotta transkripti lukeutuu kuin se keskustelu, joka se oli, ja siirsi omat kuvakaappauksesi omalle puolelleen sitä vastaamaan. (Yksi tämän ikkunan julkaisuista, v1.6.14, oli puhdas uudelleenkoonti — ei tarinaa siinä, ja on rehellistä sanoa se ääneen.)

Kolme asiaa, jotka striimaava käyttöliittymä opetti meille

  1. Puskuroi striimatut kentät, julkaise kerran per pala. Jokaisen server-sent eventin annattaminen ohjata omaa tilapäivitystään on välkyntä. Ref-kerääjä plus yksi julkaisu per pala, puuhun joka ei koskaan purkaudu, muuttaa hermostuneen kokoamisen rauhalliseksi täyttymiseksi.
  2. Kaikki mikä striimaa tarvitsee striimaustietoisen renderöijän. Renderöijä, joka jäsentää koko merkkijonon uudelleen per token, välkkyy keskeneräisellä markdownilla. Käytä sellaista, joka on rakennettu keskeneräiselle syötteelle — ja jos sinulla on jo sellainen toiselle pinnalle, käytä sitä uudelleen ennen kuin rakennat toisen.
  3. Muotoiltu kopiointi koskee leikepöytäformaatteja, ei sarjallistamista. Kirjoita text/html upotetuilla kuvilla ja text/plain -varajärjestely. Leikepöytä suunniteltiin kantamaan molemmat; käytä sitä.

Tämä on viides luku luotettavuus- ja viimeistelytarinassa, josta tulee GeekBye v2. Edellisen luvun näet täältä: earbuds-bugi (v1.6.12); siitä, missä sama server-sent-events-idea muuttui myöhemmin kokonaiseksi varakuljetukseksi: live-transkribointi kun palomuuri estää WebSocketit (v2.0.8); ja koko kaaren näet täältä: ohjelmiston toimittamisen anatomia täydellisyyteen.

Aiheeseen liittyvät artikkelit

Hiljaisuus oli kantava
Steven
Steven6 min lukuaika

Hiljaisuus oli kantava

GeekBye v1:n kaksi viimeistä julkaisua kertovat samasta epämukavasta totuudesta: reaaliaikainen transkriptio oikean verkon yli ei ole häviötöntä, ja rehellinen liike on lakata teeskentelemästä että se on. v1.8.20 säilytti jokaisesta äänipalasta kopion levyllä ennen kuin pudotti sen uudelleenyhdistämisen aikana, ja alkoi merkitä transkription aukot ääneen. v1.9.0 lakkasi lähettämästä hiljaisuutta kaistanleveyden säästämiseksi — ja huomasi, että hiljaisuus oli juuri se signaali, jolla transkriboija tiesi lauseen päättyneen. Kaksi julkaisua asioiden pois heittämisen hinnasta.

Ohjelmistokehitys
Audio
Luotettavuus
Kolme verbiä, jotka pitävät Web Audion elossa
Steven
Steven7 min lukuaika

Kolme verbiä, jotka pitävät Web Audion elossa

Kaksi GeekBye-julkaisua, kahden kuukauden välein ja kahdessa eri tiedostossa, opettivat ääni-koodillemme saman opetuksen vastakkaisista päistä: lakkaa kohtelemasta selaimen AudioContextia kertakäyttöisenä. Yksi julkaisu oppi kutsumaan resume() kontekstille, jonka macOS oli hiljaa keskeyttänyt kesken tallennuksen; toinen oppi kutsumaan suspend() eikä close(), niin että peräkkäiset istunnot lakkaavat törmäämästä Chromiumin noin-kuuden-kontekstin kattoon. Resume, suspend, close — siinä on koko juoni.

Ohjelmistokehitys
Audio
Desktop
Puhelun erottaminen avoimesta sovelluksesta
Steven
Steven7 min lukuaika

Puhelun erottaminen avoimesta sovelluksesta

GeekBye voi huomata, että olet liittynyt videokokoukseen, ja tarjoutua tallentamaan sen. Tunnistus osoittautuu helpommaksi puoliskoksi — Swift-binääri, joka lukee ikkunoiden otsikoita joka kymmenes sekunti. Vaikea puolisko on tarkkuus: olla laukeamatta kun Zoom on vain auki, olla tarjoamatta kokousta jota jo tallennat, ja olla mykistämättä mikrofonia puhelussa jossa oikeasti olet. Kolme julkaisua, ja jokainen niistä on vartija jonka oli opittava olemaan kukistamatta itseään.

Ohjelmistokehitys
macOS
Desktop