| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  | #frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module DiscourseAi | 
					
						
							|  |  |  |   module AiBot | 
					
						
							|  |  |  |     module Commands | 
					
						
							|  |  |  |       class Command | 
					
						
							|  |  |  |         class << self | 
					
						
							|  |  |  |           def name | 
					
						
							|  |  |  |             raise NotImplemented | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           def invoked?(cmd_name) | 
					
						
							|  |  |  |             cmd_name == name | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           def desc | 
					
						
							|  |  |  |             raise NotImplemented | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           def extra_context | 
					
						
							|  |  |  |             "" | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-22 12:09:14 +10:00
										 |  |  |         attr_reader :bot_user, :args | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |         def initialize(bot_user, args) | 
					
						
							|  |  |  |           @bot_user = bot_user | 
					
						
							|  |  |  |           @args = args | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 23:08:17 +10:00
										 |  |  |         def bot | 
					
						
							|  |  |  |           @bot ||= DiscourseAi::AiBot::Bot.as(bot_user) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |         def standalone? | 
					
						
							|  |  |  |           false | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def low_cost? | 
					
						
							|  |  |  |           false | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def result_name | 
					
						
							|  |  |  |           raise NotImplemented | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def name | 
					
						
							|  |  |  |           raise NotImplemented | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def process(post) | 
					
						
							|  |  |  |           raise NotImplemented | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def description_args | 
					
						
							|  |  |  |           {} | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def custom_raw | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def chain_next_response | 
					
						
							|  |  |  |           true | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def invoke_and_attach_result_to(post) | 
					
						
							|  |  |  |           post.post_custom_prompt ||= post.build_post_custom_prompt(custom_prompt: []) | 
					
						
							|  |  |  |           prompt = post.post_custom_prompt.custom_prompt || [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           prompt << ["!#{self.class.name} #{args}", bot_user.username] | 
					
						
							|  |  |  |           prompt << [process(args), result_name] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           post.post_custom_prompt.update!(custom_prompt: prompt) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           raw = +<<~HTML | 
					
						
							|  |  |  |           <details> | 
					
						
							|  |  |  |             <summary>#{I18n.t("discourse_ai.ai_bot.command_summary.#{self.class.name}")}</summary> | 
					
						
							|  |  |  |             <p> | 
					
						
							|  |  |  |               #{I18n.t("discourse_ai.ai_bot.command_description.#{self.class.name}", self.description_args)} | 
					
						
							|  |  |  |             </p>
 | 
					
						
							|  |  |  |           </details>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           HTML | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           raw << custom_raw if custom_raw.present? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 23:08:17 +10:00
										 |  |  |           replacement = "!#{self.class.name} #{args}" | 
					
						
							|  |  |  |           raw = post.raw.sub(replacement, raw) if post.raw.include?(replacement) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |           if chain_next_response | 
					
						
							|  |  |  |             post.raw = raw | 
					
						
							|  |  |  |             post.save!(validate: false) | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             post.revise(bot_user, { raw: raw }, skip_validations: true, skip_revision: true) | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           chain_next_response | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-22 12:09:14 +10:00
										 |  |  |         def format_results(rows, column_names = nil) | 
					
						
							|  |  |  |           rows = rows.map { |row| yield row } if block_given? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if !column_names | 
					
						
							|  |  |  |             index = -1
 | 
					
						
							|  |  |  |             column_indexes = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             rows = | 
					
						
							|  |  |  |               rows.map do |data| | 
					
						
							|  |  |  |                 new_row = [] | 
					
						
							|  |  |  |                 data.each do |key, value| | 
					
						
							|  |  |  |                   found_index = column_indexes[key.to_s] ||= (index += 1) | 
					
						
							|  |  |  |                   new_row[found_index] = value | 
					
						
							|  |  |  |                 end | 
					
						
							|  |  |  |                 new_row | 
					
						
							|  |  |  |               end | 
					
						
							|  |  |  |             column_names = column_indexes.keys | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |           # two tokens per delimiter is a reasonable balance | 
					
						
							|  |  |  |           # there may be a single delimiter solution but GPT has | 
					
						
							|  |  |  |           # a hard time dealing with escaped characters | 
					
						
							|  |  |  |           delimiter = "¦" | 
					
						
							|  |  |  |           formatted = +"" | 
					
						
							|  |  |  |           formatted << column_names.join(delimiter) | 
					
						
							|  |  |  |           formatted << "\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           rows.each do |array| | 
					
						
							|  |  |  |             array.map! { |item| item.to_s.gsub(delimiter, "|").gsub(/\n/, " ") } | 
					
						
							|  |  |  |             formatted << array.join(delimiter) | 
					
						
							|  |  |  |             formatted << "\n" | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           formatted | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-20 17:45:54 +10:00
										 |  |  |         protected | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         attr_reader :bot_user, :args | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |