Skip to content

Commit

Permalink
DEV: A rake task to generate topics (#1171)
Browse files Browse the repository at this point in the history
Adds a new rake task to generate topics using the AI helper model.

Generate topics using 
```
bundle exec rake "ai:generate_topics[Don Quijote de la Mancha\, Cien años de soledad\, Os Lusíadas\,三国演义]"
```

For now it randomly chooses a non-read-restricted category and a random bunch of users.
  • Loading branch information
nattsw authored Mar 6, 2025
1 parent 01893bb commit 5b63e59
Showing 1 changed file with 101 additions and 0 deletions.
101 changes: 101 additions & 0 deletions lib/tasks/create_topics.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# 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

0 comments on commit 5b63e59

Please sign in to comment.