mirror of
				https://github.com/discourse/discourse-ai.git
				synced 2025-10-30 22:18:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			124 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			124 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| module DiscourseAi
 | |
|   module Summarization
 | |
|     module Models
 | |
|       class Llama2 < Base
 | |
|         def display_name
 | |
|           "Llama2's #{SiteSetting.ai_hugging_face_model_display_name.presence || model}"
 | |
|         end
 | |
| 
 | |
|         def correctly_configured?
 | |
|           SiteSetting.ai_hugging_face_api_url.present?
 | |
|         end
 | |
| 
 | |
|         def configuration_hint
 | |
|           I18n.t(
 | |
|             "discourse_ai.summarization.configuration_hint",
 | |
|             count: 1,
 | |
|             setting: "ai_hugging_face_api_url",
 | |
|           )
 | |
|         end
 | |
| 
 | |
|         def concatenate_summaries(summaries, &on_partial_blk)
 | |
|           prompt = <<~TEXT
 | |
|             [INST] <<SYS>>
 | |
|             You are a helpful bot
 | |
|             <</SYS>>
 | |
| 
 | |
|             Concatenate these disjoint summaries, creating a cohesive narrative:
 | |
|             #{summaries.join("\n")} [/INST]
 | |
|           TEXT
 | |
| 
 | |
|           completion(prompt, &on_partial_blk)
 | |
|         end
 | |
| 
 | |
|         def summarize_with_truncation(contents, opts, &on_partial_blk)
 | |
|           text_to_summarize = contents.map { |c| format_content_item(c) }.join
 | |
|           truncated_content = tokenizer.truncate(text_to_summarize, available_tokens)
 | |
| 
 | |
|           prompt = <<~TEXT
 | |
|             [INST] <<SYS>>
 | |
|             #{build_base_prompt(opts)}
 | |
|             <</SYS>>
 | |
| 
 | |
|             Summarize the following in up to 400 words:
 | |
|             #{truncated_content} [/INST]
 | |
|             Here is a summary of the above topic:
 | |
|           TEXT
 | |
| 
 | |
|           completion(prompt, &on_partial_blk)
 | |
|         end
 | |
| 
 | |
|         def summarize_single(chunk_text, opts, &on_partial_blk)
 | |
|           summarize_chunk(chunk_text, opts.merge(single_chunk: true), &on_partial_blk)
 | |
|         end
 | |
| 
 | |
|         private
 | |
| 
 | |
|         def summarize_chunk(chunk_text, opts, &on_partial_blk)
 | |
|           summary_instruction =
 | |
|             if opts[:single_chunk]
 | |
|               "Summarize the following forum discussion, creating a cohesive narrative:"
 | |
|             else
 | |
|               "Summarize the following in up to 400 words:"
 | |
|             end
 | |
| 
 | |
|           prompt = <<~TEXT
 | |
|             [INST] <<SYS>>
 | |
|             #{build_base_prompt(opts)}
 | |
|             <</SYS>>
 | |
| 
 | |
|             #{summary_instruction}
 | |
|             #{chunk_text} [/INST]
 | |
|             Here is a summary of the above topic:
 | |
|           TEXT
 | |
| 
 | |
|           completion(prompt, &on_partial_blk)
 | |
|         end
 | |
| 
 | |
|         def build_base_prompt(opts)
 | |
|           base_prompt = <<~TEXT
 | |
|             You are a summarization bot.
 | |
|             You effectively summarise any text and reply ONLY with ONLY the summarized text.
 | |
|             You condense it into a shorter version.
 | |
|             You understand and generate Discourse forum Markdown.
 | |
|           TEXT
 | |
| 
 | |
|           if opts[:resource_path]
 | |
|             base_prompt +=
 | |
|               "Try generating links as well the format is #{opts[:resource_path]}. eg: [ref](#{opts[:resource_path]}/77)\n"
 | |
|           end
 | |
| 
 | |
|           base_prompt += "The discussion title is: #{opts[:content_title]}.\n" if opts[
 | |
|             :content_title
 | |
|           ]
 | |
| 
 | |
|           base_prompt
 | |
|         end
 | |
| 
 | |
|         def completion(prompt, &on_partial_blk)
 | |
|           if on_partial_blk
 | |
|             on_partial_read =
 | |
|               Proc.new { |partial| on_partial_blk.call(partial.dig(:token, :text).to_s) }
 | |
| 
 | |
|             ::DiscourseAi::Inference::HuggingFaceTextGeneration.perform!(
 | |
|               prompt,
 | |
|               model,
 | |
|               &on_partial_read
 | |
|             )
 | |
|           else
 | |
|             ::DiscourseAi::Inference::HuggingFaceTextGeneration.perform!(prompt, model).dig(
 | |
|               :generated_text,
 | |
|             )
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         def tokenizer
 | |
|           DiscourseAi::Tokenizer::Llama2Tokenizer
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 |