Discourse AI: solicitação do Gemini bloqueada via promptFeedback.blockReason é tratada como resposta vazia e nunca traduzida

Descrição

Quando a API Gemini do Google recusa uma solicitação com promptFeedback.blockReason, o Discourse AI trata a resposta como uma resposta bem-sucedida, porém vazia. Na tradução por IA, isso tem dois efeitos: o tópico ou a publicação nunca recebe um idioma detectado, portanto nunca é traduzido, e o backfill envia a mesma solicitação bloqueada novamente em execuções posteriores. Nada indica a um administrador que um bloqueio ocorreu.

Observado em um site auto-hospedado executando release/2026.7 (2026.7.3, commit f1caa6321287f918644fba9ff8943579fdcf027a) com o plugin discourse-ai integrado. A solicitação era a detecção de idioma de um título de tópico, usando o agente de detector de idioma integrado em gemini-3.5-flash-lite. O título era uma frase curta e inofensiva em sueco. O mesmo título foi traduzido por gemini-3.8-flash sem um bloqueio.

O Google respondeu com HTTP 200 e um único evento de stream sem candidatos:

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

A solicitação já carrega BLOCK_NONE para as quatro categorias de segurança que o Discourse AI define, portanto este bloqueio não é um daqueles influenciados por essas configurações. O log de auditoria registra response_status 200.

Causa raiz

  1. DiscourseAi::Completions::Endpoints::Gemini#decode_chunk lê apenas candidates de cada evento de stream. promptFeedback nunca é inspecionado, portanto uma solicitação bloqueada produz um resultado vazio sem erro.
  2. DiscourseAi::Translation::LanguageDetector#detect retorna nil quando a resposta não corresponde ao seu padrão de tag de idioma, o que é o caso para uma resposta vazia. O tópico ou a publicação mantém locale como NULL.
  3. Jobs::TopicsLocaleDetectionBackfill seleciona tópicos com locale NULL em cada execução e não tem limite de tentativas, portanto, lendo o código, a solicitação bloqueada é repetida a cada cinco minutos por quanto tempo o tópico estiver na janela de backfill. Publicações são limitadas a duas tentativas de detecção por dia pela cota de relocalize, e são reenviadas a cada dia.
  4. Candidatos de tradução requerem um locale detectado, portanto o item nunca é traduzido.

O mesmo código está presente em main desde 2026-10-03.

Correção sugerida

  1. Detectar promptFeedback.blockReason no endpoint Gemini e levantar ou registrar um erro distinto e visível que nomeie o motivo.
  2. Registrar a falha contra o tópico ou a publicação para que o backfill pare de repetir uma solicitação que será bloqueada novamente, ou aplicar um limite de tentativas à detecção de tópico, como já existe para publicações.
  3. Exibir itens bloqueados na página de administração de tradução, para que um administrador possa definir o idioma manualmente.

Reproduzir

O bloqueio depende do filtro do Google e não pode ser forçado com uma entrada conhecida. Chamadas afetadas podem ser encontradas no log de auditoria:

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

Solução alternativa

Definir o idioma do tópico manualmente permite que a tradução prossiga. Neste site, o modelo de tradução não bloqueou o mesmo texto.

1 curtida

obrigado pelo relatório @Sailor :+1:

será corrigido por FIX: Handle Gemini errors and blocked prompts reported in successful responses - Pull Request #44262 - discourse/discourse - GitHub

1 curtida

Obrigado pela correção rápida. Uma parte deste relatório continua aberta após o #44262, pelo menos é o que o diff mostra.

Com a alteração, uma detecção bloqueada é registrada, e, no caso de tópicos, isso é limitado a duas tentativas por dia. O próprio item ainda fica sem um locale definido, portanto nunca é traduzido e permanece contando como aguardando detecção indefinidamente. Nada na interface administrativa mostra quais são esses itens.

Três casos ocorreram durante uma única backfill de cerca de 6.700 posts e seus tópicos neste site, todos usando gemini-3.5-flash-lite com PROHIBITED_CONTENT, e todos inofensivos: um título de tópico, um post de uma frase e um post consistindo em um único link.

Duas possíveis soluções:

  1. Um locale de fallback quando a detecção é bloqueada, por exemplo, o locale da interface do autor, ou, para uma resposta, o locale do seu tópico. No único caso aqui em que o locale foi definido manualmente e o item foi deixado para a backfill, o modelo de tradução o traduziu sem objeção.
  2. Caso contrário, uma lista ou contagem de itens cuja detecção foi bloqueada na página administrativa de tradução, para que o locale possa ser definido manualmente.