This reverts commit 5fec8fe79e
.
This commit is contained in:
parent
5fec8fe79e
commit
244ec9d61e
|
@ -1,35 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module DiscourseAi
|
|
||||||
module AiBot
|
|
||||||
class ConversationsController < ::ApplicationController
|
|
||||||
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
|
||||||
requires_login
|
|
||||||
|
|
||||||
def index
|
|
||||||
page = params[:page].to_i
|
|
||||||
per_page = params[:per_page]&.to_i || 40
|
|
||||||
|
|
||||||
bot_user_ids = EntryPoint.all_bot_ids
|
|
||||||
base_query =
|
|
||||||
Topic
|
|
||||||
.private_messages_for_user(current_user)
|
|
||||||
.joins(:topic_users)
|
|
||||||
.where(topic_users: { user_id: bot_user_ids })
|
|
||||||
.distinct
|
|
||||||
total = base_query.count
|
|
||||||
pms = base_query.order(last_posted_at: :desc).offset(page * per_page).limit(per_page)
|
|
||||||
|
|
||||||
render json: {
|
|
||||||
conversations: serialize_data(pms, BasicTopicSerializer),
|
|
||||||
meta: {
|
|
||||||
total: total,
|
|
||||||
page: page,
|
|
||||||
per_page: per_page,
|
|
||||||
has_more: total > (page + 1) * per_page,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -9,7 +9,6 @@ export default class AiBotHeaderIcon extends Component {
|
||||||
@service currentUser;
|
@service currentUser;
|
||||||
@service siteSettings;
|
@service siteSettings;
|
||||||
@service composer;
|
@service composer;
|
||||||
@service router;
|
|
||||||
|
|
||||||
get bots() {
|
get bots() {
|
||||||
const availableBots = this.currentUser.ai_enabled_chat_bots
|
const availableBots = this.currentUser.ai_enabled_chat_bots
|
||||||
|
@ -25,9 +24,6 @@ export default class AiBotHeaderIcon extends Component {
|
||||||
|
|
||||||
@action
|
@action
|
||||||
compose() {
|
compose() {
|
||||||
if (this.siteSettings.ai_enable_experimental_bot_ux) {
|
|
||||||
return this.router.transitionTo("discourse-ai-bot-conversations");
|
|
||||||
}
|
|
||||||
composeAiBotMessage(this.bots[0], this.composer);
|
composeAiBotMessage(this.bots[0], this.composer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
import Component from "@glimmer/component";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
import DButton from "discourse/components/d-button";
|
|
||||||
import { AI_CONVERSATIONS_PANEL } from "../services/ai-conversations-sidebar-manager";
|
|
||||||
|
|
||||||
export default class AiBotSidebarNewConversation extends Component {
|
|
||||||
@service router;
|
|
||||||
@service sidebarState;
|
|
||||||
|
|
||||||
get shouldRender() {
|
|
||||||
return (
|
|
||||||
this.router.currentRouteName !== "discourse-ai-bot-conversations" &&
|
|
||||||
this.sidebarState.isCurrentPanel(AI_CONVERSATIONS_PANEL)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
<template>
|
|
||||||
{{#if this.shouldRender}}
|
|
||||||
<DButton
|
|
||||||
@route="/discourse-ai/ai-bot/conversations"
|
|
||||||
@label="discourse_ai.ai_bot.conversations.new"
|
|
||||||
@icon="plus"
|
|
||||||
class="ai-new-question-button btn-default"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</template>
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
import Controller from "@ember/controller";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
import { tracked } from "@ember-compat/tracked-built-ins";
|
|
||||||
|
|
||||||
export default class DiscourseAiBotConversations extends Controller {
|
|
||||||
@service aiBotConversationsHiddenSubmit;
|
|
||||||
@service currentUser;
|
|
||||||
|
|
||||||
@tracked selectedPersona = this.personaOptions[0].username;
|
|
||||||
|
|
||||||
textarea = null;
|
|
||||||
|
|
||||||
init() {
|
|
||||||
super.init(...arguments);
|
|
||||||
this.selectedPersonaChanged(this.selectedPersona);
|
|
||||||
}
|
|
||||||
|
|
||||||
get personaOptions() {
|
|
||||||
if (this.currentUser.ai_enabled_personas) {
|
|
||||||
return this.currentUser.ai_enabled_personas
|
|
||||||
.filter((persona) => persona.username)
|
|
||||||
.map((persona) => {
|
|
||||||
return {
|
|
||||||
id: persona.id,
|
|
||||||
username: persona.username,
|
|
||||||
name: persona.name,
|
|
||||||
description: persona.description,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayPersonaSelector() {
|
|
||||||
return this.personaOptions.length > 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
get filterable() {
|
|
||||||
return this.personaOptions.length > 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
selectedPersonaChanged(username) {
|
|
||||||
this.selectedPersona = username;
|
|
||||||
this.aiBotConversationsHiddenSubmit.personaUsername = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
updateInputValue(event) {
|
|
||||||
this._autoExpandTextarea();
|
|
||||||
this.aiBotConversationsHiddenSubmit.inputValue = event.target.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
handleKeyDown(event) {
|
|
||||||
if (event.key === "Enter" && !event.shiftKey) {
|
|
||||||
this.aiBotConversationsHiddenSubmit.submitToBot();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setTextArea(element) {
|
|
||||||
this.textarea = element;
|
|
||||||
}
|
|
||||||
|
|
||||||
_autoExpandTextarea() {
|
|
||||||
this.textarea.style.height = "auto";
|
|
||||||
this.textarea.style.height = this.textarea.scrollHeight + "px";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
export default function () {
|
|
||||||
this.route("discourse-ai-bot-conversations", {
|
|
||||||
path: "/discourse-ai/ai-bot/conversations",
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -23,43 +23,24 @@ export function showShareConversationModal(modal, topicId) {
|
||||||
.catch(popupAjaxError);
|
.catch(popupAjaxError);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function composeAiBotMessage(
|
export function composeAiBotMessage(targetBot, composer) {
|
||||||
targetBot,
|
|
||||||
composer,
|
|
||||||
options = {
|
|
||||||
skipFocus: false,
|
|
||||||
topicBody: "",
|
|
||||||
personaUsername: null,
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
const currentUser = composer.currentUser;
|
const currentUser = composer.currentUser;
|
||||||
const draftKey = "new_private_message_ai_" + new Date().getTime();
|
const draftKey = "new_private_message_ai_" + new Date().getTime();
|
||||||
|
|
||||||
let botUsername;
|
let botUsername = currentUser.ai_enabled_chat_bots.find(
|
||||||
if (targetBot) {
|
(bot) => bot.model_name === targetBot
|
||||||
botUsername = currentUser.ai_enabled_chat_bots.find(
|
).username;
|
||||||
(bot) => bot.model_name === targetBot
|
|
||||||
)?.username;
|
|
||||||
} else if (options.personaUsername) {
|
|
||||||
botUsername = options.personaUsername;
|
|
||||||
} else {
|
|
||||||
botUsername = currentUser.ai_enabled_chat_bots[0].username;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = {
|
composer.focusComposer({
|
||||||
action: Composer.PRIVATE_MESSAGE,
|
fallbackToNewTopic: true,
|
||||||
recipients: botUsername,
|
openOpts: {
|
||||||
topicTitle: i18n("discourse_ai.ai_bot.default_pm_prefix"),
|
action: Composer.PRIVATE_MESSAGE,
|
||||||
archetypeId: "private_message",
|
recipients: botUsername,
|
||||||
draftKey,
|
topicTitle: i18n("discourse_ai.ai_bot.default_pm_prefix"),
|
||||||
hasGroups: false,
|
archetypeId: "private_message",
|
||||||
warningsDisabled: true,
|
draftKey,
|
||||||
};
|
hasGroups: false,
|
||||||
|
warningsDisabled: true,
|
||||||
if (options.skipFocus) {
|
},
|
||||||
data.topicBody = options.topicBody;
|
});
|
||||||
await composer.open(data);
|
|
||||||
} else {
|
|
||||||
composer.focusComposer({ fallbackToNewTopic: true, openOpts: data });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { next } from "@ember/runloop";
|
|
||||||
import Service, { service } from "@ember/service";
|
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
|
||||||
import { i18n } from "discourse-i18n";
|
|
||||||
import { composeAiBotMessage } from "../lib/ai-bot-helper";
|
|
||||||
|
|
||||||
export default class AiBotConversationsHiddenSubmit extends Service {
|
|
||||||
@service composer;
|
|
||||||
@service aiConversationsSidebarManager;
|
|
||||||
@service dialog;
|
|
||||||
|
|
||||||
personaUsername;
|
|
||||||
|
|
||||||
inputValue = "";
|
|
||||||
|
|
||||||
@action
|
|
||||||
focusInput() {
|
|
||||||
this.composer.destroyDraft();
|
|
||||||
this.composer.close();
|
|
||||||
next(() => {
|
|
||||||
document.getElementById("custom-homepage-input").focus();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
async submitToBot() {
|
|
||||||
this.composer.destroyDraft();
|
|
||||||
this.composer.close();
|
|
||||||
|
|
||||||
if (this.inputValue.length < 10) {
|
|
||||||
return this.dialog.alert({
|
|
||||||
message: i18n(
|
|
||||||
"discourse_ai.ai_bot.conversations.min_input_length_message"
|
|
||||||
),
|
|
||||||
didConfirm: () => this.focusInput(),
|
|
||||||
didCancel: () => this.focusInput(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// we are intentionally passing null as the targetBot to allow for the
|
|
||||||
// function to select the first available bot. This will be refactored in the
|
|
||||||
// future to allow for selecting a specific bot.
|
|
||||||
await composeAiBotMessage(null, this.composer, {
|
|
||||||
skipFocus: true,
|
|
||||||
topicBody: this.inputValue,
|
|
||||||
personaUsername: this.personaUsername,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.composer.save();
|
|
||||||
this.aiConversationsSidebarManager.newTopicForceSidebar = true;
|
|
||||||
if (this.inputValue.length > 10) {
|
|
||||||
// prevents submitting same message again when returning home
|
|
||||||
// but avoids deleting too-short message on submit
|
|
||||||
this.inputValue = "";
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
popupAjaxError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
import { tracked } from "@glimmer/tracking";
|
|
||||||
import Service, { service } from "@ember/service";
|
|
||||||
import { ADMIN_PANEL, MAIN_PANEL } from "discourse/lib/sidebar/panels";
|
|
||||||
|
|
||||||
export const AI_CONVERSATIONS_PANEL = "ai-conversations";
|
|
||||||
|
|
||||||
export default class AiConversationsSidebarManager extends Service {
|
|
||||||
@service sidebarState;
|
|
||||||
|
|
||||||
@tracked newTopicForceSidebar = false;
|
|
||||||
|
|
||||||
forceCustomSidebar() {
|
|
||||||
// Set the panel to your custom panel
|
|
||||||
this.sidebarState.setPanel(AI_CONVERSATIONS_PANEL);
|
|
||||||
|
|
||||||
// Use separated mode to ensure independence from hamburger menu
|
|
||||||
this.sidebarState.setSeparatedMode();
|
|
||||||
|
|
||||||
// Hide panel switching buttons to keep UI clean
|
|
||||||
this.sidebarState.hideSwitchPanelButtons();
|
|
||||||
|
|
||||||
this.sidebarState.isForcingSidebar = true;
|
|
||||||
document.body.classList.add("has-ai-conversations-sidebar");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
stopForcingCustomSidebar() {
|
|
||||||
// This method is called when leaving your route
|
|
||||||
// Only restore main panel if we previously forced ours
|
|
||||||
document.body.classList.remove("has-ai-conversations-sidebar");
|
|
||||||
const isAdminSidebarActive =
|
|
||||||
this.sidebarState.currentPanel?.key === ADMIN_PANEL;
|
|
||||||
// only restore main panel if we previously forced our sidebar
|
|
||||||
// and not if we are in admin sidebar
|
|
||||||
if (this.sidebarState.isForcingSidebar && !isAdminSidebarActive) {
|
|
||||||
this.sidebarState.setPanel(MAIN_PANEL); // Return to main sidebar panel
|
|
||||||
this.sidebarState.isForcingSidebar = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
import { hash } from "@ember/helper";
|
|
||||||
import { on } from "@ember/modifier";
|
|
||||||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
|
||||||
import RouteTemplate from "ember-route-template";
|
|
||||||
import DButton from "discourse/components/d-button";
|
|
||||||
import { i18n } from "discourse-i18n";
|
|
||||||
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
|
|
||||||
|
|
||||||
export default RouteTemplate(
|
|
||||||
<template>
|
|
||||||
<div class="ai-bot-conversations">
|
|
||||||
{{#if @controller.displayPersonaSelector}}
|
|
||||||
<div class="ai-bot-conversations__persona-selector">
|
|
||||||
<DropdownSelectBox
|
|
||||||
class="persona-llm-selector__persona-dropdown"
|
|
||||||
@value={{@controller.selectedPersona}}
|
|
||||||
@valueProperty="username"
|
|
||||||
@content={{@controller.personaOptions}}
|
|
||||||
@options={{hash icon="robot" filterable=@controller.filterable}}
|
|
||||||
@onChange={{@controller.selectedPersonaChanged}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="ai-bot-conversations__content-wrapper">
|
|
||||||
|
|
||||||
<h1>{{i18n "discourse_ai.ai_bot.conversations.header"}}</h1>
|
|
||||||
<div class="ai-bot-conversations__input-wrapper">
|
|
||||||
<textarea
|
|
||||||
{{didInsert @controller.setTextArea}}
|
|
||||||
{{on "input" @controller.updateInputValue}}
|
|
||||||
{{on "keydown" @controller.handleKeyDown}}
|
|
||||||
id="ai-bot-conversations-input"
|
|
||||||
placeholder={{i18n "discourse_ai.ai_bot.conversations.placeholder"}}
|
|
||||||
minlength="10"
|
|
||||||
rows="1"
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@action={{@controller.aiBotConversationsHiddenSubmit.submitToBot}}
|
|
||||||
@icon="paper-plane"
|
|
||||||
@title="discourse_ai.ai_bot.conversations.header"
|
|
||||||
class="ai-bot-button btn-primary ai-conversation-submit"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<p class="ai-disclaimer">
|
|
||||||
{{i18n "discourse_ai.ai_bot.conversations.disclaimer"}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
);
|
|
|
@ -1,256 +0,0 @@
|
||||||
import { tracked } from "@glimmer/tracking";
|
|
||||||
import { TrackedArray } from "@ember-compat/tracked-built-ins";
|
|
||||||
import { ajax } from "discourse/lib/ajax";
|
|
||||||
import { bind } from "discourse/lib/decorators";
|
|
||||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
|
||||||
import { i18n } from "discourse-i18n";
|
|
||||||
import AiBotSidebarNewConversation from "../discourse/components/ai-bot-sidebar-new-conversation";
|
|
||||||
import { isPostFromAiBot } from "../discourse/lib/ai-bot-helper";
|
|
||||||
import { AI_CONVERSATIONS_PANEL } from "../discourse/services/ai-conversations-sidebar-manager";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "ai-conversations-sidebar",
|
|
||||||
|
|
||||||
initialize() {
|
|
||||||
withPluginApi((api) => {
|
|
||||||
const currentUser = api.container.lookup("service:current-user");
|
|
||||||
if (!currentUser) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const aiConversationsSidebarManager = api.container.lookup(
|
|
||||||
"service:ai-conversations-sidebar-manager"
|
|
||||||
);
|
|
||||||
const appEvents = api.container.lookup("service:app-events");
|
|
||||||
const messageBus = api.container.lookup("service:message-bus");
|
|
||||||
|
|
||||||
api.addSidebarPanel(
|
|
||||||
(BaseCustomSidebarPanel) =>
|
|
||||||
class AiConversationsSidebarPanel extends BaseCustomSidebarPanel {
|
|
||||||
key = AI_CONVERSATIONS_PANEL;
|
|
||||||
hidden = true;
|
|
||||||
displayHeader = true;
|
|
||||||
expandActiveSection = true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
api.renderInOutlet("sidebar-footer-actions", AiBotSidebarNewConversation);
|
|
||||||
api.addSidebarSection(
|
|
||||||
(BaseCustomSidebarSection, BaseCustomSidebarSectionLink) => {
|
|
||||||
const AiConversationLink = class extends BaseCustomSidebarSectionLink {
|
|
||||||
route = "topic.fromParamsNear";
|
|
||||||
|
|
||||||
constructor(topic) {
|
|
||||||
super(...arguments);
|
|
||||||
this.topic = topic;
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return this.topic.title;
|
|
||||||
}
|
|
||||||
|
|
||||||
get models() {
|
|
||||||
return [
|
|
||||||
this.topic.slug,
|
|
||||||
this.topic.id,
|
|
||||||
this.topic.last_read_post_number || 0,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
get title() {
|
|
||||||
return this.topic.title;
|
|
||||||
}
|
|
||||||
|
|
||||||
get text() {
|
|
||||||
return this.topic.title;
|
|
||||||
}
|
|
||||||
|
|
||||||
get classNames() {
|
|
||||||
return `ai-conversation-${this.topic.id}`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return class extends BaseCustomSidebarSection {
|
|
||||||
@tracked links = new TrackedArray();
|
|
||||||
@tracked topics = [];
|
|
||||||
@tracked hasMore = [];
|
|
||||||
page = 0;
|
|
||||||
isFetching = false;
|
|
||||||
totalTopicsCount = 0;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
this.fetchMessages();
|
|
||||||
|
|
||||||
appEvents.on("topic:created", this, "addNewMessageToSidebar");
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
willDestroy() {
|
|
||||||
this.removeScrollListener();
|
|
||||||
appEvents.on("topic:created", this, "addNewMessageToSidebar");
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return "ai-conversations-history";
|
|
||||||
}
|
|
||||||
|
|
||||||
get text() {
|
|
||||||
return i18n(
|
|
||||||
"discourse_ai.ai_bot.conversations.messages_sidebar_title"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get sidebarElement() {
|
|
||||||
return document.querySelector(
|
|
||||||
".sidebar-wrapper .sidebar-sections"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
addNewMessageToSidebar(topic) {
|
|
||||||
this.addNewMessage(topic);
|
|
||||||
this.watchForTitleUpdate(topic);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
removeScrollListener() {
|
|
||||||
const sidebar = this.sidebarElement;
|
|
||||||
if (sidebar) {
|
|
||||||
sidebar.removeEventListener("scroll", this.scrollHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
attachScrollListener() {
|
|
||||||
const sidebar = this.sidebarElement;
|
|
||||||
if (sidebar) {
|
|
||||||
sidebar.addEventListener("scroll", this.scrollHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
scrollHandler() {
|
|
||||||
const sidebarElement = this.sidebarElement;
|
|
||||||
if (!sidebarElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scrollPosition = sidebarElement.scrollTop;
|
|
||||||
const scrollHeight = sidebarElement.scrollHeight;
|
|
||||||
const clientHeight = sidebarElement.clientHeight;
|
|
||||||
|
|
||||||
// When user has scrolled to bottom with a small threshold
|
|
||||||
if (scrollHeight - scrollPosition - clientHeight < 100) {
|
|
||||||
if (this.hasMore && !this.isFetching) {
|
|
||||||
this.loadMore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchMessages(isLoadingMore = false) {
|
|
||||||
if (this.isFetching) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.isFetching = true;
|
|
||||||
const data = await ajax(
|
|
||||||
"/discourse-ai/ai-bot/conversations.json",
|
|
||||||
{
|
|
||||||
data: { page: this.page, per_page: 40 },
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isLoadingMore) {
|
|
||||||
this.topics = [...this.topics, ...data.conversations];
|
|
||||||
} else {
|
|
||||||
this.topics = data.conversations;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.totalTopicsCount = data.meta.total;
|
|
||||||
this.hasMore = data.meta.has_more;
|
|
||||||
this.isFetching = false;
|
|
||||||
this.removeScrollListener();
|
|
||||||
this.buildSidebarLinks();
|
|
||||||
this.attachScrollListener();
|
|
||||||
} catch {
|
|
||||||
this.isFetching = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadMore() {
|
|
||||||
if (this.isFetching || !this.hasMore) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.page = this.page + 1;
|
|
||||||
this.fetchMessages(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildSidebarLinks() {
|
|
||||||
this.links = this.topics.map(
|
|
||||||
(topic) => new AiConversationLink(topic)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
addNewMessage(newTopic) {
|
|
||||||
this.links = [new AiConversationLink(newTopic), ...this.links];
|
|
||||||
}
|
|
||||||
|
|
||||||
watchForTitleUpdate(topic) {
|
|
||||||
const channel = `/discourse-ai/ai-bot/topic/${topic.topic_id}`;
|
|
||||||
const topicId = topic.topic_id;
|
|
||||||
const callback = this.updateTopicTitle.bind(this);
|
|
||||||
messageBus.subscribe(channel, ({ title }) => {
|
|
||||||
callback(topicId, title);
|
|
||||||
messageBus.unsubscribe(channel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTopicTitle(topicId, title) {
|
|
||||||
// update the topic title in the sidebar, instead of the default title
|
|
||||||
const text = document.querySelector(
|
|
||||||
`.sidebar-section-link-wrapper .ai-conversation-${topicId} .sidebar-section-link-content-text`
|
|
||||||
);
|
|
||||||
if (text) {
|
|
||||||
text.innerText = title;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
AI_CONVERSATIONS_PANEL
|
|
||||||
);
|
|
||||||
|
|
||||||
const setSidebarPanel = (transition) => {
|
|
||||||
if (transition?.to?.name === "discourse-ai-bot-conversations") {
|
|
||||||
return aiConversationsSidebarManager.forceCustomSidebar();
|
|
||||||
}
|
|
||||||
|
|
||||||
const topic = api.container.lookup("controller:topic").model;
|
|
||||||
if (
|
|
||||||
topic?.archetype === "private_message" &&
|
|
||||||
topic.postStream.posts.some((post) =>
|
|
||||||
isPostFromAiBot(post, currentUser)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return aiConversationsSidebarManager.forceCustomSidebar();
|
|
||||||
}
|
|
||||||
|
|
||||||
// newTopicForceSidebar is set to true when a new topic is created. We have
|
|
||||||
// this because the condition `postStream.posts` above will not be true as the bot response
|
|
||||||
// is not in the postStream yet when this initializer is ran. So we need to force
|
|
||||||
// the sidebar to open when creating a new topic. After that, we set it to false again.
|
|
||||||
if (aiConversationsSidebarManager.newTopicForceSidebar) {
|
|
||||||
aiConversationsSidebarManager.newTopicForceSidebar = false;
|
|
||||||
return aiConversationsSidebarManager.forceCustomSidebar();
|
|
||||||
}
|
|
||||||
|
|
||||||
aiConversationsSidebarManager.stopForcingCustomSidebar();
|
|
||||||
};
|
|
||||||
|
|
||||||
api.container
|
|
||||||
.lookup("service:router")
|
|
||||||
.on("routeDidChange", setSidebarPanel);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,325 +0,0 @@
|
||||||
// Hide the new question button from the hamburger menu's footer
|
|
||||||
.hamburger-panel .ai-new-question-button {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.has-ai-conversations-sidebar {
|
|
||||||
.sidebar-wrapper {
|
|
||||||
.sidebar-footer-actions {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-left: 0;
|
|
||||||
align-items: flex-end;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.ai-new-question-button {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-container {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ai related sidebar content
|
|
||||||
[data-section-name="ai-conversations-history"] {
|
|
||||||
.sidebar-section-header-wrapper {
|
|
||||||
pointer-events: none;
|
|
||||||
font-size: var(--font-down-1);
|
|
||||||
|
|
||||||
.sidebar-section-header-caret {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-section-header-text {
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-section-link-wrapper {
|
|
||||||
.sidebar-section-link {
|
|
||||||
height: unset;
|
|
||||||
padding-block: 0.65em;
|
|
||||||
font-size: var(--font-down-1);
|
|
||||||
letter-spacing: 0.35px;
|
|
||||||
border-radius: 0 var(--border-radius) var(--border-radius) 0;
|
|
||||||
|
|
||||||
.sidebar-section-link-content-text {
|
|
||||||
white-space: normal;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-section-link-prefix {
|
|
||||||
align-self: start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// topic elements
|
|
||||||
#topic-footer-button-share-and-invite,
|
|
||||||
body:not(.staff) #topic-footer-button-archive,
|
|
||||||
#topic-footer-buttons .topic-notifications-button,
|
|
||||||
.bookmark-menu-trigger,
|
|
||||||
.more-topics__container,
|
|
||||||
.private-message-glyph-wrapper,
|
|
||||||
.topic-header-participants,
|
|
||||||
.topic-above-footer-buttons-outlet,
|
|
||||||
.topic-map,
|
|
||||||
.timeline-ago,
|
|
||||||
#topic-footer-buttons .topic-footer-main-buttons details {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topic-timer-info {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topic-owner .actions .create-flag {
|
|
||||||
// why flag my own post
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container.posts {
|
|
||||||
margin-bottom: 0;
|
|
||||||
|
|
||||||
.topic-navigation.with-timeline {
|
|
||||||
top: calc(var(--header-offset, 60px) + 5.5em);
|
|
||||||
}
|
|
||||||
|
|
||||||
.topic-navigation {
|
|
||||||
.topic-notifications-button {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#topic-title {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.title-wrapper {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 960px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.small-action,
|
|
||||||
.onscreen-post .row {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#topic-footer-buttons {
|
|
||||||
width: calc(100% - 6.5em);
|
|
||||||
margin-top: 0;
|
|
||||||
|
|
||||||
@media screen and (max-width: 924px) {
|
|
||||||
max-width: unset;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1300px) {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 51em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topic-footer-main-buttons {
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
@media screen and (min-width: 1180px) {
|
|
||||||
margin-right: 0.6em;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 924px) {
|
|
||||||
margin-right: 0.6em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#topic-progress-wrapper.docked {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 924px) {
|
|
||||||
.archetype-private_message .topic-post:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nav.post-controls .actions button {
|
|
||||||
padding: 0.5em 0.65em;
|
|
||||||
|
|
||||||
&.reply {
|
|
||||||
.d-icon {
|
|
||||||
margin-right: 0.45em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.topic-footer-main-buttons {
|
|
||||||
margin-left: calc(var(--topic-avatar-width) - 1.15em);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ai-bot-conversations {
|
|
||||||
height: calc(100dvh - var(--header-offset) - 1.25em);
|
|
||||||
|
|
||||||
@media screen and (min-width: 675px) {
|
|
||||||
border: 1px solid var(--primary-low);
|
|
||||||
padding: 2em 2em 3em;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
height: calc(100dvh - var(--header-offset) - 10em);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__persona-selector {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__content-wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
box-sizing: border-box;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__input-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: stretch;
|
|
||||||
gap: 0.5em;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 90dvw;
|
|
||||||
margin-top: 2em;
|
|
||||||
|
|
||||||
@media screen and (min-width: 600px) {
|
|
||||||
width: 80%;
|
|
||||||
max-width: 46em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
align-self: end;
|
|
||||||
min-height: 2.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ai-bot-conversations-input {
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
resize: none;
|
|
||||||
border-radius: var(--d-button-border-radius);
|
|
||||||
max-height: 30vh;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--primary-high);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin-bottom: 0.45em;
|
|
||||||
max-width: 20em;
|
|
||||||
text-align: center;
|
|
||||||
font-size: var(--font-up-6);
|
|
||||||
line-height: var(--line-height-medium);
|
|
||||||
|
|
||||||
@media screen and (min-height: 300px) {
|
|
||||||
margin-top: -1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-height: 600px) {
|
|
||||||
margin-top: -3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-height: 900px) {
|
|
||||||
margin-top: -6em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ai-disclaimer {
|
|
||||||
text-align: center;
|
|
||||||
font-size: var(--font-down-1);
|
|
||||||
color: var(--primary-700);
|
|
||||||
|
|
||||||
@media screen and (min-width: 600px) {
|
|
||||||
width: 80%;
|
|
||||||
max-width: 46em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-footer-wrapper {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.powered-by-discourse {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// composer
|
|
||||||
.reply-details .dropdown-select-box.composer-actions,
|
|
||||||
.composer-fields {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
// hide user stuff
|
|
||||||
.new-user-wrapper {
|
|
||||||
.user-navigation {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-main .about.collapsed-info .details {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-content {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 600px) {
|
|
||||||
.share-ai-conversation-button {
|
|
||||||
.d-icon {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d-button-label {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-view {
|
|
||||||
nav.post-controls .actions button.reply .d-icon {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-dropdown {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-custom-sections {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// custom user card link
|
|
||||||
.user-card-meta__profile-link {
|
|
||||||
display: block;
|
|
||||||
padding: 0.5em 0 0.25em;
|
|
||||||
|
|
||||||
.d-icon {
|
|
||||||
font-size: var(--font-down-1);
|
|
||||||
margin-right: 0.15em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -716,14 +716,6 @@ en:
|
||||||
5-pro: "Gemini"
|
5-pro: "Gemini"
|
||||||
mixtral-8x7B-Instruct-V0:
|
mixtral-8x7B-Instruct-V0:
|
||||||
"1": "Mixtral-8x7B V0.1"
|
"1": "Mixtral-8x7B V0.1"
|
||||||
conversations:
|
|
||||||
header: "What can I help with?"
|
|
||||||
submit: "Submit question"
|
|
||||||
disclaimer: "Generative AI can make mistakes. Verify important information."
|
|
||||||
placeholder: "Ask a question..."
|
|
||||||
new: "New Question"
|
|
||||||
min_input_length_message: "Message must be longer than 10 characters"
|
|
||||||
messages_sidebar_title: "Conversations"
|
|
||||||
sentiments:
|
sentiments:
|
||||||
dashboard:
|
dashboard:
|
||||||
title: "Sentiment"
|
title: "Sentiment"
|
||||||
|
|
|
@ -37,10 +37,6 @@ DiscourseAi::Engine.routes.draw do
|
||||||
get "/preview/:topic_id" => "shared_ai_conversations#preview"
|
get "/preview/:topic_id" => "shared_ai_conversations#preview"
|
||||||
end
|
end
|
||||||
|
|
||||||
scope module: :ai_bot, path: "/ai-bot/conversations" do
|
|
||||||
get "/" => "conversations#index"
|
|
||||||
end
|
|
||||||
|
|
||||||
scope module: :ai_bot, path: "/ai-bot/artifacts" do
|
scope module: :ai_bot, path: "/ai-bot/artifacts" do
|
||||||
get "/:id" => "artifacts#show"
|
get "/:id" => "artifacts#show"
|
||||||
get "/:id/:version" => "artifacts#show"
|
get "/:id/:version" => "artifacts#show"
|
||||||
|
|
|
@ -390,7 +390,3 @@ discourse_ai:
|
||||||
ai_rag_images_enabled:
|
ai_rag_images_enabled:
|
||||||
default: false
|
default: false
|
||||||
hidden: true
|
hidden: true
|
||||||
ai_enable_experimental_bot_ux:
|
|
||||||
default: false
|
|
||||||
client: true
|
|
||||||
|
|
||||||
|
|
|
@ -43,8 +43,6 @@ register_asset "stylesheets/modules/ai-bot/common/ai-persona.scss"
|
||||||
register_asset "stylesheets/modules/ai-bot/common/ai-discobot-discoveries.scss"
|
register_asset "stylesheets/modules/ai-bot/common/ai-discobot-discoveries.scss"
|
||||||
register_asset "stylesheets/modules/ai-bot/mobile/ai-persona.scss", :mobile
|
register_asset "stylesheets/modules/ai-bot/mobile/ai-persona.scss", :mobile
|
||||||
|
|
||||||
register_asset "stylesheets/modules/ai-bot-conversations/common.scss"
|
|
||||||
|
|
||||||
register_asset "stylesheets/modules/embeddings/common/semantic-related-topics.scss"
|
register_asset "stylesheets/modules/embeddings/common/semantic-related-topics.scss"
|
||||||
register_asset "stylesheets/modules/embeddings/common/semantic-search.scss"
|
register_asset "stylesheets/modules/embeddings/common/semantic-search.scss"
|
||||||
|
|
||||||
|
|
|
@ -1,185 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
RSpec.describe "AI Bot - Personal Message", type: :system do
|
|
||||||
let(:topic_page) { PageObjects::Pages::Topic.new }
|
|
||||||
let(:composer) { PageObjects::Components::Composer.new }
|
|
||||||
let(:ai_pm_homepage) { PageObjects::Components::AiPmHomepage.new }
|
|
||||||
let(:sidebar) { PageObjects::Components::NavigationMenu::Sidebar.new }
|
|
||||||
let(:header_dropdown) { PageObjects::Components::NavigationMenu::HeaderDropdown.new }
|
|
||||||
let(:dialog) { PageObjects::Components::Dialog.new }
|
|
||||||
|
|
||||||
fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }
|
|
||||||
|
|
||||||
fab!(:claude_2) do
|
|
||||||
Fabricate(
|
|
||||||
:llm_model,
|
|
||||||
provider: "anthropic",
|
|
||||||
url: "https://api.anthropic.com/v1/messages",
|
|
||||||
name: "claude-2",
|
|
||||||
)
|
|
||||||
end
|
|
||||||
fab!(:bot_user) do
|
|
||||||
toggle_enabled_bots(bots: [claude_2])
|
|
||||||
SiteSetting.ai_bot_enabled = true
|
|
||||||
claude_2.reload.user
|
|
||||||
end
|
|
||||||
fab!(:bot) do
|
|
||||||
persona =
|
|
||||||
AiPersona
|
|
||||||
.find(DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::General])
|
|
||||||
.class_instance
|
|
||||||
.new
|
|
||||||
DiscourseAi::Personas::Bot.as(bot_user, persona: persona)
|
|
||||||
end
|
|
||||||
|
|
||||||
fab!(:pm) do
|
|
||||||
Fabricate(
|
|
||||||
:private_message_topic,
|
|
||||||
title: "This is my special PM",
|
|
||||||
user: user,
|
|
||||||
topic_allowed_users: [
|
|
||||||
Fabricate.build(:topic_allowed_user, user: user),
|
|
||||||
Fabricate.build(:topic_allowed_user, user: bot_user),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
end
|
|
||||||
fab!(:first_post) do
|
|
||||||
Fabricate(:post, topic: pm, user: user, post_number: 1, raw: "This is a reply by the user")
|
|
||||||
end
|
|
||||||
fab!(:second_post) do
|
|
||||||
Fabricate(:post, topic: pm, user: bot_user, post_number: 2, raw: "This is a bot reply")
|
|
||||||
end
|
|
||||||
fab!(:third_post) do
|
|
||||||
Fabricate(
|
|
||||||
:post,
|
|
||||||
topic: pm,
|
|
||||||
user: user,
|
|
||||||
post_number: 3,
|
|
||||||
raw: "This is a second reply by the user",
|
|
||||||
)
|
|
||||||
end
|
|
||||||
fab!(:topic_user) { Fabricate(:topic_user, topic: pm, user: user) }
|
|
||||||
fab!(:topic_bot_user) { Fabricate(:topic_user, topic: pm, user: bot_user) }
|
|
||||||
|
|
||||||
fab!(:persona) do
|
|
||||||
persona =
|
|
||||||
AiPersona.create!(
|
|
||||||
name: "Test Persona",
|
|
||||||
description: "A test persona",
|
|
||||||
allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]],
|
|
||||||
enabled: true,
|
|
||||||
system_prompt: "You are a helpful bot",
|
|
||||||
)
|
|
||||||
|
|
||||||
persona.create_user!
|
|
||||||
persona.update!(
|
|
||||||
default_llm_id: claude_2.id,
|
|
||||||
allow_chat_channel_mentions: true,
|
|
||||||
allow_topic_mentions: true,
|
|
||||||
)
|
|
||||||
persona
|
|
||||||
end
|
|
||||||
|
|
||||||
before do
|
|
||||||
SiteSetting.ai_enable_experimental_bot_ux = true
|
|
||||||
SiteSetting.ai_bot_enabled = true
|
|
||||||
Jobs.run_immediately!
|
|
||||||
SiteSetting.ai_bot_allowed_groups = "#{Group::AUTO_GROUPS[:trust_level_0]}"
|
|
||||||
sign_in(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "has normal bot interaction when `ai_enable_experimental_bot_ux` is disabled" do
|
|
||||||
SiteSetting.ai_enable_experimental_bot_ux = false
|
|
||||||
visit "/"
|
|
||||||
find(".ai-bot-button").click
|
|
||||||
|
|
||||||
expect(ai_pm_homepage).to have_no_homepage
|
|
||||||
expect(composer).to be_opened
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when `ai_enable_experimental_bot_ux` is enabled" do
|
|
||||||
it "renders landing page on bot click" do
|
|
||||||
visit "/"
|
|
||||||
find(".ai-bot-button").click
|
|
||||||
expect(ai_pm_homepage).to have_homepage
|
|
||||||
expect(sidebar).to be_visible
|
|
||||||
end
|
|
||||||
|
|
||||||
it "displays error when message is too short" do
|
|
||||||
visit "/"
|
|
||||||
find(".ai-bot-button").click
|
|
||||||
|
|
||||||
ai_pm_homepage.input.fill_in(with: "a")
|
|
||||||
ai_pm_homepage.submit
|
|
||||||
expect(ai_pm_homepage).to have_too_short_dialog
|
|
||||||
dialog.click_yes
|
|
||||||
expect(composer).to be_closed
|
|
||||||
end
|
|
||||||
|
|
||||||
it "renders sidebar even when navigation menu is set to header" do
|
|
||||||
SiteSetting.navigation_menu = "header dropdown"
|
|
||||||
visit "/"
|
|
||||||
find(".ai-bot-button").click
|
|
||||||
expect(ai_pm_homepage).to have_homepage
|
|
||||||
expect(sidebar).to be_visible
|
|
||||||
expect(header_dropdown).to be_visible
|
|
||||||
end
|
|
||||||
|
|
||||||
it "hides default content in the sidebar" do
|
|
||||||
visit "/"
|
|
||||||
find(".ai-bot-button").click
|
|
||||||
|
|
||||||
expect(ai_pm_homepage).to have_homepage
|
|
||||||
expect(sidebar).to have_no_tags_section
|
|
||||||
expect(sidebar).to have_no_section("categories")
|
|
||||||
expect(sidebar).to have_no_section("messages")
|
|
||||||
expect(sidebar).to have_no_section("chat-dms")
|
|
||||||
expect(sidebar).to have_no_section("chat-channels")
|
|
||||||
expect(sidebar).to have_no_section("user-threads")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "shows the bot conversation in the sidebar" do
|
|
||||||
visit "/"
|
|
||||||
find(".ai-bot-button").click
|
|
||||||
|
|
||||||
expect(ai_pm_homepage).to have_homepage
|
|
||||||
expect(sidebar).to have_section("ai-conversations-history")
|
|
||||||
expect(sidebar).to have_section_link(pm.title)
|
|
||||||
expect(sidebar).to have_no_css("button.ai-new-question-button")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "navigates to the bot conversation when clicked" do
|
|
||||||
visit "/"
|
|
||||||
find(".ai-bot-button").click
|
|
||||||
|
|
||||||
expect(ai_pm_homepage).to have_homepage
|
|
||||||
sidebar.find(
|
|
||||||
".sidebar-section[data-section-name='ai-conversations-history'] a.sidebar-section-link",
|
|
||||||
).click
|
|
||||||
expect(topic_page).to have_topic_title(pm.title)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "displays sidebar and 'new question' on the topic page" do
|
|
||||||
topic_page.visit_topic(pm)
|
|
||||||
expect(sidebar).to be_visible
|
|
||||||
expect(sidebar).to have_css("button.ai-new-question-button")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "redirect to the homepage when 'new question' is clicked" do
|
|
||||||
topic_page.visit_topic(pm)
|
|
||||||
expect(sidebar).to be_visible
|
|
||||||
sidebar.find("button.ai-new-question-button").click
|
|
||||||
expect(ai_pm_homepage).to have_homepage
|
|
||||||
end
|
|
||||||
|
|
||||||
it "can send a new message to the bot" do
|
|
||||||
topic_page.visit_topic(pm)
|
|
||||||
topic_page.click_reply_button
|
|
||||||
expect(composer).to be_opened
|
|
||||||
|
|
||||||
composer.fill_in(with: "Hello bot replying to you")
|
|
||||||
composer.submit
|
|
||||||
expect(page).to have_content("Hello bot replying to you")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,32 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module PageObjects
|
|
||||||
module Components
|
|
||||||
class AiPmHomepage < PageObjects::Components::Base
|
|
||||||
HOMEPAGE_WRAPPER_CLASS = ".ai-bot-conversations__content-wrapper"
|
|
||||||
|
|
||||||
def input
|
|
||||||
page.find("#ai-bot-conversations-input")
|
|
||||||
end
|
|
||||||
|
|
||||||
def submit
|
|
||||||
page.find(".ai-conversation-submit").click
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_too_short_dialog?
|
|
||||||
page.find(
|
|
||||||
".dialog-content",
|
|
||||||
text: I18n.t("js.discourse_ai.ai_bot.conversations.min_input_length_message"),
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_homepage?
|
|
||||||
page.has_css?(HOMEPAGE_WRAPPER_CLASS)
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_no_homepage?
|
|
||||||
page.has_no_css?(HOMEPAGE_WRAPPER_CLASS)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -50,8 +50,6 @@ acceptance("AI Helper - Post Helper Menu", function (needs) {
|
||||||
done: false,
|
done: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.get("/discourse-ai/ai-bot/conversations.json", () => {});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("displays streamed explanation", async function (assert) {
|
test("displays streamed explanation", async function (assert) {
|
||||||
|
|
|
@ -29,8 +29,6 @@ acceptance("Topic - Summary", function (needs) {
|
||||||
done: false,
|
done: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.get("/discourse-ai/ai-bot/conversations.json", () => {});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
needs.hooks.beforeEach(() => {
|
needs.hooks.beforeEach(() => {
|
||||||
|
|
Loading…
Reference in New Issue