UX: Remove multi llm selector from header and move to composer (#619)

LLM selector control had no memory and was awkward to click.

Instead we now:

- Clearly display which llm you are talking to
- Allow you to change llm direct from composer
This commit is contained in:
Sam 2024-05-14 17:54:54 +10:00 committed by GitHub
parent 8eee6893d6
commit cb23ae614f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 74 additions and 78 deletions

View File

@ -4,9 +4,7 @@ import { service } from "@ember/service";
import { gt } from "truth-helpers";
import DButton from "discourse/components/d-button";
import i18n from "discourse-common/helpers/i18n";
import DMenu from "float-kit/components/d-menu";
import { composeAiBotMessage } from "../lib/ai-bot-helper";
import AiBotHeaderPanel from "./ai-bot-header-panel";
export default class AiBotHeaderIcon extends Component {
@service siteSettings;
@ -26,24 +24,12 @@ export default class AiBotHeaderIcon extends Component {
<template>
{{#if (gt this.bots.length 0)}}
<li>
{{#if (gt this.bots.length 1)}}
<DMenu
@icon="robot"
@title={{i18n "discourse_ai.ai_bot.shortcut_title"}}
class="ai-bot-button icon btn-flat"
>
<:content as |args|>
<AiBotHeaderPanel @closePanel={{args.close}} />
</:content>
</DMenu>
{{else}}
<DButton
@icon="robot"
@title={{i18n "discourse_ai.ai_bot.shortcut_title"}}
class="ai-bot-button icon btn-flat"
@action={{this.compose}}
/>
{{/if}}
<DButton
@icon="robot"
@title={{i18n "discourse_ai.ai_bot.shortcut_title"}}
class="ai-bot-button icon btn-flat"
@action={{this.compose}}
/>
</li>
{{/if}}
</template>

View File

@ -1,10 +0,0 @@
<div class="bot-panel ai-bot-available-bot-options">
{{#each this.botNames as |bot|}}
<DButton
@translatedTitle={{bot.humanized}}
@translatedLabel={{bot.humanized}}
@action={{action "composeMessageWithTargetBot" bot.modelName}}
class="btn-flat ai-bot-available-bot-content"
/>
{{/each}}
</div>

View File

@ -1,33 +0,0 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import I18n from "I18n";
import { composeAiBotMessage } from "discourse/plugins/discourse-ai/discourse/lib/ai-bot-helper";
export default class AiBotHeaderPanel extends Component {
@service siteSettings;
@service composer;
@action
composeMessageWithTargetBot(target) {
this.#composeAiBotMessage(target);
}
get botNames() {
return this.enabledBotOptions.map((bot) => {
return {
humanized: I18n.t(`discourse_ai.ai_bot.bot_names.${bot}`),
modelName: bot,
};
});
}
get enabledBotOptions() {
return this.siteSettings.ai_bot_enabled_chat_bots.split("|");
}
#composeAiBotMessage(targetBot) {
this.args.closePanel();
composeAiBotMessage(targetBot, this.composer);
}
}

View File

@ -1,7 +1,10 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { hash } from "@ember/helper";
import { next } from "@ember/runloop";
import { inject as service } from "@ember/service";
import KeyValueStore from "discourse/lib/key-value-store";
import I18n from "I18n";
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
function isBotMessage(composer, currentUser) {
@ -28,9 +31,14 @@ export default class BotSelector extends Component {
}
@service currentUser;
@service siteSettings;
@tracked llm;
STORE_NAMESPACE = "discourse_ai_persona_selector_";
LLM_STORE_NAMESPACE = "discourse_ai_llm_selector_";
preferredPersonaStore = new KeyValueStore(this.STORE_NAMESPACE);
preferredLlmStore = new KeyValueStore(this.LLM_STORE_NAMESPACE);
constructor() {
super(...arguments);
@ -47,6 +55,14 @@ export default class BotSelector extends Component {
}
this.composer.metaData = { ai_persona_id: this._value };
let llm = this.preferredLlmStore.getObject("id");
llm = llm || this.llmOptions[0].id;
if (llm) {
next(() => {
this.currentLlm = llm;
});
}
}
}
@ -80,14 +96,49 @@ export default class BotSelector extends Component {
this.composer.metaData = { ai_persona_id: newValue };
}
get currentLlm() {
return this.llm;
}
set currentLlm(newValue) {
this.llm = newValue;
const botUsername = this.currentUser.ai_enabled_chat_bots.find(
(bot) => bot.model_name === this.llm
).username;
this.preferredLlmStore.setObject({ key: "id", value: newValue });
this.composer.set("targetRecipients", botUsername);
}
get llmOptions() {
return this.siteSettings.ai_bot_enabled_chat_bots
.split("|")
.filter(Boolean)
.map((bot) => {
return {
id: bot,
name: I18n.t(`discourse_ai.ai_bot.bot_names.${bot}`),
};
});
}
<template>
<div class="gpt-persona">
<DropdownSelectBox
class="persona-selector__dropdown"
@value={{this.value}}
@content={{this.botOptions}}
@options={{hash icon="robot" filterable=this.filterable}}
/>
<div class="persona-llm-selector">
<div class="gpt-persona">
<DropdownSelectBox
class="persona-llm-selector__persona-dropdown"
@value={{this.value}}
@content={{this.botOptions}}
@options={{hash icon="robot" filterable=this.filterable}}
/>
</div>
<div class="llm-selector">
<DropdownSelectBox
class="persona-llm-selector__llm-dropdown"
@value={{this.currentLlm}}
@content={{this.llmOptions}}
@options={{hash icon="globe"}}
/>
</div>
</div>
</template>
}

View File

@ -9,12 +9,15 @@ nav.post-controls .actions button.cancel-streaming {
display: none;
}
}
.gpt-persona {
margin-bottom: 5px;
margin-top: -10px;
.persona-llm-selector {
display: flex;
justify-content: space-between;
align-items: center;
.select-kit.single-select.dropdown-select-box ul.select-kit-collection {
max-height: 200px;
}
margin-bottom: 1em;
}
}

View File

@ -23,9 +23,6 @@ RSpec.describe "AI chat channel summarization", type: :system, js: true do
expect(page).to have_selector(".ai-bot-button")
find(".ai-bot-button").click
expect(page).to have_selector(".ai-bot-available-bot-content")
find("button.ai-bot-available-bot-content:first-child").click
# composer is open
expect(page).to have_selector(".d-editor-container")

View File

@ -13,7 +13,8 @@ RSpec.describe "AI personas", type: :system, js: true do
it "remembers the last selected persona" do
visit "/"
find(".d-header .ai-bot-button").click()
persona_selector = PageObjects::Components::SelectKit.new(".persona-selector__dropdown")
persona_selector =
PageObjects::Components::SelectKit.new(".persona-llm-selector__persona-dropdown")
id = DiscourseAi::AiBot::Personas::Persona.all(user: admin).first.id
@ -24,7 +25,8 @@ RSpec.describe "AI personas", type: :system, js: true do
visit "/"
find(".d-header .ai-bot-button").click()
persona_selector = PageObjects::Components::SelectKit.new(".persona-selector__dropdown")
persona_selector =
PageObjects::Components::SelectKit.new(".persona-llm-selector__persona-dropdown")
persona_selector.expand
expect(persona_selector).to have_selected_value(-2)
end