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

Commit c294b6d

Browse files
authored
FEATURE: allow llm triage to automatically hide posts (#820)
Previous to this change we could flag, but there was no way to hide content and treat the flag as spam. We had the option to hide topics, but this is not desirable for a spam reply. New option allows triage to hide a post if it is a reply, if the post happens to be the first post on the topic, the topic will be hidden.
1 parent 110a162 commit c294b6d

File tree

6 files changed

+62
-11
lines changed

6 files changed

+62
-11
lines changed

config/locales/client.en.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,12 @@ en:
9999
hide_topic:
100100
label: "Hide topic"
101101
description: "Make topic non visible to the public if triggered"
102+
flag_type:
103+
label: "Flag type"
104+
description: "Type of flag to apply to the post (spam or simply raise for review)"
102105
flag_post:
103-
label: "Send to review"
104-
description: "Puts the post into the review queue if triggered, for moderators to triage"
106+
label: "Flag post"
107+
description: "Flags post (either as spam or for review)"
105108
model:
106109
label: "Model"
107110
description: "Language model used for triage"

config/locales/server.en.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
en:
22
discourse_automation:
3+
ai:
4+
flag_types:
5+
review: "Add post to review queue"
6+
spam: "Flag as spam and hide post"
37
scriptables:
48
llm_triage:
59
title: Triage posts using AI

discourse_automation/llm_triage.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@
2121
field :tags, component: :tags
2222
field :hide_topic, component: :boolean
2323
field :flag_post, component: :boolean
24+
field :flag_type,
25+
component: :choices,
26+
required: false,
27+
extra: {
28+
content: DiscourseAi::Automation.flag_types,
29+
},
30+
default: "review"
2431
field :canned_reply, component: :message
2532
field :canned_reply_user, component: :user
2633

@@ -41,6 +48,7 @@
4148
tags = fields.dig("tags", "value")
4249
hide_topic = fields.dig("hide_topic", "value")
4350
flag_post = fields.dig("flag_post", "value")
51+
flag_type = fields.dig("flag_type", "value")
4452

4553
begin
4654
RateLimiter.new(
@@ -68,6 +76,7 @@
6876
canned_reply_user: canned_reply_user,
6977
hide_topic: hide_topic,
7078
flag_post: flag_post,
79+
flag_type: flag_type.to_s.to_sym,
7180
automation: self.automation,
7281
)
7382
rescue => e

lib/automation.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
module DiscourseAi
44
module Automation
5+
def self.flag_types
6+
[
7+
{ id: "review", translated_name: I18n.t("discourse_automation.ai.flag_types.review") },
8+
{ id: "spam", translated_name: I18n.t("discourse_automation.ai.flag_types.spam") },
9+
]
10+
end
511
def self.available_models
612
values = DB.query_hash(<<~SQL)
713
SELECT display_name AS translated_name, id AS id

lib/automation/llm_triage.rb

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ def self.handle(
1414
canned_reply_user: nil,
1515
hide_topic: nil,
1616
flag_post: nil,
17+
flag_type: nil,
1718
automation: nil
1819
)
1920
if category_id.blank? && tags.blank? && canned_reply.blank? && hide_topic.blank? &&
@@ -65,22 +66,32 @@ def self.handle(
6566
post.topic.update!(visible: false) if hide_topic
6667

6768
if flag_post
68-
reviewable =
69-
ReviewablePost.needs_review!(target: post, created_by: Discourse.system_user)
70-
7169
score_reason =
7270
I18n
7371
.t("discourse_automation.scriptables.llm_triage.flagged_post")
7472
.sub("%%LLM_RESPONSE%%", result)
7573
.sub("%%AUTOMATION_ID%%", automation&.id.to_s)
7674
.sub("%%AUTOMATION_NAME%%", automation&.name.to_s)
7775

78-
reviewable.add_score(
79-
Discourse.system_user,
80-
ReviewableScore.types[:needs_approval],
81-
reason: score_reason,
82-
force_review: true,
83-
)
76+
if flag_type == :spam
77+
PostActionCreator.new(
78+
Discourse.system_user,
79+
post,
80+
PostActionType.types[:spam],
81+
message: score_reason,
82+
queue_for_review: true,
83+
).perform
84+
else
85+
reviewable =
86+
ReviewablePost.needs_review!(target: post, created_by: Discourse.system_user)
87+
88+
reviewable.add_score(
89+
Discourse.system_user,
90+
ReviewableScore.types[:needs_approval],
91+
reason: score_reason,
92+
force_review: true,
93+
)
94+
end
8495
end
8596
end
8697
end

spec/lib/modules/automation/llm_triage_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22
describe DiscourseAi::Automation::LlmTriage do
33
fab!(:post)
4+
fab!(:reply) { Fabricate(:post, topic: post.topic, user: Fabricate(:user)) }
45
fab!(:llm_model)
56

67
def triage(**args)
@@ -92,6 +93,23 @@ def triage(**args)
9293
expect(reviewable.reviewable_scores.first.reason).to include("bad")
9394
end
9495

96+
it "can handle spam flags" do
97+
DiscourseAi::Completions::Llm.with_prepared_responses(["bad"]) do
98+
triage(
99+
post: post,
100+
model: "custom:#{llm_model.id}",
101+
system_prompt: "test %%POST%%",
102+
search_for_text: "bad",
103+
flag_post: true,
104+
flag_type: :spam,
105+
automation: nil,
106+
)
107+
end
108+
109+
expect(post.reload).to be_hidden
110+
expect(post.topic.reload.visible).to eq(false)
111+
end
112+
95113
it "can handle garbled output from LLM" do
96114
DiscourseAi::Completions::Llm.with_prepared_responses(["Bad.\n\nYo"]) do
97115
triage(

0 commit comments

Comments
 (0)