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
|
module ::Jobs
|
||||||
class HotTopicsGistBatch < ::Jobs::Base
|
class HotTopicsGistBatch < ::Jobs::Base
|
||||||
def execute(args)
|
def execute(_args)
|
||||||
return if !SiteSetting.discourse_ai_enabled
|
return if !SiteSetting.discourse_ai_enabled
|
||||||
return if !SiteSetting.ai_summarization_enabled
|
return if !SiteSetting.ai_summarization_enabled
|
||||||
return if SiteSetting.ai_summarize_max_hot_topics_gists_per_batch.zero?
|
return if SiteSetting.ai_summarize_max_hot_topics_gists_per_batch.zero?
|
||||||
|
@ -15,11 +15,7 @@ module ::Jobs
|
||||||
summarizer = DiscourseAi::Summarization.topic_gist(topic)
|
summarizer = DiscourseAi::Summarization.topic_gist(topic)
|
||||||
gist = summarizer.existing_summary
|
gist = summarizer.existing_summary
|
||||||
|
|
||||||
if gist.blank? || gist.outdated
|
summarizer.force_summarize(Discourse.system_user) if gist.blank? || gist.outdated
|
||||||
summarizer.delete_cached_summaries!
|
|
||||||
|
|
||||||
summarizer.summarize(Discourse.system_user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
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
|
# To make sure hot topic gists are inmediately up to date, we rely on this event
|
||||||
# instead of using a scheduled job.
|
# instead of using a scheduled job.
|
||||||
plugin.on(:topic_hot_scores_updated) { Jobs.enqueue(:hot_topics_gist_batch) }
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -66,6 +66,11 @@ module DiscourseAi
|
||||||
AiSummary.where(target: strategy.target, summary_type: strategy.type).destroy_all
|
AiSummary.where(target: strategy.target, summary_type: strategy.type).destroy_all
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def force_summarize(user, &on_partial_blk)
|
||||||
|
delete_cached_summaries!
|
||||||
|
summarize(user, &on_partial_blk)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
attr_reader :persist_summaries
|
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