2023-11-03 07:30:09 -04:00
|
|
|
import { hbs } from "ember-cli-htmlbars";
|
|
|
|
import { withPluginApi } from "discourse/lib/plugin-api";
|
2023-08-30 02:15:03 -04:00
|
|
|
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
|
2024-11-12 13:46:17 -05:00
|
|
|
import { withSilencedDeprecations } from "discourse-common/lib/deprecated";
|
2024-03-08 05:07:48 -05:00
|
|
|
import AiBotHeaderIcon from "../discourse/components/ai-bot-header-icon";
|
2024-11-12 13:46:17 -05:00
|
|
|
import AiCancelStreamingButton from "../discourse/components/post-menu/ai-cancel-streaming-button";
|
|
|
|
import AiDebugButton from "../discourse/components/post-menu/ai-debug-button";
|
|
|
|
import AiShareButton from "../discourse/components/post-menu/ai-share-button";
|
|
|
|
import {
|
|
|
|
isPostFromAiBot,
|
|
|
|
showShareConversationModal,
|
|
|
|
} from "../discourse/lib/ai-bot-helper";
|
|
|
|
import { streamPostText } from "../discourse/lib/ai-streamer/progress-handlers";
|
2023-05-05 14:28:31 -04:00
|
|
|
|
2024-02-29 15:53:42 -05:00
|
|
|
let enabledChatBotIds = [];
|
2024-04-15 09:22:06 -04:00
|
|
|
let allowDebug = false;
|
2024-11-12 13:46:17 -05:00
|
|
|
|
2023-05-05 14:28:31 -04:00
|
|
|
function isGPTBot(user) {
|
2024-02-29 15:53:42 -05:00
|
|
|
return user && enabledChatBotIds.includes(user.id);
|
2023-05-05 14:28:31 -04:00
|
|
|
}
|
|
|
|
|
2023-05-16 13:38:21 -04:00
|
|
|
function attachHeaderIcon(api) {
|
2024-03-08 05:07:48 -05:00
|
|
|
api.headerIcons.add("ai", AiBotHeaderIcon);
|
2023-05-16 13:38:21 -04:00
|
|
|
}
|
|
|
|
|
2023-05-05 14:28:31 -04:00
|
|
|
function initializeAIBotReplies(api) {
|
2024-11-12 13:46:17 -05:00
|
|
|
initializePauseButton(api);
|
2023-05-05 14:28:31 -04:00
|
|
|
|
|
|
|
api.modifyClass("controller:topic", {
|
|
|
|
pluginId: "discourse-ai",
|
|
|
|
|
|
|
|
onAIBotStreamedReply: function (data) {
|
2024-10-22 13:55:35 -04:00
|
|
|
streamPostText(this.model.postStream, data);
|
2023-05-05 14:28:31 -04:00
|
|
|
},
|
|
|
|
subscribe: function () {
|
|
|
|
this._super();
|
|
|
|
|
|
|
|
if (
|
|
|
|
this.model.isPrivateMessage &&
|
|
|
|
this.model.details.allowed_users &&
|
2023-05-11 09:03:03 -04:00
|
|
|
this.model.details.allowed_users.filter(isGPTBot).length >= 1
|
2023-05-05 14:28:31 -04:00
|
|
|
) {
|
2024-01-09 07:20:28 -05:00
|
|
|
// we attempt to recover the last message in the bus
|
|
|
|
// so we subscribe at -2
|
2023-05-05 14:28:31 -04:00
|
|
|
this.messageBus.subscribe(
|
|
|
|
`discourse-ai/ai-bot/topic/${this.model.id}`,
|
2024-01-09 07:20:28 -05:00
|
|
|
this.onAIBotStreamedReply.bind(this),
|
|
|
|
-2
|
2023-05-05 14:28:31 -04:00
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
unsubscribe: function () {
|
2023-05-11 09:03:03 -04:00
|
|
|
this.messageBus.unsubscribe("discourse-ai/ai-bot/topic/*");
|
2023-05-05 14:28:31 -04:00
|
|
|
this._super();
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-08-30 02:15:03 -04:00
|
|
|
function initializePersonaDecorator(api) {
|
|
|
|
let topicController = null;
|
|
|
|
api.decorateWidget(`poster-name:after`, (dec) => {
|
|
|
|
if (!isGPTBot(dec.attrs.user)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// this is hacky and will need to change
|
|
|
|
// trouble is we need to get the model for the topic
|
|
|
|
// and it is not available in the decorator
|
|
|
|
// long term this will not be a problem once we remove widgets and
|
|
|
|
// have a saner structure for our model
|
|
|
|
topicController =
|
|
|
|
topicController || api.container.lookup("controller:topic");
|
|
|
|
|
|
|
|
return dec.widget.attach("persona-flair", {
|
|
|
|
topicController,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
registerWidgetShim(
|
|
|
|
"persona-flair",
|
|
|
|
"span.persona-flair",
|
|
|
|
hbs`{{@data.topicController.model.ai_persona_name}}`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-11-12 13:46:17 -05:00
|
|
|
function initializePauseButton(api) {
|
|
|
|
const transformerRegistered = api.registerValueTransformer(
|
|
|
|
"post-menu-buttons",
|
|
|
|
({ value: dag, context: { post, firstButtonKey } }) => {
|
|
|
|
if (isGPTBot(post.user)) {
|
|
|
|
dag.add("ai-cancel-gpt", AiCancelStreamingButton, {
|
|
|
|
before: firstButtonKey,
|
|
|
|
after: ["ai-share", "ai-debug"],
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
const silencedKey =
|
|
|
|
transformerRegistered && "discourse.post-menu-widget-overrides";
|
|
|
|
|
|
|
|
withSilencedDeprecations(silencedKey, () => initializePauseWidgetButton(api));
|
|
|
|
}
|
|
|
|
|
|
|
|
function initializePauseWidgetButton(api) {
|
|
|
|
api.addPostMenuButton("cancel-gpt", (post) => {
|
|
|
|
if (isGPTBot(post.user)) {
|
|
|
|
return {
|
|
|
|
icon: "pause",
|
|
|
|
action: "cancelStreaming",
|
|
|
|
title: "discourse_ai.ai_bot.cancel_streaming",
|
|
|
|
className: "btn btn-default cancel-streaming",
|
|
|
|
position: "first",
|
|
|
|
};
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
api.attachWidgetAction("post", "cancelStreaming", function () {
|
|
|
|
AiCancelStreamingButton.cancelStreaming(this.model);
|
|
|
|
});
|
|
|
|
}
|
2024-02-20 00:16:23 -05:00
|
|
|
|
2024-04-15 09:22:06 -04:00
|
|
|
function initializeDebugButton(api) {
|
|
|
|
const currentUser = api.getCurrentUser();
|
|
|
|
if (!currentUser || !currentUser.ai_enabled_chat_bots || !allowDebug) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-11-12 13:46:17 -05:00
|
|
|
const transformerRegistered = api.registerValueTransformer(
|
|
|
|
"post-menu-buttons",
|
|
|
|
({ value: dag, context: { post, firstButtonKey } }) => {
|
|
|
|
if (post.topic?.archetype === "private_message") {
|
|
|
|
dag.add("ai-debug", AiDebugButton, {
|
|
|
|
before: firstButtonKey,
|
|
|
|
after: "ai-share",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
const silencedKey =
|
|
|
|
transformerRegistered && "discourse.post-menu-widget-overrides";
|
|
|
|
|
|
|
|
withSilencedDeprecations(silencedKey, () => initializeDebugWidgetButton(api));
|
|
|
|
}
|
|
|
|
|
|
|
|
function initializeDebugWidgetButton(api) {
|
|
|
|
const currentUser = api.getCurrentUser();
|
|
|
|
|
2024-04-15 09:22:06 -04:00
|
|
|
let debugAiResponse = async function ({ post }) {
|
|
|
|
const modal = api.container.lookup("service:modal");
|
2024-11-12 13:46:17 -05:00
|
|
|
AiDebugButton.debugAiResponse(post, modal);
|
2024-04-15 09:22:06 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
api.addPostMenuButton("debugAi", (post) => {
|
|
|
|
if (post.topic?.archetype !== "private_message") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-11-12 13:46:17 -05:00
|
|
|
if (!isPostFromAiBot(post, currentUser)) {
|
|
|
|
return;
|
2024-04-15 09:22:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
action: debugAiResponse,
|
|
|
|
icon: "info",
|
|
|
|
className: "post-action-menu__debug-ai",
|
|
|
|
title: "discourse_ai.ai_bot.debug_ai",
|
|
|
|
position: "first",
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-12-29 03:47:47 -05:00
|
|
|
function initializeShareButton(api) {
|
|
|
|
const currentUser = api.getCurrentUser();
|
|
|
|
if (!currentUser || !currentUser.ai_enabled_chat_bots) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-11-12 13:46:17 -05:00
|
|
|
const transformerRegistered = api.registerValueTransformer(
|
|
|
|
"post-menu-buttons",
|
|
|
|
({ value: dag, context: { post, firstButtonKey } }) => {
|
|
|
|
if (post.topic?.archetype === "private_message") {
|
|
|
|
dag.add("ai-share", AiShareButton, {
|
|
|
|
before: firstButtonKey,
|
|
|
|
});
|
|
|
|
}
|
2023-12-29 03:47:47 -05:00
|
|
|
}
|
2024-11-12 13:46:17 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
const silencedKey =
|
|
|
|
transformerRegistered && "discourse.post-menu-widget-overrides";
|
|
|
|
|
|
|
|
withSilencedDeprecations(silencedKey, () => initializeShareWidgetButton(api));
|
|
|
|
}
|
|
|
|
|
|
|
|
function initializeShareWidgetButton(api) {
|
|
|
|
const currentUser = api.getCurrentUser();
|
|
|
|
|
|
|
|
let shareAiResponse = async function ({ post, showFeedback }) {
|
|
|
|
const modal = api.container.lookup("service:modal");
|
|
|
|
AiShareButton.shareAiResponse(post, modal, showFeedback);
|
2023-12-29 03:47:47 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
api.addPostMenuButton("share", (post) => {
|
2024-02-20 00:16:23 -05:00
|
|
|
// for backwards compat so we don't break if topic is undefined
|
|
|
|
if (post.topic?.archetype !== "private_message") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-11-12 13:46:17 -05:00
|
|
|
if (!isPostFromAiBot(post, currentUser)) {
|
|
|
|
return;
|
2023-12-29 03:47:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
action: shareAiResponse,
|
2024-10-01 18:38:57 -04:00
|
|
|
icon: "far-copy",
|
2024-11-12 13:46:17 -05:00
|
|
|
className: "post-action-menu__share-ai",
|
2023-12-29 03:47:47 -05:00
|
|
|
title: "discourse_ai.ai_bot.share",
|
|
|
|
position: "first",
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-03-12 01:51:41 -04:00
|
|
|
function initializeShareTopicButton(api) {
|
|
|
|
const modal = api.container.lookup("service:modal");
|
|
|
|
const currentUser = api.container.lookup("current-user:main");
|
|
|
|
|
|
|
|
api.registerTopicFooterButton({
|
|
|
|
id: "share-ai-conversation",
|
|
|
|
icon: "share-alt",
|
|
|
|
label: "discourse_ai.ai_bot.share_ai_conversation.name",
|
|
|
|
title: "discourse_ai.ai_bot.share_ai_conversation.title",
|
|
|
|
action() {
|
|
|
|
showShareConversationModal(modal, this.topic.id);
|
|
|
|
},
|
|
|
|
classNames: ["share-ai-conversation-button"],
|
|
|
|
dependentKeys: ["topic.ai_persona_name"],
|
|
|
|
displayed() {
|
|
|
|
return (
|
|
|
|
currentUser?.can_share_ai_bot_conversations &&
|
2024-03-12 20:24:22 -04:00
|
|
|
this.topic.ai_persona_name
|
2024-03-12 01:51:41 -04:00
|
|
|
);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:28:31 -04:00
|
|
|
export default {
|
|
|
|
name: "discourse-ai-bot-replies",
|
|
|
|
|
|
|
|
initialize(container) {
|
2023-05-16 13:38:21 -04:00
|
|
|
const user = container.lookup("service:current-user");
|
|
|
|
|
2023-09-03 21:52:44 -04:00
|
|
|
if (user?.ai_enabled_chat_bots) {
|
2024-02-29 15:53:42 -05:00
|
|
|
enabledChatBotIds = user.ai_enabled_chat_bots.map((bot) => bot.id);
|
2024-04-15 09:22:06 -04:00
|
|
|
allowDebug = user.can_debug_ai_bot_conversations;
|
2024-03-08 05:07:48 -05:00
|
|
|
withPluginApi("1.6.0", attachHeaderIcon);
|
2024-11-12 13:46:17 -05:00
|
|
|
withPluginApi("1.34.0", initializeAIBotReplies);
|
2023-08-30 02:15:03 -04:00
|
|
|
withPluginApi("1.6.0", initializePersonaDecorator);
|
2024-11-12 13:46:17 -05:00
|
|
|
withPluginApi("1.34.0", (api) => initializeDebugButton(api, container));
|
|
|
|
withPluginApi("1.34.0", (api) => initializeShareButton(api, container));
|
2024-03-12 01:51:41 -04:00
|
|
|
withPluginApi("1.22.0", (api) =>
|
|
|
|
initializeShareTopicButton(api, container)
|
|
|
|
);
|
2023-05-05 14:28:31 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|