Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.

Commit 70a0951

Browse files
committed
FEATURE: Use different personas to power AI helper features.
You can now edit each AI helper prompt individually through personas, limit access to specific groups, set different LLMs, etc.
1 parent 59f4b66 commit 70a0951

33 files changed

+841
-577
lines changed

app/controllers/discourse_ai/ai_helper/assistant_controller.rb

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,24 @@ def suggest
2525
input = get_text_param!
2626
force_default_locale = params[:force_default_locale] || false
2727

28-
prompt = CompletionPrompt.find_by(id: params[:mode])
28+
raise Discourse::InvalidParameters.new(:mode) if params[:mode].blank?
2929

30-
raise Discourse::InvalidParameters.new(:mode) if !prompt || !prompt.enabled?
31-
32-
if prompt.id == CompletionPrompt::CUSTOM_PROMPT
30+
if params[:mode] == DiscourseAi::AiHelper::Assistant::CUSTOM_PROMPT
3331
raise Discourse::InvalidParameters.new(:custom_prompt) if params[:custom_prompt].blank?
34-
35-
prompt.custom_instruction = params[:custom_prompt]
3632
end
3733

38-
return suggest_thumbnails(input) if prompt.id == CompletionPrompt::ILLUSTRATE_POST
34+
if params[:mode] == DiscourseAi::AiHelper::Assistant::ILLUSTRATE_POST
35+
return suggest_thumbnails(input)
36+
end
3937

4038
hijack do
4139
render json:
4240
DiscourseAi::AiHelper::Assistant.new.generate_and_send_prompt(
43-
prompt,
41+
params[:mode],
4442
input,
4543
current_user,
4644
force_default_locale: force_default_locale,
45+
custom_prompt: params[:custom_prompt],
4746
),
4847
status: 200
4948
end
@@ -60,13 +59,10 @@ def suggest_title
6059
input = get_text_param!
6160
end
6261

63-
prompt = CompletionPrompt.enabled_by_name("generate_titles")
64-
raise Discourse::InvalidParameters.new(:mode) if !prompt
65-
6662
hijack do
6763
render json:
6864
DiscourseAi::AiHelper::Assistant.new.generate_and_send_prompt(
69-
prompt,
65+
DiscourseAi::AiHelper::Assistant::GENERATE_TITLES,
7066
input,
7167
current_user,
7268
),
@@ -115,12 +111,12 @@ def stream_suggestion
115111
location = params[:location]
116112
raise Discourse::InvalidParameters.new(:location) if !location
117113

118-
prompt = CompletionPrompt.find_by(id: params[:mode])
119-
120-
raise Discourse::InvalidParameters.new(:mode) if !prompt || !prompt.enabled?
121-
return suggest_thumbnails(input) if prompt.id == CompletionPrompt::ILLUSTRATE_POST
114+
raise Discourse::InvalidParameters.new(:mode) if params[:mode].blank?
115+
if params[:mode] == DiscourseAi::AiHelper::Assistant::ILLUSTRATE_POST
116+
return suggest_thumbnails(input)
117+
end
122118

123-
if prompt.id == CompletionPrompt::CUSTOM_PROMPT
119+
if params[:mode] == DiscourseAi::AiHelper::Assistant::CUSTOM_PROMPT
124120
raise Discourse::InvalidParameters.new(:custom_prompt) if params[:custom_prompt].blank?
125121
end
126122

@@ -133,7 +129,7 @@ def stream_suggestion
133129
:stream_composer_helper,
134130
user_id: current_user.id,
135131
text: text,
136-
prompt: prompt.name,
132+
prompt: params[:mode],
137133
custom_prompt: params[:custom_prompt],
138134
force_default_locale: params[:force_default_locale] || false,
139135
client_id: params[:client_id],
@@ -149,7 +145,7 @@ def stream_suggestion
149145
post_id: post.id,
150146
user_id: current_user.id,
151147
text: text,
152-
prompt: prompt.name,
148+
prompt: params[:mode],
153149
custom_prompt: params[:custom_prompt],
154150
client_id: params[:client_id],
155151
)

app/jobs/regular/stream_composer_helper.rb

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,16 @@ def execute(args)
1010
return unless args[:text]
1111
return unless args[:client_id]
1212

13-
prompt = CompletionPrompt.enabled_by_name(args[:prompt])
14-
15-
if prompt.id == CompletionPrompt::CUSTOM_PROMPT
16-
prompt.custom_instruction = args[:custom_prompt]
17-
end
13+
helper_mode = args[:prompt]
1814

1915
DiscourseAi::AiHelper::Assistant.new.stream_prompt(
20-
prompt,
16+
helper_mode,
2117
args[:text],
2218
user,
2319
"/discourse-ai/ai-helper/stream_composer_suggestion",
2420
force_default_locale: args[:force_default_locale],
2521
client_id: args[:client_id],
22+
custom_prompt: args[:custom_prompt],
2623
)
2724
end
2825
end

app/jobs/regular/stream_post_helper.rb

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,12 @@ def execute(args)
1414

1515
return unless user.guardian.can_see?(post)
1616

17-
prompt = CompletionPrompt.enabled_by_name(args[:prompt])
17+
helper_mode = args[:prompt]
1818

19-
if prompt.id == CompletionPrompt::CUSTOM_PROMPT
20-
prompt.custom_instruction = args[:custom_prompt]
21-
end
22-
23-
if prompt.name == "explain"
24-
input = <<~TEXT
25-
<term>#{args[:text]}</term>
26-
<context>#{post.raw}</context>
19+
if helper_mode == DiscourseAi::AiHelper::Assistant::EXPLAIN
20+
input = <<~TEXT.strip
21+
<term>#{args[:text]}</term>
22+
<context>#{post.raw}</context>
2723
<topic>#{topic.title}</topic>
2824
#{reply_to ? "<replyTo>#{reply_to.raw}</replyTo>" : nil}
2925
TEXT
@@ -32,10 +28,11 @@ def execute(args)
3228
end
3329

3430
DiscourseAi::AiHelper::Assistant.new.stream_prompt(
35-
prompt,
31+
helper_mode,
3632
input,
3733
user,
3834
"/discourse-ai/ai-helper/stream_suggestion/#{post.id}",
35+
custom_prompt: args[:custom_prompt],
3936
)
4037
end
4138
end

assets/javascripts/discourse/components/ai-composer-helper-menu.gjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export default class AiComposerHelperMenu extends Component {
7373
}
7474

7575
prompts.forEach((p) => {
76-
this.prompts[p.id] = p;
76+
this.prompts[p.name] = p;
7777
});
7878

7979
this.promptTypes = prompts.reduce((memo, p) => {
@@ -116,7 +116,7 @@ export default class AiComposerHelperMenu extends Component {
116116
if (option.name === "illustrate_post") {
117117
return this.modal.show(ThumbnailSuggestion, {
118118
model: {
119-
mode: option.id,
119+
mode: option.name,
120120
selectedText: this.args.data.selectedText,
121121
thumbnails: this.thumbnailSuggestions,
122122
},
@@ -128,7 +128,7 @@ export default class AiComposerHelperMenu extends Component {
128128

129129
return this.modal.show(ModalDiffModal, {
130130
model: {
131-
mode: option.id,
131+
mode: option.name,
132132
selectedText: this.args.data.selectedText,
133133
revert: this.undoAiAction,
134134
toolbarEvent: this.args.data.toolbarEvent,

assets/javascripts/discourse/components/ai-helper-options-list.gjs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,12 @@ export default class AiHelperOptionsList extends Component {
2929
@submit={{@performAction}}
3030
/>
3131
{{else}}
32-
<li data-name={{option.translated_name}} data-value={{option.id}}>
32+
<li data-name={{option.translated_name}} data-value={{option.name}}>
3333
<DButton
3434
@icon={{option.icon}}
3535
@translatedLabel={{option.translated_name}}
3636
@action={{fn @performAction option}}
3737
data-name={{option.name}}
38-
data-value={{option.id}}
3938
class="ai-helper-options__button"
4039
>
4140
{{#if (and (eq option.name "proofread") this.showShortcut)}}

assets/javascripts/discourse/components/ai-post-helper-menu.gjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ export default class AiPostHelperMenu extends Component {
202202
this._activeAiRequest = ajax("/discourse-ai/ai-helper/suggest", {
203203
method: "POST",
204204
data: {
205-
mode: option.id,
205+
mode: option.name,
206206
text: this.args.data.quoteState.buffer,
207207
custom_prompt: this.customPromptValue,
208208
},
@@ -238,7 +238,7 @@ export default class AiPostHelperMenu extends Component {
238238
method: "POST",
239239
data: {
240240
location: "post",
241-
mode: option.id,
241+
mode: option.name,
242242
text: this.args.data.selectedText,
243243
post_id: this.args.data.quoteState.postId,
244244
custom_prompt: this.customPromptValue,

assets/javascripts/discourse/connectors/fast-edit-footer-after/ai-edit-suggestion-button.gjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export default class AiEditSuggestionButton extends Component {
3434
this._activeAIRequest = ajax("/discourse-ai/ai-helper/suggest", {
3535
method: "POST",
3636
data: {
37-
mode: this.mode.id,
37+
mode: this.mode.name,
3838
text: this.args.outletArgs.initialValue,
3939
custom_prompt: "",
4040
},

assets/javascripts/initializers/admin-plugin-configuration-nav.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,11 @@ export default {
4141
route: "adminPlugins.show.discourse-ai-spam",
4242
description: "discourse_ai.spam.spam_description",
4343
},
44-
// TODO(@keegan / @roman): Uncomment this when structured output is merged
45-
// {
46-
// label: "discourse_ai.features.short_title",
47-
// route: "adminPlugins.show.discourse-ai-features",
48-
// description: "discourse_ai.features.description",
49-
// },
44+
{
45+
label: "discourse_ai.features.short_title",
46+
route: "adminPlugins.show.discourse-ai-features",
47+
description: "discourse_ai.features.description",
48+
},
5049
]);
5150
});
5251
},

assets/javascripts/initializers/ai-helper.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function initializeAiHelperTrigger(api) {
4444

4545
const mode = currentUser?.ai_helper_prompts.find(
4646
(p) => p.name === "proofread"
47-
).id;
47+
).name;
4848

4949
modal.show(ModalDiffModal, {
5050
model: {

config/locales/server.en.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ en:
330330
short_summarizer:
331331
name: "Summarizer (short form)"
332332
description: "Default persona used to power AI short summaries for topic lists' items"
333+
<<<<<<< Updated upstream
333334
concept_finder:
334335
name: "Concept Finder"
335336
description: "AI Bot specialized in identifying concepts and themes in content"
@@ -339,6 +340,35 @@ en:
339340
concept_deduplicator:
340341
name: "Concept Deduplicator"
341342
description: "AI Bot specialized in deduplicating concepts"
343+
=======
344+
custom_prompt:
345+
name: "Custom Prompt"
346+
description: "Default persona powering the AI helper's custom prompt feature"
347+
smart_dates:
348+
name: "Smart Dates"
349+
description: "Default persona powering the AI helper's smart dates feature"
350+
markdown_table_generator:
351+
name: "Markdown Table Generator"
352+
description: "Default persona powering the AI helper's generate Markdown table feature"
353+
post_illustrator:
354+
name: "Post Illustrator"
355+
description: "Generates StableDiffusion prompts to power the AI helper's illustrate post feature"
356+
proofreader:
357+
name: "Proofreader"
358+
description: "Default persona powering the AI helper's proofread text feature"
359+
titles_generator:
360+
name: "Titles Generator"
361+
description: "Default persona powering the AI helper's suggest topic titles feature"
362+
tutor:
363+
name: "Tutor"
364+
description: "Default persona powering the AI helper's explain feature"
365+
translator:
366+
name: "Translator"
367+
description: "Default persona powering the AI helper's translator feature"
368+
image_captioner:
369+
name: "Image Captions"
370+
description: "Default persona powering the AI helper's image caption feature"
371+
>>>>>>> Stashed changes
342372
topic_not_found: "Summary unavailable, topic not found!"
343373
summarizing: "Summarizing topic"
344374
searching: "Searching for: '%{query}'"

0 commit comments

Comments
 (0)