Gebouw meestal lucid-nmt: A Production-Ready (EasyNMT compatibel) Vertaaldienst (Nederlands (Dutch))

Gebouw meestal lucid-nmt: A Production-Ready (EasyNMT compatibel) Vertaaldienst

Saturday, 08 November 2025

//

45 minute read

Inleiding

Een FastAPI-implementatie Exact kopiëren van de API van EasyNMT (https://github.com/UKPLAB/EasyNMT) een uitstekend maar verlaten neuraal-machine-vertalingsproject.

Maar ik heb zo veel leuke functies toegevoegd om de betrouwbaarheid te verhogen en het klaar te maken voor gebruik in een productiesysteem.Denk snelle vertaling zelf gehost...).

Sinds het begin van deze blog, een grote passie is auto-vertaling van blog artikelen.

JA Ik weet dat 'google doet dit' in browsers etc..etc...maar dat is niet het punt.mostlylucid-nmtIk wilde weten hoe ik het moest doen!

Plus het is leuk om gastvrij te zijn voor mensen die geen Engels lezen (ook al lezen ze Engels als tweede taal, het is FAR moeilijker om te ontleden).Dus bedacht ik hoe het te doen; evenals het delen van hoe dit soort systeem te bouwen.Oh en het gaf me ideeën over hoe het te gebruiken in ASP.NET voor automatische lokalisatie van tekst (inclusief dynamische tekst) met behulp van SignalR & een glad realtime updatesysteem. (

Blijf kijken.Oh and I've made a demo available here; https://nmtdemo.mostlylucid.net/demo/ it's only running in an old laptop without no GPU but gives you the idea (and lets me test lengevity).In wezen; mensen schrijven onzin tekst die SUPER luidruchtig is voor machines om efficiënt om te gaan.

Er werd dus veel uitgezocht over problemen met EasyNMT (het was echt een onderzoeksproject).

Nu

is ontworpen om een gevecht getest (wel het vertalen van de tienduizenden woorden op hier!) nuttig systeem voor elke vertaling. Een soort BabelFish API. Het heeft ook alle leren die ik heb van drie decennia van het bouwen van productie servers & systemen. Variërend van 429 codes om de cliënt te vertellen om zich terug te trekken, terug te keren metadata over vertalingen om klanten te helpen, extra eindpunten om meer gegevens te krijgen en VAN COURSE een demo pagina

Dat laat mij zowel tijdens het ontwikkelen als u een manier om een toneelstuk te hebben.

Ischreef een heel systeemhttp://<server>:<port>/demoom dat te laten gebeuren met een geweldig project genaamd EasyNMT.

Demo

[TOC]

Hoe dan ook, als je net die repo hebt gecontroleerd weet je dat er een probleem is... het is al jaren niet aangeraakt.

Het is een eenvoudige, snelle manier om een vertaling te krijgen API zonder de noodzaak om te betalen voor een bepaalde dienst of een full-size LLM om vertaling te krijgen (langzaam).

In onze vorige berichten hebben we besproken hoe we EasyNMT kunnen integreren met ASP.NET applicaties voor achtergrondvertaling.

**Maar naarmate de tijd voortging, begonnen de scheuren te zien.**Het was tijd voor iets beters.

**Zoals gewoonlijk is het allemaal op GitHub en alle gratis voor gebruik etc...**Docker Pulls

  • cpu: Reusing loaded model for en->de (3/10 models in cache)
  • cpu-min: Need to load model for en->fr (3/10 models in cache)
  • gpugpu-min
  • **Demo...zie later voor de demo pagina!**Een volledige (meestal) interactieve demo-pagina
  • (bijof gewoon de wortel)
  • **Wat is nieuw?**Voordat we duiken in de snelle start, hier is wat maakt deze versie game-changing:

Belangrijke updates (v3.1) - Intelligentie & ZichtbaarheidNieuw in v3.1:

  • **De nieuwste versie brengt enorme verbeteringen aan betrouwbaarheid, zichtbaarheid van de prestaties en intelligente modelselectie!**1.
  • Slimme Model Caching met Zichtbaarheid- Kijk precies wat er gebeurt:
  • Cache HIT loggingCache MISS-logging
  • Cache status tracking: Toont gebruikspercentage en geladen modellen
  • Uitzettingswaarschuwingen: Wis waarschuwingen wanneer cache vol is en modellen worden verwijderd
  • Toename van de capaciteit:
    ====================================================================================================
      🚀 DOWNLOADING MODEL
      Model: facebook/mbart-large-50-many-to-many-mmt
      Family: mbart50
      Direction: en → bn
      Device: GPU (cuda:0)
      Total Size: 2.46 GB
      Files: 6 main files
    ====================================================================================================
    [Progress bars for each file...]
    ====================================================================================================
      ✅ MODEL READY
      Model: facebook/mbart-large-50-many-to-many-mmt
      Translation: en → bn is now available
    ====================================================================================================
    

**: Standaard cachegrootte gestoten naar 10 modellen (vanaf 6)**Per model apparaatlogging

  • : Zie precies welke GPU/CPU elk model gebruikt2.
  • Verbeterde downloadvoortgangIk vraag me niet meer af of het vastzit.
  • Grootte voor download:
    [Pivot] Languages reachable from en: 85 languages
    [Pivot] Languages that can reach bn: 42 languages
    [Pivot] Found 38 possible pivot languages
    [Pivot] Selected pivot: en → hi → bn (both legs verified)
    
  • **: Toont de totale downloadgrootte (bv. "Total Size: 2,46 GB")**Aantal bestanden
  • : Toont het aantal bestanden om te downloadenApparaatweergave

: Toont doelapparaat (GPU/CPU) in bannerVoortgangsbalk

  • **: Mooie tqdm voortgang voor elk bestand (wanneer op TTY)**Aanvullingsbanner
  • : Duidelijke succesboodschap wanneer het model klaar isVoorbeelduitvoer
  • 3.:
    Request: en→bn with opus-mt
    Trying families: ['opus-mt', 'mbart50', 'm2m100']  ✓ All three!
    opus-mt: Failed (model doesn't exist)
    mbart50: Success! (auto-fallback worked)
    

Data-aangedreven intelligente pivot-selectie- Geen blinde pogingen meer.

  • Slimme snijlogicaLoading mbart50 model on GPU (cuda:0)
  • : Vindt talen waar beide draaipoten bestaanModel loaded on device: cuda:0
  • Vermijdt mislukte pogingenSuccessfully loaded... on GPU (cuda:0)

: Zal niet proberen en→es→bn als es→bn niet bestaatVoorbeeld voor nl→bn

  • Terugvalprioriteiten->hi, hi->bn
  • : Engels → Spaans → Frans → Duits → Chinees → Russisch
  • Transparant loggen[Pivot] Both legs loaded and cached. Ready to translate.

: Zie precies waarom elk draaipunt werd gekozen of overgeslagen4.

  • Vaste automatische terugval
    • Geen dubbele pogingen meer.model_familyProbeert altijd terugvallen
  • : Zelfs als de voorkeur familie "moet" ondersteunen het paar

Enkele poging per gezin

: Geen twee keer hetzelfde model meer proberenVoorbeeldstroom

  • GPU-helderheid
    • Altijd weten waar uw modellen zijn:
  • Elke modelbelasting toont:
  • Na belasting bevestigt:

**Succesboodschap omvat:**6.

  • Pivot Model Caching- Efficiënt pivot hergebruik:
  • **Beide poten van het draaipunt apart gecached:**Volgende keer nl→hi of hi→bn nodig, instant cache hit!
  • **Logboek wissen:**7.
  • Modelselectie per verzoek

**- Werkt al in demo:**Demo dropdown maakt het selecteren van opus-mt, mbart50 of m2m100 mogelijk

  • Backend respect
  • parameter per verzoek
  • Modellen apart gecached door familie voor direct schakelen
  • Belangrijke updates (v3.0)
  • 1.requirements-prod.txtVerbeterde demopagina

**- Productie-ready interactieve interface:**Volledige viewport layout (100vw/100vh) voor meeslepende vertaalervaring

  • **Selecteer de juiste selecteer dropdowns (geen clunky input/datalist meer)**Live model familie switching (Opus-MT, mBART50, M2M100)
  • Dynamische taalbelasting op basis van het geselecteerde modelSchuifbare uitvoergebieden voor grote vertalingen
  • **2.**Performance-optimized defaults
    • "Snel mogelijk" uit de doos:
  • GPU

**: FP16 ingeschakeld, BATCH_SIZE=64, MAX_INFLIGHT=1 (optimale voor enkele GPU)**CPU

  • : WEB_CONCURRENCY=4, MAX_INFLIGHT=4, BATCH_SIZE=16 (gebruik alle kernen)
  • Snel afsluiten
  • : 5-seconde gracieuze timeout (niet meer 20-seconden hangen)
  • Containers stoppen schoon zonder enge SIGKILL berichten
  • Productie Build Optimalisatie

**- Kleinere, snellere afbeeldingen:**Verwijderde test afhankelijkheden (pytest, pytest-cov) uit productiebouw

  • Bespaart ~200MB per afbeeldingCPU-afbeeldingen: ~8-10GB (volledig), ~3-4GB (min)
  • **GPU-afbeeldingen: ~12-15GB (volledig), ~6-8GB (min)**Alle gebruik
  • voor minimale voetafdruk4.

Uitgebreide test & belastingstest- Valideer alles:

  • Live API test suite
  • (30+ tests) voor gezondheid, vertaling, detectie, ontdekking
  • k6-belastingstest

met realistische verkeerspatronenCross-platform validatiescripts

  • /discover/opus-mt(PowerShell + Bash)
  • /discover/mbart50Tests voor modeldownloads en spilvertaling terugval
  • /discover/m2m100Geautomatiseerde rooktests voor snelle validatie

**5.**Implementatiedocumentatie

    • Productie klaar vanaf dag één:
  • 4 tuning scenario's: Max Doorvoer, Low Latency, High Concurrency, Memory-Constrained
  • Docker Stel voorbeelden samen met GPU/CPU configuraties

Kubernetes manifesteert zich met PVC, grondstoffenlimieten, gezondheidscontrolesVoorbeelden van Azure Container Instances

  • scottgal/mostlylucid-nmt:cpuLaadbegeleidings- en monitoringaanbevelingen:latestConcurrency versus throughput trade-offs uitgelegd
  • scottgal/mostlylucid-nmt:cpu-min6.
  • scottgal/mostlylucid-nmt:gpuDrie modelfamilies
  • scottgal/mostlylucid-nmt:gpu-min- Kies het beste voor uw behoeften:

Opus-MT: 1200+ paar, beste kwaliteit (afzonderlijke modellen)

  • mBART50latest, min, gpu, gpu-min: 50 talen, enkel 2.4GB model, 2.450 paren
  • M2M10020250108.143022: 100 talen, enkele 2.2GB model, 9.900 paar

Automatisch terugvallen- Intelligent selecteert het best beschikbare model:

  • **De primaire familie instellen (bv. Opus-MT voor kwaliteit)**Automatisch probeert mBART50/M2M100 als paar niet beschikbaar
  • Maximale dekking zonder opoffering van kwaliteit8.
  • Model Discovery- Dynamisch query beschikbare modellen:
    • Alle 1200+ paren van Hugging Face

- Alle mBART50 paren- Alle M2M100 paren

  • Minimale afbeeldingen

- Kleinere, flexibele implementaties:

Geen vooraf geladen modellen (download on-demand)

Volume-geplaatste persistente cache

Schakel modelfamilies om zonder herbouw**10.**Single Docker repository

  • Alle varianten op één plaats: |-----|-----------------|------|-------------|----------| | cpu(oflatest) | scottgal/mostlylucid-nmt:cpu) - CPU | cpu-min | scottgal/mostlylucid-nmt:cpu-min- CPU minimaal | gpu | scottgal/mostlylucid-nmt:gpu- GPU met CUDA 12.6 | gpu-min | scottgal/mostlylucid-nmt:gpu-min- GPU minimaal

**11.**Juiste versie

    • Alle afbeeldingen bevatten datumversiering:
  • Genoemde labels (
  • ) wijzen altijd op de meest recente bouw
  • Onveranderlijke versietags (bijv.

) voor het vastzetten van specifieke constructies

Volledige OCI labels voor het volgen van versies, bouwdata en git commits

docker run -d \
  --name mostlylucid-nmt \
  -p 8000:8000 \
  scottgal/mostlylucid-nmt

12.

curl -X POST "http://localhost:8000/translate" \
  -H "Content-Type: application/json" \
  -d '{
    "text": ["Hello, how are you?"],
    "target_lang": "de"
  }'

Nieuwste basisafbeeldingen

{
  "translated": ["Hallo, wie geht es Ihnen?"],
  "target_lang": "de",
  "source_lang": "en",
  "translation_time": 0.34
}

- Veiligheids- en prestatieverbeteringen:

Python 3.12-slank

docker run -d \
  --name mostlylucid-nmt \
  --gpus all \
  -p 8000:8000 \
  -e EASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}' \
  scottgal/mostlylucid-nmt:gpu

voor CPU-afbeeldingen (adressen Python 3.11 kwetsbaarheden)

CUDA 12.6

met Ubuntu 24.04 voor GPU beelden (laatste NVIDIA stack)

docker run -d \
  --name mostlylucid-nmt \
  -p 8000:8000 \
  -v $HOME/model-cache:/models \
  -e MODEL_CACHE_DIR=/models \
  scottgal/mostlylucid-nmt:cpu-min

PyTorch met CUDA 12.4

docker run -d `
  --name mostlylucid-nmt `
  -p 8000:8000 `
  -v ${HOME}/model-cache:/models `
  -e MODEL_CACHE_DIR=/models `
  scottgal/mostlylucid-nmt:cpu-min

(compatibel met CUDA 12,6 runtime)

docker run -d ^
  --name mostlylucid-nmt ^
  -p 8000:8000 ^
  -v %USERPROFILE%/model-cache:/models ^
  -e MODEL_CACHE_DIR=/models ^
  scottgal/mostlylucid-nmt:cpu-min

Alle afhankelijkheden bijgewerkt naar de nieuwste beveiligde versies

13.

curl http://localhost:8000/healthz

Deprecatiewaarschuwingen (fixed deprecation warnings)

- Toekomstbestendig:

Verwijderde verouderde TRANSFORMERS_CACHE (nu met HF_HOME)Compatibel met Transformers v5Snel starten (5 minuten)

http://localhost:8000/demo/

Demo

### Wil je gewoon gaan vertalen?

Hier is de absolute eenvoudigste manier om te draaien meestal lucid-nmt:

Beschikbare Docker-afbeeldingen

  • Alle varianten zijn verkrijgbaar bij
  • één repository
  • met verschillende tags:

Tag: volledige afbeeldingsnaam Grootte Beschrijving Gebruikscase:

  • (of
  • CPU met broncode Productie CPU-implementaties
  • CPU minimal, no preloaded modellen... Volumekaart cache, flexibel...
  • GPU met CUDA 12.6 + bron + productie GPU-implementaties
  • GPU minimaal, geen vooraf geladen modellen GPU met volume-map cache

Minimale afbeeldingen

  • worden aanbevolen voor:
  • Productie-implementaties met volume-map cache
  • Gebruik van mBART50 of M2M100 (enkele grote modellen)

De grootte van de container klein houden

  • Flexibiliteit om modelfamilies om te schakelen zonder herbouwEenvoudigste start (Opus-MT CPU)
    • Trek en loop:
  • **2.**Vertaal een tekst:
    • Respons:
    • GPU versneld (10x sneller)

Vereist NVIDIA Docker runtime:

  • Met Persistente Model CacheDownload modellen eenmaal en houd ze over container herstarten:
  • **Linux/Mac:**Windows (PowerShell):
  • **Windows (CMD):**Modellen downloaden automatisch bij het eerste gebruik en blijven staan in uw lokale map!

Gezondheidscontrole:

  • Dat is de vijf minuten snelle start!
    • **Voor productie-implementatie, configuratie en geavanceerde functies, blijf lezen.**Interactieve demopagina
    • De service is voorzien van een full-featuredinteractieve demo pagina
    • **dat maakt het gemakkelijk om vertalingen te testen zonder het schrijven van een code.**Toegang tot het op:
  • Demofuncties

De demo pagina biedt een complete vertaaltestomgeving met:

// Example: Translating a 5000-word article
Input: Long article with multiple paragraphs

Step 1: Split by paragraphs (preserves structure)
  → Paragraph 1 (800 chars)
  → Paragraph 2 (1200 chars)
  → Paragraph 3 (600 chars)
  ...

Step 2: Group into ~1000 character chunks
  → Chunk 1: Paragraphs 1-2
  → Chunk 2: Paragraph 3-4
  → Chunk 3: Paragraphs 5-6

Step 3: Translate each chunk sequentially
  → Shows progress: "Translating chunk 1/3..."
  → Shows progress: "Translating chunk 2/3..."
  → Shows progress: "Translating chunk 3/3..."

Step 4: Reassemble with paragraph breaks
  → Final output: Complete translated article with preserved formatting

Taalselectie

Auto-bevolkte taal dropdowns van de live service

  • Verwissel bron/doeltalen met één klik
  • Ondersteunt alle 100+ talen die in de dienst zijn geconfigureerd
  • Smart Text Chunking

Behandelt automatisch grote tekstinvoeren van elke grootte

  • Intelligente splitsingen door alinea's, met behoud van documentstructuur
  • Terugvallen tot zinsdelen voor zeer lange alinea's
  • Toont vooruitgang voor multi-chunk vertalingen ("Vertalen chunk 2/5...)
  • Naadloos herassembleren brokken met de juiste afstand

3.

  • Taaldetectie
  • Detecteer de brontaal met één klik
  • Populeert automatisch brontaal uitklapmenu
  • Werkt met tekst tot 5000 tekens

4.

  1. Geavanceerde opties:

    • Straalgrootte
    • : Kwaliteit van de vertaling controleren (1-10)
    • Hogere waarden = betere kwaliteit maar langzamer
    • Lagere waarden = snellere doorvoer
  2. Splitsing van de straf:

    • : Automatische zin splitsen aan/uit
    • Ingeschakeld (standaard): Splitst lange teksten in zinnen voor betere kwaliteit
    • Uitgeschakeld: Vertaalt hele tekst als één blok (sneller voor korte teksten)
  3. Real-Time Statistieken:

    • Vertaaltijd
    • : Toont de werkelijke server-side vertaalduur
    • Tekenaantal
    • : Live count as you type

Statusindicator

: Idle → Translating → Klaar/Fout

  • **6.**Model Family Discovery
  • **Verken de beschikbare vertaalparen voor elke modelfamilie:**Opus-MT
  • : 1200+ taalparenmBART50
  • : 50 talen, 2.450 parenM2M100

: 100 talen, 9.900 paar/demo/Zie precies welke taalparen beschikbaar zijn voordat u vertaalt

Hoe Text Chunking werkt

De demo implementeert intelligente tekstchunking aan de clientzijde:Waarom de Demo gebruiken?Snel testenTestvertalingen zonder code te schrijvenBeschikbaarheid taalpaar valideren

Vergelijk vertaalkwaliteit met verschillende bundelgroottes

  1. **Testranden (emoji, symbolen, speciale tekens)**Ontwikkelingshulp
  2. Zie exact API-verzoek/antwoordformaatVerifieer de gezondheid van de dienst alvorens te integreren
  3. Testprestaties met verschillende tekstgroottesOntdek beschikbare modelfamilies
  4. KlantreferentieToont juiste API gebruikspatronen
  5. **Demonstreert foutafhandeling (429, taaldetectie)**Voorbeeld van chunking-implementatie
  6. Real-world retry logicaVoorbeeld gebruikEenvoudige vertaling.
  7. **Plakken tekst: "Hallo, hoe gaat het vandaag?"**Selecteer doel: Duits
  8. **Klik op "Vertalen"**Resultaat: "Hallo, wie geht es Ihnen heute?"

Lange documentvertaling

Volledige blogpost plakken (5000+ woorden)Demo brokken het automatisch in beheersbare stukkenToont vooruitgang zoals elke brok vertaalt

Geeft volledig vertaald document terug

TaaldetectieTekst in onbekende taal plakkenKlik op "Taal detecteren"

Demo identificeert taal en updates dropdown

  • Klaar om onmiddellijk te vertalenTechnische details
  • **De demo pagina is:**Op zichzelf staande
  • : Enkelvoudig HTML-bestand met embedded JavaScriptNul afhankelijkheden
  • : Geen externe bibliotheken vereistMobielvriendelijk
  • : Responsive design werkt op alle apparatenProductie gereed
  • : Dezelfde logica kan gebruikt worden in uw appsToegang tot de live demo op

Op jouw lopende zaak!

  • De problemen met EasyNMTDit is niet dumpen op
  • EasyNMThet deed iets dat niets anders kon en ik heb gebouwd
  • Veel projecten die er gebruik van maken
  • **Het wordt gewoon lang in de tand, dus... wat voor problemen hebben we?**Oh mijn, er zijn er veel.
  • **EasyNMT is bijna tien jaar geleden gebouwd.**Technologie is verder gegaan... en het was nooit bedoeld om een productie-niveau systeem te zijn.
  • **Hier zijn enkele van de kwesties:**Het crasht... een heleboel.

Het is niet ontworpen om te herstellen van problemen zo vaak valt gewoon over.

  • **Het is SUPER PICKY over de input.**Emoticons, symbolen, zelfs getallen kunnen het verwarren.
  • **Het is niet ontworpen voor enige lading.**Zie hierboven.
  • **Het is nooit ontworpen om te zijn.**Het is niet ontworpen om zijn modellen bij te werken.
  • **of worden (gemakkelijk) gebouwd met ingebouwde modellen.**De GPU CUDA spul is oud
  • **zo langzamer dan het nodig is.**Je kunt niets repareren.
  • De Python code staat weer op de repoNiet geweldig.

**Geen tegendruk of wachtrij.**Stuur te veel verzoeken en het valt gewoon over.MODEL_FAMILYGeen opmerkzaamheid.

# Opus-MT (default, best quality)
MODEL_FAMILY=opus-mt

# mBART50 (50 languages, single model)
MODEL_FAMILY=mbart50

# M2M100 (100 languages, broadest coverage)
MODEL_FAMILY=m2m100

Als het misgaat, vlieg je blind.

De oplossing: Meestal Lucid-NMTDus... besloot ik om een nieuwe en verbeterde EasyNMT te bouwen, numeestal lucid-nmt


  1. Dit is niet alleen een patch job; het is een complete herschrijven met productie gebruik in het achterhoofd.MODEL_FAMILYDit is wat het beter maakt:opus-mtOndersteuning van de familie met meerdere models
  2. Meestal ondersteunt Lucid-NMT nu
  3. drie vertaalmodellenfamilies
  4. , geeft u flexibiliteit op basis van uw behoeften:

Opus-MT (Helsinki-NLP) - Standaard

# Set primary to Opus-MT (best quality)
MODEL_FAMILY=opus-mt
AUTO_MODEL_FALLBACK=1
MODEL_FALLBACK_ORDER=opus-mt,mbart50,m2m100

# Request Ukrainian → French
# 1. Try Opus-MT first (not available)
# 2. Automatically fall back to mBART50 (available!)
# 3. Translation succeeds with mBART50

Dekking:

  • 1200+ vertaalparen voor 150+ talenArchitectuur:
  • Apart model per vertaalrichtingKwaliteit:
  • Beste algemene vertaalkwaliteitUse Case:
  • Productievertalingen waar kwaliteit van belang isModelgrootte:

300-500MB per richting

# Enable auto-fallback (default: enabled)
AUTO_MODEL_FALLBACK=1

# Set fallback priority (default: opus-mt → mbart50 → m2m100)
MODEL_FALLBACK_ORDER="opus-mt,mbart50,m2m100"

# Disable for strict single-family mode
AUTO_MODEL_FALLBACK=0

Voorbeeld:

Engels→Duits is een ander model dan Duits→Engels

  1. **mBART50 (Facebook)**Dekking:
  2. 50 talen, all-to-all vertalingArchitectuur:
  3. Eén meertalig modelKwaliteit:
  4. Goede kwaliteit, vooral voor grote talenUse Case:
  5. Ruimtegebonden implementaties of veel taalparenModelgrootte:
  6. **~2.4GB (enkel model voor alle 50 talen)**Voordeel:
  7. Eén model behandelt 2.450 vertaalparenM2M100 (Facebook)
  8. **Dekking:**100 talen, all-to-all vertaling
  9. **Architectuur:**Eén meertalig model
  10. **Kwaliteit:**Goede kwaliteit met breedste taaldekking
  11. **Use Case:**Maximale dekking van de taal-minModelgrootte:

~2.2GB (enkel model voor alle 100 talen)

Voordeel:

Een model behandelt 9.900 vertaalparen

Schakelen is eenvoudig

    • zet gewoon de
  • omgevingsvariabele:
  • Automatic Model Family Fallback - NIEUW!

Een van de krachtigste nieuwe features is

  • automatische terugval tussen modelfamilies
  • Dit zorgt voor een maximale dekking van het talenpaar, terwijl de kwaliteit van de vertaling wordt geprioriteerd.

**Hoe het werkt:**Je hebt een primaire ingesteld.

  • **(b.v.,**voor de beste kwaliteit)
  • Wanneer u een vertaalpaar aanvraagt dat niet beschikbaar is in de primaire familieHet systeem probeert automatisch de volgende familie in de terugval volgorde
  • Dit gaat door tot er een geschikt model is gevondenVoorbeeldscenario:

Voordelen:

Maximale dekking:

Steun 100+ talen zonder meerdere implementaties te beheren

  • Kwaliteitsprioriteit:
  • Maakt altijd gebruik van het best beschikbare model voor elk paar
  • Nulconfiguratie:
  • Werkt automatisch, geen handmatige interventie nodig

Transparant loggen:

  • Zie welke modelfamilie voor elke vertaling werd gebruikt
  • Configuratie:
  • Deze functie is perfect voor productie-omgevingen waar u wilt maximale dekking zonder op te offeren kwaliteit!
  • Belangrijkste verbeteringen

Meervoudige gezinsondersteuning

# NMT: Fits on a USB stick
du -sh model-cache/
2.5G    model-cache/

# LLM: Needs serious storage
du -sh llama-models/
140G    llama-models/

- Kies uit Opus-MT (1200+ paar), mBART50 (50 talen), of M2M100 (100 talen).

Modelbevindingseindpunten

    • Dynamisch query beschikbare modellen van Hugging Face voor elke familie.
  • Robuuste invoerafhandeling
    • Emoji?
  • Nummers?

Symbolen?

  • Kom maar op.
  • De dienst omvat nu uitgebreide input sanitisatie en symbool maskering.
  • Verzoek in de wachtrij en tegendruk
    • Ingebouwde semafore-gebaseerde wachtrij met intelligente retry-after schattingen.

LRU-modelcaching

  • Beheert VRAM automatisch door oude modellen uit te zetten wanneer de cache vol is. |--------|------|------| Moderne CUDA-ondersteuning
  • Gebruikt PyTorch met CUDA 12.6, ondersteunt FP16/BF16 voor 2x snelheidsverbeteringen. Observeerbaarheid van de productie
  • Gezondheidschecks, paraatheid sondes, cache status, gestructureerde logging. Graceful shutdown

- Geen verweesde verzoeken meer of corrupte staat.

EasyNMT-compatibele API

    • Drop-in vervanging voor bestaande integraties.
  • Terugval van de vertaling van de pivot
    • Als een direct taalpaar niet beschikbaar is, dan zijn er automatisch routes door het Engels (of de door u gekozen pivot).
  • Minimale Docker-afbeeldingen
    • Nieuw

varianten met een volume-kaart cache voor kleinere implementaties.

  • ⚠️ Sometimes adds interpretations not in original
  • ⚠️ Quality varies with prompt phrasing
  • ⚠️ Can be "creative" with technical terms
  • ⚠️ Needs careful prompt engineering
  • ⚠️ Unpredictable with edge cases

Waarom NMT over LLM's voor vertaling?

Input: "The API returns a 429 status code when rate limited."

NMT (Opus-MT): "Die API gibt einen 429-Statuscode zurück, wenn sie ratenbegrenzt ist."
(Accurate, preserves technical terms)

LLM (might do): "Die API sendet den Fehlercode 429, wenn zu viele Anfragen gestellt werden."
(Interprets rather than translates, adds context not in original)

Je zou je kunnen afvragen: "Waarom een toegewijde NMT dienst gebruiken wanneer LLM's zoals GPT-4, Claude of Llama kunnen vertalen?" Geweldige vraag.

Hier is de reality check gebaseerd op productiegebruik:

  • Snelheid: 10-100x Sneller
  • NMT (meestal lucid-nmt):
  • CPU: ~0,3-1,0 seconden per zin
  • GPU (FP16): ~0,05-0,2 seconden per zin
  • Batchverwerking: 50+ zinnen/seconde op GPU
  • LLM's:

GPT-4: 3-10 seconden per aanvraag (API latentie + generatie)

  • Llama 3 70B: 5-15 seconden per zin (lokaal)
  • Claude 3: 2-8 seconden per aanvraag (API latentie)
  • Echt voorbeeld:
  • Het vertalen van een 1000-woord blog post:
  • meestal lucid-nmt (GPU)

: 5-10 seconden

GPT-4 API**: 30-60 seconden**Lokale Llama 70B

  • : 2-5 minuten
  • Wanneer je auto-vertaling van honderden blog berichten naar 12+ talen, dat snelheidsverschil is MASSIVE.
  • Modelgrootte: 500MB vs 140GB
  • NMT-modellen:

Opus-MT (per richting): 300-500MB

MBART50 (alle 50 talen): 2.4GBM2M100 (alle 100 talen): 2.2GB

Totaal voor 100+ talen: ~2.2GB

flowchart LR
    A[HTTP Client] --> B[API Gateway]
    B --> C[Translation Endpoint]
    C --> D{Has Capacity?}
    D -->|Yes| E[Translation Service]
    D -->|No| F[Queue with 429]
    F --> E
    E --> G[Process Pipeline]
    G --> H[Get Model from Cache]
    H --> I[Translate]
    I --> J[Return Response]
    J --> A

LLM's:

  1. Lama 3 8B: ~16GBLlama 3 70B: ~140GB
  2. Mixtral 8x7B: ~90GBGPT-4: Niet beschikbaar voor zelfhosting
  3. **Storage-impact:**Resource Requirements: Laptop vs Server Farm
    • **NMT CPU Implementatie:**Runs fine on: 2 CPU cores, 4GB RAM
    • Docker-afbeelding: 1.5-2.5GBInvloed: alleen CPU, geen GPU nodigRetry-AfterKosten: $10-20/maand VPS
  4. **LLM-eisen:**Llama 3 70B: heeft 80GB+ VRAM nodig (A100 GPU)
    • Kleinere 7B-13B modellen: nog steeds 16-32GB RAM minimum
    • API kosten: $0,03-0.30 per 1000 tokens (toevoegt snel!)
    • Zelfhosting: $1000+/maand voor serieuze GPU
    • Echte kosten vergelijking voor 10.000 blog post vertalingen:
  5. Methode Kosten Tijd* Meestal lucid-nmt (CPU) * $20/maand VPS * 2-3 uur *
      • Meestal lucid-nmt (GPU) * $50/maand GPU VPS * 15-30 minuten *
    • GPT-4 API $15 info 8-15 uur
      • Claude API * $200-400 * 6-12 uur *
  6. Local Llama 70B $1000+/maand hardware 20-40 uurKwaliteit: Doel-gebouwd vs General-Purpose

**NMT sterktes:**Speciaal voor vertaling opgeleidRetry-AfterConsistente kwaliteit (zelfde input = dezelfde output)

Geen "hallucinaties" - pure vertaling

Handelt technische inhoud, code, formatteren goed

Geen prompt engineering nodig

sequenceDiagram
    participant Client
    participant API
    participant Queue
    participant Translator
    participant Cache
    participant Model

    Client->>API: POST /translate
    API->>Queue: Acquire slot

    alt Queue has space
        Queue-->>API: Slot acquired
        API->>Translator: Process translation
        Translator->>Translator: Sanitize input
        Translator->>Translator: Split sentences
        Translator->>Translator: Chunk text
        Translator->>Translator: Mask symbols
        Translator->>Cache: Get model (en→de)

        alt Cache hit
            Cache-->>Translator: Return cached model
        else Cache miss
            Cache->>Model: Load from Hugging Face
            Model-->>Cache: Pipeline loaded
            Cache->>Cache: Evict old if at capacity
            Cache-->>Translator: Return model
        end

        Translator->>Model: Translate batches
        Model-->>Translator: Translations
        Translator->>Translator: Unmask symbols
        Translator->>Translator: Post-process
        Translator-->>API: Translations
        API->>Queue: Release slot
        API-->>Client: 200 OK + translations
    else Queue full
        Queue-->>API: Overflow error
        API-->>Client: 429 Too Many Requests\nRetry-After: X seconds
    end

LLM-uitdagingen:

Voorbeeldscenario:

graph LR
    A[Raw Input] --> B{Sanitize?}
    B -->|Yes| C[Check Noise]
    B -->|No| D[Split Sentences]
    C -->|Is Noise| Z[Return Placeholder]
    C -->|Valid| D

    D --> E[Enforce Max Length]
    E --> F[Chunk for Batching]
    F --> G{Symbol Masking?}

    G -->|Yes| H[Mask Digits/Punct/Emoji]
    G -->|No| I[Translate]
    H --> I

    I --> J{Direct Model?}
    J -->|Available| K[Direct Translation]
    J -->|Not Available| L{Pivot Fallback?}

    L -->|Yes| M[src→en→tgt]
    L -->|No| Z
    K --> N[Unmask Syis robust input handling. Here's what happens:

**Noise Detection:**
- Strips control characters (except \t, \n, \r)
- Checks minimum character count (default: 1)
- Calculates alphanumeric ratio (default: must be ≥20%)
- Rejects pure emoji, pure punctuation, or pure whitespace

**Symbol Masking:**
Why mask symbols? Translation models are trained on text, not emoji or special symbols. These can confuse them or get mangled. So we:

1. Extract all digits, punctuation, and emoji as contiguous runs
2. Replace them with sentinel tokens: `⟪MSK0⟫`, `⟪MSK1⟫`, etc.
3. Translate the masked text
4. Restore the original symbols in their positions

Example:

Input: "Hello 👋 world! Price: $99.99" Wanneer moet u ze gebruiken? (👋) (!) (:) ($99.99)


**Post-Processing:**
After translation, we remove "symbol loops" - repeated symbols that weren't in the source:

Gebruik NMT (meestal lucid-nmt) wanneer: Je hebt consistente, snelle vertaling nodig op schaal Begrotingszaken (zelfhosting of hoog volume)


### Sentence Splitting & Chunking

Long texts get split intelligently:

```mermaid
graph TD
    A[Long Text] --> B[Split on . ! ? …]
    B --> C{Sentence > 500 chars?}
    C -->|Yes| D[Split on word boundaries]
    C -->|No| E[Keep sentence]
    D --> E

    E --> F[Group into chunks ≤900 chars]
    F --> G[Translate each chunk]
    G --> H[Join with space]

Je vertaalt technische inhoud, code, gestructureerde data

  • Je hebt deterministische output nodig (zelfde input = dezelfde output)
  • U wilt draaien op CPU of bescheiden hardware
  • Je bouwt geautomatiseerde vertaalpijpleidingen.

LLM's gebruiken wanneer:

Je hebt creatieve aanpassing nodig, geen letterlijke vertaling

stateDiagram-v2
    [*] --> CheckCache
    CheckCache --> CacheHit: Model exists
    CheckCache --> CacheMiss: Model not loaded

    CacheHit --> MoveToEnd: Update LRU order
    MoveToEnd --> ReturnModel

    CacheMiss --> CheckCapacity
    CheckCapacity --> LoadModel: Space available
    CheckCapacity --> EvictOldest: Cache full

    EvictOldest --> MoveToCPU: Free VRAM
    MoveToCPU --> ClearCUDA: torch.cuda.empty_cache()
    ClearCUDA --> LoadModel

    LoadModel --> AddToCache
    AddToCache --> ReturnModel
    ReturnModel --> [*]

Context en culturele nuance zijn belangrijker dan snelheid

  • Je doet weinig, eenmalige vertalingen.
  • U moet vertalen + samenvatting + herschrijven in één stap
  • Je bent oké met variabele kosten en tragere verwerking
  • De onderste regel
  • Voor

geautomatiseerde blogvertaling

(my use case), NMT is de duidelijke winnaar:

# Semaphore limits concurrent translations
MAX_INFLIGHT = 1  # On GPU, 1 at a time for efficiency
MAX_QUEUE_SIZE = 1000  # Up to 1000 waiting

# When full:
# - Returns 429 Too Many Requests
# - Includes Retry-After header
# - Estimates wait time based on average duration

Vertaalt 100+ blogberichten naar 12 talen in ~30 minuten (GPU)

avg_duration = 2.5 seconds (tracked with EMA)
waiters = 100
slots = 1
estimated_wait = (100 / 1) * 2.5 = 250 seconds
clamped = min(250, 120) = 120 seconds
Retry-After: 120

Loopt op een $50/maand VPS

Consistente kwaliteit voor alle posten

graph LR
    A[Ukrainian Text] --> B{Direct uk→fr?}
    B -->|Exists| C[Translate Directly]
    B -->|Missing| D[Pivot via English]

    D --> E[uk→en]
    E --> F[en→fr]
    F --> G[French Result]
    C --> G

Totale setup: One Docker container

Proberen dit met LLMs zou kosten honderden dollars per maand in API-kosten of vereisen een $ 2000+ GPU-server om zelf-host.

Het snelheidsverschil alleen al maakt NMT de enige praktische keuze voor productie vertaalleidingen.

TL;DR:

NMT is speciaal gebouwd voor vertaling, draait op bescheiden hardware, en is 10-100x sneller dan LLM's. Als u snelle, consistente, kosteneffectieve vertaling op schaal nodig hebt, wint NMT handen naar beneden.

# src/core/cache.py
from collections import OrderedDict
import torch

class LRUPipelineCache:
    """LRU cache that automatically cleans up GPU memory when evicting models."""

    def __init__(self, capacity: int):
        self.cache = OrderedDict()  # Maintains insertion order
        self.capacity = capacity

    def get(self, key: str):
        """Get model from cache, moves it to end (most recently used)."""
        if key not in self.cache:
            return None
        self.cache.move_to_end(key)  # Mark as recently used
        return self.cache[key]

    def put(self, key: str, value):
        """Add model to cache, evicting oldest if at capacity."""
        if key in self.cache:
            self.cache.move_to_end(key)
        else:
            self.cache[key] = value

        # If cache is full, evict the oldest model
        if len(self.cache) > self.capacity:
            oldest_key, oldest_pipeline = self.cache.popitem(last=False)

            # MAGIC: Move evicted model to CPU to free GPU memory
            try:
                oldest_pipeline.model.to("cpu")
                if torch.cuda.is_available():
                    torch.cuda.empty_cache()  # Tell GPU to release memory
                logger.info(f"Evicted {oldest_key}, freed GPU memory")
            except Exception as e:
                logger.warning(f"Failed to clean GPU memory: {e}")

Overzicht architectuur

  • OrderedDictDe aanvraagstroom is eenvoudig:
  • Clientstuurt een vertaalverzoek naar API Gateway (Gunicorn + Uvicorn werknemers)
  • API GatewayRoutes naar het vertaaleindpunt
  • Capaciteitscontrole: Systeemcontroles of het in staat is het verzoek te behandelen

Ja.

→ Verzoek gaat onmiddellijk naar de vertaaldienst

# src/services/model_manager.py
def get_pipeline(self, src: str, tgt: str):
    """Try to get translation model, with automatic fallback to other providers."""

    # Determine which model families support this language pair
    families_to_try = []

    if config.AUTO_MODEL_FALLBACK:
        # Try families in priority order: opus-mt → mbart50 → m2m100
        for family in config.MODEL_FALLBACK_ORDER.split(","):
            if self._is_pair_supported(src, tgt, family.strip()):
                families_to_try.append(family.strip())

    # Try each family until one succeeds
    last_error = None
    for family in families_to_try:
        try:
            model_name, src_lang, tgt_lang, _ = self._get_model_name_and_langs(src, tgt, family)

            if family != config.MODEL_FAMILY:
                logger.info(f"Using fallback '{family}' for {src}->{tgt}")

            # Load the model from HuggingFace
            pipeline = transformers.pipeline(
                "translation",
                model=model_name,
                device=device_manager.device_index,
                src_lang=src_lang,
                tgt_lang=tgt_lang
            )

            self.cache.put(f"{src}->{tgt}", pipeline)
            return pipeline

        except Exception as e:
            last_error = e
            logger.warning(f"Family '{family}' failed for {src}->{tgt}: {e}")
            continue  # Try next family

    # All families failed
    raise ModelLoadError(f"{src}->{tgt}", last_error)

Nee

  • → Verzoek in de wachtrij, client ontvangt HTTP 429 metkop
  • Vertaaldienstverwerkt het verzoek via de pijpleiding:
  • Invoerreiniging en splitsing van zinsdelenSymboolmaskering (emojis, speciale tekens)
  • Vertaling met behulp van gecachede modellenSymbool ontmaskerd en nabewerking

Modelcache

(LRU) verstrekt vertaalmodellen:

# src/services/queue_manager.py
import asyncio
from contextlib import asynccontextmanager

class QueueManager:
    """Manages request queuing and backpressure."""

    def __init__(self, max_inflight: int, max_queue: int):
        self.semaphore = asyncio.Semaphore(max_inflight)  # Limit concurrent translations
        self.max_queue_size = max_queue
        self.waiting_count = 0
        self.inflight_count = 0
        self.avg_duration_sec = 5.0  # Exponential moving average

    @asynccontextmanager
    async def acquire_slot(self):
        """Try to get a translation slot, track metrics, handle queueing."""

        # Check if queue is too full
        if self.waiting_count >= self.max_queue_size:
            # Calculate how long client should wait before retrying
            retry_after = self._estimate_retry_after()
            raise QueueOverflowError(self.waiting_count, retry_after)

        self.waiting_count += 1
        try:
            # Wait for available slot (this is the queue!)
            await self.semaphore.acquire()
            self.waiting_count -= 1
            self.inflight_count += 1

            start_time = time.time()
            yield  # Let the translation happen

            # Update average duration for retry-after estimates
            duration = time.time() - start_time
            alpha = config.RETRY_AFTER_ALPHA  # Smoothing factor (0.2)
            self.avg_duration_sec = alpha * duration + (1 - alpha) * self.avg_duration_sec

        finally:
            self.inflight_count -= 1
            self.semaphore.release()

    def _estimate_retry_after(self) -> int:
        """Smart calculation: how many waiting / how many slots * avg time per request."""
        if self.inflight_count == 0:
            return config.RETRY_AFTER_MIN_SEC

        # If 10 people waiting and 2 slots available, and each takes 5 seconds:
        # retry_after = (10 / 2) * 5 = 25 seconds
        retry_sec = (self.waiting_count / self.semaphore._value) * self.avg_duration_sec

        # Clamp between min and max
        return max(
            config.RETRY_AFTER_MIN_SEC,
            min(int(retry_sec), config.RETRY_AFTER_MAX_SEC)
        )

Cache hit → Snelle reactie

  • Cache miss → Laden van HuggingFace HubCache vol → Auto-evict oude modellen, duidelijk CUDA geheugenmax_inflight)
  • Respons (@asynccontextmanagerkeert terug naar client
  • **Sleutelontwerp:**Het tegendrukmechanisme (queue + HTTP 429) voorkomt crashes onder belasting.
  • Wanneer overweldigd, de service wachtrijen verzoeken in plaats van sterven, waardoor clients intelligente retry timing viaheaders.
  • Hoe het werkt: Deep DiveVerzoekstroom

Als er een vertaalverzoek binnenkomt, zie je wat er gebeurt:

Invoerverwerkingspijpleiding

# src/utils/symbol_masking.py
import re

def mask_symbols(text: str) -> tuple[str, dict[str, str]]:
    """Replace special symbols with placeholders before translation."""

    originals = {}
    masked_text = text
    placeholder_counter = 0

    # Pattern: Match emojis, symbols, special punctuation
    # \U0001F300-\U0001F9FF = emoji range
    # [\u2600-\u26FF\u2700-\u27BF] = misc symbols
    symbol_pattern = re.compile(
        r'[\U0001F300-\U0001F9FF\u2600-\u26FF\u2700-\u27BF'
        r'\u00A9\u00AE\u2122\u2139\u3030\u303D\u3297\u3299]+'
    )

    for match in symbol_pattern.finditer(text):
        symbol = match.group()
        placeholder = f"__SYMBOL_{placeholder_counter}__"
        originals[placeholder] = symbol
        masked_text = masked_text.replace(symbol, placeholder, 1)
        placeholder_counter += 1

    return masked_text, originals

def unmask_symbols(text: str, originals: dict[str, str]) -> str:
    """Restore original symbols after translation."""
    for placeholder, original in originals.items():
        text = text.replace(placeholder, original)
    return text

De service maakt gebruik van een geavanceerde multi-stage pijplijn om rommelige real-world tekst te verwerken:

# Before translation:
text = "Hello! 👋 Check out this cool feature 🚀"

# Mask symbols:
masked, originals = mask_symbols(text)
# masked = "Hello! __SYMBOL_0__ Check out this cool feature __SYMBOL_1__"
# originals = {"__SYMBOL_0__": "👋", "__SYMBOL_1__": "🚀"}

# Translate the masked text:
translated = translate(masked, "de")  # → "Hallo! __SYMBOL_0__ Schau dir diese coole Funktion an __SYMBOL_1__"

# Unmask symbols:
final = unmask_symbols(translated, originals)
# final = "Hallo! 👋 Schau dir diese coole Funktion an 🚀"

Gemaskerd: "Hallo, MSK0 wereld, MSK1 Prijs, MSK2 en MSK3"

  • **Bron: "Hallo wereld"**Slechte vertaling: "Hola mundo!!!!!!"
  • Geschoond: "Hola mundo" # Verwijdert de !!!! loopDit garandeert:👋Modellen verstikken niet op enorme ingangen__SYMBOL_0__We kunnen efficiënt batchen
  • Context wordt bewaard binnen redelijke grenzenModel Caching & Geheugenbeheer

De LRU cache is slim over GPU geheugen:

Waarom dit belangrijk is:

# src/utils/text_processing.py
def chunk_sentences(sentences: list[str], max_chars: int = 900) -> list[list[str]]:
    """Group sentences into chunks that fit within model's max input length."""

    chunks = []
    current_chunk = []
    current_length = 0

    for sentence in sentences:
        sentence_len = len(sentence)

        # If this sentence alone is too long, it goes in its own chunk
        if sentence_len > max_chars:
            if current_chunk:
                chunks.append(current_chunk)
                current_chunk = []
                current_length = 0
            chunks.append([sentence])
            continue

        # If adding this sentence exceeds limit, start new chunk
        if current_length + sentence_len + 1 > max_chars:
            chunks.append(current_chunk)
            current_chunk = [sentence]
            current_length = sentence_len
        else:
            current_chunk.append(sentence)
            current_length += sentence_len + 1  # +1 for space

    # Don't forget the last chunk!
    if current_chunk:
        chunks.append(current_chunk)

    return chunks

def split_sentences(text: str, max_sentence_chars: int = 500) -> list[str]:
    """Split text into sentences, enforcing max length."""

    # Split on common sentence terminators
    sentences = re.split(r'([.!?…]+\s+)', text)

    result = []
    for sentence in sentences:
        if not sentence or sentence.isspace():
            continue

        # If sentence is too long, split on word boundaries
        if len(sentence) > max_sentence_chars:
            words = sentence.split()
            current = []
            current_len = 0

            for word in words:
                if current_len + len(word) + 1 > max_sentence_chars:
                    result.append(' '.join(current))
                    current = [word]
                    current_len = len(word)
                else:
                    current.append(word)
                    current_len += len(word) + 1

            if current:
                result.append(' '.join(current))
        else:
            result.append(sentence.strip())

    return result

GPU geheugen is kostbaar

  • Vertaalmodellen zijn 300-500MB per stukLaden modellen is traag (1-3 seconden).!?…We houden de 6 meest recente modellen warm
  • Oude modellen worden automatisch uitgezetWachtrij & Backpressure
  • **In plaats van te crashen onder belasting, vraagt de service wachtrijen:**De retry schatting is slim:
  • Pivot Vertaling TerugvalNiet alle talenparen hebben directe modellen op Hugging Face.

Oplossing?

Pivot via het Engels:

# src/services/model_discovery.py
import httpx
from datetime import datetime, timedelta

class ModelDiscoveryService:
    """Discovers available translation models with 1-hour cache."""

    def __init__(self):
        self._cache = {}  # Cache results to avoid hammering HuggingFace API
        self._cache_ttl = timedelta(hours=1)
        self._hf_api_base = "https://huggingface.co/api/models"

    async def discover_opus_mt_pairs(self, force_refresh: bool = False):
        """Query HuggingFace for all Helsinki-NLP Opus-MT models."""

        cache_key = "opus-mt"

        # Check cache first
        if not force_refresh and cache_key in self._cache:
            cached_data, cached_time = self._cache[cache_key]
            if datetime.now() - cached_time < self._cache_ttl:
                return cached_data  # Cache hit!

        # Cache miss - query HuggingFace API
        async with httpx.AsyncClient() as client:
            response = await client.get(
                self._hf_api_base,
                params={
                    "author": "Helsinki-NLP",
                    "search": "opus-mt",
                    "limit": 1000
                },
                timeout=30.0
            )
            models = response.json()

        # Extract language pairs from model names
        # Example: "Helsinki-NLP/opus-mt-en-de" → ("en", "de")
        pairs = []
        for model in models:
            model_id = model.get("modelId", "")
            if model_id.startswith("Helsinki-NLP/opus-mt-"):
                # Extract the language codes after "opus-mt-"
                lang_part = model_id.replace("Helsinki-NLP/opus-mt-", "")
                if "-" in lang_part:
                    src, tgt = lang_part.split("-", 1)
                    pairs.append({"source": src, "target": tgt})

        # Cache the results
        self._cache[cache_key] = (pairs, datetime.now())

        return pairs

Deze dubbele latentie maar zorgt voor dekking voor alle ondersteunde taalparen.

  • Code Deep Dive: Cool Features Uitgelegd (httpxLaten we een aantal van de meest interessante delen van de codebase verkennen!
  • **Dit zijn echte productiepatronen die de service robuust en efficiënt maken.**Elk knipsel bevat verklaringen geschikt voor niet-Python ontwikkelaars.
  • **1.**Slimme LRU-cache met GPU-geheugenbeheerenEen van de coolste functies is de intelligente modelcache die weet hoe GPU-geheugen te verwerken:deWat gebeurt hier?Helsinki-NLP/opus-mt-en-de
  • : Als een normaal woordenboek, maar herinnert zich de orde items werden toegevoegdLRU (Last Recently Used)

: Wanneer de cache vol is, schop dan het model eruit dat in de langste tijd niet is gebruikt

GPU-opruiming

# src/core/device.py
import torch

class DeviceManager:
    """Smart device selection with GPU auto-detection."""

    def __init__(self):
        self.use_gpu = self._should_use_gpu()
        self.device_index = self._resolve_device()
        self.device_str = "cpu" if self.device_index < 0 else f"cuda:{self.device_index}"

        # Auto-configure parallel translation slots based on device
        if self.device_index >= 0:
            # GPU: Run translations serially to avoid VRAM fragmentation
            self.max_inflight = 1
        else:
            # CPU: Can handle multiple translations in parallel
            self.max_inflight = config.MAX_WORKERS_BACKEND

        self._log_device_info()

    def _should_use_gpu(self) -> bool:
        """Check if GPU should be used."""
        if config.USE_GPU.lower() == "false":
            return False
        if config.USE_GPU.lower() == "true":
            return torch.cuda.is_available()
        # "auto" mode: use GPU if available
        return torch.cuda.is_available()

    def _resolve_device(self) -> int:
        """Returns device index: -1 for CPU, 0+ for CUDA."""
        if not self.use_gpu:
            return -1

        # Check if specific CUDA device requested
        if config.DEVICE and config.DEVICE.startswith("cuda:"):
            device_num = int(config.DEVICE.split(":")[1])
            return device_num

        return 0  # Use first GPU

    def _log_device_info(self):
        """Log device information at startup."""
        if self.device_index >= 0:
            gpu_name = torch.cuda.get_device_name(self.device_index)
            vram_gb = torch.cuda.get_device_properties(self.device_index).total_memory / 1e9
            logger.info(f"Using GPU: {gpu_name} ({vram_gb:.1f}GB VRAM)")
            logger.info(f"Max inflight translations: {self.max_inflight} (GPU mode)")
        else:
            cpu_count = os.cpu_count()
            logger.info(f"Using CPU ({cpu_count} cores)")
            logger.info(f"Max inflight translations: {self.max_inflight} (CPU mode)")

# Global singleton instance
device_manager = DeviceManager()

: Wanneer het uitzetten van een model, we expliciet verplaatsen naar CPU-geheugen en vertellen de GPU om zijn bronnen vrij te geven

  • Waarom het belangrijk is: Zonder dit, GPU geheugen zou vullen en crashen na het laden van 2-3 modellen!
  • **2.**Automatisch Model Familie Terugvalmax_inflight=1Deze slimme functie probeert meerdere AI-modelproviders automatisch als de eerste niet het taalpaar heeft dat je nodig hebt:max_inflight=4Wat gebeurt hier?
  • Fallbackketting: Als Opus-MT geen Oekraïens→Frans heeft, probeer dan automatisch mBART50, dan M2M100DEVICE=cuda:1
  • Geen handmatige interventie: Gebruikers vragen gewoon een vertaling en krijgen het beste beschikbare model
  • Fout bij afhandelen: Als alle families falen, gooien we een duidelijke fout met de laatste fout reden

Slimme caching

: Succesvolle modellen worden gecached met de taal paar sleutel

# Snippet from QueueManager showing EMA calculation
def update_avg_duration(self, new_duration: float):
    """Update average duration using exponential moving average."""

    # EMA formula: new_avg = α × new_value + (1 - α) × old_avg
    # α = smoothing factor (0.0 to 1.0)
    #   - Higher α = more weight to recent values (faster adaptation)
    #   - Lower α = more weight to historical values (more stable)

    alpha = 0.2  # 20% weight to new value, 80% to historical

    self.avg_duration_sec = (
        alpha * new_duration +
        (1 - alpha) * self.avg_duration_sec
    )

3.

# Initial average: 5.0 seconds
# New request takes: 10.0 seconds

# EMA calculation:
new_avg = 0.2 * 10.0 + 0.8 * 5.0
        = 2.0 + 4.0
        = 6.0 seconds

# Next request takes: 3.0 seconds
new_avg = 0.2 * 3.0 + 0.8 * 6.0
        = 0.6 + 4.8
        = 5.4 seconds

Wachtrij met tegendruk aanvragen (HTTP 429)

  • **Productie-kwaliteit wachtrij die voorkomt dat de server crasht onder zware belasting:**Wat gebeurt hier?
  • Semafore: Als een uitsmijter in een club - laat N mensen in één keer binnen (N =
  • Contextbeheer): Metriek automatisch volgen en opruimen
  • Slimme retry-after: vertelt klanten "terug te komen in 25 seconden" op basis van wachtrijdiepte en gemiddelde aanvraagtijdRetry-AfterExponentieel bewegend gemiddelde

: gladstrijkt pieken in de duur van de aanvraag

  • Waarom het belangrijk is: Onder zware belasting, geeft HTTP 429 terug in plaats van oneindig te crashen of in de rij te staan
  • **4.**Symbool Masking Magic
  • **Bewaart speciale tekens (emojis, symbolen) die vertaalmodellen kunnen verknoeien:**Voorbeeldgebruik:
  • **Wat gebeurt hier?**Regex patroon
  • : Komt overeen met emoji en speciaal symbool Unicode bereikPlaatshoudersysteem
  • : Wisselsmet

tijdelijk

Waarom het belangrijk is

: Vertaalmodellen soms corrupt of verwijderen emojis - dit bewaart ze perfect!

5.

# Prefer GPU if available (default)
USE_GPU=auto

# Force GPU
USE_GPU=true

# Force CPU
USE_GPU=false

# Explicit device override
DEVICE=cuda:0
DEVICE=cpu

Intelligente tekstchunking

# Model family selection (NEW in v2.0!)
MODEL_FAMILY=opus-mt   # Best quality (default)
MODEL_FAMILY=mbart50   # 50 languages, single model
MODEL_FAMILY=m2m100    # 100 languages, maximum coverage

# Auto-fallback between model families (NEW in v2.0!)
AUTO_MODEL_FALLBACK=1  # Enabled by default
MODEL_FALLBACK_ORDER="opus-mt,mbart50,m2m100"  # Priority order

# Volume-mapped model cache (NEW in v2.0!)
MODEL_CACHE_DIR=/models  # Persistent cache directory

# Model arguments passed to transformers.pipeline
EASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}'
EASYNMT_MODEL_ARGS='{"torch_dtype":"bf16","cache_dir":"/models"}'

# Preload models at startup (reduces first-request latency)
PRELOAD_MODELS="en->de,de->en,fr->en"

# LRU cache capacity
MAX_CACHED_MODELS=6

Breek lange teksten in stukken die passen bij modellimieten met behoud van zinsgrenzen:

  • **Wat gebeurt hier?**Splitsing van de straf

    • opus-mt: Gebruikt regex om op te splitsen
    • mbart50met behoud van de interpunctie
    • m2m100Hebzuchtig brokeren
  • : Verpakt zoveel mogelijk zinnen in elke brok zonder de limiet te overschrijdenWoordgrens splitsen

    • 1: Als één zin te lang is, splitst hij zich op spaties in plaats van middenwoord te knippen
    • 0Waarom het belangrijk is
  • **: Vertaalmodellen hebben ingangslimieten (meestal 512-1024 tokens).**Dit zorgt ervoor dat we ze nooit overschrijden terwijl we de context intact houden.

    • 6."opus-mt,mbart50,m2m100"Async Model Discovery met Caching
    • Dynamisch ontdekt beschikbare vertaalmodellen van HuggingFace:"m2m100,mbart50,opus-mt"Wat gebeurt hier?
  • Async HTTP-client): Maakt niet-blokkerende HTTP-verzoeken naar HuggingFace

    • Op tijd gebaseerde caching/models: Bewaart resultaten gedurende 1 uur om snelheidsbeperking te vermijden-v ./model-cache:/models
    • Modelnaam ontleden
    • : Extracten

en

  • fp16van
  • bf16Waarom het belangrijk is
  • fp32: HuggingFace heeft 1200+ Opus-MT modellen.

Het opvragen van ze kost 10 seconden.

# Batch size for translation (higher = faster but more VRAM)
EASYNMT_BATCH_SIZE=16  # CPU: 8-16, GPU: 32-64

# Maximum text length per item
EASYNMT_MAX_TEXT_LEN=1000

# Maximum beam size (higher = better quality but slower)
EASYNMT_MAX_BEAM_SIZE=5

# Worker thread pools
MAX_WORKERS_BACKEND=1    # Translation workers
MAX_WORKERS_FRONTEND=2   # Language detection workers

Caching maakt het direct!

# Enable request queueing (highly recommended)
ENABLE_QUEUE=1

# Max concurrent translations
# Auto: 1 on GPU, MAX_WORKERS_BACKEND on CPU
MAX_INFLIGHT_TRANSLATIONS=1

# Max queued requests before 429
MAX_QUEUE_SIZE=1000

# Per-request timeout (0 = disabled)
TRANSLATE_TIMEOUT_SEC=180

# Retry-After estimation
RETRY_AFTER_MIN_SEC=1      # Floor
RETRY_AFTER_MAX_SEC=120    # Ceiling
RETRY_AFTER_ALPHA=0.2      # EMA smoothing factor

7.

# Enable input filtering
INPUT_SANITIZE=1

# Minimum alphanumeric ratio (0.2 = 20%)
INPUT_MIN_ALNUM_RATIO=0.2

# Minimum character count
INPUT_MIN_CHARS=1

# Language code for undetermined/noise
UNDETERMINED_LANG_CODE=und

Apparaat Auto-Detection

# Default sentence splitting behavior
PERFORM_SENTENCE_SPLITTING_DEFAULT=1

# Max chars per sentence before word-boundary split
MAX_SENTENCE_CHARS=500

# Max chars per chunk for batching
MAX_CHUNK_CHARS=900

# Sentence joiner
JOIN_SENTENCES_WITH=" "

Detecteert en gebruikt automatisch GPU indien beschikbaar:

# Enable symbol masking
SYMBOL_MASKING=1

# What to mask
MASK_DIGITS=1    # Mask 0-9
MASK_PUNCT=1     # Mask .,!? etc.
MASK_EMOJI=1     # Mask 😀🎉 etc.

Wat gebeurt hier?

# Align response array length to input
ALIGN_RESPONSES=1

# Placeholder for failed items (when aligned)
SANITIZE_PLACEHOLDER=""

# Response format
EASYNMT_RESPONSE_MODE=strings    # ["translation1", "translation2"]
EASYNMT_RESPONSE_MODE=objects    # [{"text":"translation1"}, ...]

GPU-detectie

# Enable two-hop translation via pivot
PIVOT_FALLBACK=1

# Pivot language (usually English)
PIVOT_LANG=en

: Gebruik PyTorch om te controleren of CUDA beschikbaar is

# Log level
LOG_LEVEL=INFO

# Per-request logging (verbose)
REQUEST_LOG=1

# Format
LOG_FORMAT=plain    # Human-readable
LOG_FORMAT=json     # Structured JSON

# File logging with rotation
LOG_TO_FILE=1
LOG_FILE_PATH=/var/log/marian-translator/app.log
LOG_FILE_MAX_BYTES=10485760    # 10MB
LOG_FILE_BACKUP_COUNT=5

# Include raw text in logs (privacy risk!)
LOG_INCLUDE_TEXT=0

Auto-configuratie

# Periodically clear CUDA cache (seconds, 0=disabled)
CUDA_CACHE_CLEAR_INTERVAL_SEC=0

: Sets

# Worker count (use 1 for single GPU)
WEB_CONCURRENCY=1

# Request timeout
TIMEOUT=60

# Graceful shutdown timeout
GRACEFUL_TIMEOUT=20

# Keep-alive timeout
KEEP_ALIVE=5

op GPU (vermijd VRAM-fragmentatie) vs

over CPU (maximale parallellisme)

# GET request
curl "http://localhost:8000/translate?target_lang=de&text=Hello%20world&source_lang=en"

# Response
{
  "translations": ["Hallo Welt"]
}

Apparaatselectie

# POST request
curl -X POST http://localhost:8000/translate \
  -H 'Content-Type: application/json' \
  -d '{
    "text": [
      "Hello world",
      "This is a test",
      "Machine translation is amazing"
    ],
    "target_lang": "de",
    "source_lang": "en",
    "beam_size": 1,
    "perform_sentence_splitting": true
  }'

# Response
{
  "target_lang": "de",
  "source_lang": "en",
  "translated": [
    "Hallo Welt",
    "Das ist ein Test",
    "Maschinenübersetzung ist erstaunlich"
  ],
  "translation_time": 0.342
}

: Kan specifieke GPU richten met

# Omit source_lang for auto-detection
curl -X POST http://localhost:8000/translate \
  -H 'Content-Type: application/json' \
  -d '{
    "text": ["Bonjour le monde"],
    "target_lang": "en"
  }'

# Response
{
  "target_lang": "en",
  "source_lang": "fr",  # Detected
  "translated": ["Hello world"],
  "translation_time": 0.156
}

Loggen

# GET
curl "http://localhost:8000/language_detection?text=Hola%20mundo"
# {"language": "es"}

# POST with batch
curl -X POST http://localhost:8000/language_detection \
  -H 'Content-Type: application/json' \
  -d '{"text": ["Hello", "Bonjour", "Hola"]}'
# {"languages": ["en", "fr", "es"]}

: Toont GPU naam en VRAM bij opstarten voor debuggen

# Health check
curl http://localhost:8000/healthz
# {"status": "ok"}

# Readiness
curl http://localhost:8000/readyz
# {
#   "status": "ready",
#   "device": "cuda:0",
#   "queue_enabled": true,
#   "max_inflight": 1
# }

# Cache status
curl http://localhost:8000/cache
# {
#   "capacity": 6,
#   "size": 3,
#   "keys": ["en->de", "de->en", "fr->en"],
#   "device": "cuda:0",
#   "inflight": 1,
#   "queue_enabled": true
# }

# Model info
curl http://localhost:8000/model_name | jq

Singleton patroon

# When queue is full, you get 429
curl -X POST http://localhost:8000/translate \
  -H 'Content-Type: application/json' \
  -d '{"text": ["test"], "target_lang": "de"}'

# Response: 429 Too Many Requests
# Headers: Retry-After: 45
# Body:
{
  "message": "Too many requests; queue full",
  "retry_after_sec": 45
}

# Proper client behavior:
# 1. Read Retry-After header
# 2. Wait that long + jitter
# 3. Retry request

: Een instantie gedeeld over de gehele toepassing

Exponential Moving Average for Retry-After

Gladde retry-after schatting die zich aanpast aan de werkelijke aanvraagduur:

Voorbeeld:

.\build-all.ps1

Wat gebeurt hier?

chmod +x build-all.sh
./build-all.sh

EMA (Exponentieel verplaatsingsgemiddelde)

: Zoals een gewogen gemiddelde dat meer belang geeft aan recente waardenSmoothing factor (α):

  1. : Controleert hoe snel we ons aanpassen aan veranderingen (latest, min, gpu, gpu-minWaarom niet gewoon gemiddeld?
  2. : EMA past zich sneller aan veranderingen aan tijdens het filteren van spikesWaarom het belangrijk is20250108.143022: Geeft klanten realistisch

tijden die zich aanpassen aan de huidige systeembelasting

# Always get the latest version
docker pull scottgal/mostlylucid-nmt:cpu
# Or use the :latest alias
docker pull scottgal/mostlylucid-nmt:latest

# Pin to a specific version for reproducibility
docker pull scottgal/mostlylucid-nmt:cpu-20250108.143022
docker pull scottgal/mostlylucid-nmt:cpu-min-20250108.143022

Deze code patronen tonen productie-kwaliteit Python praktijken:

Middelenbeheer

  • : Expliciete GPU geheugen opruimingEervolle degradatie
  • : Automatische terugval tussen modelleveranciersBackpressure handling
  • : Wachtrij + HTTP 429 in plaats van crashesGegevensintegriteit
  • : Symbool maskering behoudt speciale tekensPrestatieoptimalisatie

: Slimme caching, chunking en parallelle verwerking

docker inspect scottgal/mostlylucid-nmt:cpu | jq '.[0].Config.Labels'

Waarneembaarheid: Gedetailleerde logging en metrics tracking.

Elk van deze functies lost een echte productie probleem dat zou leiden tot crashes, fouten of slechte gebruikerservaring zonder hen!

Configuratiehandleiding

# Using pre-built image from Docker Hub (recommended)
docker run -d \
  --name translator \
  -p 8000:8000 \
  -e ENABLE_QUEUE=1 \
  -e MAX_QUEUE_SIZE=500 \
  -e EASYNMT_BATCH_SIZE=16 \
  -e TIMEOUT=180 \
  -e LOG_LEVEL=INFO \
  -e REQUEST_LOG=0 \
  scottgal/mostlylucid-nmt

# Or build locally
docker build -t mostlylucid-nmt .
docker run -d --name translator -p 8000:8000 mostlylucid-nmt

# Check logs
docker logs -f translator

De service is zeer configureerbaar via omgevingsvariabelen.

# Using pre-built GPU image from Docker Hub (recommended)
docker run -d \
  --name translator-gpu \
  --gpus all \
  -p 8000:8000 \
  -e USE_GPU=true \
  -e DEVICE=cuda:0 \
  -e PRELOAD_MODELS="en->de,de->en,en->fr,fr->en,en->es,es->en" \
  -e EASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}' \
  -e EASYNMT_BATCH_SIZE=64 \
  -e MAX_CACHED_MODELS=8 \
  -e ENABLE_QUEUE=1 \
  -e MAX_QUEUE_SIZE=2000 \
  -e WEB_CONCURRENCY=1 \
  -e TIMEOUT=180 \
  -e GRACEFUL_TIMEOUT=30 \
  -e LOG_FORMAT=json \
  -e LOG_TO_FILE=1 \
  -v /var/log/translator:/var/log/marian-translator \
  scottgal/mostlylucid-nmt:gpu

# Or build locally
docker build -f Dockerfile.gpu -t mostlylucid-nmt:gpu .
docker run -d --name translator-gpu --gpus all -p 8000:8000 mostlylucid-nmt:gpu

# Monitor cache and performance
watch -n 5 "curl -s http://localhost:8000/cache | jq"

Hier is de complete gids:

version: '3.8'

services:
  translator:
    image: scottgal/mostlylucid-nmt:gpu  # Use pre-built image
    container_name: translator
    restart: unless-stopped

    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

    ports:
      - "8000:8000"

    environment:
      USE_GPU: "true"
      DEVICE: "cuda:0"
      PRELOAD_MODELS: "en->de,de->en,en->fr,fr->en"
      EASYNMT_MODEL_ARGS: '{"torch_dtype":"fp16"}'
      EASYNMT_BATCH_SIZE: "64"
      MAX_CACHED_MODELS: "8"
      ENABLE_QUEUE: "1"
      MAX_QUEUE_SIZE: "2000"
      WEB_CONCURRENCY: "1"
      TIMEOUT: "180"
      LOG_FORMAT: "json"
      LOG_TO_FILE: "1"

    volumes:
      - translator-logs:/var/log/marian-translator
      - translator-cache:/root/.cache/huggingface

    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/healthz"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

volumes:
  translator-logs:
  translator-cache:

Apparaatselectie

apiVersion: apps/v1
kind: Deployment
metadata:
  name: translator
spec:
  replicas: 2  # Scale horizontally for CPU, use 1 per GPU
  selector:
    matchLabels:
      app: translator
  template:
    metadata:
      labels:
        app: translator
    spec:
      containers:
      - name: translator
        image: scottgal/mostlylucid-nmt:gpu
        ports:
        - containerPort: 8000
        env:
        - name: USE_GPU
          value: "true"
        - name: EASYNMT_MODEL_ARGS
          value: '{"torch_dtype":"fp16"}'
        - name: PRELOAD_MODELS
          value: "en->de,de->en"
        - name: ENABLE_QUEUE
          value: "1"
        - name: MAX_QUEUE_SIZE
          value: "2000"

        resources:
          requests:
            memory: "4Gi"
            cpu: "2"
            nvidia.com/gpu: 1
          limits:
            memory: "8Gi"
            cpu: "4"
            nvidia.com/gpu: 1

        livenessProbe:
          httpGet:
            path: /healthz
            port: 8000
          initialDelaySeconds: 30
          periodSeconds: 10

        readinessProbe:
          httpGet:
            path: /readyz
            port: 8000
          initialDelaySeconds: 20
          periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
  name: translator
spec:
  selector:
    app: translator
  ports:
  - port: 80
    targetPort: 8000
  type: LoadBalancer

Modelconfiguratie

Nieuwe configuratie uitgelegd:

  1. MODEL_FAMILLY

    EASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}'
    
    • : Kies welke modelfamilie te gebruiken als primaire
    • : Beste kwaliteit, 1200+ paar, aparte modellen
    • : Goede kwaliteit, 50 talen, single 2.4GB model
  2. : Goede kwaliteit, 100 talen, enkele 2.2GB model

    # Start high, reduce if you get OOM
    EASYNMT_BATCH_SIZE=64  # Try 128 on large GPUs
    
  3. AUTO_MODEL_FALLBACK

    PRELOAD_MODELS="en->de,de->en,en->fr,fr->en,en->es,es->en"
    
  4. : Probeer automatisch andere families als het paar niet beschikbaar is

    WEB_CONCURRENCY=1
    MAX_INFLIGHT_TRANSLATIONS=1
    
  5. (standaard): ingeschakeld - maximale dekking

    MAX_CACHED_MODELS=10  # Keep more models in VRAM
    
  6. : Gehandicapt - strikte eengezinsmodus

    # beam_size=1 is 3-5x faster than beam_size=5
    # Quality difference is often minimal
    curl -X POST ... -d '{"beam_size": 1, ...}'
    

MODEL_FALLBACK_ORDER

  1. : Prioriteitsorde voor terugval

    EASYNMT_BATCH_SIZE=8
    
  2. Standaard:

    MAX_WORKERS_BACKEND=4
    MAX_INFLIGHT_TRANSLATIONS=4
    WEB_CONCURRENCY=2
    
  3. (kwaliteit eerst)

    PERFORM_SENTENCE_SPLITTING_DEFAULT=0
    

Alternatief:

  1. (dekking eerst)

    // Bad: 100 separate requests
    for (const text of texts) {
      await translate(text);
    }
    
    // Good: 1 batch request
    await translate(texts);
    
  2. MODEL_CACHE_DIR

    async function translateWithRetry(texts) {
      try {
        return await translate(texts);
      } catch (err) {
        if (err.status === 429) {
          const retryAfter = err.headers['retry-after'];
          const jitter = Math.random() * 5;
          await sleep((retryAfter + jitter) * 1000);
          return translateWithRetry(texts);
        }
        throw err;
      }
    }
    
  3. : Persistente modelopslag via Docker volumes

    // Reuse HTTP connections
    const agent = new https.Agent({ keepAlive: true });
    
  4. Instellen op

    // Bad: mixed language pairs in one request
    translate([
      { text: "Hello", sourceLang: "en", targetLang: "de" },
      { text: "Bonjour", sourceLang: "fr", targetLang: "de" }
    ]);
    
    // Good: group by language pair
    translateBatch(enToDe, "en", "de");
    translateBatch(frToDe, "fr", "de");
    

en volume van de kaart:

Modellen blijven over container herstarten

  1. Gedeelde cache tussen meerdere containerstorch_dtype-opties:
  2. (float16): 2x sneller op GPU, de helft van het geheugen, verwaarloosbaar kwaliteitsverlies(bfloat16): Betere numerieke stabiliteit dan fp16, vereist moderne GPU's
  3. (float32): Volledige precisie, langzaamste maar meest nauwkeurigVertalingsinstellingen
  4. Wachtrijen voor & prestatiesInvoerreiniging
  5. Verdachte verwerkingSymboolmaskeren
  6. ResponsgedragPivot Fallback
  7. LoggenOnderhoud

Gunicorn (Dokter)

Voorbeelden van gebruik

translation_requests_total{lang_pair="en->de",status="success"} 1523
translation_requests_total{lang_pair="en->de",status="error"} 7
translation_duration_seconds{lang_pair="en->de",quantile="0.5"} 0.342
translation_duration_seconds{lang_pair="en->de",quantile="0.95"} 1.234
translation_queue_depth 23
translation_cache_size 6
translation_cache_hits_total 8234
translation_cache_misses_total 142

Basisvertaling

# Enable JSON logging
LOG_FORMAT=json REQUEST_LOG=1

# Output example
{
  "ts": "2025-01-08T15:30:45+0000",
  "level": "INFO",
  "name": "app",
  "message": "translate_post done items=5 dt=0.342s",
  "req_id": "a3d2f5b1-c4e6-4f7a-9d8c-1e2f3a4b5c6d",
  "endpoint": "/translate",
  "src": "en",
  "tgt": "de",
  "items": 5,
  "duration_ms": 342
}

Batchvertaling (aanbevolen)

Autotaaldetectie

Alleen taaldetectie |---------|---------|-----------------| | ObservabiliteitseindpuntenOmgaan met Backpressure | Bouwen en versierenAlle Docker-afbeeldingen bevatten nu de juiste versiering en metadata voor het volgen. | Snel bouwenBouw alle 4 varianten met automatische datetime versiering: | **Vensters:**Linux/Mac: | VersiestrategieElk gebouw creëert | twee tagsBenoemde tag | ) - wijst altijd op de meest recenteVersie-tag | (b.v.,) - onveranderlijke snapshot | **Bijvoorbeeld:**OCI-etiketten | **Elke afbeelding bevat metadata:**Versie | **: Bouwtijdstempel (JJJJMMDD.HHMMSS)**Bouwdatum | : ISO 8601 tijdstempelGit commit

: Korte SHA

Variant

: cpu-full, cpu-min, gpu-full, of gpu-minInspecteer labels:

Voor gedetailleerde bouwinstructies en CI/CD-integratie, zie

  • BUILD.mdMAX_QUEUE_SIZE
  • Implementatie
  • CPU-inzetMAX_INFLIGHT_TRANSLATIONSGPU-implementatie
  • Docker-composeComment

Kubernetes Implementatie

Optimalisatie van de prestatiesGPU Optimalisatie Checklist

FP16-precisie gebruiken

  • 2x snellere gevolgtrekkingENABLE_QUEUE=1
  • De helft van het VRAM-gebruik

Verwaarloosbaar kwaliteitsverlies voor vertaling

Groepsgrootte instellenHete modellen voorladen

Enige werknemer per GPU

  • Cachegrootte vergrotenEASYNMT_BATCH_SIZE
  • Onderste bundelgrootte voor doorvoerMAX_CACHED_MODELS
  • CPU Optimalisatie ChecklistEASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}'
  • Lagere batchgrootteWEB_CONCURRENCY=1Parallellisme verhogenMAX_INFLIGHT_TRANSLATIONS=1

Het splitsen van zinsdelen voor korte teksten uitschakelen

Best practices van opdrachtgeversPartijverzoeken

Respect opnieuw proberen-na

PRELOAD_MODELS="en->de,de->en"

Verbindingspooling gebruiken

Groeperen op taalpaar Helsinki-NLP/opus-mt-{src}-{tgt}Monitoring en Waarneming

Sleutel Metrics naar Track

  • Translation throughputPIVOT_FALLBACK=1(verzoeken/sec)
  • Gemiddelde latentiecurl http://localhost:8000/lang_pairs

(p50, p95, p99)

Wachtrijdiepte(huidige wachttijd)

Cache hit rate

  • (% van de verzoeken die cache raken)MASK_EMOJI=0FoutpercentageMASK_PUNCT=0
  • (5xx responsen)SYMBOL_MASKING=0

GPU-benutting

(indien van toepassing)

public class MostlyLucidNmtClient
{
    private readonly HttpClient _httpClient;
    private readonly string _baseUrl;

    public MostlyLucidNmtClient(HttpClient httpClient, string baseUrl)
    {
        _httpClient = httpClient;
        _baseUrl = baseUrl;
    }

    public async Task<TranslationResponse> TranslateAsync(
        List<string> texts,
        string targetLang,
        string sourceLang = "",
        int beamSize = 1,
        bool performSentenceSplitting = true,
        CancellationToken cancellationToken = default)
    {
        var request = new TranslationRequest
        {
            Text = texts,
            TargetLang = targetLang,
            SourceLang = sourceLang,
            BeamSize = beamSize,
            PerformSentenceSplitting = performSentenceSplitting
        };

        var response = await _httpClient.PostAsJsonAsync(
            $"{_baseUrl}/translate",
            request,
            cancellationToken);

        if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
        {
            // Read Retry-After header
            var retryAfter = response.Headers.RetryAfter?.Delta?.TotalSeconds ?? 30;
            var jitter = Random.Shared.Next(0, 5);
            await Task.Delay(TimeSpan.FromSeconds(retryAfter + jitter), cancellationToken);

            // Retry
            return await TranslateAsync(texts, targetLang, sourceLang, beamSize,
                performSentenceSplitting, cancellationToken);
        }

        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<TranslationResponse>(cancellationToken);
    }
}

public class TranslationRequest
{
    [JsonPropertyName("text")]
    public List<string> Text { get; set; }

    [JsonPropertyName("target_lang")]
    public string TargetLang { get; set; }

    [JsonPropertyName("source_lang")]
    public string SourceLang { get; set; }

    [JsonPropertyName("beam_size")]
    public int BeamSize { get; set; }

    [JsonPropertyName("perform_sentence_splitting")]
    public bool PerformSentenceSplitting { get; set; }
}

public class TranslationResponse
{
    [JsonPropertyName("target_lang")]
    public string TargetLang { get; set; }

    [JsonPropertyName("source_lang")]
    public string SourceLang { get; set; }

    [JsonPropertyName("translated")]
    public List<string> Translated { get; set; }

    [JsonPropertyName("translation_time")]
    public double TranslationTime { get; set; }
}

Geheugengebruik

services.AddHttpClient<MostlyLucidNmtClient>(client =>
{
    client.BaseAddress = new Uri("http://translator:8000");
    client.Timeout = TimeSpan.FromMinutes(3);
});

(VRAM voor GPU, RAM voor CPU)

Voorbeeld Prometheus Metrics**Als u Prometheus integreert (niet ingebouwd, maar eenvoudig toe te voegen):**Gestructureerd logging-voorbeeld

Je kunt dit doorsluizen naar Elasticsearch, CloudWatch of elke log aggregator.

Vergelijking: EasyNMT vs. MeestalLucid-NMT

  • Kenmerken EasyNMT Meestal Lucid-NMT
  • Stabiliteit
  • Crashes vaak . Production-ready, sierlijke fout behandeling .

Invoerafhandeling

  • Faalt op emoji/symbolen Robuuste sanitisatie + symbool maskering
  • Backpressure
  • Geen, OOM's onder belasting Semafore + wachtrij met retry-after

Waarneembaarheid

  • Minimaal aantal gezondheids-/ready-/cache-eindpunten, gestructureerde logs
  • GPU-ondersteuning
  • CUDA 10.x (oud) CUDA 12.6, ondersteuning van FP16/BF16
  • Modelbeheer

Handmatig, geen caching LRU cache met auto-eviction

  • Behandelen van veroordelingen
  • Basic splitting Smart chunking + batching
  • Vertaling door Pivot
  • Geen automatische terugval via het Engels
  • Graceful Shutdown

Ja, met time-out.

Configuratie40+ env vars voor fine-tuningAPI-compatibiliteit

EasyNMT endpoints 100% compatibel + extensies

  • CodekwaliteitModulair, getypt, getest
  • Problemen oplossen429 Te veel verzoeken
  • **Oorzaak:**De wachtrij is vol.
  • **Oplossing:**Toename
  • **Voeg meer replica's toe (horizontale schaling)**Toename
  • **(als je hoofdruimte hebt)**Verminder batchgroottes van klanten
  • 503 Service niet beschikbaarOorzaak:
  • **In de wachtrij uitgeschakeld en alle slots bezet.**Oplossing:

In de wachtrij zetten:

# Maximum coverage with auto-fallback (recommended!)
docker run -d -p 8000:8000 \
  -v ./model-cache:/models \
  -e MODEL_CACHE_DIR=/models \
  -e AUTO_MODEL_FALLBACK=1 \
  -e MODEL_FALLBACK_ORDER="opus-mt,mbart50,m2m100" \
  scottgal/mostlylucid-nmt:cpu-min

# GPU with best quality
docker run -d --gpus all -p 8000:8000 \
  -e USE_GPU=true \
  -e MODEL_FAMILY=opus-mt \
  -e EASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}' \
  scottgal/mostlylucid-nmt:gpu

# Test it
curl -X POST http://localhost:8000/translate \
  -H 'Content-Type: application/json' \
  -d '{"text": ["Hello world"], "target_lang": "de"}'

Verhoog de vluchtlimiet als u middelen heeft

OOM (Out of Memory) op GPU

Oorzaak:

Batch grootte te hoog of te veel modellen gecached.


Oplossing:

en

Traag eerste verzoek[Oorzaak:

Model is niet voorgeladen.

Translation NMT Neural Machine Translation Python FastAPI Docker CUDA PyTorch Transformers Helsinki-NLP Production Microservices API

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.