mirror of
https://github.com/discourse/discourse.git
synced 2026-08-09 21:45:25 +08:00
## Problem
A topic title translation was saved truncated: `Permanently Delete does
not show (even after 5 minutes)` → `"Endgültig löschen`.
The raw completion showed the model emitted `{"output": "\"Endgültig
löschen" }` with `finish_reason: stop` at 12 tokens — complete, valid
JSON that our parsing pipeline reproduced faithfully. The truncation
happened at generation time: the model opened a quoted UI label with an
escaped ASCII quote (`\"`), then sampled a bare `"` where it meant `\"`.
Under strict `json_schema` guided decoding that bare quote terminates
the JSON string value, and with `additionalProperties: false` the
grammar then only allows `}`, forcing the generation to end
mid-sentence.
Native typographic quotation marks (`„…“`, `«…»`, `「…」`) are ordinary
characters inside a JSON string — no escaping, no ambiguity — so they
can't trigger this failure mode. A parallel successful run that happened
to use `„…“` completed fine.
## Fix
`PostRawTranslator` already has a rule instructing native quotation
marks. This adds the same rule to `TopicTitleTranslator` and
`ShortTextTranslator`:
- **TopicTitleTranslator**: new guideline 6, plus a few-shot example
using the exact title from the incident, showing German `„…“` around the
UI label.
- **ShortTextTranslator**: new guideline 5, plus a `Click "Reply"` →
`Cliquez sur « Répondre »` example.
- **PostRawTranslator**: the existing rule's German example closed with
an ASCII `"` instead of `“` — fixed.
Seeded system agents re-sync `system_prompt` and `examples` from the
classes on migration, so sites pick this up automatically.
55 lines
2.2 KiB
Ruby
Vendored
55 lines
2.2 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
module DiscourseAi
|
|
module Agents
|
|
class ShortTextTranslator < Agent
|
|
def self.default_enabled
|
|
false
|
|
end
|
|
|
|
def system_prompt
|
|
<<~PROMPT.strip
|
|
You are a translation service specializing in translating short pieces of text or a few words.
|
|
These words may be things like a name, description, or title. Adhere to the following guidelines:
|
|
|
|
1. Keep proper nouns (like 'Minecraft' or 'Toyota') and technical terms (like 'JSON') in their original language
|
|
2. Keep the translated content close to the original length
|
|
3. Translation maintains the original meaning
|
|
4. Preserve any Markdown, HTML elements, links, parenthesis, or newlines
|
|
5. Never use ASCII double quotes (") inside the translation. For quoted text, use the target language's native quotation marks — for example German „…“, French «…», Japanese 「…」
|
|
|
|
The text to translate will be provided in JSON format with the following structure:
|
|
{"content": "Text to translate", "target_locale": "Target language code"}
|
|
|
|
Format your response as a JSON object with a single key named "output", which has the translation as the value.
|
|
Your output should be in the following format:
|
|
|
|
{"output": "xx"}
|
|
|
|
Where "xx" is replaced by the translation.
|
|
reply with valid JSON only
|
|
PROMPT
|
|
end
|
|
|
|
def response_format
|
|
[{ "key" => "output", "type" => "string" }]
|
|
end
|
|
|
|
def examples
|
|
[
|
|
[{ content: "Japan", target_locale: "es" }.to_json, { output: "Japón" }.to_json],
|
|
[{ content: "Cats and Dogs", target_locale: "zh_CN" }.to_json, { output: "猫和狗" }.to_json],
|
|
[
|
|
{ content: "Q&A", target_locale: "pt" }.to_json,
|
|
{ output: "Perguntas e Respostas" }.to_json,
|
|
],
|
|
[{ content: "Minecraft", target_locale: "fr" }.to_json, { output: "Minecraft" }.to_json],
|
|
[
|
|
{ content: %(Click "Reply"), target_locale: "fr" }.to_json,
|
|
{ output: "Cliquez sur « Répondre »" }.to_json,
|
|
],
|
|
]
|
|
end
|
|
end
|
|
end
|
|
end
|