UX: Improve seeded LLM edit page (#856)

This commit is contained in:
Keegan George 2024-10-24 05:58:27 +09:00 committed by GitHub
parent 0aa2789437
commit 9af0c2e719
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 85 additions and 41 deletions

View File

@ -1,7 +1,7 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { Input } from "@ember/component";
import { concat, get } from "@ember/helper";
import { concat, get, hash } from "@ember/helper";
import { on } from "@ember/modifier";
import { action, computed } from "@ember/object";
import { LinkTo } from "@ember/routing";
@ -189,6 +189,7 @@ export default class AiLlmEditorForm extends Component {
class="ai-llm-editor-input ai-llm-editor__display-name"
@type="text"
@value={{@model.display_name}}
disabled={{this.seeded}}
/>
</div>
<div class="control-group">
@ -197,24 +198,26 @@ export default class AiLlmEditorForm extends Component {
class="ai-llm-editor-input ai-llm-editor__name"
@type="text"
@value={{@model.name}}
disabled={{this.seeded}}
/>
<DTooltip
@icon="question-circle"
@content={{I18n.t "discourse_ai.llms.hints.name"}}
@content={{i18n "discourse_ai.llms.hints.name"}}
/>
</div>
<div class="control-group">
<label>{{I18n.t "discourse_ai.llms.provider"}}</label>
<label>{{i18n "discourse_ai.llms.provider"}}</label>
<ComboBox
@value={{@model.provider}}
@content={{this.selectedProviders}}
@class="ai-llm-editor__provider"
@options={{hash disabled=this.seeded}}
/>
</div>
{{#unless this.seeded}}
{{#if this.canEditURL}}
<div class="control-group">
<label>{{I18n.t "discourse_ai.llms.url"}}</label>
<label>{{i18n "discourse_ai.llms.url"}}</label>
<Input
class="ai-llm-editor-input ai-llm-editor__url"
@type="text"
@ -224,7 +227,7 @@ export default class AiLlmEditorForm extends Component {
</div>
{{/if}}
<div class="control-group">
<label>{{I18n.t "discourse_ai.llms.api_key"}}</label>
<label>{{i18n "discourse_ai.llms.api_key"}}</label>
<div class="ai-llm-editor__secret-api-key-group">
<Input
@value={{@model.api_key}}
@ -241,7 +244,7 @@ export default class AiLlmEditorForm extends Component {
</div>
{{#each-in this.metaProviderParams as |field type|}}
<div class="control-group ai-llm-editor-provider-param__{{type}}">
<label>{{I18n.t
<label>{{i18n
(concat "discourse_ai.llms.provider_fields." field)
}}</label>
{{#if (eq type "checkbox")}}
@ -258,7 +261,7 @@ export default class AiLlmEditorForm extends Component {
</div>
{{/each-in}}
<div class="control-group">
<label>{{I18n.t "discourse_ai.llms.tokenizer"}}</label>
<label>{{i18n "discourse_ai.llms.tokenizer"}}</label>
<ComboBox
@value={{@model.tokenizer}}
@content={{@llms.resultSetMeta.tokenizers}}
@ -278,63 +281,64 @@ export default class AiLlmEditorForm extends Component {
/>
<DTooltip
@icon="question-circle"
@content={{I18n.t "discourse_ai.llms.hints.max_prompt_tokens"}}
@content={{i18n "discourse_ai.llms.hints.max_prompt_tokens"}}
/>
</div>
<div class="control-group ai-llm-editor__vision-enabled">
<Input @type="checkbox" @checked={{@model.vision_enabled}} />
<label>{{I18n.t "discourse_ai.llms.vision_enabled"}}</label>
<label>{{i18n "discourse_ai.llms.vision_enabled"}}</label>
<DTooltip
@icon="question-circle"
@content={{I18n.t "discourse_ai.llms.hints.vision_enabled"}}
@content={{i18n "discourse_ai.llms.hints.vision_enabled"}}
/>
</div>
<div class="control-group ai-llm-editor__enabled-chat-bot">
<Input @type="checkbox" @checked={{@model.enabled_chat_bot}} />
<label>{{I18n.t "discourse_ai.llms.enabled_chat_bot"}}</label>
<label>{{i18n "discourse_ai.llms.enabled_chat_bot"}}</label>
<DTooltip
@icon="question-circle"
@content={{I18n.t "discourse_ai.llms.hints.enabled_chat_bot"}}
@content={{i18n "discourse_ai.llms.hints.enabled_chat_bot"}}
/>
</div>
{{#if @model.user}}
<div class="control-group">
<label>{{i18n "discourse_ai.llms.ai_bot_user"}}</label>
<a
class="avatar"
href={{@model.user.path}}
data-user-card={{@model.user.username}}
>
{{Avatar @model.user.avatar_template "small"}}
</a>
<LinkTo @route="adminUser" @model={{this.adminUser}}>
{{@model.user.username}}
</LinkTo>
</div>
{{/if}}
{{/unless}}
{{#if @model.user}}
<div class="control-group">
<label>{{i18n "discourse_ai.llms.ai_bot_user"}}</label>
<a
class="avatar"
href={{@model.user.path}}
data-user-card={{@model.user.username}}
>
{{Avatar @model.user.avatar_template "small"}}
</a>
<LinkTo @route="adminUser" @model={{this.adminUser}}>
{{@model.user.username}}
</LinkTo>
</div>
{{/if}}
{{#unless this.seeded}}
<div class="control-group ai-llm-editor__action_panel">
<DButton
class="ai-llm-editor__test"
@action={{this.test}}
@disabled={{this.testRunning}}
>
{{I18n.t "discourse_ai.llms.tests.title"}}
</DButton>
@label="discourse_ai.llms.tests.title"
/>
<DButton
class="btn-primary ai-llm-editor__save"
@action={{this.save}}
@disabled={{this.isSaving}}
>
{{I18n.t "discourse_ai.llms.save"}}
</DButton>
@label="discourse_ai.llms.save"
/>
{{#unless @model.isNew}}
<DButton
@action={{this.delete}}
class="btn-danger ai-llm-editor__delete"
>
{{I18n.t "discourse_ai.llms.delete"}}
</DButton>
@label="discourse_ai.llms.delete"
/>
{{/unless}}
</div>
{{/unless}}
@ -343,12 +347,12 @@ export default class AiLlmEditorForm extends Component {
{{#if this.displayTestResult}}
{{#if this.testRunning}}
<div class="spinner small"></div>
{{I18n.t "discourse_ai.llms.tests.running"}}
{{i18n "discourse_ai.llms.tests.running"}}
{{else}}
{{#if this.testResult}}
<div class="ai-llm-editor-tests__success">
{{icon "check"}}
{{I18n.t "discourse_ai.llms.tests.success"}}
{{i18n "discourse_ai.llms.tests.success"}}
</div>
{{else}}
<div class="ai-llm-editor-tests__failure">

View File

@ -135,7 +135,7 @@ export default class AiLlmsListEditor extends Component {
</thead>
<tbody>
{{#each @llms as |llm|}}
<tr data-persona-id={{llm.id}} class="ai-llm-list__row">
<tr data-llm-id={{llm.name}} class="ai-llm-list__row">
<td class="column-name">
<h3>{{llm.display_name}}</h3>
<p>
@ -149,7 +149,7 @@ export default class AiLlmsListEditor extends Component {
</ul>
{{/if}}
</td>
<td>
<td class="column-provider">
{{i18n
(concat "discourse_ai.llms.providers." llm.provider)
}}

View File

@ -89,3 +89,14 @@ Fabricator(:ollama_model, from: :llm_model) do
url "http://api.ollama.ai/api/chat"
provider_params { { enable_native_tool: true } }
end
Fabricator(:seeded_model, from: :llm_model) do
id "-2"
display_name "CDCK Hosted Model"
name "cdck-hosted"
provider "fake"
api_key "DSC"
tokenizer "DiscourseAi::Tokenizer::OpenAiTokenizer"
url "https://cdck.test/"
enabled_chat_bot true
end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
RSpec.describe "Managing LLM configurations", type: :system do
RSpec.describe "Managing LLM configurations", type: :system, js: true do
fab!(:admin)
before do
@ -69,4 +69,33 @@ RSpec.describe "Managing LLM configurations", type: :system do
expect(llm.vision_enabled).to eq(true)
expect(llm.user_id).not_to be_nil
end
context "when seeded LLM is present" do
fab!(:llm_model) { Fabricate(:seeded_model) }
it "shows the provider as CDCK in the UI" do
visit "/admin/plugins/discourse-ai/ai-llms"
expect(page).to have_css(
"[data-llm-id='cdck-hosted'] .column-provider",
text: I18n.t("js.discourse_ai.llms.providers.CDCK"),
)
end
it "shows an info alert to the user about the seeded LLM" do
visit "/admin/plugins/discourse-ai/ai-llms"
find("[data-llm-id='#{llm_model.name}'] .column-edit .btn").click()
expect(page).to have_css(
".alert.alert-info",
text: I18n.t("js.discourse_ai.llms.seeded_warning"),
)
end
it "limits and shows disabled inputs for the seeded LLM" do
visit "/admin/plugins/discourse-ai/ai-llms"
find("[data-llm-id='cdck-hosted'] .column-edit .btn").click()
expect(page).to have_css(".ai-llm-editor__display-name[disabled]")
expect(page).to have_css(".ai-llm-editor__name[disabled]")
expect(page).to have_css(".ai-llm-editor__provider.is-disabled")
end
end
end