La traducción se trunca silenciosamente cuando falla el análisis de la secuencia JSON (sin generar error)

Resumen
DiscourseAi::Translation::PostLocalizer (localización de contenido) ocasionalmente
produce traducciones truncadas sin registrar ningún error en ningún lugar (registro de Rails,
registro de Sidekiq, colas de trabajos muertos/reintentos de Sidekiq). El truncamiento ocurre en medio de una oración,
de forma silenciosa, y el resultado parcial se guarda como si fuera una traducción completa y exitosa.

Observado en: Discourse core + discourse-ai 46bea6613 (2026-07-09), LLM:
Gemini 2.5 Flash a través del proveedor de Google.

Evidencia de reproducción
Dos casos reales en nuestra instancia:

  • Una publicación en alemán de ~2090 caracteres traducida al español produjo solo 50 caracteres,
    cortándose inmediatamente antes de una palabra entre comillas (“aufwendiger”) en el texto
    original.
  • Una publicación en alemán de ~1830 caracteres traducida al inglés (83 caracteres) y al eslovaco
    (97 caracteres) — ambas se cortaron en el exacto mismo punto de la oración,
    inmediatamente antes de un código corto de emoji (:collision:) en el texto original.
    Todos los demás idiomas (fr, it, pt, ru, he) para la misma publicación se
    tradujeron completamente (1600–1950 caracteres).

Causa raíz (hasta donde puedo rastrearla)
En lib/completions/structured_output.rb, StructuredOutput#read_buffered_property
recurre a DiscourseAi::Utils::BestEffortJsonParser.extract_key siempre que
@partial_json_tracker.broken? sea verdadero. Este recurso devuelve lo que se
había analizado antes de que la transmisión JSON se rompiera — sin excepción, sin advertencia, sin
indicación alguna de que la salida está incompleta. En
lib/translation/base_translator.rb#get_translation, este valor se utiliza tal cual
como traducción final y se persiste mediante PostLocalization#save!.

Ambos puntos de truncamiento en nuestros datos caen inmediatamente antes de un carácter
“especial” en el texto original (una comilla recta, un código corto de emoji),
sugiriendo que el rastreador de transmisión JSON se confunde con ciertos
caracteres/secuencias en la salida transmitida del modelo y marca la transmisión
como rota? prematuramente.

Impacto
El contenido se publica con traducciones visiblemente incompletas y a veces sin sentido,
y no hay señal operativa (registro, trabajo muerto, notificación de administrador)
para detectarlo. Solo lo descubrimos porque un miembro de la comunidad
notó texto corrupto en medio de una oración en la interfaz de usuario.

Correcciones sugeridas

  • Mostrar una advertencia/error (Rails.logger + Discourse-error-log) siempre que
    StructuredOutput#broken? sea verdadero y se utilice el recurso de mejor esfuerzo, para que
    los administradores puedan detectar esto en lugar de enviar contenido parcial de forma silenciosa.
  • Considere validar la longitud traducida contra un umbral de coherencia relativo
    a la longitud del texto original antes de guardar una PostLocalization.

Estoy encantado de proporcionar pares completos de texto original/traducido de forma privada si es útil para
debuggear el rastreador JSON.

1 me gusta