|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +namespace :ai do |
| 4 | + desc "Generate topics with AI post content using random users and categories. Use it this way rake ai:generate_topics['title1\,title2\,title3']" |
| 5 | + task :generate_topics, [:titles] => [:environment] do |task, args| |
| 6 | + titles = args[:titles].include?(",") ? args[:titles].split(",").map(&:strip) : [args[:titles]] |
| 7 | + puts "Will create #{titles.size} #{"topics".pluralize(titles.size)}: #{titles.join(", ")}" |
| 8 | + |
| 9 | + titles.each do |title| |
| 10 | + next if Topic.find_by_title(TextCleaner.clean_title(TextSentinel.title_sentinel(title).text)) |
| 11 | + category = |
| 12 | + Category |
| 13 | + .where.not(id: SiteSetting.uncategorized_category_id) |
| 14 | + .where(read_restricted: false) |
| 15 | + .order("RANDOM()") |
| 16 | + .first |
| 17 | + users = User.real.activated.not_suspended.where(staged: false).order("RANDOM()").limit(12) |
| 18 | + RateLimiter.disable |
| 19 | + |
| 20 | + creator = TopicGenerator.new(title) |
| 21 | + first_post = creator.get_first_post |
| 22 | + replies_count = rand(4..10) |
| 23 | + replies = creator.get_replies(replies_count) |
| 24 | + |
| 25 | + post = create_topic(category, first_post, title, users) |
| 26 | + replies.each_with_index { |reply, i| create_post(users[i + 1], post.topic_id, reply) } |
| 27 | + puts "'#{title}' has #{replies.size} replies" |
| 28 | + end |
| 29 | + end |
| 30 | + |
| 31 | + def create_topic(category, first_post, title, users) |
| 32 | + puts "#{users.first.username} will create topic '#{title}' in category '#{category.name}'" |
| 33 | + post = |
| 34 | + PostCreator.create!( |
| 35 | + users.first, |
| 36 | + title: title, |
| 37 | + raw: first_post, |
| 38 | + category: category.id, |
| 39 | + skip_guardian: true, |
| 40 | + ) |
| 41 | + puts "Created topic '#{title}' (#{post.topic_id}) in category '#{category.name}'" |
| 42 | + post |
| 43 | + end |
| 44 | + |
| 45 | + def create_post(user, topic_id, raw) |
| 46 | + puts "#{user.username} will reply to topic #{topic_id}" |
| 47 | + PostCreator.create!(user, topic_id:, raw:, skip_guardian: true) |
| 48 | + end |
| 49 | + |
| 50 | + class TopicGenerator |
| 51 | + def initialize(title) |
| 52 | + @title = title |
| 53 | + end |
| 54 | + |
| 55 | + def get_first_post |
| 56 | + TopicGenerator.generate(<<~PROMPT) |
| 57 | + Write and opening topic about title: #{@title}. |
| 58 | + - content must be in the same language as title |
| 59 | + - content in markdown |
| 60 | + - content should exclude the title |
| 61 | + - maximum of 200 words |
| 62 | + PROMPT |
| 63 | + end |
| 64 | + |
| 65 | + def get_replies(count) |
| 66 | + JSON.parse( |
| 67 | + TopicGenerator.generate(<<~PROMPT).gsub(/```json\n?|\```/, "").gsub(/,\n\n/, ",\n").strip, |
| 68 | + Write #{count} replies to a topic with title #{@title}. |
| 69 | + - respond in an array of strings within double quotes ["", "", ""] |
| 70 | + - each with a maximum of 100 words |
| 71 | + - keep to same language of title |
| 72 | + - each reply may contain markdown to bold, italicize, link, or bullet point |
| 73 | + - do not return anything else other than the array |
| 74 | + - the last item in the array should not have a trailing comma |
| 75 | + - Example return value ["I agree with you. So and so...", "It is fun ... etc"] |
| 76 | + PROMPT |
| 77 | + ) |
| 78 | + end |
| 79 | + |
| 80 | + private |
| 81 | + |
| 82 | + def self.generate(prompt) |
| 83 | + return "" if prompt.blank? |
| 84 | + |
| 85 | + prompt = |
| 86 | + DiscourseAi::Completions::Prompt.new( |
| 87 | + "You are a forum user writing concise, informative posts. Keep responses focused and natural.", |
| 88 | + messages: [{ type: :user, content: prompt, id: "user" }], |
| 89 | + ) |
| 90 | + |
| 91 | + DiscourseAi::Completions::Llm.proxy(SiteSetting.ai_helper_model).generate( |
| 92 | + prompt, |
| 93 | + user: Discourse.system_user, |
| 94 | + feature_name: "topic-generator", |
| 95 | + ) |
| 96 | + rescue => e |
| 97 | + Rails.logger.error("AI TopicGenerator Error: #{e.message}") |
| 98 | + "" |
| 99 | + end |
| 100 | + end |
| 101 | +end |
0 commit comments