FEATURE: attempt to include related topics above suggested (#28)
Allows related topics to show up for logged on users - Introduces a new "Related Topics" block above suggested when related topics exist - Renames `ai_embeddings_semantic_suggested_topics_anons_enabled` -> `ai_embeddings_semantic_suggested_topics_enabled` (given it is only deployed on 1 site not bothering with a migration) - Adds an integration test to ensure data arrives correctly on the client
This commit is contained in:
parent
b942a18298
commit
1d097b9d82
|
@ -0,0 +1,15 @@
|
|||
export default {
|
||||
shouldRender(args) {
|
||||
return (args.model.related_topics?.length || 0) > 0;
|
||||
},
|
||||
setupComponent(args, component) {
|
||||
if (component.model.related_topics) {
|
||||
component.set(
|
||||
"relatedTopics",
|
||||
component.model.related_topics.map((topic) =>
|
||||
this.store.createRecord("topic", topic)
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
<div>
|
||||
<h3 id="related-topics-title" class="related-topics-title">
|
||||
{{i18n "discourse_ai.related_topics.title"}}
|
||||
</h3>
|
||||
<BasicTopicList @topics={{this.relatedTopics}} />
|
||||
</div>
|
|
@ -25,3 +25,7 @@
|
|||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.topic-above-suggested-outlet.related-topics {
|
||||
margin: 4.5em 0 1em;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
en:
|
||||
js:
|
||||
discourse_ai:
|
||||
related_topics:
|
||||
title: "Related Topics"
|
||||
ai_helper:
|
||||
title: "Suggest changes using AI"
|
||||
description: "Choose one of the options below, and the AI will suggest you a new version of the text."
|
||||
|
|
|
@ -44,7 +44,7 @@ en:
|
|||
ai_embeddings_models: "Discourse will generate embeddings for each of the models enabled here"
|
||||
ai_embeddings_semantic_suggested_model: "Model to use for suggested topics."
|
||||
ai_embeddings_generate_for_pms: "Generate embeddings for personal messages."
|
||||
ai_embeddings_semantic_suggested_topics_anons_enabled: "Use Semantic Search for suggested topics for anonymous users."
|
||||
ai_embeddings_semantic_suggested_topics_enabled: "Use Semantic Search for related topics."
|
||||
ai_embeddings_pg_connection_string: "PostgreSQL connection string for the embeddings module. Needs pgvector extension enabled and a series of tables created. See docs for more info."
|
||||
|
||||
reviewables:
|
||||
|
|
|
@ -134,5 +134,5 @@ plugins:
|
|||
- multi-qa-mpnet-base-dot-v1
|
||||
- paraphrase-multilingual-mpnet-base-v2
|
||||
ai_embeddings_generate_for_pms: false
|
||||
ai_embeddings_semantic_suggested_topics_anons_enabled: false
|
||||
ai_embeddings_semantic_suggested_topics_enabled: false
|
||||
ai_embeddings_pg_connection_string: ""
|
||||
|
|
|
@ -11,6 +11,43 @@ module DiscourseAi
|
|||
end
|
||||
|
||||
def inject_into(plugin)
|
||||
plugin.add_to_class(:topic_view, :related_topics) do
|
||||
if !@guardian&.user || topic.private_message? ||
|
||||
!SiteSetting.ai_embeddings_semantic_suggested_topics_enabled
|
||||
return nil
|
||||
end
|
||||
|
||||
@related_topics ||=
|
||||
TopicList.new(
|
||||
:suggested,
|
||||
nil,
|
||||
DiscourseAi::Embeddings::SemanticSuggested.candidates_for(topic),
|
||||
).topics
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
%i[topic_view TopicViewPosts].each do |serializer|
|
||||
plugin.add_to_serializer(serializer, :related_topics) do
|
||||
if object.next_page.nil? && !object.topic.private_message? && scope.authenticated?
|
||||
object.related_topics.map do |t|
|
||||
SuggestedTopicSerializer.new(t, scope: scope, root: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# custom include method so we also check on semantic search
|
||||
plugin.add_to_serializer(serializer, :include_related_topics?) do
|
||||
plugin.enabled? && SiteSetting.ai_embeddings_semantic_suggested_topics_enabled
|
||||
end
|
||||
end
|
||||
|
||||
callback =
|
||||
Proc.new do |topic|
|
||||
if SiteSetting.ai_embeddings_enabled
|
||||
|
|
|
@ -4,10 +4,14 @@ module DiscourseAi
|
|||
module Embeddings
|
||||
class SemanticSuggested
|
||||
def self.build_suggested_topics(topic, pm_params, topic_query)
|
||||
return unless SiteSetting.ai_embeddings_semantic_suggested_topics_anons_enabled
|
||||
return unless SiteSetting.ai_embeddings_semantic_suggested_topics_enabled
|
||||
return if topic_query.user
|
||||
return if topic.private_message?
|
||||
|
||||
{ result: candidates_for(topic), params: {} }
|
||||
end
|
||||
|
||||
def self.candidates_for(topic)
|
||||
cache_for =
|
||||
case topic.created_at
|
||||
when 6.hour.ago..Time.now
|
||||
|
@ -28,19 +32,16 @@ module DiscourseAi
|
|||
rescue StandardError => e
|
||||
Rails.logger.error("SemanticSuggested: #{e}")
|
||||
Jobs.enqueue(:generate_embeddings, topic_id: topic.id)
|
||||
return { result: [], params: {} }
|
||||
return ::Topic.none
|
||||
end
|
||||
|
||||
# array_position forces the order of the topics to be preserved
|
||||
candidates =
|
||||
::Topic
|
||||
.visible
|
||||
.listable_topics
|
||||
.secured
|
||||
.where(id: candidate_ids)
|
||||
.order("array_position(ARRAY#{candidate_ids}, id)")
|
||||
|
||||
{ result: candidates, params: {} }
|
||||
::Topic
|
||||
.visible
|
||||
.listable_topics
|
||||
.secured
|
||||
.where(id: candidate_ids)
|
||||
.order("array_position(ARRAY#{candidate_ids}, id)")
|
||||
end
|
||||
|
||||
def self.search_suggestions(topic)
|
||||
|
|
|
@ -12,7 +12,7 @@ describe DiscourseAi::Embeddings::SemanticSuggested do
|
|||
fab!(:secured_category) { Fabricate(:category, read_restricted: true) }
|
||||
fab!(:secured_category_topic) { Fabricate(:topic, category: secured_category) }
|
||||
|
||||
before { SiteSetting.ai_embeddings_semantic_suggested_topics_anons_enabled = true }
|
||||
before { SiteSetting.ai_embeddings_semantic_suggested_topics_enabled = true }
|
||||
|
||||
describe "#build_suggested_topics" do
|
||||
before do
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
describe ::TopicsController do
|
||||
fab!(:topic) { Fabricate(:topic) }
|
||||
fab!(:topic1) { Fabricate(:topic) }
|
||||
fab!(:topic2) { Fabricate(:topic) }
|
||||
fab!(:user) { Fabricate(:admin) }
|
||||
|
||||
before do
|
||||
Discourse.cache.clear
|
||||
SiteSetting.ai_embeddings_semantic_suggested_topics_enabled = true
|
||||
end
|
||||
|
||||
after { Discourse.cache.clear }
|
||||
|
||||
context "when a user is logged on" do
|
||||
it "includes related topics in payload when configured" do
|
||||
DiscourseAi::Embeddings::SemanticSuggested.stubs(:search_suggestions).returns([topic2.id])
|
||||
sign_in(user)
|
||||
|
||||
get("#{topic.relative_url}.json")
|
||||
json = response.parsed_body
|
||||
|
||||
expect(json["suggested_topics"].length).to eq(0)
|
||||
expect(json["related_topics"].length).to be > 0
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue