FEATURE: improve visibility of AI usage in LLM page (#845)
This changeset: 1. Corrects some issues with "force_default_llm" not applying 2. Expands the LLM list page to show LLM usage 3. Clarifies better what "enabling a bot" on an llm means (you get it in the selector)
This commit is contained in:
parent
712a07c39b
commit
a1f859a415
|
@ -10,6 +10,8 @@ export default DiscourseRoute.extend({
|
||||||
record.set("rag_chunk_tokens", 374);
|
record.set("rag_chunk_tokens", 374);
|
||||||
record.set("rag_chunk_overlap_tokens", 10);
|
record.set("rag_chunk_overlap_tokens", 10);
|
||||||
record.set("rag_conversation_chunks", 10);
|
record.set("rag_conversation_chunks", 10);
|
||||||
|
record.set("allow_personal_messages", true);
|
||||||
|
record.set("tool_details", false);
|
||||||
return record;
|
return record;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,9 @@ module DiscourseAi
|
||||||
llms,
|
llms,
|
||||||
each_serializer: LlmModelSerializer,
|
each_serializer: LlmModelSerializer,
|
||||||
root: false,
|
root: false,
|
||||||
|
scope: {
|
||||||
|
llm_usage: DiscourseAi::Configuration::LlmEnumerator.global_usage,
|
||||||
|
},
|
||||||
).as_json,
|
).as_json,
|
||||||
meta: {
|
meta: {
|
||||||
provider_params: LlmModel.provider_params,
|
provider_params: LlmModel.provider_params,
|
||||||
|
|
|
@ -22,7 +22,16 @@ class LlmModelSerializer < ApplicationSerializer
|
||||||
has_one :user, serializer: BasicUserSerializer, embed: :object
|
has_one :user, serializer: BasicUserSerializer, embed: :object
|
||||||
|
|
||||||
def used_by
|
def used_by
|
||||||
DiscourseAi::Configuration::LlmValidator.new.modules_using(object)
|
llm_usage =
|
||||||
|
(
|
||||||
|
if (scope && scope[:llm_usage])
|
||||||
|
scope[:llm_usage]
|
||||||
|
else
|
||||||
|
DiscourseAi::Configuration::LlmEnumerator.global_usage
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
llm_usage[object.id]
|
||||||
end
|
end
|
||||||
|
|
||||||
def api_key
|
def api_key
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { later } from "@ember/runloop";
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import { eq } from "truth-helpers";
|
import { eq } from "truth-helpers";
|
||||||
import DButton from "discourse/components/d-button";
|
import DButton from "discourse/components/d-button";
|
||||||
import DToggleSwitch from "discourse/components/d-toggle-switch";
|
|
||||||
import Avatar from "discourse/helpers/bound-avatar-template";
|
import Avatar from "discourse/helpers/bound-avatar-template";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import icon from "discourse-common/helpers/d-icon";
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
|
@ -59,7 +58,20 @@ export default class AiLlmEditorForm extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
get modulesUsingModel() {
|
get modulesUsingModel() {
|
||||||
return this.args.model.used_by?.join(", ");
|
const usedBy = this.args.model.used_by?.filter((m) => m.type !== "ai_bot");
|
||||||
|
|
||||||
|
if (!usedBy || usedBy.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localized = usedBy.map((m) => {
|
||||||
|
return I18n.t(`discourse_ai.llms.usage.${m.type}`, {
|
||||||
|
persona: m.name,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: this is not perfectly localized
|
||||||
|
return localized.join(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
get seeded() {
|
get seeded() {
|
||||||
|
@ -157,20 +169,6 @@ export default class AiLlmEditorForm extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
|
||||||
async toggleEnabledChatBot() {
|
|
||||||
this.args.model.set("enabled_chat_bot", !this.args.model.enabled_chat_bot);
|
|
||||||
if (!this.args.model.isNew) {
|
|
||||||
try {
|
|
||||||
await this.args.model.update({
|
|
||||||
enabled_chat_bot: this.args.model.enabled_chat_bot,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
popupAjaxError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
{{#if this.seeded}}
|
{{#if this.seeded}}
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
|
@ -291,12 +289,12 @@ export default class AiLlmEditorForm extends Component {
|
||||||
@content={{I18n.t "discourse_ai.llms.hints.vision_enabled"}}
|
@content={{I18n.t "discourse_ai.llms.hints.vision_enabled"}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group ai-llm-editor__enabled-chat-bot">
|
||||||
<DToggleSwitch
|
<Input @type="checkbox" @checked={{@model.enabled_chat_bot}} />
|
||||||
class="ai-llm-editor__enabled-chat-bot"
|
<label>{{I18n.t "discourse_ai.llms.enabled_chat_bot"}}</label>
|
||||||
@state={{@model.enabled_chat_bot}}
|
<DTooltip
|
||||||
@label="discourse_ai.llms.enabled_chat_bot"
|
@icon="question-circle"
|
||||||
{{on "click" this.toggleEnabledChatBot}}
|
@content={{I18n.t "discourse_ai.llms.hints.enabled_chat_bot"}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{{#if @model.user}}
|
{{#if @model.user}}
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { concat, fn } from "@ember/helper";
|
import { concat, fn } from "@ember/helper";
|
||||||
import { on } from "@ember/modifier";
|
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { LinkTo } from "@ember/routing";
|
import { LinkTo } from "@ember/routing";
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
|
import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
|
||||||
import DToggleSwitch from "discourse/components/d-toggle-switch";
|
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
|
||||||
import icon from "discourse-common/helpers/d-icon";
|
import icon from "discourse-common/helpers/d-icon";
|
||||||
import i18n from "discourse-common/helpers/i18n";
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
import I18n from "discourse-i18n";
|
import I18n from "discourse-i18n";
|
||||||
|
@ -100,18 +97,13 @@ export default class AiLlmsListEditor extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
localizeUsage(usage) {
|
||||||
async toggleEnabledChatBot(llm) {
|
if (usage.type === "ai_persona") {
|
||||||
const oldValue = llm.enabled_chat_bot;
|
return I18n.t("discourse_ai.llms.usage.ai_persona", {
|
||||||
const newValue = !oldValue;
|
persona: usage.name,
|
||||||
try {
|
|
||||||
llm.set("enabled_chat_bot", newValue);
|
|
||||||
await llm.update({
|
|
||||||
enabled_chat_bot: newValue,
|
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} else {
|
||||||
llm.set("enabled_chat_bot", oldValue);
|
return I18n.t("discourse_ai.llms.usage." + usage.type);
|
||||||
popupAjaxError(err);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +130,6 @@ export default class AiLlmsListEditor extends Component {
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{i18n "discourse_ai.llms.display_name"}}</th>
|
<th>{{i18n "discourse_ai.llms.display_name"}}</th>
|
||||||
<th>{{i18n "discourse_ai.llms.provider"}}</th>
|
<th>{{i18n "discourse_ai.llms.provider"}}</th>
|
||||||
<th>{{i18n "discourse_ai.llms.enabled_chat_bot"}}</th>
|
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -150,18 +141,19 @@ export default class AiLlmsListEditor extends Component {
|
||||||
<p>
|
<p>
|
||||||
{{this.modelDescription llm}}
|
{{this.modelDescription llm}}
|
||||||
</p>
|
</p>
|
||||||
|
{{#if llm.used_by}}
|
||||||
|
<ul class="ai-llm-list-editor__usages">
|
||||||
|
{{#each llm.used_by as |usage|}}
|
||||||
|
<li>{{this.localizeUsage usage}}</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{i18n
|
{{i18n
|
||||||
(concat "discourse_ai.llms.providers." llm.provider)
|
(concat "discourse_ai.llms.providers." llm.provider)
|
||||||
}}
|
}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
|
||||||
<DToggleSwitch
|
|
||||||
@state={{llm.enabled_chat_bot}}
|
|
||||||
{{on "click" (fn this.toggleEnabledChatBot llm)}}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td class="column-edit">
|
<td class="column-edit">
|
||||||
<LinkTo
|
<LinkTo
|
||||||
@route="adminPlugins.show.discourse-ai-llms.show"
|
@route="adminPlugins.show.discourse-ai-llms.show"
|
||||||
|
|
|
@ -336,6 +336,7 @@ export default class PersonaEditor extends Component {
|
||||||
disabled={{this.editingModel.system}}
|
disabled={{this.editingModel.system}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{{#if this.editingModel.user}}
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label>{{I18n.t "discourse_ai.ai_persona.default_llm"}}</label>
|
<label>{{I18n.t "discourse_ai.ai_persona.default_llm"}}</label>
|
||||||
<AiLlmSelector
|
<AiLlmSelector
|
||||||
|
@ -358,6 +359,7 @@ export default class PersonaEditor extends Component {
|
||||||
{{I18n.t "discourse_ai.ai_persona.force_default_llm"}}</label>
|
{{I18n.t "discourse_ai.ai_persona.force_default_llm"}}</label>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
{{#unless @model.isNew}}
|
{{#unless @model.isNew}}
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label>{{I18n.t "discourse_ai.ai_persona.user"}}</label>
|
<label>{{I18n.t "discourse_ai.ai_persona.user"}}</label>
|
||||||
|
|
|
@ -58,7 +58,13 @@ export default class BotSelector extends Component {
|
||||||
this.setAllowLLMSelector();
|
this.setAllowLLMSelector();
|
||||||
|
|
||||||
let llm = this.preferredLlmStore.getObject("id");
|
let llm = this.preferredLlmStore.getObject("id");
|
||||||
llm = llm || this.llmOptions[0].id;
|
|
||||||
|
const llmOption =
|
||||||
|
this.llmOptions.find((innerLlmOption) => innerLlmOption.id === llm) ||
|
||||||
|
this.llmOptions[0];
|
||||||
|
|
||||||
|
llm = llmOption.id;
|
||||||
|
|
||||||
if (llm) {
|
if (llm) {
|
||||||
next(() => {
|
next(() => {
|
||||||
this.currentLlm = llm;
|
this.currentLlm = llm;
|
||||||
|
@ -96,6 +102,7 @@ export default class BotSelector extends Component {
|
||||||
this.preferredPersonaStore.setObject({ key: "id", value: newValue });
|
this.preferredPersonaStore.setObject({ key: "id", value: newValue });
|
||||||
this.composer.metaData = { ai_persona_id: newValue };
|
this.composer.metaData = { ai_persona_id: newValue };
|
||||||
this.setAllowLLMSelector();
|
this.setAllowLLMSelector();
|
||||||
|
this.resetTargetRecipients();
|
||||||
}
|
}
|
||||||
|
|
||||||
setAllowLLMSelector() {
|
setAllowLLMSelector() {
|
||||||
|
@ -112,11 +119,16 @@ export default class BotSelector extends Component {
|
||||||
|
|
||||||
set currentLlm(newValue) {
|
set currentLlm(newValue) {
|
||||||
this.llm = newValue;
|
this.llm = newValue;
|
||||||
|
this.preferredLlmStore.setObject({ key: "id", value: newValue });
|
||||||
|
|
||||||
|
this.resetTargetRecipients();
|
||||||
|
}
|
||||||
|
|
||||||
|
resetTargetRecipients() {
|
||||||
|
if (this.allowLLMSelector) {
|
||||||
const botUsername = this.currentUser.ai_enabled_chat_bots.find(
|
const botUsername = this.currentUser.ai_enabled_chat_bots.find(
|
||||||
(bot) => bot.model_name === this.llm
|
(bot) => bot.model_name === this.llm
|
||||||
).username;
|
).username;
|
||||||
this.preferredLlmStore.setObject({ key: "id", value: newValue });
|
|
||||||
if (this.allowLLMSelector) {
|
|
||||||
this.composer.set("targetRecipients", botUsername);
|
this.composer.set("targetRecipients", botUsername);
|
||||||
} else {
|
} else {
|
||||||
const persona = this.currentUser.ai_enabled_personas.find(
|
const persona = this.currentUser.ai_enabled_personas.find(
|
||||||
|
|
|
@ -51,7 +51,8 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__vision-enabled {
|
&__vision-enabled,
|
||||||
|
&__enabled-chat-bot {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
@ -150,3 +151,17 @@
|
||||||
letter-spacing: 0.1px;
|
letter-spacing: 0.1px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-llm-list-editor__usages {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0.5em 0 0 0;
|
||||||
|
display: flex;
|
||||||
|
li {
|
||||||
|
font-size: var(--font-down-2);
|
||||||
|
border-radius: 0.25em;
|
||||||
|
background: var(--primary-very-low);
|
||||||
|
border: 1px solid var(--primary-low);
|
||||||
|
padding: 1px 3px;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -252,7 +252,7 @@ en:
|
||||||
max_prompt_tokens: "Number of tokens for the prompt"
|
max_prompt_tokens: "Number of tokens for the prompt"
|
||||||
url: "URL of the service hosting the model"
|
url: "URL of the service hosting the model"
|
||||||
api_key: "API Key of the service hosting the model"
|
api_key: "API Key of the service hosting the model"
|
||||||
enabled_chat_bot: "Allow AI Bot"
|
enabled_chat_bot: "Allow AI Bot selector"
|
||||||
vision_enabled: "Vision enabled"
|
vision_enabled: "Vision enabled"
|
||||||
ai_bot_user: "AI Bot User"
|
ai_bot_user: "AI Bot User"
|
||||||
save: "Save"
|
save: "Save"
|
||||||
|
@ -262,9 +262,14 @@ en:
|
||||||
confirm_delete: Are you sure you want to delete this model?
|
confirm_delete: Are you sure you want to delete this model?
|
||||||
delete: Delete
|
delete: Delete
|
||||||
seeded_warning: "This model is pre-configured on your site and cannot be edited."
|
seeded_warning: "This model is pre-configured on your site and cannot be edited."
|
||||||
|
usage:
|
||||||
|
ai_bot: "AI Bot"
|
||||||
|
ai_helper: "AI Helper"
|
||||||
|
ai_persona: "Persona (%{persona})"
|
||||||
|
ai_summarization: "Summarization"
|
||||||
in_use_warning:
|
in_use_warning:
|
||||||
one: "This model is currently used by the %{settings} setting. If misconfigured, the feature won't work as expected."
|
one: "This model is currently used by %{settings}. If misconfigured, the feature won't work as expected."
|
||||||
other: "This model is currently used by the following settings: %{settings}. If misconfigured, features won't work as expected. "
|
other: "This model is currently used by the following: %{settings}. If misconfigured, features won't work as expected. "
|
||||||
|
|
||||||
model_description:
|
model_description:
|
||||||
none: "General settings that work for most language models"
|
none: "General settings that work for most language models"
|
||||||
|
@ -299,7 +304,7 @@ en:
|
||||||
max_prompt_tokens: "Max numbers of tokens for the prompt. As a rule of thumb, this should be 50% of the model's context window."
|
max_prompt_tokens: "Max numbers of tokens for the prompt. As a rule of thumb, this should be 50% of the model's context window."
|
||||||
name: "We include this in the API call to specify which model we'll use."
|
name: "We include this in the API call to specify which model we'll use."
|
||||||
vision_enabled: "If enabled, the AI will attempt to understand images. It depends on the model being used supporting vision. Supported by latest models from Anthropic, Google, and OpenAI."
|
vision_enabled: "If enabled, the AI will attempt to understand images. It depends on the model being used supporting vision. Supported by latest models from Anthropic, Google, and OpenAI."
|
||||||
|
enabled_chat_bot: "If enabled, users can select this model when creating PMs with the AI bot."
|
||||||
providers:
|
providers:
|
||||||
aws_bedrock: "AWS Bedrock"
|
aws_bedrock: "AWS Bedrock"
|
||||||
anthropic: "Anthropic"
|
anthropic: "Anthropic"
|
||||||
|
|
|
@ -5,6 +5,42 @@ require "enum_site_setting"
|
||||||
module DiscourseAi
|
module DiscourseAi
|
||||||
module Configuration
|
module Configuration
|
||||||
class LlmEnumerator < ::EnumSiteSetting
|
class LlmEnumerator < ::EnumSiteSetting
|
||||||
|
def self.global_usage
|
||||||
|
rval = Hash.new { |h, k| h[k] = [] }
|
||||||
|
|
||||||
|
if SiteSetting.ai_bot_enabled
|
||||||
|
LlmModel
|
||||||
|
.where("enabled_chat_bot = ?", true)
|
||||||
|
.pluck(:id)
|
||||||
|
.each { |llm_id| rval[llm_id] << { type: :ai_bot } }
|
||||||
|
|
||||||
|
AiPersona
|
||||||
|
.where("force_default_llm = ?", true)
|
||||||
|
.pluck(:default_llm, :name, :id)
|
||||||
|
.each do |llm_name, name, id|
|
||||||
|
llm_id = llm_name.split(":").last.to_i
|
||||||
|
rval[llm_id] << { type: :ai_persona, name: name, id: id }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if SiteSetting.ai_helper_enabled
|
||||||
|
model_id = SiteSetting.ai_helper_model.split(":").last.to_i
|
||||||
|
rval[model_id] << { type: :ai_helper }
|
||||||
|
end
|
||||||
|
|
||||||
|
if SiteSetting.ai_summarization_enabled
|
||||||
|
model_id = SiteSetting.ai_summarization_model.split(":").last.to_i
|
||||||
|
rval[model_id] << { type: :ai_summarization }
|
||||||
|
end
|
||||||
|
|
||||||
|
if SiteSetting.ai_embeddings_semantic_search_enabled
|
||||||
|
model_id = SiteSetting.ai_embeddings_semantic_search_hyde_model.split(":").last.to_i
|
||||||
|
rval[model_id] << { type: :ai_embeddings_semantic_search }
|
||||||
|
end
|
||||||
|
|
||||||
|
rval
|
||||||
|
end
|
||||||
|
|
||||||
def self.valid_value?(val)
|
def self.valid_value?(val)
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,17 @@ RSpec.describe DiscourseAi::Admin::AiLlmsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET #index" do
|
describe "GET #index" do
|
||||||
|
fab!(:llm_model) { Fabricate(:llm_model, enabled_chat_bot: true) }
|
||||||
|
fab!(:llm_model2) { Fabricate(:llm_model) }
|
||||||
|
fab!(:ai_persona) do
|
||||||
|
Fabricate(
|
||||||
|
:ai_persona,
|
||||||
|
name: "Cool persona",
|
||||||
|
force_default_llm: true,
|
||||||
|
default_llm: "custom:#{llm_model2.id}",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
it "includes all available providers metadata" do
|
it "includes all available providers metadata" do
|
||||||
get "/admin/plugins/discourse-ai/ai-llms.json"
|
get "/admin/plugins/discourse-ai/ai-llms.json"
|
||||||
expect(response).to be_successful
|
expect(response).to be_successful
|
||||||
|
@ -17,6 +28,44 @@ RSpec.describe DiscourseAi::Admin::AiLlmsController do
|
||||||
*DiscourseAi::Completions::Llm.provider_names,
|
*DiscourseAi::Completions::Llm.provider_names,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "lists enabled features on appropriate LLMs" do
|
||||||
|
SiteSetting.ai_bot_enabled = true
|
||||||
|
|
||||||
|
# setting the setting calls the model
|
||||||
|
DiscourseAi::Completions::Llm.with_prepared_responses(["OK"]) do
|
||||||
|
SiteSetting.ai_helper_model = "custom:#{llm_model.id}"
|
||||||
|
SiteSetting.ai_helper_enabled = true
|
||||||
|
end
|
||||||
|
|
||||||
|
DiscourseAi::Completions::Llm.with_prepared_responses(["OK"]) do
|
||||||
|
SiteSetting.ai_summarization_model = "custom:#{llm_model2.id}"
|
||||||
|
SiteSetting.ai_summarization_enabled = true
|
||||||
|
end
|
||||||
|
|
||||||
|
DiscourseAi::Completions::Llm.with_prepared_responses(["OK"]) do
|
||||||
|
SiteSetting.ai_embeddings_semantic_search_hyde_model = "custom:#{llm_model2.id}"
|
||||||
|
SiteSetting.ai_embeddings_semantic_search_enabled = true
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/admin/plugins/discourse-ai/ai-llms.json"
|
||||||
|
|
||||||
|
llms = response.parsed_body["ai_llms"]
|
||||||
|
|
||||||
|
model_json = llms.find { |m| m["id"] == llm_model.id }
|
||||||
|
expect(model_json["used_by"]).to contain_exactly(
|
||||||
|
{ "type" => "ai_bot" },
|
||||||
|
{ "type" => "ai_helper" },
|
||||||
|
)
|
||||||
|
|
||||||
|
model2_json = llms.find { |m| m["id"] == llm_model2.id }
|
||||||
|
|
||||||
|
expect(model2_json["used_by"]).to contain_exactly(
|
||||||
|
{ "type" => "ai_persona", "name" => "Cool persona", "id" => ai_persona.id },
|
||||||
|
{ "type" => "ai_summarization" },
|
||||||
|
{ "type" => "ai_embeddings_semantic_search" },
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST #create" do
|
describe "POST #create" do
|
||||||
|
|
|
@ -12,11 +12,8 @@ RSpec.describe "Managing LLM configurations", type: :system do
|
||||||
visit "/admin/plugins/discourse-ai/ai-llms"
|
visit "/admin/plugins/discourse-ai/ai-llms"
|
||||||
|
|
||||||
find("[data-llm-id='anthropic-claude-3-haiku'] button").click()
|
find("[data-llm-id='anthropic-claude-3-haiku'] button").click()
|
||||||
|
|
||||||
find("input.ai-llm-editor__api-key").fill_in(with: "abcd")
|
find("input.ai-llm-editor__api-key").fill_in(with: "abcd")
|
||||||
|
find(".ai-llm-editor__enabled-chat-bot input").click
|
||||||
PageObjects::Components::DToggleSwitch.new(".ai-llm-editor__enabled-chat-bot").toggle
|
|
||||||
|
|
||||||
find(".ai-llm-editor__save").click()
|
find(".ai-llm-editor__save").click()
|
||||||
|
|
||||||
expect(page).to have_current_path("/admin/plugins/discourse-ai/ai-llms")
|
expect(page).to have_current_path("/admin/plugins/discourse-ai/ai-llms")
|
||||||
|
@ -55,8 +52,7 @@ RSpec.describe "Managing LLM configurations", type: :system do
|
||||||
find(".select-kit-row[data-name=\"Llama3Tokenizer\"]").click
|
find(".select-kit-row[data-name=\"Llama3Tokenizer\"]").click
|
||||||
|
|
||||||
find(".ai-llm-editor__vision-enabled input").click
|
find(".ai-llm-editor__vision-enabled input").click
|
||||||
|
find(".ai-llm-editor__enabled-chat-bot input").click
|
||||||
PageObjects::Components::DToggleSwitch.new(".ai-llm-editor__enabled-chat-bot").toggle
|
|
||||||
|
|
||||||
find(".ai-llm-editor__save").click()
|
find(".ai-llm-editor__save").click()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue