| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module DiscourseAi | 
					
						
							|  |  |  |   module AiHelper | 
					
						
							|  |  |  |     class LlmPrompt | 
					
						
							| 
									
										
										
										
											2023-09-01 21:10:58 -03:00
										 |  |  |       def available_prompts(name_filter: nil) | 
					
						
							|  |  |  |         cp = CompletionPrompt | 
					
						
							|  |  |  |         cp = cp.where(name: name_filter) if name_filter.present? | 
					
						
							|  |  |  |         cp | 
					
						
							| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  |           .where(provider: enabled_provider) | 
					
						
							|  |  |  |           .where(enabled: true) | 
					
						
							|  |  |  |           .map do |prompt| | 
					
						
							|  |  |  |             translation = | 
					
						
							|  |  |  |               I18n.t("discourse_ai.ai_helper.prompts.#{prompt.name}", default: nil) || | 
					
						
							|  |  |  |                 prompt.translated_name || prompt.name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               id: prompt.id, | 
					
						
							|  |  |  |               name: prompt.name, | 
					
						
							|  |  |  |               translated_name: translation, | 
					
						
							|  |  |  |               prompt_type: prompt.prompt_type, | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def generate_and_send_prompt(prompt, text) | 
					
						
							| 
									
										
										
										
											2023-08-25 15:54:51 -03:00
										 |  |  |         case enabled_provider | 
					
						
							|  |  |  |         when "openai" | 
					
						
							| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  |           openai_call(prompt, text) | 
					
						
							| 
									
										
										
										
											2023-08-25 15:54:51 -03:00
										 |  |  |         when "anthropic" | 
					
						
							| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  |           anthropic_call(prompt, text) | 
					
						
							| 
									
										
										
										
											2023-08-25 15:54:51 -03:00
										 |  |  |         when "huggingface" | 
					
						
							|  |  |  |           huggingface_call(prompt, text) | 
					
						
							| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def enabled_provider | 
					
						
							| 
									
										
										
										
											2023-08-25 15:54:51 -03:00
										 |  |  |         case SiteSetting.ai_helper_model | 
					
						
							|  |  |  |         when /gpt/ | 
					
						
							| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  |           "openai" | 
					
						
							| 
									
										
										
										
											2023-08-25 15:54:51 -03:00
										 |  |  |         when /claude/ | 
					
						
							| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  |           "anthropic" | 
					
						
							| 
									
										
										
										
											2023-08-25 15:54:51 -03:00
										 |  |  |         else | 
					
						
							|  |  |  |           "huggingface" | 
					
						
							| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       private | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def generate_diff(text, suggestion) | 
					
						
							|  |  |  |         cooked_text = PrettyText.cook(text) | 
					
						
							|  |  |  |         cooked_suggestion = PrettyText.cook(suggestion) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         DiscourseDiff.new(cooked_text, cooked_suggestion).inline_html | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def parse_content(prompt, content) | 
					
						
							|  |  |  |         return "" if content.blank? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-25 15:54:51 -03:00
										 |  |  |         case enabled_provider | 
					
						
							|  |  |  |         when "openai" | 
					
						
							| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  |           return content.strip if !prompt.list? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           content.gsub("\"", "").gsub(/\d./, "").split("\n").map(&:strip) | 
					
						
							| 
									
										
										
										
											2023-08-25 15:54:51 -03:00
										 |  |  |         when "anthropic" | 
					
						
							| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  |           parse_antropic_content(prompt, content) | 
					
						
							| 
									
										
										
										
											2023-08-25 15:54:51 -03:00
										 |  |  |         when "huggingface" | 
					
						
							|  |  |  |           return [content.strip.delete_prefix('"').delete_suffix('"')] if !prompt.list? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           content.gsub("\"", "").gsub(/\d./, "").split("\n").map(&:strip) | 
					
						
							| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def openai_call(prompt, text) | 
					
						
							|  |  |  |         result = { type: prompt.prompt_type } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         messages = prompt.messages_with_user_input(text) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result[:suggestions] = DiscourseAi::Inference::OpenAiCompletions | 
					
						
							| 
									
										
										
										
											2023-05-11 10:03:03 -03:00
										 |  |  |           .perform!(messages, SiteSetting.ai_helper_model) | 
					
						
							| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  |           .dig(:choices) | 
					
						
							|  |  |  |           .to_a | 
					
						
							|  |  |  |           .flat_map { |choice| parse_content(prompt, choice.dig(:message, :content).to_s) } | 
					
						
							|  |  |  |           .compact_blank | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result[:diff] = generate_diff(text, result[:suggestions].first) if prompt.diff? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def anthropic_call(prompt, text) | 
					
						
							|  |  |  |         result = { type: prompt.prompt_type } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         filled_message = prompt.messages_with_user_input(text) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         message = | 
					
						
							|  |  |  |           filled_message.map { |msg| "#{msg["role"]}: #{msg["content"]}" }.join("\n\n") + | 
					
						
							|  |  |  |             "Assistant:" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         response = DiscourseAi::Inference::AnthropicCompletions.perform!(message) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result[:suggestions] = parse_content(prompt, response.dig(:completion)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result[:diff] = generate_diff(text, result[:suggestions].first) if prompt.diff? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-25 15:54:51 -03:00
										 |  |  |       def huggingface_call(prompt, text) | 
					
						
							|  |  |  |         result = { type: prompt.prompt_type } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         message = prompt.messages_with_user_input(text) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         response = | 
					
						
							|  |  |  |           DiscourseAi::Inference::HuggingFaceTextGeneration.perform!( | 
					
						
							|  |  |  |             message, | 
					
						
							|  |  |  |             SiteSetting.ai_helper_model, | 
					
						
							|  |  |  |           ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result[:suggestions] = parse_content(prompt, response.dig(:generated_text)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result[:diff] = generate_diff(text, result[:suggestions].first) if prompt.diff? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         result | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  |       def parse_antropic_content(prompt, content) | 
					
						
							|  |  |  |         if prompt.list? | 
					
						
							|  |  |  |           suggestions = Nokogiri::HTML5.fragment(content).search("ai").map(&:text) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if suggestions.length > 1
 | 
					
						
							|  |  |  |             suggestions | 
					
						
							|  |  |  |           else | 
					
						
							| 
									
										
										
										
											2023-04-10 16:02:44 -03:00
										 |  |  |             suggestions.first.split("\n").map(&:strip) | 
					
						
							| 
									
										
										
										
											2023-04-10 11:04:42 -03:00
										 |  |  |           end | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           [Nokogiri::HTML5.fragment(content).at("ai").text] | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |