102 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			102 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| namespace :ai do
 | |
|   desc "Generate topics with AI post content using random users and categories. Use it this way rake ai:generate_topics['title1\,title2\,title3']"
 | |
|   task :generate_topics, [:titles] => [:environment] do |task, args|
 | |
|     titles = args[:titles].include?(",") ? args[:titles].split(",").map(&:strip) : [args[:titles]]
 | |
|     puts "Will create #{titles.size} #{"topics".pluralize(titles.size)}: #{titles.join(", ")}"
 | |
| 
 | |
|     titles.each do |title|
 | |
|       next if Topic.find_by_title(TextCleaner.clean_title(TextSentinel.title_sentinel(title).text))
 | |
|       category =
 | |
|         Category
 | |
|           .where.not(id: SiteSetting.uncategorized_category_id)
 | |
|           .where(read_restricted: false)
 | |
|           .order("RANDOM()")
 | |
|           .first
 | |
|       users = User.real.activated.not_suspended.where(staged: false).order("RANDOM()").limit(12)
 | |
|       RateLimiter.disable
 | |
| 
 | |
|       creator = TopicGenerator.new(title)
 | |
|       first_post = creator.get_first_post
 | |
|       replies_count = rand(4..10)
 | |
|       replies = creator.get_replies(replies_count)
 | |
| 
 | |
|       post = create_topic(category, first_post, title, users)
 | |
|       replies.each_with_index { |reply, i| create_post(users[i + 1], post.topic_id, reply) }
 | |
|       puts "'#{title}' has #{replies.size} replies"
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def create_topic(category, first_post, title, users)
 | |
|     puts "#{users.first.username} will create topic '#{title}' in category '#{category.name}'"
 | |
|     post =
 | |
|       PostCreator.create!(
 | |
|         users.first,
 | |
|         title: title,
 | |
|         raw: first_post,
 | |
|         category: category.id,
 | |
|         skip_guardian: true,
 | |
|       )
 | |
|     puts "Created topic '#{title}' (#{post.topic_id}) in category '#{category.name}'"
 | |
|     post
 | |
|   end
 | |
| 
 | |
|   def create_post(user, topic_id, raw)
 | |
|     puts "#{user.username} will reply to topic #{topic_id}"
 | |
|     PostCreator.create!(user, topic_id:, raw:, skip_guardian: true)
 | |
|   end
 | |
| 
 | |
|   class TopicGenerator
 | |
|     def initialize(title)
 | |
|       @title = title
 | |
|     end
 | |
| 
 | |
|     def get_first_post
 | |
|       TopicGenerator.generate(<<~PROMPT)
 | |
|         Write and opening topic about title: #{@title}.
 | |
|         - content must be in the same language as title
 | |
|         - content in markdown
 | |
|         - content should exclude the title
 | |
|         - maximum of 200 words
 | |
|       PROMPT
 | |
|     end
 | |
| 
 | |
|     def get_replies(count)
 | |
|       JSON.parse(
 | |
|         TopicGenerator.generate(<<~PROMPT).gsub(/```json\n?|\```/, "").gsub(/,\n\n/, ",\n").strip,
 | |
|                 Write #{count} replies to a topic with title #{@title}.
 | |
|                 - respond in an array of strings within double quotes ["", "", ""]
 | |
|                 - each with a maximum of 100 words
 | |
|                 - keep to same language of title
 | |
|                 - each reply may contain markdown to bold, italicize, link, or bullet point
 | |
|                 - do not return anything else other than the array
 | |
|                 - the last item in the array should not have a trailing comma
 | |
|                 - Example return value ["I agree with you. So and so...", "It is fun ... etc"]
 | |
|               PROMPT
 | |
|       )
 | |
|     end
 | |
| 
 | |
|     private
 | |
| 
 | |
|     def self.generate(prompt)
 | |
|       return "" if prompt.blank?
 | |
| 
 | |
|       prompt =
 | |
|         DiscourseAi::Completions::Prompt.new(
 | |
|           "You are a forum user writing concise, informative posts. Keep responses focused and natural.",
 | |
|           messages: [{ type: :user, content: prompt, id: "user" }],
 | |
|         )
 | |
| 
 | |
|       DiscourseAi::Completions::Llm.proxy(SiteSetting.ai_helper_model).generate(
 | |
|         prompt,
 | |
|         user: Discourse.system_user,
 | |
|         feature_name: "topic-generator",
 | |
|       )
 | |
|     rescue => e
 | |
|       Rails.logger.error("AI TopicGenerator Error: #{e.message}")
 | |
|       ""
 | |
|     end
 | |
|   end
 | |
| end
 |