From 3c55ea8fc0587f5d7dd6a6f34cbf3381f869751b Mon Sep 17 00:00:00 2001 From: Rafael dos Santos Silva Date: Mon, 30 Oct 2023 11:56:33 -0300 Subject: [PATCH] FEATURE: Automatic Chat Thread titles (#269) * FEATURE: Automatic Chat Thread titles * do not gen title for empty threads * make it default disabled for now --- config/settings.yml | 4 ++ lib/modules/ai_helper/chat_thread_titler.rb | 37 +++++++++++++++++++ lib/modules/ai_helper/entry_point.rb | 14 ++++++- .../regular/generate_chat_thread_title.rb | 21 +++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 lib/modules/ai_helper/chat_thread_titler.rb create mode 100644 lib/modules/ai_helper/jobs/regular/generate_chat_thread_title.rb diff --git a/config/settings.yml b/config/settings.yml index 33625e81..05fb12c1 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -176,6 +176,10 @@ discourse_ai: default: "3|14" # 3: @staff, 14: @trust_level_4 allow_any: false refresh: true + ai_helper_automatic_chat_thread_title: + default: false + ai_helper_automatic_chat_thread_title_delay: + default: 5 ai_embeddings_enabled: default: false diff --git a/lib/modules/ai_helper/chat_thread_titler.rb b/lib/modules/ai_helper/chat_thread_titler.rb new file mode 100644 index 00000000..2e78068f --- /dev/null +++ b/lib/modules/ai_helper/chat_thread_titler.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module DiscourseAi + module AiHelper + class ChatThreadTitler + def initialize(thread) + @thread = thread + end + + def thread_content + # Replace me by a proper API call + @thread + .chat_messages + .joins(:user) + .pluck(:username, :message) + .map { |username, message| "#{username}: #{message}" } + end + + def suggested_title + input_hash = { text: thread_content } + + return nil if input_hash[:text].blank? + + llm_prompt = + DiscourseAi::AiHelper::LlmPrompt + .new + .available_prompts(name_filter: "generate_titles") + .first + prompt = CompletionPrompt.find_by(id: llm_prompt[:id]) + raise Discourse::InvalidParameters.new(:mode) if !prompt || !prompt.enabled? + + response = DiscourseAi::AiHelper::LlmPrompt.new.generate_and_send_prompt(prompt, input_hash) + response.dig(:suggestions).first + end + end + end +end diff --git a/lib/modules/ai_helper/entry_point.rb b/lib/modules/ai_helper/entry_point.rb index 73b5921e..01995d59 100644 --- a/lib/modules/ai_helper/entry_point.rb +++ b/lib/modules/ai_helper/entry_point.rb @@ -3,9 +3,11 @@ module DiscourseAi module AiHelper class EntryPoint def load_files + require_relative "chat_thread_titler" + require_relative "jobs/regular/generate_chat_thread_title" require_relative "llm_prompt" - require_relative "semantic_categorizer" require_relative "painter" + require_relative "semantic_categorizer" require_relative "topic_helper" end @@ -16,6 +18,16 @@ module DiscourseAi additional_icons = %w[spell-check language] additional_icons.each { |icon| plugin.register_svg_icon(icon) } + + plugin.on(:chat_thread_created) do |thread| + return unless SiteSetting.composer_ai_helper_enabled + return unless SiteSetting.ai_helper_automatic_chat_thread_title + Jobs.enqueue_in( + SiteSetting.ai_helper_automatic_chat_thread_title_delay.minutes, + :generate_chat_thread_title, + thread_id: thread.id, + ) + end end end end diff --git a/lib/modules/ai_helper/jobs/regular/generate_chat_thread_title.rb b/lib/modules/ai_helper/jobs/regular/generate_chat_thread_title.rb new file mode 100644 index 00000000..3e8a74b9 --- /dev/null +++ b/lib/modules/ai_helper/jobs/regular/generate_chat_thread_title.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Jobs + class GenerateChatThreadTitle < ::Jobs::Base + sidekiq_options queue: "low" + + def execute(args) + return unless SiteSetting.ai_helper_automatic_chat_thread_title + return if (thread_id = args[:thread_id]).blank? + + thread = ::Chat::Thread.find_by_id(thread_id) + return if thread.nil? || thread.title.present? + + title = DiscourseAi::AiHelper::ChatThreadTitler.new(thread).suggested_title + return if title.blank? + + # TODO use a proper API that will make the new title update live + thread.update!(title: title) + end + end +end