discourse-ai/lib/modules/ai_bot/jobs/regular/create_ai_reply.rb

77 lines
2.3 KiB
Ruby

# frozen_string_literal: true
module ::Jobs
class CreateAiReply < ::Jobs::Base
sidekiq_options retry: false
def execute(args)
return unless post = Post.includes(:topic).find_by(id: args[:post_id])
prompt = CompletionPrompt.bot_prompt_with_topic_context(post)
redis_stream_key = nil
reply = +""
bot_reply_post = nil
start = Time.now
DiscourseAi::Inference::OpenAiCompletions.perform!(
prompt,
temperature: 0.4,
top_p: 0.9,
max_tokens: 3000,
) do |partial, cancel|
content_delta = partial.dig(:choices, 0, :delta, :content)
reply << content_delta if content_delta
if redis_stream_key && !Discourse.redis.get(redis_stream_key)
cancel&.call
bot_reply_post.update!(raw: reply, cooked: PrettyText.cook(reply)) if bot_reply_post
end
next if reply.length < SiteSetting.min_personal_message_post_length
# Minor hack to skip the delay during tests.
next if (Time.now - start < 0.5) && !Rails.env.test?
if bot_reply_post
Discourse.redis.expire(redis_stream_key, 60)
start = Time.now
MessageBus.publish(
"discourse-ai/ai-bot/topic/#{post.topic_id}",
{ raw: reply.dup, post_id: bot_reply_post.id, post_number: bot_reply_post.post_number },
user_ids: post.topic.allowed_user_ids,
)
else
bot_reply_post =
PostCreator.create!(
Discourse.gpt_bot,
topic_id: post.topic_id,
raw: reply,
skip_validations: false,
)
redis_stream_key = "gpt_cancel:#{bot_reply_post.id}"
Discourse.redis.setex(redis_stream_key, 60, 1)
end
end
MessageBus.publish(
"discourse-ai/ai-bot/topic/#{post.topic_id}",
{ done: true, post_id: bot_reply_post.id, post_number: bot_reply_post.post_number },
user_ids: post.topic.allowed_user_ids,
)
if bot_reply_post
bot_reply_post.revise(
Discourse.gpt_bot,
{ raw: reply },
skip_validations: true,
skip_revision: true,
)
end
rescue => e
Discourse.warn_exception(e, message: "ai-bot: Reply failed")
end
end
end