FEATURE: Fast-track gist regeneration when a hot topic gets a new post (#860)
* FEATURE: Fast-track gist regeneration when a hot topic gets a new post * DEV: Introduce an upsert-like summarize * FIX: Only enqueue fast-track gist for hot hot hot topics --------- Co-authored-by: Rafael Silva <xfalcox@gmail.com>
This commit is contained in:
parent
33da27e231
commit
a2b1ea3c63
|
@ -2,7 +2,7 @@
|
|||
|
||||
module ::Jobs
|
||||
class HotTopicsGistBatch < ::Jobs::Base
|
||||
def execute(args)
|
||||
def execute(_args)
|
||||
return if !SiteSetting.discourse_ai_enabled
|
||||
return if !SiteSetting.ai_summarization_enabled
|
||||
return if SiteSetting.ai_summarize_max_hot_topics_gists_per_batch.zero?
|
||||
|
@ -15,11 +15,7 @@ module ::Jobs
|
|||
summarizer = DiscourseAi::Summarization.topic_gist(topic)
|
||||
gist = summarizer.existing_summary
|
||||
|
||||
if gist.blank? || gist.outdated
|
||||
summarizer.delete_cached_summaries!
|
||||
|
||||
summarizer.summarize(Discourse.system_user)
|
||||
end
|
||||
summarizer.force_summarize(Discourse.system_user) if gist.blank? || gist.outdated
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ::Jobs
|
||||
class UpdateHotTopicGist < ::Jobs::Base
|
||||
sidekiq_options retry: false
|
||||
|
||||
def execute(args)
|
||||
return if !SiteSetting.discourse_ai_enabled
|
||||
return if !SiteSetting.ai_summarization_enabled
|
||||
return if SiteSetting.ai_summarize_max_hot_topics_gists_per_batch.zero?
|
||||
|
||||
topic = Topic.find_by(id: args[:topic_id])
|
||||
return if topic.blank?
|
||||
|
||||
return if !TopicHotScore.where(topic: topic).exists?
|
||||
|
||||
summarizer = DiscourseAi::Summarization.topic_gist(topic)
|
||||
gist = summarizer.existing_summary
|
||||
return if gist.blank?
|
||||
return if !gist.outdated
|
||||
|
||||
summarizer.force_summarize(Discourse.system_user)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -47,6 +47,19 @@ module DiscourseAi
|
|||
# To make sure hot topic gists are inmediately up to date, we rely on this event
|
||||
# instead of using a scheduled job.
|
||||
plugin.on(:topic_hot_scores_updated) { Jobs.enqueue(:hot_topics_gist_batch) }
|
||||
|
||||
# As this event can be triggered quite often, let's be overly cautious enqueueing
|
||||
# jobs if the feature is disabled.
|
||||
plugin.on(:post_created) do |post|
|
||||
if SiteSetting.discourse_ai_enabled && SiteSetting.ai_summarization_enabled &&
|
||||
SiteSetting.ai_summarize_max_hot_topics_gists_per_batch > 0 && post.topic
|
||||
hot_score = TopicHotScore.find_by(topic: post.topic)
|
||||
|
||||
if hot_score.exists? && hot_score.updated_at > 1.day.ago
|
||||
Jobs.enqueue(:update_hot_topic_gist, topic_id: post&.topic_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -66,6 +66,11 @@ module DiscourseAi
|
|||
AiSummary.where(target: strategy.target, summary_type: strategy.type).destroy_all
|
||||
end
|
||||
|
||||
def force_summarize(user, &on_partial_blk)
|
||||
delete_cached_summaries!
|
||||
summarize(user, &on_partial_blk)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :persist_summaries
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Jobs::UpdateHotTopicGist do
|
||||
describe "#execute" do
|
||||
fab!(:topic_1) { Fabricate(:topic) }
|
||||
fab!(:post_1) { Fabricate(:post, topic: topic_1, post_number: 1) }
|
||||
fab!(:post_2) { Fabricate(:post, topic: topic_1, post_number: 2) }
|
||||
|
||||
before do
|
||||
assign_fake_provider_to(:ai_summarization_model)
|
||||
SiteSetting.ai_summarization_enabled = true
|
||||
SiteSetting.ai_summarize_max_hot_topics_gists_per_batch = 100
|
||||
end
|
||||
|
||||
context "when the hot topic has a gist" do
|
||||
before { TopicHotScore.create!(topic_id: topic_1.id, score: 0.1) }
|
||||
fab!(:ai_gist) do
|
||||
Fabricate(:topic_ai_gist, target: topic_1, original_content_sha: AiSummary.build_sha("12"))
|
||||
end
|
||||
let(:updated_gist) { "They updated me :(" }
|
||||
|
||||
context "when it's up to date" do
|
||||
it "does nothing" do
|
||||
DiscourseAi::Completions::Llm.with_prepared_responses([updated_gist]) do
|
||||
subject.execute(topic_id: topic_1.id)
|
||||
end
|
||||
|
||||
gist = AiSummary.gist.find_by(target: topic_1)
|
||||
expect(AiSummary.gist.where(target: topic_1).count).to eq(1)
|
||||
expect(gist.summarized_text).not_to eq(updated_gist)
|
||||
end
|
||||
end
|
||||
|
||||
context "when it's outdated" do
|
||||
it "regenerates the gist using the latest data" do
|
||||
Fabricate(:post, topic: topic_1, post_number: 3)
|
||||
|
||||
DiscourseAi::Completions::Llm.with_prepared_responses([updated_gist]) do
|
||||
subject.execute(topic_id: topic_1.id)
|
||||
end
|
||||
|
||||
gist = AiSummary.gist.find_by(target: topic_1)
|
||||
expect(AiSummary.gist.where(target: topic_1).count).to eq(1)
|
||||
expect(gist.summarized_text).to eq(updated_gist)
|
||||
expect(gist.original_content_sha).to eq(AiSummary.build_sha("123"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the topic doesn't have a hot topic score" do
|
||||
it "does nothing" do
|
||||
subject.execute({})
|
||||
|
||||
gist = AiSummary.gist.find_by(target: topic_1)
|
||||
expect(gist).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when the topic has a hot topic score but no gist" do
|
||||
before { TopicHotScore.create!(topic_id: topic_1.id, score: 0.1) }
|
||||
|
||||
it "does nothing" do
|
||||
subject.execute({})
|
||||
|
||||
gist = AiSummary.gist.find_by(target: topic_1)
|
||||
expect(gist).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue