La traduction est tronquée silencieusement lorsque l'analyse du flux JSON échoue (aucune erreur n'est signalée)

Résumé
DiscourseAi::Translation::PostLocalizer (localisation du contenu) produit
parfois des traductions tronquées sans qu’aucune erreur ne soit consignée
partout (journal Rails, journal Sidekiq, files Sidekiq morts/réessayés).
Le tronquement se produit en plein milieu d’une phrase, silencieusement, et
le résultat partiel est enregistré comme s’il s’agissait d’une traduction
complète et réussie.

Observé sur : Discourse core + discourse-ai 46bea6613 (2026-07-09), LLM :
Gemini 2.5 Flash via le fournisseur Google.

Preuves de reproduction
Deux cas réels sur notre instance :

  • Un message allemand d’environ 2090 caractères traduit en espagnol n’a
    produit que 50 caractères, coupant immédiatement avant un mot entre
    guillemets (“aufwendiger”) dans le texte source.
  • Un message allemand d’environ 1830 caractères traduit en anglais (83
    caractères) et en slovaque (97 caractères) — les deux ont été coupés au
    point exact de la phrase, immédiatement avant un code court d’emoji
    (:collision:) dans le texte source. Toutes les autres langues (fr, it, pt,
    ru, he) pour le même message ont été traduites complètement (1600–1950
    caractères).

Cause racine (dans la mesure où je peux la tracer)
Dans lib/completions/structured_output.rb, StructuredOutput#read_buffered_property
recourt à DiscourseAi::Utils::BestEffortJsonParser.extract_key chaque fois
que @partial_json_tracker.broken? est vrai. Ce recours renvoie ce qui a été
analysé avant que le flux JSON ne se brise — sans exception, sans avertissement,
sans aucune indication que la sortie est incomplète. Dans
lib/translation/base_translator.rb#get_translation, cette valeur est utilisée
telle quelle comme traduction finale et persistée via PostLocalization#save!.

Les deux points de tronquement dans nos données tombent immédiatement avant un
caractère “spécial” dans le texte source (un guillemet droit, un code court
d’emoji), ce qui suggère que le traqueur de flux JSON se trompe face à certains
caractères/séquences dans la sortie diffusée par le modèle et marque le flux
comme broken? prématurément.

Impact
Le contenu est publié avec des traductions visiblement incomplètes, parfois
dénuées de sens, et il n’y a aucun signal opérationnel (journal, tâche morte,
notification administrateur) pour les détecter. Nous ne l’avons découvert que
parce qu’un membre de la communauté a repéré du texte incohérent en plein
milieu de phrase dans l’interface utilisateur.

Corrections suggérées

  • Afficher un avertissement/erreur (Rails.logger + Discourse-error-log) chaque
    fois que StructuredOutput#broken? est vrai et que le recours au meilleur
    effort est utilisé, afin que les administrateurs puissent détecter ce problème
    au lieu de diffuser silencieusement du contenu partiel.
  • Envisager de valider la longueur traduite par rapport à un seuil de bon sens
    relatif à la longueur source avant d’enregistrer une PostLocalization.

Je peux fournir les paires de textes bruts/traduits complets en privé si cela
est utile pour déboguer le traqueur JSON.

1 « J'aime »