mirror of
https://github.com/discourse/discourse-ai.git
synced 2025-05-10 10:28:00 +00:00
FEATURE: correctly decorate AI bots (#1300)
AI bots come in 2 flavors 1. An LLM and LLM user, in this case we should decorate posts with persona name 2. A Persona user, in this case, in PMs we decorate with LLM name (2) is a significant improvement, cause previously when creating a conversation you could not tell which LLM you were talking to by simply looking at the post, you would have to scroll to the top of the page. * lint * translation missing
This commit is contained in:
parent
8b90ce7c86
commit
7dc3c30fa4
@ -7,21 +7,40 @@ import ShareFullTopicModal from "../components/modal/share-full-topic-modal";
|
||||
|
||||
const MAX_PERSONA_USER_ID = -1200;
|
||||
|
||||
let enabledChatBotIds;
|
||||
let enabledChatBotMap = null;
|
||||
|
||||
function ensureBotMap() {
|
||||
if (!enabledChatBotMap) {
|
||||
const currentUser = getOwnerWithFallback(this).lookup(
|
||||
"service:current-user"
|
||||
);
|
||||
enabledChatBotMap = {};
|
||||
currentUser.ai_enabled_chat_bots.forEach((bot) => {
|
||||
enabledChatBotMap[bot.id] = bot;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function isGPTBot(user) {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!enabledChatBotIds) {
|
||||
const currentUser = getOwnerWithFallback(this).lookup(
|
||||
"service:current-user"
|
||||
);
|
||||
enabledChatBotIds = currentUser.ai_enabled_chat_bots.map((bot) => bot.id);
|
||||
ensureBotMap();
|
||||
return !!enabledChatBotMap[user.id];
|
||||
}
|
||||
|
||||
export function getBotType(user) {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
return enabledChatBotIds.includes(user.id);
|
||||
ensureBotMap();
|
||||
const bot = enabledChatBotMap[user.id];
|
||||
if (!bot) {
|
||||
return;
|
||||
}
|
||||
return bot.is_persona ? "persona" : "llm";
|
||||
}
|
||||
|
||||
export function isPostFromAiBot(post, currentUser) {
|
||||
|
@ -8,6 +8,7 @@ import AiCancelStreamingButton from "../discourse/components/post-menu/ai-cancel
|
||||
import AiDebugButton from "../discourse/components/post-menu/ai-debug-button";
|
||||
import AiShareButton from "../discourse/components/post-menu/ai-share-button";
|
||||
import {
|
||||
getBotType,
|
||||
isGPTBot,
|
||||
showShareConversationModal,
|
||||
} from "../discourse/lib/ai-bot-helper";
|
||||
@ -62,13 +63,19 @@ function initializePersonaDecorator(api) {
|
||||
|
||||
function initializeWidgetPersonaDecorator(api) {
|
||||
api.decorateWidget(`poster-name:after`, (dec) => {
|
||||
if (!isGPTBot(dec.attrs.user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const botType = getBotType(dec.attrs.user);
|
||||
// we have 2 ways of decorating
|
||||
// 1. if a bot is a LLM we decorate with persona name
|
||||
// 2. if bot is a persona we decorate with LLM name
|
||||
if (botType === "llm") {
|
||||
return dec.widget.attach("persona-flair", {
|
||||
personaName: dec.model?.topic?.ai_persona_name,
|
||||
});
|
||||
} else if (botType === "persona") {
|
||||
return dec.widget.attach("persona-flair", {
|
||||
personaName: dec.model?.llm_name,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
registerWidgetShim(
|
||||
|
@ -78,7 +78,6 @@ article.streaming nav.post-controls .actions button.cancel-streaming {
|
||||
.topic-body .persona-flair {
|
||||
order: 2;
|
||||
font-size: var(--font-down-1);
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
details.ai-quote {
|
||||
|
@ -113,6 +113,7 @@ en:
|
||||
ai_discord_search_mode: "Select the search mode to use for Discord search"
|
||||
ai_discord_search_persona: "The persona to use for Discord search."
|
||||
ai_discord_allowed_guilds: "Discord guilds (servers) where the bot is allowed to search"
|
||||
ai_enable_experimental_bot_ux: "Enable experimental bot UI that allows for a more dedicated experience"
|
||||
|
||||
reviewables:
|
||||
reasons:
|
||||
|
@ -4,6 +4,7 @@ module DiscourseAi
|
||||
module AiBot
|
||||
USER_AGENT = "Discourse AI Bot 1.0 (https://www.discourse.org)"
|
||||
TOPIC_AI_BOT_PM_FIELD = "is_ai_bot_pm"
|
||||
POST_AI_LLM_NAME_FIELD = "ai_llm_name"
|
||||
|
||||
class EntryPoint
|
||||
Bot = Struct.new(:id, :name, :llm)
|
||||
@ -65,6 +66,10 @@ module DiscourseAi
|
||||
end
|
||||
|
||||
def inject_into(plugin)
|
||||
# Long term we need a better API here
|
||||
# we only want to load this custom field for bots
|
||||
TopicView.default_post_custom_fields << POST_AI_LLM_NAME_FIELD
|
||||
|
||||
plugin.register_topic_custom_field_type(TOPIC_AI_BOT_PM_FIELD, :string)
|
||||
|
||||
plugin.on(:topic_created) do |topic|
|
||||
@ -139,6 +144,14 @@ module DiscourseAi
|
||||
end,
|
||||
) { true }
|
||||
|
||||
plugin.add_to_serializer(
|
||||
:post,
|
||||
:llm_name,
|
||||
include_condition: -> do
|
||||
object.topic.private_message? && object.custom_fields[POST_AI_LLM_NAME_FIELD]
|
||||
end,
|
||||
) { object.custom_fields[POST_AI_LLM_NAME_FIELD] }
|
||||
|
||||
plugin.add_to_serializer(
|
||||
:current_user,
|
||||
:ai_enabled_personas,
|
||||
|
@ -458,6 +458,9 @@ module DiscourseAi
|
||||
skip_jobs: true,
|
||||
post_type: post_type,
|
||||
skip_guardian: true,
|
||||
custom_fields: {
|
||||
DiscourseAi::AiBot::POST_AI_LLM_NAME_FIELD => bot.llm.llm_model.name,
|
||||
},
|
||||
)
|
||||
|
||||
publish_update(reply_post, { raw: reply_post.cooked })
|
||||
|
@ -783,6 +783,10 @@ RSpec.describe DiscourseAi::AiBot::Playground do
|
||||
last_post = post.topic.posts.order(:post_number).last
|
||||
expect(last_post.raw).to eq("Yes I can")
|
||||
expect(last_post.user_id).to eq(persona.user_id)
|
||||
|
||||
expect(last_post.custom_fields[DiscourseAi::AiBot::POST_AI_LLM_NAME_FIELD]).to eq(
|
||||
gpt_35_turbo.name,
|
||||
)
|
||||
end
|
||||
|
||||
it "picks the correct llm for persona in PMs" do
|
||||
|
37
spec/requests/ai_bot/topic_serialization_spec.rb
Normal file
37
spec/requests/ai_bot/topic_serialization_spec.rb
Normal file
@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "AI Bot Post Serializer" do
|
||||
fab!(:current_user) { Fabricate(:user) }
|
||||
fab!(:bot_user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
SiteSetting.ai_bot_enabled = true
|
||||
sign_in(current_user)
|
||||
end
|
||||
|
||||
describe "llm_name in post serializer" do
|
||||
it "includes llm_name when custom field is set in a PM" do
|
||||
pm_topic = Fabricate(:private_message_topic, user: current_user)
|
||||
|
||||
# Create a bot post with the custom field set
|
||||
bot_post =
|
||||
Fabricate(
|
||||
:post,
|
||||
topic: pm_topic,
|
||||
user: bot_user,
|
||||
custom_fields: {
|
||||
DiscourseAi::AiBot::POST_AI_LLM_NAME_FIELD => "bob",
|
||||
},
|
||||
)
|
||||
|
||||
get "/t/#{pm_topic.id}.json"
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
json = response.parsed_body
|
||||
bot_post_data = json["post_stream"]["posts"].find { |p| p["id"] == bot_post.id }
|
||||
|
||||
expect(bot_post_data).to have_key("llm_name")
|
||||
expect(bot_post_data["llm_name"]).to eq("bob")
|
||||
end
|
||||
end
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user