| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module DiscourseAi | 
					
						
							|  |  |  |   module AiBot | 
					
						
							|  |  |  |     class Bot | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |       class Functions | 
					
						
							|  |  |  |         attr_reader :functions | 
					
						
							|  |  |  |         attr_reader :current_function | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def initialize | 
					
						
							|  |  |  |           @functions = [] | 
					
						
							|  |  |  |           @current_function = nil | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def add_function(name) | 
					
						
							|  |  |  |           @current_function = { name: name, arguments: +"" } | 
					
						
							|  |  |  |           functions << current_function | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def add_argument_fragment(fragment) | 
					
						
							|  |  |  |           @current_function[:arguments] << fragment | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 23:08:17 +10:00
										 |  |  |       attr_reader :bot_user | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |       BOT_NOT_FOUND = Class.new(StandardError) | 
					
						
							| 
									
										
										
										
											2023-06-20 15:44:03 +10:00
										 |  |  |       MAX_COMPLETIONS = 6
 | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |       def self.as(bot_user) | 
					
						
							|  |  |  |         available_bots = [DiscourseAi::AiBot::OpenAiBot, DiscourseAi::AiBot::AnthropicBot] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         bot = | 
					
						
							|  |  |  |           available_bots.detect(-> { raise BOT_NOT_FOUND }) do |bot_klass| | 
					
						
							|  |  |  |             bot_klass.can_reply_as?(bot_user) | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         bot.new(bot_user) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def initialize(bot_user) | 
					
						
							|  |  |  |         @bot_user = bot_user | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-16 14:38:21 -03:00
										 |  |  |       def update_pm_title(post) | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |         prompt = title_prompt(post) | 
					
						
							| 
									
										
										
										
											2023-05-16 14:38:21 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         new_title = get_updated_title(prompt) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         PostRevisor.new(post.topic.first_post, post.topic).revise!( | 
					
						
							|  |  |  |           bot_user, | 
					
						
							|  |  |  |           title: new_title.sub(/\A"/, "").sub(/"\Z/, ""), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-06 07:09:33 +10:00
										 |  |  |       def max_commands_per_reply=(val) | 
					
						
							|  |  |  |         @max_commands_per_reply = val | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def max_commands_per_reply | 
					
						
							|  |  |  |         @max_commands_per_reply || 5
 | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |       def reply_to( | 
					
						
							|  |  |  |         post, | 
					
						
							|  |  |  |         total_completions: 0, | 
					
						
							|  |  |  |         bot_reply_post: nil, | 
					
						
							|  |  |  |         prefer_low_cost: false, | 
					
						
							|  |  |  |         standalone: false | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |         return if total_completions > MAX_COMPLETIONS | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         prompt = | 
					
						
							|  |  |  |           if standalone && post.post_custom_prompt | 
					
						
							|  |  |  |             username, standalone_prompt = post.post_custom_prompt.custom_prompt.last | 
					
						
							|  |  |  |             [build_message(username, standalone_prompt)] | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             bot_prompt_with_topic_context(post) | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         redis_stream_key = nil | 
					
						
							| 
									
										
										
										
											2023-06-20 15:44:03 +10:00
										 |  |  |         partial_reply = +"" | 
					
						
							| 
									
										
										
										
											2023-05-23 23:08:17 +10:00
										 |  |  |         reply = +(bot_reply_post ? bot_reply_post.raw.dup : "") | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |         start = Time.now | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |         setup_cancel = false | 
					
						
							| 
									
										
										
										
											2023-05-23 23:08:17 +10:00
										 |  |  |         context = {} | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |         functions = Functions.new | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-22 12:09:14 +10:00
										 |  |  |         submit_prompt(prompt, prefer_low_cost: prefer_low_cost) do |partial, cancel| | 
					
						
							| 
									
										
										
										
											2023-06-20 15:44:03 +10:00
										 |  |  |           current_delta = get_delta(partial, context) | 
					
						
							|  |  |  |           partial_reply << current_delta | 
					
						
							|  |  |  |           reply << current_delta | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |           populate_functions(partial, functions) | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |           if redis_stream_key && !Discourse.redis.get(redis_stream_key) | 
					
						
							|  |  |  |             cancel&.call | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             bot_reply_post.update!(raw: reply, cooked: PrettyText.cook(reply)) if bot_reply_post | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           next if reply.length < SiteSetting.min_personal_message_post_length | 
					
						
							|  |  |  |           # Minor hack to skip the delay during tests. | 
					
						
							|  |  |  |           next if (Time.now - start < 0.5) && !Rails.env.test? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if bot_reply_post | 
					
						
							|  |  |  |             Discourse.redis.expire(redis_stream_key, 60) | 
					
						
							|  |  |  |             start = Time.now | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             publish_update(bot_reply_post, raw: reply.dup) | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             bot_reply_post = | 
					
						
							|  |  |  |               PostCreator.create!( | 
					
						
							|  |  |  |                 bot_user, | 
					
						
							|  |  |  |                 topic_id: post.topic_id, | 
					
						
							|  |  |  |                 raw: reply, | 
					
						
							|  |  |  |                 skip_validations: false, | 
					
						
							|  |  |  |               ) | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if !setup_cancel && bot_reply_post | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |             redis_stream_key = "gpt_cancel:#{bot_reply_post.id}" | 
					
						
							|  |  |  |             Discourse.redis.setex(redis_stream_key, 60, 1) | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |             setup_cancel = true | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if bot_reply_post | 
					
						
							|  |  |  |           publish_update(bot_reply_post, done: true) | 
					
						
							| 
									
										
										
										
											2023-05-23 23:08:17 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |           bot_reply_post.revise( | 
					
						
							|  |  |  |             bot_user, | 
					
						
							|  |  |  |             { raw: reply }, | 
					
						
							|  |  |  |             skip_validations: true, | 
					
						
							|  |  |  |             skip_revision: true, | 
					
						
							|  |  |  |           ) | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |           bot_reply_post.post_custom_prompt ||= post.build_post_custom_prompt(custom_prompt: []) | 
					
						
							|  |  |  |           prompt = post.post_custom_prompt.custom_prompt || [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 15:44:03 +10:00
										 |  |  |           prompt << [partial_reply, bot_user.username] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |           post.post_custom_prompt.update!(custom_prompt: prompt) | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2023-06-06 07:09:33 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |         if functions.functions.length > 0
 | 
					
						
							| 
									
										
										
										
											2023-06-06 07:09:33 +10:00
										 |  |  |           chain = false | 
					
						
							|  |  |  |           standalone = false | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |           functions.functions.each do |function| | 
					
						
							|  |  |  |             name, args = function[:name], function[:arguments] | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |             if command_klass = available_commands.detect { |cmd| cmd.invoked?(name) } | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |               command = command_klass.new(bot_user, args) | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |               chain_intermediate, bot_reply_post = | 
					
						
							|  |  |  |                 command.invoke_and_attach_result_to(bot_reply_post, post) | 
					
						
							| 
									
										
										
										
											2023-06-06 07:09:33 +10:00
										 |  |  |               chain ||= chain_intermediate | 
					
						
							|  |  |  |               standalone ||= command.standalone? | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |             end | 
					
						
							| 
									
										
										
										
											2023-06-06 07:09:33 +10:00
										 |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if chain | 
					
						
							|  |  |  |             reply_to( | 
					
						
							|  |  |  |               bot_reply_post, | 
					
						
							|  |  |  |               total_completions: total_completions + 1, | 
					
						
							|  |  |  |               bot_reply_post: bot_reply_post, | 
					
						
							|  |  |  |               standalone: standalone, | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |         end | 
					
						
							|  |  |  |       rescue => e | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |         if Rails.env.development? | 
					
						
							|  |  |  |           p e | 
					
						
							|  |  |  |           puts e.backtrace | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |         raise e if Rails.env.test? | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |         Discourse.warn_exception(e, message: "ai-bot: Reply failed") | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-16 14:38:21 -03:00
										 |  |  |       def bot_prompt_with_topic_context(post, prompt: "topic") | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |         messages = [] | 
					
						
							|  |  |  |         conversation = conversation_context(post) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |         rendered_system_prompt = system_prompt(post) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         total_prompt_tokens = tokenize(rendered_system_prompt).length | 
					
						
							| 
									
										
										
										
											2023-05-22 12:09:14 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |         messages = | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |           conversation.reduce([]) do |memo, (raw, username, function)| | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |             break(memo) if total_prompt_tokens >= prompt_limit | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |             tokens = tokenize(raw.to_s) | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |             while !raw.blank? && tokens.length + total_prompt_tokens > prompt_limit | 
					
						
							|  |  |  |               raw = raw[0..-100] || "" | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |               tokens = tokenize(raw.to_s) | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |             end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |             next(memo) if raw.blank? | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |             total_prompt_tokens += tokens.length | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |             memo.unshift(build_message(username, raw, function: !!function)) | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |         messages.unshift(build_message(bot_user.username, rendered_system_prompt, system: true)) | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |         messages | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def prompt_limit | 
					
						
							|  |  |  |         raise NotImplemented | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-16 14:38:21 -03:00
										 |  |  |       def title_prompt(post) | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |         [build_message(bot_user.username, <<~TEXT)] | 
					
						
							| 
									
										
										
										
											2023-05-16 14:38:21 -03:00
										 |  |  |           Suggest a 7 word title for the following topic without quoting any of it: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           #{post.topic.posts[1..-1].map(&:raw).join("\n\n")[0..prompt_limit]} | 
					
						
							|  |  |  |         TEXT | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |       def available_commands | 
					
						
							| 
									
										
										
										
											2023-06-01 09:10:33 +10:00
										 |  |  |         # by default assume bots have no access to commands | 
					
						
							|  |  |  |         # for now we need GPT 4 to properly work with them | 
					
						
							|  |  |  |         [] | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def system_prompt_style!(style) | 
					
						
							|  |  |  |         @style = style | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def system_prompt(post) | 
					
						
							|  |  |  |         return "You are a helpful Bot" if @style == :simple | 
					
						
							|  |  |  |         <<~TEXT | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |           You are a helpful Discourse assistant. | 
					
						
							|  |  |  |           You understand and generate Discourse Markdown. | 
					
						
							|  |  |  |           You live in a Discourse Forum Message. | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |           You live in the forum with the URL: #{Discourse.base_url} | 
					
						
							|  |  |  |           The title of your site: #{SiteSetting.title} | 
					
						
							|  |  |  |           The description is: #{SiteSetting.site_description} | 
					
						
							|  |  |  |           The participants in this conversation are: #{post.topic.allowed_users.map(&:username).join(", ")} | 
					
						
							|  |  |  |           The date now is: #{Time.zone.now}, much has changed since you were trained. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |           #{available_commands.map(&:custom_system_message).compact.join("\n")} | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |         TEXT | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-22 12:09:14 +10:00
										 |  |  |       def tokenize(text) | 
					
						
							|  |  |  |         raise NotImplemented | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def submit_prompt(prompt, prefer_low_cost: false, &blk) | 
					
						
							|  |  |  |         raise NotImplemented | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 23:08:17 +10:00
										 |  |  |       def get_delta(partial, context) | 
					
						
							|  |  |  |         raise NotImplemented | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 08:45:31 +10:00
										 |  |  |       def populate_functions(partial, functions) | 
					
						
							|  |  |  |         raise NotImplemented | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 23:08:17 +10:00
										 |  |  |       protected | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-16 14:38:21 -03:00
										 |  |  |       def get_updated_title(prompt) | 
					
						
							|  |  |  |         raise NotImplemented | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |       def model_for(bot) | 
					
						
							|  |  |  |         raise NotImplemented | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def conversation_context(post) | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |         context = | 
					
						
							|  |  |  |           post | 
					
						
							|  |  |  |             .topic | 
					
						
							|  |  |  |             .posts | 
					
						
							|  |  |  |             .includes(:user) | 
					
						
							|  |  |  |             .joins("LEFT JOIN post_custom_prompts ON post_custom_prompts.post_id = posts.id") | 
					
						
							|  |  |  |             .where("post_number <= ?", post.post_number) | 
					
						
							|  |  |  |             .order("post_number desc") | 
					
						
							|  |  |  |             .where("post_type = ?", Post.types[:regular]) | 
					
						
							|  |  |  |             .limit(50) | 
					
						
							|  |  |  |             .pluck(:raw, :username, "post_custom_prompts.custom_prompt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-22 12:09:14 +10:00
										 |  |  |         first = true | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |         context.each do |raw, username, custom_prompt| | 
					
						
							|  |  |  |           if custom_prompt.present? | 
					
						
							| 
									
										
										
										
											2023-05-22 12:09:14 +10:00
										 |  |  |             if first | 
					
						
							|  |  |  |               custom_prompt.reverse_each { |message| result << message } | 
					
						
							|  |  |  |               first = false | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |               result << custom_prompt.first | 
					
						
							|  |  |  |             end | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |           else | 
					
						
							|  |  |  |             result << [raw, username] | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def publish_update(bot_reply_post, payload) | 
					
						
							|  |  |  |         MessageBus.publish( | 
					
						
							|  |  |  |           "discourse-ai/ai-bot/topic/#{bot_reply_post.topic_id}", | 
					
						
							|  |  |  |           payload.merge(post_id: bot_reply_post.id, post_number: bot_reply_post.post_number), | 
					
						
							|  |  |  |           user_ids: bot_reply_post.topic.allowed_user_ids, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |