Roman Rizzi 58b96eda6c
REFACTOR: Build related topics using TopicQuery. (#124)
TopicQuery already provides a lot of safeguards and options for filtering topic, and enforcing permissions. It makes sense to rely on it as other plugins like discourse-assign do.

As a bonus, we now have access to the current_user while serializing these topics, so users will see things like unread posts count just like we do for the lists.
2023-08-02 16:58:09 -03:00

86 lines
3.0 KiB
Ruby

# frozen_string_literal: true
module DiscourseAi
module Embeddings
class EntryPoint
def load_files
require_relative "models/base"
require_relative "models/all_mpnet_base_v2"
require_relative "models/text_embedding_ada_002"
require_relative "models/multilingual_e5_large"
require_relative "strategies/truncation"
require_relative "manager"
require_relative "jobs/regular/generate_embeddings"
require_relative "semantic_related"
require_relative "semantic_search"
end
def inject_into(plugin)
# Include random topics in the suggested list *only* if there are no related topics.
plugin.register_modifier(
:topic_view_suggested_topics_options,
) do |suggested_options, topic_view|
related_topics = topic_view.related_topics
include_random = related_topics.nil? || related_topics.length == 0
suggested_options.merge(include_random: include_random)
end
# Query and serialize related topics.
plugin.add_to_class(:topic_view, :related_topics) do
if topic.private_message? || !SiteSetting.ai_embeddings_semantic_related_topics_enabled
return nil
end
@related_topics ||= TopicQuery.new(@user).list_semantic_related_topics(topic).topics
end
plugin.add_to_class(:topic_query, :list_semantic_related_topics) do |topic|
query_opts = {
skip_ordering: true,
per_page: SiteSetting.ai_embeddings_semantic_related_topics,
unordered: true,
}
if !SiteSetting.ai_embeddings_semantic_related_include_closed_topics
query_opts[:status] = "open"
end
create_list(:semantic_related, query_opts) do |topics|
candidate_ids = DiscourseAi::Embeddings::SemanticRelated.related_topic_ids_for(topic)
topics
.where.not(id: topic.id)
.where(id: candidate_ids)
.order("array_position(ARRAY#{candidate_ids}, topics.id)") # array_position forces the order of the topics to be preserved
end
end
%i[topic_view TopicViewPosts].each do |serializer|
plugin.add_to_serializer(
serializer,
:related_topics,
include_condition: -> { SiteSetting.ai_embeddings_semantic_related_topics_enabled },
) do
if object.next_page.nil? && !object.topic.private_message?
object.related_topics.map do |t|
SuggestedTopicSerializer.new(t, scope: scope, root: false)
end
end
end
end
# embeddings generation.
callback =
Proc.new do |topic|
if SiteSetting.ai_embeddings_enabled
Jobs.enqueue(:generate_embeddings, topic_id: topic.id)
end
end
plugin.on(:topic_created, &callback)
plugin.on(:topic_edited, &callback)
end
end
end
end