Skip to content

Commit 627a099

Browse files
authored
FIX: Use specific prompts for topic titles (#252)
This commit forks the existing DiscourseAi translator into a PostTranslator and TopicTranslator, with varying prompts to cater for the different content that can occur in post raw and topic titles. We need to do this as titles tend to have a lot of text, and not have html or markdown, and the existing post content prompts are causing AI to pad the titles with html that does not exist. This commit also improves the prompts for both types.
1 parent 5e833a8 commit 627a099

File tree

7 files changed

+111
-55
lines changed

7 files changed

+111
-55
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# frozen_string_literal: true
2+
3+
module DiscourseAi
4+
class BaseTranslator
5+
def initialize(text, target_language)
6+
@text = text
7+
@target_language = target_language
8+
end
9+
10+
def translate
11+
prompt =
12+
DiscourseAi::Completions::Prompt.new(
13+
build_prompt(@target_language),
14+
messages: [{ type: :user, content: "#{@text}", id: "user" }],
15+
)
16+
17+
DiscourseAi::Completions::Llm.proxy(SiteSetting.ai_helper_model).generate(
18+
prompt,
19+
user: Discourse.system_user,
20+
feature_name: "translator-translate",
21+
)
22+
end
23+
24+
private
25+
26+
def build_prompt(target_language)
27+
prompt_template % { target_language: target_language }
28+
end
29+
30+
def prompt_template
31+
raise NotImplementedError
32+
end
33+
end
34+
end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# frozen_string_literal: true
2+
3+
module DiscourseAi
4+
class PostTranslator < BaseTranslator
5+
PROMPT_TEMPLATE = <<~TEXT.freeze
6+
Translate this content to "%{target_language}". You must:
7+
1. Translate the content accurately while preserving any Markdown, HTML elements, or newlines
8+
2. Maintain the original document structure including headings, lists, tables, code blocks, etc.
9+
3. Preserve all links, images, and other media references without translation
10+
4. Handle code snippets appropriately - don't translate variable names, functions, or syntax within code blocks (```), but translate comments
11+
5. When encountering technical terminology, provide the accepted target language term if it exists, or transliterate if no equivalent exists, with the original term in parentheses
12+
6. For ambiguous terms or phrases, choose the most contextually appropriate translation
13+
7. Do not add any content besides the translation
14+
8. The translation must not have other languages other than the original and the target language
15+
9. You are being consumed via an API, only EVER return the translated text, do not return any other information
16+
TEXT
17+
18+
private def prompt_template
19+
PROMPT_TEMPLATE
20+
end
21+
end
22+
end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
module DiscourseAi
4+
class TopicTranslator < BaseTranslator
5+
PROMPT_TEMPLATE = <<~TEXT.freeze
6+
Translate this topic title to "%{target_language}"
7+
- Keep the original language when it is a proper noun or technical term
8+
- The translation should be around the same length as the original
9+
TEXT
10+
11+
private def prompt_template
12+
PROMPT_TEMPLATE
13+
end
14+
end
15+
end

app/services/discourse_ai/translator.rb

Lines changed: 0 additions & 41 deletions
This file was deleted.

app/services/discourse_translator/discourse_ai.rb

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,19 @@ def self.translate!(translatable, target_locale_sym = I18n.locale)
3333
),
3434
)
3535
end
36+
37+
language = get_language_name(target_locale_sym)
3638
translated =
37-
::DiscourseAi::Translator.new(
38-
text_for_translation(translatable, raw: true),
39-
target_locale_sym,
40-
).translate
39+
case translatable.class.name
40+
when "Post"
41+
::DiscourseAi::PostTranslator.new(
42+
text_for_translation(translatable, raw: true),
43+
language,
44+
).translate
45+
when "Topic"
46+
::DiscourseAi::TopicTranslator.new(text_for_translation(translatable), language).translate
47+
end
48+
4149
DiscourseTranslator::TranslatedContentNormalizer.normalize(translatable, translated)
4250
end
4351

@@ -47,5 +55,10 @@ def self.required_settings_enabled
4755
SiteSetting.translator_enabled && SiteSetting.translator_provider == "DiscourseAi" &&
4856
SiteSetting.discourse_ai_enabled && SiteSetting.ai_helper_enabled
4957
end
58+
59+
def self.get_language_name(target_locale_sym)
60+
LocaleSiteSetting.language_names.dig(target_locale_sym.to_s, "name") ||
61+
"locale \"#{target_locale_sym}\""
62+
end
5063
end
5164
end

spec/services/discourse_ai/translator_spec.rb renamed to spec/services/discourse_ai/base_translator_spec.rb

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
require "rails_helper"
44

5-
describe DiscourseAi::Translator do
5+
describe DiscourseAi::BaseTranslator do
66
before do
77
Fabricate(:fake_model).tap do |fake_llm|
88
SiteSetting.public_send("ai_helper_model=", "custom:#{fake_llm.id}")
@@ -17,19 +17,21 @@
1717
it "creates the correct prompt" do
1818
allow(DiscourseAi::Completions::Prompt).to receive(:new).with(
1919
<<~TEXT,
20-
You are an expert translator specializing in converting Markdown content from any source language to target locale "de". Your task is to:
21-
1. Translate the content accurately while preserving all Markdown formatting elements
20+
Translate this content to "de". You must:
21+
1. Translate the content accurately while preserving any Markdown, HTML elements, or newlines
2222
2. Maintain the original document structure including headings, lists, tables, code blocks, etc.
2323
3. Preserve all links, images, and other media references without translation
2424
4. Handle code snippets appropriately - don't translate variable names, functions, or syntax within code blocks (```), but translate comments
2525
5. When encountering technical terminology, provide the accepted target language term if it exists, or transliterate if no equivalent exists, with the original term in parentheses
2626
6. For ambiguous terms or phrases, choose the most contextually appropriate translation
27-
7. You are being consumed via an API, only EVER return the translated text, do not return any other information
27+
7. Do not add any content besides the translation
28+
8. The translation must not have other languages other than the original and the target language
29+
9. You are being consumed via an API, only EVER return the translated text, do not return any other information
2830
TEXT
2931
messages: [{ type: :user, content: "cats are great", id: "user" }],
3032
).and_call_original
3133

32-
described_class.new(text_to_translate, target_language).translate
34+
DiscourseAi::PostTranslator.new(text_to_translate, target_language).translate
3335
end
3436

3537
it "sends the translation prompt to the selected ai helper model" do
@@ -46,13 +48,13 @@
4648
feature_name: "translator-translate",
4749
)
4850

49-
described_class.new(text_to_translate, target_language).translate
51+
DiscourseAi::PostTranslator.new(text_to_translate, target_language).translate
5052
end
5153

5254
it "returns the translation from the llm's response" do
5355
DiscourseAi::Completions::Llm.with_prepared_responses(["hur dur hur dur!"]) do
5456
expect(
55-
described_class.new(text_to_translate, target_language).translate,
57+
DiscourseAi::PostTranslator.new(text_to_translate, target_language).translate,
5658
).to eq "hur dur hur dur!"
5759
end
5860
end

spec/services/discourse_ai_spec.rb

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
# frozen_string_literal: true
22

3-
require "rails_helper"
4-
53
describe DiscourseTranslator::DiscourseAi do
64
fab!(:post)
5+
fab!(:topic)
76

87
before do
98
Fabricate(:fake_model).tap do |fake_llm|
@@ -36,7 +35,10 @@
3635
end
3736

3837
describe ".translate" do
39-
before { post.set_detected_locale("de") }
38+
before do
39+
post.set_detected_locale("de")
40+
topic.set_detected_locale("de")
41+
end
4042

4143
it "translates the post and returns [locale, translated_text]" do
4244
DiscourseAi::Completions::Llm.with_prepared_responses(["some translated text"]) do
@@ -45,5 +47,14 @@
4547
expect(translated_text).to eq "<p>some translated text</p>"
4648
end
4749
end
50+
51+
it "translates the topic" do
52+
allow(::DiscourseAi::TopicTranslator).to receive(:new).and_call_original
53+
DiscourseAi::Completions::Llm.with_prepared_responses(["some translated text"]) do
54+
locale, translated_text = DiscourseTranslator::DiscourseAi.translate(topic)
55+
expect(locale).to eq "de"
56+
expect(translated_text).to eq "some translated text"
57+
end
58+
end
4859
end
4960
end

0 commit comments

Comments
 (0)