UX: Improve seeded LLM edit page (#856)
This commit is contained in:
parent
0aa2789437
commit
9af0c2e719
|
@ -1,7 +1,7 @@
|
||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { tracked } from "@glimmer/tracking";
|
import { tracked } from "@glimmer/tracking";
|
||||||
import { Input } from "@ember/component";
|
import { Input } from "@ember/component";
|
||||||
import { concat, get } from "@ember/helper";
|
import { concat, get, hash } from "@ember/helper";
|
||||||
import { on } from "@ember/modifier";
|
import { on } from "@ember/modifier";
|
||||||
import { action, computed } from "@ember/object";
|
import { action, computed } from "@ember/object";
|
||||||
import { LinkTo } from "@ember/routing";
|
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"
|
class="ai-llm-editor-input ai-llm-editor__display-name"
|
||||||
@type="text"
|
@type="text"
|
||||||
@value={{@model.display_name}}
|
@value={{@model.display_name}}
|
||||||
|
disabled={{this.seeded}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
|
@ -197,24 +198,26 @@ export default class AiLlmEditorForm extends Component {
|
||||||
class="ai-llm-editor-input ai-llm-editor__name"
|
class="ai-llm-editor-input ai-llm-editor__name"
|
||||||
@type="text"
|
@type="text"
|
||||||
@value={{@model.name}}
|
@value={{@model.name}}
|
||||||
|
disabled={{this.seeded}}
|
||||||
/>
|
/>
|
||||||
<DTooltip
|
<DTooltip
|
||||||
@icon="question-circle"
|
@icon="question-circle"
|
||||||
@content={{I18n.t "discourse_ai.llms.hints.name"}}
|
@content={{i18n "discourse_ai.llms.hints.name"}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label>{{I18n.t "discourse_ai.llms.provider"}}</label>
|
<label>{{i18n "discourse_ai.llms.provider"}}</label>
|
||||||
<ComboBox
|
<ComboBox
|
||||||
@value={{@model.provider}}
|
@value={{@model.provider}}
|
||||||
@content={{this.selectedProviders}}
|
@content={{this.selectedProviders}}
|
||||||
@class="ai-llm-editor__provider"
|
@class="ai-llm-editor__provider"
|
||||||
|
@options={{hash disabled=this.seeded}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{{#unless this.seeded}}
|
{{#unless this.seeded}}
|
||||||
{{#if this.canEditURL}}
|
{{#if this.canEditURL}}
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label>{{I18n.t "discourse_ai.llms.url"}}</label>
|
<label>{{i18n "discourse_ai.llms.url"}}</label>
|
||||||
<Input
|
<Input
|
||||||
class="ai-llm-editor-input ai-llm-editor__url"
|
class="ai-llm-editor-input ai-llm-editor__url"
|
||||||
@type="text"
|
@type="text"
|
||||||
|
@ -224,7 +227,7 @@ export default class AiLlmEditorForm extends Component {
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="control-group">
|
<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">
|
<div class="ai-llm-editor__secret-api-key-group">
|
||||||
<Input
|
<Input
|
||||||
@value={{@model.api_key}}
|
@value={{@model.api_key}}
|
||||||
|
@ -241,7 +244,7 @@ export default class AiLlmEditorForm extends Component {
|
||||||
</div>
|
</div>
|
||||||
{{#each-in this.metaProviderParams as |field type|}}
|
{{#each-in this.metaProviderParams as |field type|}}
|
||||||
<div class="control-group ai-llm-editor-provider-param__{{type}}">
|
<div class="control-group ai-llm-editor-provider-param__{{type}}">
|
||||||
<label>{{I18n.t
|
<label>{{i18n
|
||||||
(concat "discourse_ai.llms.provider_fields." field)
|
(concat "discourse_ai.llms.provider_fields." field)
|
||||||
}}</label>
|
}}</label>
|
||||||
{{#if (eq type "checkbox")}}
|
{{#if (eq type "checkbox")}}
|
||||||
|
@ -258,7 +261,7 @@ export default class AiLlmEditorForm extends Component {
|
||||||
</div>
|
</div>
|
||||||
{{/each-in}}
|
{{/each-in}}
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label>{{I18n.t "discourse_ai.llms.tokenizer"}}</label>
|
<label>{{i18n "discourse_ai.llms.tokenizer"}}</label>
|
||||||
<ComboBox
|
<ComboBox
|
||||||
@value={{@model.tokenizer}}
|
@value={{@model.tokenizer}}
|
||||||
@content={{@llms.resultSetMeta.tokenizers}}
|
@content={{@llms.resultSetMeta.tokenizers}}
|
||||||
|
@ -278,63 +281,64 @@ export default class AiLlmEditorForm extends Component {
|
||||||
/>
|
/>
|
||||||
<DTooltip
|
<DTooltip
|
||||||
@icon="question-circle"
|
@icon="question-circle"
|
||||||
@content={{I18n.t "discourse_ai.llms.hints.max_prompt_tokens"}}
|
@content={{i18n "discourse_ai.llms.hints.max_prompt_tokens"}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group ai-llm-editor__vision-enabled">
|
<div class="control-group ai-llm-editor__vision-enabled">
|
||||||
<Input @type="checkbox" @checked={{@model.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
|
<DTooltip
|
||||||
@icon="question-circle"
|
@icon="question-circle"
|
||||||
@content={{I18n.t "discourse_ai.llms.hints.vision_enabled"}}
|
@content={{i18n "discourse_ai.llms.hints.vision_enabled"}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group ai-llm-editor__enabled-chat-bot">
|
<div class="control-group ai-llm-editor__enabled-chat-bot">
|
||||||
<Input @type="checkbox" @checked={{@model.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
|
<DTooltip
|
||||||
@icon="question-circle"
|
@icon="question-circle"
|
||||||
@content={{I18n.t "discourse_ai.llms.hints.enabled_chat_bot"}}
|
@content={{i18n "discourse_ai.llms.hints.enabled_chat_bot"}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{{#if @model.user}}
|
{{/unless}}
|
||||||
<div class="control-group">
|
|
||||||
<label>{{i18n "discourse_ai.llms.ai_bot_user"}}</label>
|
{{#if @model.user}}
|
||||||
<a
|
<div class="control-group">
|
||||||
class="avatar"
|
<label>{{i18n "discourse_ai.llms.ai_bot_user"}}</label>
|
||||||
href={{@model.user.path}}
|
<a
|
||||||
data-user-card={{@model.user.username}}
|
class="avatar"
|
||||||
>
|
href={{@model.user.path}}
|
||||||
{{Avatar @model.user.avatar_template "small"}}
|
data-user-card={{@model.user.username}}
|
||||||
</a>
|
>
|
||||||
<LinkTo @route="adminUser" @model={{this.adminUser}}>
|
{{Avatar @model.user.avatar_template "small"}}
|
||||||
{{@model.user.username}}
|
</a>
|
||||||
</LinkTo>
|
<LinkTo @route="adminUser" @model={{this.adminUser}}>
|
||||||
</div>
|
{{@model.user.username}}
|
||||||
{{/if}}
|
</LinkTo>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#unless this.seeded}}
|
||||||
<div class="control-group ai-llm-editor__action_panel">
|
<div class="control-group ai-llm-editor__action_panel">
|
||||||
<DButton
|
<DButton
|
||||||
class="ai-llm-editor__test"
|
class="ai-llm-editor__test"
|
||||||
@action={{this.test}}
|
@action={{this.test}}
|
||||||
@disabled={{this.testRunning}}
|
@disabled={{this.testRunning}}
|
||||||
>
|
@label="discourse_ai.llms.tests.title"
|
||||||
{{I18n.t "discourse_ai.llms.tests.title"}}
|
/>
|
||||||
</DButton>
|
|
||||||
|
|
||||||
<DButton
|
<DButton
|
||||||
class="btn-primary ai-llm-editor__save"
|
class="btn-primary ai-llm-editor__save"
|
||||||
@action={{this.save}}
|
@action={{this.save}}
|
||||||
@disabled={{this.isSaving}}
|
@disabled={{this.isSaving}}
|
||||||
>
|
@label="discourse_ai.llms.save"
|
||||||
{{I18n.t "discourse_ai.llms.save"}}
|
/>
|
||||||
</DButton>
|
|
||||||
{{#unless @model.isNew}}
|
{{#unless @model.isNew}}
|
||||||
<DButton
|
<DButton
|
||||||
@action={{this.delete}}
|
@action={{this.delete}}
|
||||||
class="btn-danger ai-llm-editor__delete"
|
class="btn-danger ai-llm-editor__delete"
|
||||||
>
|
@label="discourse_ai.llms.delete"
|
||||||
{{I18n.t "discourse_ai.llms.delete"}}
|
/>
|
||||||
</DButton>
|
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
</div>
|
</div>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
@ -343,12 +347,12 @@ export default class AiLlmEditorForm extends Component {
|
||||||
{{#if this.displayTestResult}}
|
{{#if this.displayTestResult}}
|
||||||
{{#if this.testRunning}}
|
{{#if this.testRunning}}
|
||||||
<div class="spinner small"></div>
|
<div class="spinner small"></div>
|
||||||
{{I18n.t "discourse_ai.llms.tests.running"}}
|
{{i18n "discourse_ai.llms.tests.running"}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if this.testResult}}
|
{{#if this.testResult}}
|
||||||
<div class="ai-llm-editor-tests__success">
|
<div class="ai-llm-editor-tests__success">
|
||||||
{{icon "check"}}
|
{{icon "check"}}
|
||||||
{{I18n.t "discourse_ai.llms.tests.success"}}
|
{{i18n "discourse_ai.llms.tests.success"}}
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="ai-llm-editor-tests__failure">
|
<div class="ai-llm-editor-tests__failure">
|
||||||
|
|
|
@ -135,7 +135,7 @@ export default class AiLlmsListEditor extends Component {
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{#each @llms as |llm|}}
|
{{#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">
|
<td class="column-name">
|
||||||
<h3>{{llm.display_name}}</h3>
|
<h3>{{llm.display_name}}</h3>
|
||||||
<p>
|
<p>
|
||||||
|
@ -149,7 +149,7 @@ export default class AiLlmsListEditor extends Component {
|
||||||
</ul>
|
</ul>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="column-provider">
|
||||||
{{i18n
|
{{i18n
|
||||||
(concat "discourse_ai.llms.providers." llm.provider)
|
(concat "discourse_ai.llms.providers." llm.provider)
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -89,3 +89,14 @@ Fabricator(:ollama_model, from: :llm_model) do
|
||||||
url "http://api.ollama.ai/api/chat"
|
url "http://api.ollama.ai/api/chat"
|
||||||
provider_params { { enable_native_tool: true } }
|
provider_params { { enable_native_tool: true } }
|
||||||
end
|
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
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe "Managing LLM configurations", type: :system do
|
RSpec.describe "Managing LLM configurations", type: :system, js: true do
|
||||||
fab!(:admin)
|
fab!(:admin)
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -69,4 +69,33 @@ RSpec.describe "Managing LLM configurations", type: :system do
|
||||||
expect(llm.vision_enabled).to eq(true)
|
expect(llm.vision_enabled).to eq(true)
|
||||||
expect(llm.user_id).not_to be_nil
|
expect(llm.user_id).not_to be_nil
|
||||||
end
|
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
|
end
|
||||||
|
|
Loading…
Reference in New Issue