# Bâtiment principalementlucide-nmt: A Production-Ready (compatible EasyNMT) Service de traduction

## Présentation

Une implémentation FastAPI Copier EXACTEMENT l'API d'EasyNMT (https://github.com/UKPLab/EasyNMT) un excellent projet de traduction neurale-machine abandonné.

Mais j'ai ajouté SO MANY belles fonctionnalités pour augmenter la fiabilité et le rendre prêt pour une utilisation dans un système de production.[Pensez traduction rapide soi-même hébergé...](https://github.com/scottgal/mostlylucid.activetranslatetag)).

Depuis le début de ce blog, une grande passion a été la traduction automatique des articles de blog.

OUI Je sais que 'google fait ça' dans les navigateurs etc...etc... mais ce n'est pas le but.`mostlylucid-nmt`Je voulais savoir comment le faire !

De plus, c'est agréable d'accueillir des gens qui ne lisent pas l'anglais (même s'ils lisent l'anglais comme langue seconde, c'est FAR plus difficile à analyser).[J'ai donc travaillé sur la façon de le faire, ainsi que sur le partage de la façon de construire ce genre de système.](#interactive-demo-page)Oh et il m'a donné des idées sur la façon de l'utiliser dans ASP.NET pour la localisation automatique du texte (y compris le texte dynamique) en utilisant SignalR & un système de mise à jour en temps réel slick. (

Restez à l'écoute ![Oh et j'ai fait une démo disponible ici; https://nmtdemo.mostlylucid.net/demo/ ne fonctionne que dans un ancien ordinateur portable sans GPU, mais vous donne l'idée (et me laisse tester la lengevity).](https://www.mostlylucid.net/blog/category/EasyNMT)Essentiellement, les humains écrivent du texte de merde qui est SUPER bruyant pour les machines à manipuler efficacement.

Donc beaucoup de choses étaient en train de trouver comment travailler sur des questions avec EasyNMT (ce fut vraiment un projet de recherche).

[Tout de suite.](https://github.com/scottgal/mostlylucid-nmt)

[![est conçu pour être une bataille testée (bien traduire les dizaines de milliers de mots sur ici!) système utile pour toute traduction.](https://img.shields.io/docker/pulls/scottgal/mostlylucid-nmt)](https://hub.docker.com/r/scottgal/mostlylucid-nmt)
[![Une sorte d'API BabelFish.](https://img.shields.io/docker/v/scottgal/mostlylucid-nmt/cpu?label=cpu)](https://hub.docker.com/r/scottgal/mostlylucid-nmt)
[![Il a également tous les enseignements que j'ai de trois décennies de construction de serveurs et de systèmes de production.](https://img.shields.io/docker/v/scottgal/mostlylucid-nmt/cpu-min?label=cpu-min)](https://hub.docker.com/r/scottgal/mostlylucid-nmt)
[![Rangant à partir de 429 codes pour dire au client de reculer, retour de métadonnées sur les traductions pour aider les clients, des paramètres supplémentaires pour obtenir plus de données et DE COURSE](https://img.shields.io/docker/v/scottgal/mostlylucid-nmt/gpu?label=gpu)](https://hub.docker.com/r/scottgal/mostlylucid-nmt)
[![une page de démonstration](https://img.shields.io/docker/v/scottgal/mostlylucid-nmt/gpu-min?label=gpu-min)](https://hub.docker.com/r/scottgal/mostlylucid-nmt)

## ce qui me permet à la fois tout en développant ainsi que vous un moyen d'avoir une pièce de théâtre.

[Annexe I](#interactive-demo-page)a écrit tout un système`http://<server>:<port>/demo`pour que cela se produise avec un projet incroyable appelé EasyNMT.

<p>
<img src="/articleimages/translatedemof.png?format=webp&height=450" alt="Demo">
</p>
[TOC]

<!--category-- mostlylucid-nmt, EasyNMT,  Neural Machine Translation, Python, FastAPI, Docker, CUDA, PyTorch, Transformers, Helsinki-NLP,  API-->
<datetime class="hidden">2025-11-08T12:30</datetime>

## Chaque fois, si tu as vérifié la repo, tu sais qu'il y a un problème... ça n'a pas été touché pendant des années.

C'est un moyen simple et rapide d'obtenir une API de traduction sans avoir besoin de payer pour un service ou d'exécuter un LLM complet pour obtenir la traduction (slowly).

### Dans nos précédents articles, nous avons discuté de la façon d'intégrer EasyNMT aux applications ASP.NET pour la traduction de fond.

**Mais avec le temps, les fissures ont commencé à se manifester.**Il était temps pour quelque chose de mieux.

**Comme d'habitude, tout est sur GitHub et tout est gratuit pour l'utilisation etc...**Docker tire

- ✓ **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)`
- **gpu**gpu-min
- **Démo... voir plus tard pour la page de démonstration !**Une page de démonstration interactive complète (principalement)
- **(par rapport à l'année précédente)**ou juste la racine)
- **Quoi de neuf ?**Avant de plonger dans le démarrage rapide, voici ce qui fait cette version de changement de jeu:

**Principales mises à jour (v3.1) - Intelligence et visibilité**Nouveau dans v3.1:

- **La dernière version apporte des améliorations massives à la fiabilité, la visibilité des performances et la sélection de modèles intelligents !**1. Le Conseil de l'Europe a adopté une résolution du Conseil de l'Europe sur la situation des droits de l'homme dans le monde.
- **Modèle intelligent Caching avec visibilité**- Voir exactement ce qui se passe :
- **Cache HIT logage**Cache MISS journalisation
- **Suivi de l'état de la cache**: affiche le pourcentage d'utilisation et les modèles chargés
- **Avertissements d'éviction**: Effacer les alertes lorsque le cache est plein et que les modèles sont expulsés
- **Capacité accrue**:
  ```
  ====================================================================================================
    🚀 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
  ====================================================================================================
  ```

**: La taille du cache par défaut est passée à 10 modèles (à partir de 6)**Enregistrement de périphérique par modèle

- **: Voir exactement quel GPU/CPU chaque modèle utilise**2. Le Président. — L'ordre du jour appelle le rapport (doc.
- **Amélioration des progrès de téléchargement**- Plus besoin de se demander si c'est coincé.
- **Taille avant téléchargement**:
  ```
  [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)
  ```
- **: affiche la taille totale du téléchargement (par exemple, "Taille totale: 2,46 GB")**Nombre de fichiers
- **: Affiche le nombre de fichiers à télécharger**Affichage du périphérique

**: Affiche le périphérique cible (GPU/CPU) dans la bannière**Barres de progression

- **: Belle progression tqdm pour chaque fichier (quand sur TTY)**Bannière d'achèvement
- **: Message de succès clair lorsque le modèle est prêt**Exemple de sortie
- **3. Les droits de l'homme sont garantis par le Pacte international relatif aux droits économiques, sociaux et culturels.**:
  ```
  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)
  ```

**Sélection de pivots intelligents à transmission de données**- Plus de tentatives aveugles :

- Logique intelligente d'intersection`Loading mbart50 model on GPU (cuda:0)`
- : trouve les langues où il existe les deux jambes pivotantes`Model loaded on device: cuda:0`
- Évite les tentatives infructueuses`Successfully loaded... on GPU (cuda:0)`

**: N'essayera pas fr→es→bn si es→bn n'existe pas**Exemple pour en→bn

- Priorité de repli`en->hi`, `hi->bn`
- : Anglais → Espagnol → Français → Allemand → Chinois → Russe
- Enregistrement transparent`[Pivot] Both legs loaded and cached. Ready to translate.`

**: Voir exactement pourquoi chaque pivot a été choisi ou sauté**4. Le Président. — L'ordre du jour appelle le rapport (doc.

- Fixé Fallback automatique
- - Plus de double-trys :`model_family`Toujours essayer les replis
- : Même si la famille préférée "devrait" soutenir la paire

### Tentative unique par famille

**: Plus de réessayer le même modèle deux fois**Exemple de débit

- 5.
- Clarté du GPU
- - Savoir toujours où sont vos modèles :
- Chaque charge de modèle indique:
- Après le chargement confirme:

**Le message de réussite comprend :**6.

- **Pivot Modèle de cache**- Réutilisation efficace du pivot :
- **Les deux pattes du pivot sont mises en cache séparément:**La prochaine fois en→hi ou hi→bn nécessaire, cache instantané frappé!
- **Effacer l'enregistrement & #160;:**7.
- Sélection du modèle de demande

**- Ça marche déjà en démo :**La démo déroulante permet de sélectionner opus-mt, mbart50 ou m2m100

- Respects de l'arrière-plan
- paramètre par requête
- Modèles mis en cache séparément par famille pour la commutation instantanée
- Principales mises à jour (3.0)
- 1. Le Conseil de l'Europe a adopté une résolution du Conseil de l'Europe sur la situation des droits de l'homme dans le monde.`requirements-prod.txt`Page de démonstration améliorée

**- Interface interactive prête à la production :**Full viewport layout (100vw/100vh) pour une expérience de traduction immersive

- **Sélection des menus déroulants (plus d'entrée/liste de données maladroite)**Commutation de la famille de modèles vivants (Opus-MT, mBART50, M2M100)
- **Chargement en langage dynamique basé sur le modèle sélectionné**Zones de sortie défilantes pour les grandes traductions
- **2. Le Président. — L'ordre du jour appelle le rapport (doc.**Défauts optimisés en matière de performance
- - "Fast as possible" hors de la boîte:
- GPU

**: FP16 activé, BATCH_SIZE=64, MAX_INFLIGHT=1 (optimal pour un seul GPU)**CPU

- : WEB_CONCURRENCE=4, MAX_INFLIGHT=4, BATCH_SIZE=16 (utiliser tous les cœurs)
- Arrêt rapide
- : 5 secondes de temps libre gracieuse (pas plus de 20 secondes)
- Les conteneurs s'arrêtent sans messages SIGKILL effrayants
- 3. Les droits de l'homme sont garantis par le Pacte international relatif aux droits économiques, sociaux et culturels.
- Optimisation de la construction de production

**- Images plus petites et plus rapides:**Dépendances d'essai supprimées (pytest, pytest-cov) des constructions de production

- **Enregistre ~200 Mo par image**Images du processeur: ~8-10 Go (plein), ~3-4 Go (min)
- **Images GPU: ~12-15 Go (plein), ~6-8 Go (min)**Toutes les utilisations
- **pour une empreinte minimale**4. Le Président. — L'ordre du jour appelle le rapport (doc.

**Essais complets et essais de charge**- Valider tout :

- Suite de test d'API en direct
- (30+ tests) pour la santé, la traduction, la détection, la découverte
- Essai de charge de k6

**avec des schémas de trafic réalistes**Scénarios de validation transplateforme

- `/discover/opus-mt`(PowerShell + Bash)
- `/discover/mbart50`Tests pour les téléchargements de modèles et le repli de la traduction de pivot
- `/discover/m2m100`Essais automatisés de fumée pour une validation rapide

**5.**Documentation sur le déploiement

- - Prête à la production dès le premier jour :
- 4 scénarios de calibrage : débit maximal, faible latence, haute écueil, manque de mémoire
- Docker Composez des exemples avec des configurations GPU/CPU

**Kubernetes manifeste avec PVC, limites de ressources, contrôles de santé**Exemples d'instances de conteneurs Azure

- `scottgal/mostlylucid-nmt:cpu`Directives pour les essais de charge et recommandations en matière de surveillance`:latest`Compensation de la contre-valeur et du débit expliquée
- `scottgal/mostlylucid-nmt:cpu-min`6.
- `scottgal/mostlylucid-nmt:gpu`Trois familles modèles
- `scottgal/mostlylucid-nmt:gpu-min`- Choisissez le meilleur pour vos besoins :

**Opus-MT**: 1200+ paires, meilleure qualité (modèles séparés)

- mBART50`latest`, `min`, `gpu`, `gpu-min`: 50 langues, modèle simple de 2,4 Go, 2,450 paires
- M2M100`20250108.143022`: 100 langues, modèle unique de 2,2 Go, 9 900 paires
- 7.

**Auto-Fallback**- Sélectionne intelligemment le meilleur modèle disponible:

- **Définir la famille primaire (p. ex. Opus-MT pour la qualité)**Essaie automatiquement mBART50/M2M100 si la paire n'est pas disponible
- **Couverture maximale sans sacrifier la qualité**8.
- **Découverte du modèle**- Requête dynamique des modèles disponibles :
- - Toutes les paires de 1200+ de Hugging Face

**- Toutes les paires de mBART50**- Toutes les paires M2M100

- 9.
- Images minimales

---


## - Déploiements plus petits et flexibles:

Pas de modèles préchargés (téléchargement à la demande)

### Cache persistante en format volumétrique

Switch familles de modèles sans reconstruction**10. Le Conseil de l'Europe s'est engagé à promouvoir l'égalité des chances entre les femmes et les hommes dans le domaine de l'éducation et de la formation tout au long de la vie, en particulier dans le domaine de l'éducation et de la formation tout au long de la vie.**Dépôt unique Docker

- Toutes les variantes en un seul endroit:
|-----|-----------------|------|-------------|----------|
| `cpu`(ou`latest`) | `scottgal/mostlylucid-nmt:cpu`) - CPU
| `cpu-min` | `scottgal/mostlylucid-nmt:cpu-min`- Minimum du processeur
| `gpu` | `scottgal/mostlylucid-nmt:gpu`- GPU avec CUDA 12,6
| `gpu-min` | `scottgal/mostlylucid-nmt:gpu-min`- Minimum de GPU

**11. Le Conseil de l'Europe s'est prononcé en faveur de l'élargissement de l'Union européenne et de l'élargissement de l'Union européenne à l'Union européenne.**Version appropriée

- - Toutes les images incluent la version date :
- Balises nommées (
- ) toujours pointer vers la construction la plus récente
- Balises de version immuables (p. ex.,

### ) pour épingler des constructions spécifiques

**Étiquettes complètes de l'OCI pour le suivi des versions, les dates de construction et les commits git**

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

**12. Le Conseil de sécurité des Nations Unies a adopté le projet de résolution adopté par le Conseil de sécurité à sa dix-neuvième session.**

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

**Dernières images de base**

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

### - Amélioration de la sécurité et des performances :

Python 3.12-slim

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

### pour les images CPU (adresses des vulnérabilités Python 3.11)

CUDA 12,6

**avec Ubuntu 24.04 pour les images GPU (dernière pile NVIDIA)**

```bash
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 avec CUDA 12,4**

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

**(compatible avec CUDA 12,6 temps d'exécution)**

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

Toutes les dépendances sont mises à jour pour les dernières versions sécurisées

**13.**

```bash
curl http://localhost:8000/healthz
```

Avertissements de déprécation fixes

## - À l'épreuve de l'avenir :

Supprimé TRANSFORMERS_CACHE déprécié (maintenant en utilisant HF_HOME)**Compatible avec Transformers v5**Démarrage rapide (5 minutes)

```
http://localhost:8000/demo/
```

<p>
<img src="/articleimages/translatedemof.png?format=webp&height=800" alt="Demo">
</p>
### Tu veux juste te faire traduire ?

Voici la façon la plus simple de courir principalementlucide-nmt:

**Images Docker disponibles**

- Toutes les variantes sont disponibles à partir de
- un dépôt
- avec différentes étiquettes:

**Marque Nom de l'image complète Taille Description du boîtier d'utilisation**

- (ou
- CPU avec code source de production Déploiements de CPU de production de CPU
- CPU minimal, pas de modèles préchargés
- ~5GB=GPU avec CUDA 12,6 + source=Déploiements de GPU de production=
- GPU minimal, pas de modèles préchargés GPU avec cache en volume

**Images minimales**

- sont recommandés pour:
- Déploiements de production avec cache en volume
- Utilisation de mBART50 ou M2M100 (modèles simples de grande taille)

**Garder la taille du récipient petit**

- **Flexibilité pour changer de famille de modèles sans reconstruction**Démarrage le plus simple (CPU Opus-MT)
  - 1. Le Conseil de l'Europe a adopté une résolution du Conseil de l'Europe sur la situation des droits de l'homme dans le monde.
  - Tirer et courir:
- **2. Le Président. — L'ordre du jour appelle le rapport (doc.**Traduire un texte :
  - Réponse :
  - GPU accéléré (10x plus rapide)

**Nécessite NVIDIA Docker runtime:**

- **Avec Cache modèle persistant**Téléchargez les modèles une fois et gardez-les à travers les redémarrages de conteneurs:
- **Linux/Mac :**Fenêtres (PowerShell) :
- **Windows (CMD) :**Les modèles sont téléchargés automatiquement lors de la première utilisation et persistent dans votre répertoire local !

**Contrôle de santé:**

- C'est le départ rapide de 5 minutes !
  - **Pour le déploiement de la production, la configuration et les fonctionnalités avancées, continuez à lire.**Page de démonstration interactive
  - **Le service comprend une fonction complète**page de démonstration interactive
  - **qui rend facile de tester les traductions sans écrire de code.**Accédez à ce site à l'adresse suivante :
- Caractéristiques de la démo

### La page de démonstration fournit un environnement de test de traduction complet avec:

1. Le Conseil de l'Europe a adopté une résolution du Conseil de l'Europe sur la situation des droits de l'homme dans le monde.

```javascript
// 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
```

### Sélection de la langue

**Baisses de langues auto-populées du service en direct**

- Échanger les langues source/cible en un seul clic
- Prise en charge des plus de 100 langues configurées dans le service
- 2. Le Président. — L'ordre du jour appelle le rapport (doc.
- Numérisation de texte intelligent

**Gestion automatique des grandes entrées de texte de n'importe quelle taille**

- Séparer intelligemment par paragraphes, en préservant la structure du document
- Retour à la phrase fractionnement pour de très longs paragraphes
- Affiche les progrès pour les traductions multi-pouces ("Traduire le morceau 2/5...")
- Réassemble sans couture des morceaux avec un espacement approprié

**3. Les droits de l'homme sont garantis par le Pacte international relatif aux droits économiques, sociaux et culturels.**

- Détection de langue
- Détecter la langue source en un seul clic
- Déploie automatiquement la liste déroulante du langage source
- Fonctionne avec un texte jusqu'à 5000 caractères

### 4. Le Président. — L'ordre du jour appelle le rapport (doc.

1. **Options avancées**:
   
   - Taille du faisceau
   - : Qualité de la traduction de contrôle (1-10)
   - Valeurs plus élevées = meilleure qualité mais plus lente
   - Valeurs inférieures = débit plus rapide

2. **Séparation de la peine**:
   
   - : Couper automatiquement les phrases
   - Active (par défaut) : divise les textes longs en phrases pour une meilleure qualité
   - Handicapés: Traduit le texte entier comme un bloc (plus rapide pour les textes courts)
   - 5.

3. **Statistiques en temps réel**:
   
   - Heure de traduction
   - : Affiche la durée réelle de la traduction côté serveur
   - Nombre de caractères
   - : La vie compte comme vous tapez

### Indicateur de situation

: Idle → Traduire → Fait/Erreur

- **6.**Modèle de découverte familiale
- **Explorez les paires de traductions disponibles pour chaque famille de modèles :**Opus-MT
- **: 1200+ paires de langues**mBART50
- **: 50 langues, 2 450 paires**M2M100

: 100 langues, 9 900 paires`/demo/`Voir exactement les paires de langues disponibles avant de traduire

## Comment fonctionne le découpage de texte

La démo implémente le chunking intelligent de texte du côté client :[Pourquoi utiliser la démo ?](https://github.com/UKPLab/EasyNMT)Essai rapide[Tester les traductions sans code d'écriture](https://www.mostlylucid.net/blog/category/EasyNMT)Valider la disponibilité de la paire de langues

Comparer la qualité de la traduction avec différentes tailles de faisceau

1. **Cas de bord d'essai (emoji, symboles, caractères spéciaux)**Aide au développement
2. **Voir le format exact de requête/réponse de l'API**Vérifier la santé des services avant d'intégrer
3. **Performance d'essai avec différentes tailles de texte**Découvrez les familles de modèles disponibles
4. **Référence client**Affiche les modèles d'utilisation de l'API appropriés
5. **Démontre le traitement des erreurs (429, détection de langue)**Exemple de mise en œuvre de chunking
6. **Logique de réessayer du monde réel**Exemple d'utilisation*Traduction simple*.
7. **Coller le texte : "Bonjour, comment allez-vous aujourd'hui ?"**Sélectionnez la cible : Allemand
8. **Cliquez sur "Traduit par la Rédaction"**Résultat: "Hallo, wie geht es Ihnen heute?"

## Traduction des documents longs

Coller l'article entier du blog (5000+ mots)**Demo le découpe automatiquement en morceaux gérables**Affiche les progrès au fur et à mesure que chaque morceau se traduit

### Retourne le document entièrement traduit

Détection de langue**Coller le texte en langue inconnue**Cliquez sur "Detect language"

#### Démo identifie la langue et les mises à jour déroulantes

- **Prêt à traduire immédiatement**Détails techniques
- **La page de démonstration est :**Indépendants
- **: Un seul fichier HTML avec JavaScript intégré**Dépendances nulles
- **: Aucune bibliothèque externe n'est requise**Mobile friendly
- **: La conception réactive fonctionne sur tous les appareils**La production est prête
- **: La même logique de chunking peut être utilisée dans vos applications**Accédez à la démo live à

#### sur votre instance en cours d'exécution!

- **Les problèmes avec EasyNMT**Maintenant, ce n'est pas du dumping sur
- **FacileNMT**Il n'a rien pu faire d'autre et j'ai construit
- **un LOT de projets l'utilisant**
- **C'est de plus en plus long dans la dent, alors... Quels problèmes avons-nous ?**Oh mon, il y en a beaucoup.
- **EasyNMT a été construit il y a près de dix ans.**La technologie a évolué... et elle n'a jamais été conçue pour être un système de production.
- **Voici quelques-uns des enjeux :**Il s'écrase... un peu.

#### Il n'est pas conçu pour se remettre des problèmes si souvent juste tombe sur.

- **C'est SUPER PICKY à propos de son entrée.**Les émoticônes, les symboles, même les nombres peuvent le confondre.
- **Il n'est conçu pour aucune charge.**Voir ci-dessus.
- **Il n'a jamais été conçu pour l'être.**Il n'est pas conçu pour mettre à jour ses modèles
- **ou être (facilement) construit avec des modèles intégrés.**Son truc GPU CUDA est ancien
- **tellement plus lent qu'il n'en a besoin.**Tu ne peux rien arranger.
- **Le code Python est sur la repo mais à nouveau**pas grand

**Pas de contrepression ou de file d'attente.**Envoie trop de demandes et ça se calme.`MODEL_FAMILY`Pas d'observabilité.

```bash
# 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
```

### Quand les choses tournent mal, tu voles aveugle.

La solution : surtout Lucid-NMT**Donc... j'ai décidé de construire un EasyNMT nouveau et amélioré, maintenant**principalement lucide-nmt

****

1. Ce n'est pas seulement un travail de patch ; c'est une réécriture complète avec l'utilisation de la production à l'esprit.`MODEL_FAMILY`Voici ce qui le rend meilleur :`opus-mt`Soutien familial multi-modèles
2. La plupart du temps Lucid-NMT soutient maintenant
3. trois familles de modèles de traduction
4. , vous donnant de la flexibilité en fonction de vos besoins:

**Opus-MT (Helsinki-NLP) - Par défaut**

```bash
# 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
```

**Couverture**

- **1200+ paires de traductions pour 150+ langues**Architecture :
- **Modèle séparé par direction de traduction**Qualité:
- **Meilleure qualité générale de la traduction**Cas d'utilisation :
- **Traductions de production où la qualité est importante**Taille du modèle :

**300-500 Mo par direction**

```bash
# 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
```

Exemple :

### Anglais→L'allemand est un modèle différent de l'allemand→L'anglais

1. **mBART50 (Facebook)**Couverture
2. **50 langues, traduction intégrale**Architecture :
3. **Modèle multilingue unique**Qualité:
4. **Bonne qualité, en particulier pour les langues principales**Cas d'utilisation :
5. **Déploiements encombrés dans l'espace ou de nombreuses paires de langues**Taille du modèle :
6. **~2.4 Go (modèle unique pour les 50 langues)**Avantage:
7. **Un modèle gère 2 450 paires de traductions**M2M100 (Facebook)
8. **Couverture**100 langues, traduction intégrale
9. **Architecture :**Modèle multilingue unique
10. **Qualité:**Bonne qualité avec une couverture linguistique plus large
11. **Cas d'utilisation :**Couverture linguistique maximale`-min`Taille du modèle :

## ~2.2 Go (modèle unique pour les 100 langues)

Avantage:

### Un modèle gère 9 900 paires de traductions

**Le changement est facile**

- - juste mettre le
- variable d'environnement:
- Famille de modèles automatique Fallback - NOUVEAU!

**L'une des nouvelles fonctionnalités les plus puissantes est**

- repli automatique entre les familles de modèles
- 
- Cela garantit une couverture linguistique maximale tout en accordant la priorité à la qualité de la traduction.

**Comment ça marche :**Tu as établi un primaire.

- **(par exemple,**pour la meilleure qualité)
- **Lorsque vous demandez une paire de traduction non disponible dans la famille primaire**Le système essaie automatiquement la prochaine famille dans l'ordre de repli
- **Cela se poursuit jusqu'à ce qu'un modèle approprié soit trouvé**Exemple de scénario :

Avantages:

### Couverture maximale:

**Prise en charge de plus de 100 langues sans gestion de déploiements multiples**

- Priorité en matière de qualité:
- Toujours utiliser le meilleur modèle disponible pour chaque paire
- Configuration zéro & #160;:
- **Fonctionne automatiquement, pas d'intervention manuelle nécessaire**

**Enregistrement transparent :**

- Voir quelle famille de modèles a été utilisée pour chaque traduction
- Configuration & #160;:
- Cette fonctionnalité est parfaite pour les environnements de production où vous voulez une couverture maximale sans sacrifier la qualité!
- Principales améliorations

**Soutien familial multi-modèles**

```bash
# 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/
```

### - Choisissez parmi Opus-MT (1200+ paires), mBART50 (50 langues), ou M2M100 (100 langues).

**Paramètres de découverte du modèle**

- - Requête dynamique des modèles disponibles à partir de Hugging Face pour chaque famille.
- Manipulation d'entrées robustes
- - Emoji ?
- Des chiffres ?

**Des symboles ?**

- Amène-le.
- Le service comprend désormais une désinfection complète des entrées et un masque de symboles.
- Demander une file d'attente et une contre-pression
- - Une file d'attente sémaphore intégrée avec des estimations intelligentes.

**Modèle LRU en cache**

- Gère automatiquement VRAM en expulsant les anciens modèles lorsque le cache est plein.
|--------|------|------|
Soutien CUDA moderne
- Utilise PyTorch avec CUDA 12.6, prend en charge FP16/BF16 pour des améliorations de vitesse 2x.
Observabilité prête à la production
- Contrôles de santé, sondes de préparation, état de cache, enregistrement structuré.
Fermeture gracieuse

### - Plus de demandes orphelines ou d'état corrompu.

**API compatible EasyNMT**

- - Remplacement à l'abandon pour les intégrations existantes.
- Retour en arrière de la traduction des pivots
- - Si une paire de langues directes n'est pas disponible, il passe automatiquement par l'anglais (ou le pivot choisi).
- Minimale des images Docker
- - Nouveau

**variantes avec cache en volume pour des déploiements plus petits.**

- ⚠️ 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

**Pourquoi NMT Over LLMs pour la traduction?**

```text
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)
```

### Vous vous demandez peut-être : « Pourquoi utiliser un service NMT dédié quand les LLM comme GPT-4, Claude ou Llama peuvent traduire ? » Grande question.

**Voici la vérification de la réalité basée sur l'utilisation de la production:**

- Vitesse: 10-100x Plus rapide
- NMT (principalement lylucide-nmt):
- CPU: ~0.3-1.0 secondes par phrase
- GPU (FP16): ~0.05-0.2 secondes par phrase
- Traitement par lots: 50+ phrases/seconde sur GPU
- LLMs:

**GPT-4: 3-10 secondes par demande (latence API + génération)**

- Lama 3 70B: 5-15 secondes par phrase (locale)
- Claude 3: 2-8 secondes par demande (latence API)
- Exemple réel:
- Traduire un billet de blog de 1000 mots:
- principalement lucide-nmt (GPU)

### : 5-10 secondes

API GPT-4**: 30-60 secondes**Local Llama 70B

- : 2-5 minutes
- Lorsque vous traduisez automatiquement des centaines de billets de blog dans plus de 12 langues, cette différence de vitesse est MASSIVE.
- Taille du modèle: 500 Mo vs 140 Go
- Modèles NMT:

Opus-MT (par direction): 300-500 Mo

**mBART50 (toutes les 50 langues): 2,4 Go**M2M100 (toutes les 100 langues): 2.2GB

## Total pour plus de 100 langues: ~2.2 Go

```mermaid
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
```

LLMs:

1. **Lama 3 8B: ~16 Go**Lama 3 70B: ~140 Go
2. **Mélangeur 8x7B: ~90 Go**GPT-4: Non disponible pour l'auto-hébergement
3. **Impact sur le stockage:**Besoins en ressources: Ordinateur portable vs Serveur Farm
   - **Déploiement du processeur NMT :**Fonctionne bien sur: 2 cœurs de processeur, 4 Go de RAM
   - **Image Docker: 1,5-2.5 Go**Inférence: CPU seulement, pas de GPU nécessaire`Retry-After`Coût : 10-20 $/mois VPS
4. **Exigences en matière de LLM:**Lama 3 70B: besoins 80GB+ VRAM (A100 GPU)
   - Modèles 7B-13B plus petits: Toujours 16-32 Go de RAM minimum
   - Coûts de l'API : 0,03-0,30 $ par 1000 jetons (additionne vite !)
   - Auto-hébergement: 1000 $+/mois pour GPU sérieux
   - Comparaison des coûts réels pour 10.000 traductions de post blog:
5. **Méthode Coût Temps**Principalementlucide-nmt (CPU) $20/mois VPS $2-3 heures
   - La plupart du temps, c'est-à-dire 50 $ par mois, c'est-à-dire 15-30 minutes.
   - API GPT-4 : 150-300 $ 8-15 heures
   - Claude API de 200 $ à 400 $ 6 à 12 heures
6. **Local Llama 70B=$1000+/mois matériel=$20-40 heures=$**Qualité : Objectif-construire vs général-bénéfice

**Concentrations NMT:**Formé spécifiquement pour la traduction`Retry-After`Qualité cohérente (même entrée = même sortie)

## Pas d'hallucinations - traduction pure

### Poignées contenu technique, code, bien formater

Pas besoin d'ingénierie rapide

```mermaid
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
```

### Défis liés à la gestion durable des terres :

Exemple de scénario :

```mermaid
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"
Quand utiliser Chacun
(👋)        (!)      (:)    ($99.99)

```

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

```

Utiliser NMT (pratiquement lylucide-nmt) lorsque:
Vous avez besoin d'une traduction cohérente et rapide à l'échelle
Questions budgétaires (auto-accueil ou volume élevé)

```

### 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]

```

Vous traduisez du contenu technique, du code, des données structurées

- Vous avez besoin d'une sortie déterministe (même entrée = même sortie)
- Vous voulez exécuter sur CPU ou du matériel modeste
- Vous construisez des pipelines de traduction automatisés

### Utiliser des LLM lorsque :

Vous avez besoin d'adaptation créative, pas de traduction littérale

```mermaid
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 --> [*]
```

**Le contexte et la nuance culturelle comptent plus que la vitesse**

- Vous faites des traductions uniques et à faible volume.
- Vous devez traduire + résumer + réécrire en une seule étape
- Vous êtes d'accord avec les coûts variables et le traitement plus lent
- La ligne de fond
- Pour

### traduction automatique de blog

(mon cas d'utilisation), NMT est le gagnant net:

```python
# 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
```

Traduit plus de 100 billets de blog en 12 langues en ~30 minutes (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
```

### Exécute sur un VPS de 50 $/mois

Qualité constante de tous les postes

```mermaid
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

```

Configuration totale : Un conteneur Docker

## L'essayer avec des LLM coûterait des centaines de dollars par mois en frais d'API ou nécessiterait un serveur GPU de 2000 $+ pour s'auto-héberger.

La différence de vitesse seule fait de NMT le seul choix pratique pour les pipelines de traduction de production.

### TL;DR:

NMT est conçu pour la traduction, fonctionne sur du matériel modeste, et est 10-100x plus rapide que les LLMs. Si vous avez besoin de traduction rapide, cohérente, rentable à l'échelle, NMT gagne mains en bas.

```python
# 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}")
```

**Vue d'ensemble de l'architecture**

- `OrderedDict`Le flux de demande est simple:
- **Client**envoie une demande de traduction à API Gateway (Gunicorne + travailleurs Uvicorn)
- **Passerelle de l'API**itinéraires vers le point d'arrivée de la traduction
- **Vérification de la capacité**: Contrôle du système s'il a la capacité de traiter la demande

### Oui

→ La demande va au service de traduction immédiatement

```python
# 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)
```

**Numéro**

- **→ Requête en attente, le client reçoit HTTP 429 avec**en-tête
- **Service de traduction**traite la demande par l'intermédiaire du pipeline:
- **Assainissement des entrées et fractionnement des phrases**Masquage des symboles (emojis, caractères spéciaux)
- **Traduction à l'aide de modèles mis en cache**Démantèlement et post-traitement des symboles

### Modèle Cache

(LRU) fournit des modèles de traduction:

```python
# 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 frappé → Réponse rapide**

- **Cache miss → Charger depuis HuggingFace Hub**Cache complet → Anciens modèles d'auto-expulsion, mémoire claire CUDA`max_inflight`)
- **Réponse** (`@asynccontextmanager`retour au client
- **Conception clé :**Le mécanisme de contre-pression (rue + HTTP 429) empêche les collisions sous charge.
- **Lorsque débordé, le service file d'attentes demande au lieu de mourir, donnant aux clients intelligemment réessayer le timing via**En-têtes.
- **Comment ça marche : plongée profonde**Flux de demande

### Quand une demande de traduction arrive, voici ce qui se passe :

Pipeline de traitement d'entrées

```python
# 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
```

**Le service utilise un pipeline multi-étapes sophistiqué pour gérer le texte désordonné du monde réel:**

```python
# 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 🚀"
```

**Masqué: "Bonjour, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde, le monde.**

- **Source: "Monde de salut"**Mauvaise traduction: "Hola mundo!!!!!!!"
- **Nettoyé : "Hola mundo" # Enlève la boucle !!!!**Cela garantit:`👋`Les modèles ne s'étouffent pas sur d'énormes entrées`__SYMBOL_0__`Nous pouvons loter efficacement
- **Le contexte est préservé à l'intérieur de limites raisonnables**Modèle Caching & Gestion de la mémoire

### Le cache LRU est intelligent sur la mémoire GPU:

Pourquoi cela importe-t-il :

```python
# 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
```

**La mémoire GPU est précieuse**

- **Les modèles de traduction sont de 300-500 Mo chacun**Les modèles de chargement sont lents (1-3 secondes)`.!?…`Nous gardons les 6 modèles les plus récents chaud
- **Les anciens modèles sont automatiquement expulsés**En file d'attente et contre-pression
- **Au lieu de s'écraser sous la charge, les files d'attente de service demandent:**L'estimation de réessayer est intelligente:
- **Pivot Traduction Fallback**Toutes les paires de langues n'ont pas de modèles directs sur Hugging Face.

### Une solution ?

Pivot à travers l'anglais:

```python
# 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
```

**Cela double latence mais assure une couverture pour toutes les paires de langues supportées.**

- **Code Plongée profonde: Caractéristiques fraîches expliquées** (`httpx`Explorons quelques-unes des parties les plus intéressantes de la base de codes !
- **Ce sont des modes de production réels qui rendent le service robuste et efficace.**Chaque extrait comprend des explications adaptées aux développeurs non-Python.
- **1. Le Conseil de l'Europe a adopté une résolution du Conseil de l'Europe sur la situation des droits de l'homme dans le monde.**Smart LRU Cache avec GPU Memory Management`en`L'une des caractéristiques les plus cool est le cache de modèle intelligent qui sait gérer la mémoire GPU:`de`Qu'est-ce qui se passe ici ?`Helsinki-NLP/opus-mt-en-de`
- **: Comme un dictionnaire régulier, mais se souvient que les articles de commande ont été ajoutés**LRU (Least Recently Used)

### : Lorsque le cache est plein, lancez le modèle qui n'a pas été utilisé dans le plus long laps de temps

Nettoyage du GPU

```python
# 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()
```

**: Lors de l'expulsion d'un modèle, nous le déplaçons explicitement dans la mémoire CPU et disons au GPU de libérer ses ressources**

- **Pourquoi c'est important**: Sans cela, la mémoire GPU se remplirait et s'écraserait après le chargement de 2-3 modèles!
- **2. Le Président. — L'ordre du jour appelle le rapport (doc.**Famille de modèles automatique Fallback`max_inflight=1`Cette fonctionnalité intelligente essaie plusieurs fournisseurs de modèles d'IA automatiquement si le premier n'a pas la paire de langues dont vous avez besoin:`max_inflight=4`Qu'est-ce qui se passe ici ?
- **Chaîne de repli**: Si Opus-MT n'a pas d'ukrainien→Français, essayez automatiquement mBART50, puis M2M100`DEVICE=cuda:1`
- **Pas d'intervention manuelle**: Les utilisateurs ne demandent qu'une traduction et obtiennent le meilleur modèle disponible
- **Gestion des erreurs**: Si toutes les familles échouent, nous lançons une erreur claire avec la dernière raison d'échec

### Cache intelligente

: Les modèles réussis sont mis en cache avec la clé de la paire de langues

```python
# 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. Les droits de l'homme sont garantis par le Pacte international relatif aux droits économiques, sociaux et culturels.**

```python
# 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
```

**Demander une réponse avec contrepression (HTTP 429)**

- **Les files d'attente de qualité de production qui empêchent le serveur de s'écraser sous une charge lourde:**Qu'est-ce qui se passe ici ?
- **Sémaphore**: Comme un videur dans un club - permet seulement N personnes à la fois (N =
- **Gestionnaire de contexte**): Suivi automatique des métriques et nettoyage
- **Réessayez-vous après**: Indique aux clients "revenir dans 25 secondes" en fonction de la profondeur de la file d'attente et du temps moyen de demande`Retry-After`Moyenne mobile exponentielle

---


**: Lisse les pics dans la durée de la demande**

- **Pourquoi c'est important**: Sous charge lourde, retourne HTTP 429 au lieu de planter ou de file d'attente infiniment
- **4. Le Président. — L'ordre du jour appelle le rapport (doc.**Magie de masque de symbole
- **Préserve des caractères spéciaux (emojis, symboles) que les modèles de traduction pourraient gâcher:**Exemple d'utilisation :
- **Qu'est-ce qui se passe ici ?**Modèle Regex
- **: Correspond aux gammes d'emoji et de symbole spécial Unicode**Système de localisation
- **: Swaps**avec

temporairement

## Pourquoi c'est important

: Modèles de traduction parfois corrompus ou supprimer les emojis - cela les préserve parfaitement!

### 5.

```bash
# 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
```

### Découpe intelligente de texte

```bash
# 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
```

**Découpe les longs textes en morceaux qui correspondent aux limites du modèle tout en préservant les limites des phrases :**

- **Qu'est-ce qui se passe ici ?**Séparation des peines
  
  - `opus-mt`: Utilise le régex pour se fractionner
  - `mbart50`tout en préservant la ponctuation
  - `m2m100`Bouchées d'avidité

- **: Emballe autant de phrases que possible dans chaque tranche sans dépasser la limite**Séparation des limites des mots
  
  - `1`: Si une seule phrase est trop longue, se divise sur les espaces au lieu de couper à mi-mot
  - `0`Pourquoi c'est important

- **: Les modèles de traduction ont des limites d'entrée (généralement 512-1024 jetons).**Cela garantit que nous ne les surpassons jamais tout en gardant le contexte intact.
  
  - 6.`"opus-mt,mbart50,m2m100"`Découverte du modèle Async avec cache
  - Découvre dynamiquement les modèles de traduction disponibles de HuggingFace:`"m2m100,mbart50,opus-mt"`Qu'est-ce qui se passe ici ?

- **Client HTTP Async**) : Fait des requêtes HTTP non-bloquantes à HuggingFace
  
  - Cuisson dans le temps`/models`: Conserve les résultats pendant 1 heure pour éviter la limitation de vitesse`-v ./model-cache:/models`
  - Analyse du nom de modèle
  - : Extraits

**et**

- `fp16`à partir de
- `bf16`Pourquoi c'est important
- `fp32`: HuggingFace a 1200+ modèles Opus-MT.

### Les interroger prend ~10 secondes.

```bash
# 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
```

### La mise en cache le rend instantané !

```bash
# 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.

```bash
# 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
```

### Détection automatique du périphérique

```bash
# 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=" "
```

### Détecte et utilise automatiquement GPU si disponible:

```bash
# 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.
```

### Qu'est-ce qui se passe ici ?

```bash
# 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"}, ...]
```

### Détection GPU

```bash
# Enable two-hop translation via pivot
PIVOT_FALLBACK=1

# Pivot language (usually English)
PIVOT_LANG=en
```

### : Utilise PyTorch pour vérifier si CUDA est disponible

```bash
# 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
```

### Configuration automatique

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

### : Ensembles

```bash
# 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
```

## sur GPU (éviter la fragmentation VRAM) vs

### sur le CPU (parallélisme maximal)

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

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

### Sélection du périphérique

```bash
# 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
}
```

### : Peut cibler GPU spécifique avec

```bash
# 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
}
```

### Exploitation forestière

```bash
# 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"]}
```

### : Affiche le nom GPU et VRAM au démarrage pour le débogage

```bash
# 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
```

### Modèle de monotone

```bash
# 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
```

## : Une instance partagée sur l'ensemble de l'application

8.

### Moyenne exponentielle de déplacement pour réessayer-après

Estimation de retour à l'essai qui s'adapte aux durées réelles de la demande :

**Exemple :**

```powershell
.\build-all.ps1
```

**Qu'est-ce qui se passe ici ?**

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

### EMA (moyenne mobile exponentielle)

: Comme une moyenne pondérée qui donne plus d'importance aux valeurs récentes**Facteur de lissage (α)**:

1. **: Contrôle la rapidité avec laquelle nous nous adaptons aux changements** (`latest`, `min`, `gpu`, `gpu-min`Pourquoi pas une simple moyenne ?
2. **: EMA s'adapte plus rapidement aux changements tout en filtrant les pics**Pourquoi c'est important`20250108.143022`: donne des clients réalistes

temps qui s'adaptent à la charge actuelle du système

```bash
# 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
```

### Ces modèles de code démontrent les pratiques de production de Python :

Gestion des ressources

- **: Nettoyage explicite de la mémoire GPU**Dégradation gracieuse
- **: Retour automatique entre les fournisseurs de modèles**Manipulation de la contre-pression
- **: Queue + HTTP 429 au lieu de crashes**Intégrité des données
- **: Le masquage des symboles préserve les caractères spéciaux**Optimisation des performances

: Cacherie intelligente, chunking, et traitement parallèle

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

Observabilité[: Suivi détaillé de l'enregistrement et des mesures](https://github.com/scottgal/mostlylucid-nmt/blob/main/BUILD.md).

## Chacune de ces fonctionnalités résout un vrai problème de production qui causerait des accidents, des erreurs ou une mauvaise expérience utilisateur sans eux!

### Guide de configuration

```bash
# 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
```

### Le service est hautement configurable via des variables d'environnement.

```bash
# 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"
```

### Voici le guide complet:

```yaml
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:
```

### Sélection du périphérique

```yaml
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
```

## Configuration du modèle

### Nouvelle configuration expliquée :

1. **MODÈLE_FAMILIAL**
   
   ```bash
   EASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}'
   ```
   
   - : Choisissez la famille de modèles à utiliser comme primaire
   - : Meilleure qualité, 1200+ paires, modèles séparés
   - : Bonne qualité, 50 langues, modèle simple de 2,4 Go

2. **: Bonne qualité, 100 langues, modèle unique 2.2GB**
   
   ```bash
   # Start high, reduce if you get OOM
   EASYNMT_BATCH_SIZE=64  # Try 128 on large GPUs
   ```

3. **AUTO_MODEL_FALLBACK**
   
   ```bash
   PRELOAD_MODELS="en->de,de->en,en->fr,fr->en,en->es,es->en"
   ```

4. **: Essayez automatiquement d'autres familles si la paire n'est pas disponible**
   
   ```bash
   WEB_CONCURRENCY=1
   MAX_INFLIGHT_TRANSLATIONS=1
   ```

5. **(par défaut) : Activé - couverture maximale**
   
   ```bash
   MAX_CACHED_MODELS=10  # Keep more models in VRAM
   ```

6. **: Handicapé - mode unifamiliale strict**
   
   ```bash
   # beam_size=1 is 3-5x faster than beam_size=5
   # Quality difference is often minimal
   curl -X POST ... -d '{"beam_size": 1, ...}'
   ```

### MODÈLE D'ORDRE_D'ORDRE_DE FABRICATION

1. **: Ordre de priorité pour le retour en arrière**
   
   ```bash
   EASYNMT_BATCH_SIZE=8
   ```

2. **Par défaut & #160;:**
   
   ```bash
   MAX_WORKERS_BACKEND=4
   MAX_INFLIGHT_TRANSLATIONS=4
   WEB_CONCURRENCY=2
   ```

3. **(qualité d'abord)**
   
   ```bash
   PERFORM_SENTENCE_SPLITTING_DEFAULT=0
   ```

### Autre solution :

1. **(couverture d'abord)**
   
   ```javascript
   // Bad: 100 separate requests
   for (const text of texts) {
     await translate(text);
   }
   
   // Good: 1 batch request
   await translate(texts);
   ```

2. **MODÈLE_CACHE_DIR**
   
   ```javascript
   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. **: Stockage de modèle persistant via les volumes Docker**
   
   ```javascript
   // Reuse HTTP connections
   const agent = new https.Agent({ keepAlive: true });
   ```

4. **Réglé sur**
   
   ```javascript
   // 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");
   ```

## et volume de la carte:

### Les modèles persistent à travers les redémarrages des conteneurs

1. **cache partagé entre plusieurs conteneurs**options de type torch_dtype & #160;:
2. **(float16): 2x plus rapide sur GPU, la moitié de la mémoire, perte de qualité négligeable**(bfloat16) : Une meilleure stabilité numérique que fp16 nécessite des GPU modernes
3. **(float32): Précision totale, plus lente mais plus précise**Paramètres de traduction
4. **Mise en file d'attente & Performance**Hygiène des entrées
5. **Traitement des peines**Masquage des symboles
6. **Comportement de réponse**Retour en arrière du pivot
7. **Exploitation forestière**Entretien

### Gunicorne (Docker)

Exemples d'utilisation

```python
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
```

### Traduction de base

```bash
# 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
}
```

Traduction par lots (Recommandé)

## Détection automatique du langage

Détection de langue seulement
|---------|---------|-----------------|
| **Points finals de l'observabilité**Manipulation de la contre-pression
| **Construction et mise en forme**Toutes les images Docker incluent maintenant une version appropriée et des métadonnées pour le suivi.
| **Construction rapide**Construisez les 4 variantes avec la version automatique datetime :
| **Fenêtres & #160;:**Linux/Mac :
| **Stratégie de mise en forme**Chaque construction crée
| **deux étiquettes**Étiquette nommée
| **) - indique toujours le plus récent**Étiquette de version
| **(par exemple,**) - instantané immuable
| **Exemples:**Étiquettes du BEC
| **Chaque image comprend des métadonnées :**Version
| **: Construisez l'horodatage (AAAAMMJ.HHMMSS)**Date de construction
| **: Horodatage ISO 8601**Git commit

## : SHA courte

### Variante

**: cpu-plein, cpu-min, gpu-plein, ou gpu-min**Inspecter les étiquettes:

**Pour les instructions détaillées de construction et l'intégration CI/CD, voir**

- BÂTIMENT.md`MAX_QUEUE_SIZE`
- Déploiement
- Déploiement du processeur`MAX_INFLIGHT_TRANSLATIONS`Déploiement du GPU
- Composez Docker

### Déploiement de Kubernetes

**Optimisation des performances**Liste de contrôle pour l'optimisation du GPU

**Utiliser la précision FP16**

- 2x inférence plus rapide`ENABLE_QUEUE=1`
- La moitié de l'utilisation VRAM

### Perte de qualité négligeable pour la traduction

**Taille du lot**Précharger les modèles chauds

**Travailleur unique par GPU**

- Augmenter la taille du cache`EASYNMT_BATCH_SIZE`
- Taille du faisceau inférieur pour le débit`MAX_CACHED_MODELS`
- Liste de contrôle pour l'optimisation du processeur`EASYNMT_MODEL_ARGS='{"torch_dtype":"fp16"}'`
- Taille inférieure du lot`WEB_CONCURRENCY=1`Augmenter le parallélisme`MAX_INFLIGHT_TRANSLATIONS=1`

### Désactiver le fractionnement des phrases pour les textes courts

**Meilleures pratiques du client**Demandes de lots

**Respectez la réessayer-après**

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

### Utiliser la mise en commun des connexions

**Groupe par paire de langues** `Helsinki-NLP/opus-mt-{src}-{tgt}`Surveillance et observation

**Chiffres clés à suivre**

- Débit de traduction`PIVOT_FALLBACK=1`(demandes/sec)
- Latence moyenne`curl http://localhost:8000/lang_pairs`

### (p50, p95, p99)

**Profondeur de la file d'attente**(compte d'attente en cours)

**Taux de succès de la cache**

- (% des requêtes touchant le cache)`MASK_EMOJI=0`Taux d'erreur`MASK_PUNCT=0`
- (5xx réponses)`SYMBOL_MASKING=0`

## Utilisation du GPU

(le cas échéant)

```csharp
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; }
}
```

Utilisation de la mémoire

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

## (VRAM pour GPU, RAM pour CPU)

Exemple Prométhée Métrique**Si vous intégrez Prométhée (pas intégré, mais facile à ajouter) :**Exemple d'exploitation forestière structurée

### Vous pouvez le pipe à Elasticsearch, CloudWatch, ou n'importe quel agrégateur de log.

**Comparaison: EasyNMT vs MostlyLucid-NMT**

- Caractéristiques FacileNMT , surtout Lucid-NMT ,
- Stabilité
- Crashes fréquemment Assemblage d'erreurs gracieuse et prête à la production

**Traitement des entrées**

- Effacement sur emoji/symboles
- Contre-pression
- Aucun, OOMs sous charge.Semaphore + file d'attente avec réessayer-après.

**Observabilité**

- Minimale (Health/ready/cache endpoints), logs structurés (Health/ready/cache endpoints) (Health/ready/cache endpoints) (Logs structurés) (Health/ready/cache endpoints) (Health/cache end/cache endpoints) (Health/cache end/cache endpoints) (Health/cache end/cache endpoints) (Health/cache end/cache) (Health/cache end/cache) (Health/cache end/cache) (Health/cache end/cache) (Health/cache end/cache) (Health/cache end/cache) (Health/cache) (Health/cache)
- Prise en charge du GPU
- CUDA 10.x (ancien) CUDA 12.6, FP16/BF16 soutien
- Gestion des modèles

**Manuel, pas de cache LRU avec auto-expulsion**

- Traitement des peines
- Regroupement de base Regroupement intelligent + batchage
- Pivot Traduction
- Référence Retour automatique via l'anglais
- Fermeture gracieuse

### Oui, avec timeout

Configuration**40+ env vars pour le réglage fin**Compatibilité de l'API

### Endpoints EasyNMT 100% compatibles + extensions

- **Qualité du code**Sans entretien, monolithique, modulaire, dactylographié, testé
- **Dépannage**429 Trop de demandes
- **Cause:**La file d'attente est pleine.
- **Solution:**Augmentation
- **Ajouter d'autres répliques (échelle horizontale)**Augmentation
- **(si vous avez la salle de tête)**Réduire la taille des lots des clients
- **503 Service non disponible**Cause:
- **En file d'attente désactivée et toutes les fentes occupées.**Solution:

### Activer la file d'attente & #160;:

```bash
# 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"}'
```

### Augmentation de la limite de vol si vous avez des ressources

OOM (Out of Memory) sur GPU

**Cause:**

Taille du lot trop haut ou trop de modèles mis en cache.

---


## Solution:

- [Réduire](https://huggingface.co/Helsinki-NLP)
- [Réduire](https://huggingface.co/docs/transformers)
- [Activer FP16 :](https://fastapi.tiangolo.com/)
- [Veiller à ce que](https://pytorch.org/docs/stable/notes/cuda.html)

## et

Première demande lente[Cause:

## Modèle non préchargé.

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