Discourse AI : une requête Gemini bloquée via promptFeedback.blockReason est traitée comme une réponse vide et n'est jamais traduite

Description

Lorsque l’API Gemini de Google refuse une requête avec promptFeedback.blockReason, Discourse AI traite la réponse comme une réussite mais une réponse vide. Dans le cas de la traduction par IA, cela a deux effets : le sujet ou le message ne reçoit jamais de langue détectée, et n’est donc jamais traduit, et le backfill renvoie la même requête bloquée lors des exécutions ultérieures. Rien n’indique à un administrateur qu’un blocage a eu lieu.

Observé sur un site auto-hébergé exécutant release/2026.7 (2026.7.3, commit f1caa6321287f918644fba9ff8943579fdcf027a) avec le plugin discourse-ai inclus. La requête concernait la détection de langue d’un titre de sujet, en utilisant l’agent détecteur de langue intégré sur gemini-3.5-flash-lite. Le titre était une courte phrase inoffensive en suédois. Le même titre a été traduit par gemini-3.8-flash sans blocage.

Google a répondu avec HTTP 200 et un seul événement de flux sans candidats :

data: {"promptFeedback": {"blockReason": "PROHIBITED_CONTENT"},"usageMetadata": {"promptTokenCount": 417,"totalTokenCount": 417,"promptTokensDetails": [{"modality": "TEXT","tokenCount": 417}],"serviceTier": "standard"},"modelVersion": "gemini-3.5-flash-lite","responseId": "..."}

La requête contient déjà BLOCK_NONE pour les quatre catégories de sécurité définies par Discourse AI, ce blocage n’est donc pas influencé par ces paramètres. Le journal d’audit enregistre un response_status de 200.

Cause racine

  1. DiscourseAi::Completions::Endpoints::Gemini#decode_chunk ne lit que candidates dans chaque événement de flux. promptFeedback n’est jamais inspecté, de sorte qu’une requête bloquée produit un résultat vide sans erreur.
  2. DiscourseAi::Translation::LanguageDetector#detect retourne nil lorsque la réponse ne correspond pas à son modèle d’étiquette de langue, ce qui est le cas pour une réponse vide. Le sujet ou le message conserve locale NULL.
  3. Jobs::TopicsLocaleDetectionBackfill sélectionne les sujets avec locale NULL à chaque exécution et n’a pas de limite de tentatives, donc, en lisant le code, la requête bloquée est répétée toutes les cinq minutes tant que le sujet est dans la fenêtre de backfill. Les messages sont limités à deux tentatives de détection par jour par le quota de relocalisation, et sont réessayés chaque jour.
  4. Les candidats de traduction nécessitent un locale détecté, donc l’élément n’est jamais traduit.

Le même code est présent sur main au 03/10/2026.

Correction suggérée

  1. Détecter promptFeedback.blockReason dans le point de terminaison Gemini et lever ou journaliser une erreur distincte et visible qui nomme la raison.
  2. Enregistrer l’échec sur le sujet ou le message afin que le backfill cesse de répéter une requête qui sera à nouveau bloquée, ou appliquer une limite de tentatives à la détection des sujets comme cela existe déjà pour les messages.
  3. Afficher les éléments bloqués sur la page d’administration de la traduction, afin qu’un administrateur puisse définir la langue manuellement.

Reproduire

Le blocage dépend du filtre de Google et ne peut pas être forcé avec une entrée connue. Les appels affectés peuvent être trouvés dans le journal d’audit :

SELECT id, created_at, language_model, post_id, topic_id, response_status
FROM ai_api_audit_logs
WHERE feature_name = 'translation'
  AND raw_response_payload LIKE '%blockReason%'
ORDER BY created_at

Contournement

Définir manuellement la langue du sujet permet à la traduction de se poursuivre. Sur ce site, le modèle de traduction n’a pas bloqué le même texte.

1 « J'aime »

merci pour le signalement @Sailor :+1:

ce sera corrigé par FIX: Handle Gemini errors and blocked prompts reported in successful responses - Pull Request #44262 - discourse/discourse - GitHub

1 « J'aime »

Merci pour la correction rapide. Une partie de ce rapport reste ouverte après #44262, selon ce que montre la différence.

Avec ce changement, une détection bloquée est consignée, et pour les sujets, elle est limitée à deux tentatives par jour. L’élément lui-même reste sans localisation, il n’est donc jamais traduit et reste compté comme en attente de détection indéfiniment. Rien dans l’interface d’administration n’indique de quels éléments il s’agit.

Trois cas se sont présentés lors d’une seule réinitialisation d’environ 6 700 messages et de leurs sujets sur ce site, tous sur gemini-3.5-flash-lite avec PROHIBITED_CONTENT, et tous inoffensifs : un titre de sujet, un message d’une seule phrase et un message ne contenant qu’un lien.

Deux solutions possibles :

  1. Une localisation par défaut lorsque la détection est bloquée, par exemple la localisation de l’interface de l’auteur, ou, pour une réponse, la localisation de son sujet. Dans le seul cas ici où la localisation a ensuite été définie manuellement et l’élément laissé à la réinitialisation, le modèle de traduction l’a traduit sans objection.
  2. À défaut, une liste ou un comptage des éléments dont la détection a été bloquée sur la page d’administration de la traduction, afin que la localisation puisse être définie manuellement.