discourse/plugins/discourse-ai/discourse_automation/llm_triage.rb
Jarek Radosz 8baf4d5d4c
DEV: Enable Style/RedundantSelf rubocop rule (#40098)
(to be enabled in the shared config)
2026-05-19 19:27:45 +02:00

165 lines
5.3 KiB
Ruby
Vendored

# frozen_string_literal: true
if defined?(DiscourseAutomation)
DiscourseAutomation::Scriptable.add("llm_triage") do
version 1
run_in_background
placeholder :post
triggerables %i[post_created_edited stalled_topic]
# TODO move to triggerables
field :include_personal_messages, component: :boolean
# Inputs
field :triage_agent,
component: :choices,
required: true,
extra: {
content:
DiscourseAi::Automation.available_agent_choices(
require_user: false,
require_default_llm: true,
),
}
field :search_for_text, component: :text, required: true
field :max_post_tokens, component: :text
field :stop_sequences, component: :text_list, required: false
field :max_output_tokens, component: :text
# Actions
field :category, component: :category
field :tags, component: :tags
field :hide_topic, component: :boolean
field :flag_post, component: :boolean
field :flag_type,
component: :choices,
required: false,
extra: {
content: DiscourseAi::Automation.flag_types,
},
default: "review"
field :canned_reply_user, component: :user
field :canned_reply, component: :message
field :reply_agent,
component: :choices,
extra: {
content:
DiscourseAi::Automation.available_agent_choices(
require_user: false,
require_default_llm: true,
),
}
field :whisper, component: :boolean
field :notify_author_pm, component: :boolean
field :notify_author_pm_user, component: :user
field :notify_author_pm_message, component: :message
script do |context, fields|
post = context["post"]
post ||= context["topic"]&.posts&.find_by(post_number: 1)
next if post.blank?
next if post.user&.bot?
if post.topic.private_message?
include_personal_messages = fields.dig("include_personal_messages", "value")
next if !include_personal_messages
end
triage_agent_id = fields.dig("triage_agent", "value")
canned_reply = fields.dig("canned_reply", "value")
canned_reply_user = fields.dig("canned_reply_user", "value")
reply_agent_id = fields.dig("reply_agent", "value")
whisper = fields.dig("whisper", "value")
notify_author_pm = fields.dig("notify_author_pm", "value")
notify_author_pm_user = fields.dig("notify_author_pm_user", "value")
notify_author_pm_message = fields.dig("notify_author_pm_message", "value")
# nothing to do if we already replied
next if post.user.username == canned_reply_user
next if post.raw.strip == canned_reply.to_s.strip
search_for_text = fields.dig("search_for_text", "value")
category_id = fields.dig("category", "value")
tags = fields.dig("tags", "value")
hide_topic = fields.dig("hide_topic", "value")
flag_post = fields.dig("flag_post", "value")
flag_type = fields.dig("flag_type", "value")
max_post_tokens = fields.dig("max_post_tokens", "value").to_i
max_output_tokens = fields.dig("max_output_tokens", "value").to_i
max_output_tokens = nil if max_output_tokens <= 0
max_post_tokens = nil if max_post_tokens <= 0
stop_sequences = fields.dig("stop_sequences", "value")
system_user = Discourse.system_user
if flag_post
flagged_as =
if DiscourseAi::Automation.spam_based_flag_types.include?(flag_type)
ReviewableScore.types[:spam]
else
ReviewableScore.types[:needs_approval]
end
if ReviewableScore
.pending
.where(user: system_user, reviewable_score_type: flagged_as)
.joins(:reviewable)
.where(reviewables: { target: post })
.exists?
next
end
end
begin
RateLimiter.new(
system_user,
"llm_triage_#{post.id}",
SiteSetting.ai_automation_max_triage_per_post_per_minute,
1.minute,
).performed!
RateLimiter.new(
system_user,
"llm_triage",
SiteSetting.ai_automation_max_triage_per_minute,
1.minute,
).performed!
DiscourseAi::Automation::LlmTriage.handle(
post: post,
triage_agent_id: triage_agent_id,
search_for_text: search_for_text,
category_id: category_id,
tags: tags,
canned_reply: canned_reply,
canned_reply_user: canned_reply_user,
reply_agent_id: reply_agent_id,
whisper: whisper,
hide_topic: hide_topic,
flag_post: flag_post,
flag_type: flag_type.to_s.to_sym,
max_post_tokens: max_post_tokens,
stop_sequences: stop_sequences,
automation: automation,
max_output_tokens: max_output_tokens,
action: context["action"],
notify_author_pm: notify_author_pm,
notify_author_pm_user: notify_author_pm_user,
notify_author_pm_message: notify_author_pm_message,
)
rescue => e
Discourse.warn_exception(
e,
message: "llm_triage: skipped triage on post #{post.id} #{post.url}",
)
end
end
end
end