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
|