discourse-ai/assets/javascripts/discourse/components/ai-llms-list-editor.gjs

215 lines
6.7 KiB
Plaintext

import Component from "@glimmer/component";
import { concat, fn } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { LinkTo } from "@ember/routing";
import { inject as service } from "@ember/service";
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 i18n from "discourse-common/helpers/i18n";
import I18n from "discourse-i18n";
import AdminPageSubheader from "admin/components/admin-page-subheader";
import AdminSectionLandingItem from "admin/components/admin-section-landing-item";
import AdminSectionLandingWrapper from "admin/components/admin-section-landing-wrapper";
import AiLlmEditor from "./ai-llm-editor";
export default class AiLlmsListEditor extends Component {
@service adminPluginNavManager;
@service router;
@action
modelDescription(llm) {
// this is a bit of an odd object, it can be an llm model or a preset model
// handle both flavors
// in the case of model
let key = "";
if (typeof llm.id === "number") {
key = `${llm.provider}-${llm.name}`;
} else {
// case of preset
key = llm.id.replace(/\./g, "-");
}
key = `discourse_ai.llms.model_description.${key}`;
if (I18n.lookup(key, { ignoreMissing: true })) {
return I18n.t(key);
}
return "";
}
sanitizedTranslationKey(id) {
return id.replace(/\./g, "-");
}
get hasLlmElements() {
return this.args.llms.length !== 0;
}
get preconfiguredTitle() {
if (this.hasLlmElements) {
return "discourse_ai.llms.preconfigured.title";
} else {
return "discourse_ai.llms.preconfigured.title_no_llms";
}
}
get preConfiguredLlms() {
const options = [
{
id: "none",
name: I18n.t("discourse_ai.llms.preconfigured.fake"),
provider: "fake",
},
];
const llmsContent = this.args.llms.content.map((llm) => ({
provider: llm.provider,
name: llm.name,
}));
this.args.llms.resultSetMeta.presets.forEach((llm) => {
if (llm.models) {
llm.models.forEach((model) => {
const id = `${llm.id}-${model.name}`;
const isConfigured = llmsContent.some(
(content) =>
content.provider === llm.provider && content.name === model.name
);
if (!isConfigured) {
options.push({
id,
name: model.display_name,
provider: llm.provider,
});
}
});
}
});
return options;
}
@action
transitionToLlmEditor(llmTemplate) {
this.router.transitionTo("adminPlugins.show.discourse-ai-llms.new", {
queryParams: { llmTemplate },
});
}
@action
async toggleEnabledChatBot(llm) {
const oldValue = llm.enabled_chat_bot;
const newValue = !oldValue;
try {
llm.set("enabled_chat_bot", newValue);
await llm.update({
enabled_chat_bot: newValue,
});
} catch (err) {
llm.set("enabled_chat_bot", oldValue);
popupAjaxError(err);
}
}
<template>
<DBreadcrumbsItem
@path="/admin/plugins/{{this.adminPluginNavManager.currentPlugin.name}}/ai-llms"
@label={{i18n "discourse_ai.llms.short_title"}}
/>
<section class="ai-llm-list-editor admin-detail">
{{#if @currentLlm}}
<AiLlmEditor
@model={{@currentLlm}}
@llms={{@llms}}
@llmTemplate={{@llmTemplate}}
/>
{{else}}
{{#if this.hasLlmElements}}
<section class="ai-llms-list-editor__configured">
<AdminPageSubheader
@titleLabel="discourse_ai.llms.configured.title"
/>
<table>
<thead>
<tr>
<th>{{i18n "discourse_ai.llms.display_name"}}</th>
<th>{{i18n "discourse_ai.llms.provider"}}</th>
<th>{{i18n "discourse_ai.llms.enabled_chat_bot"}}</th>
<th></th>
</tr>
</thead>
<tbody>
{{#each @llms as |llm|}}
<tr data-persona-id={{llm.id}} class="ai-llm-list__row">
<td class="column-name">
<h3>{{llm.display_name}}</h3>
<p>
{{this.modelDescription llm}}
</p>
</td>
<td>
{{i18n
(concat "discourse_ai.llms.providers." llm.provider)
}}
</td>
<td>
<DToggleSwitch
@state={{llm.enabled_chat_bot}}
{{on "click" (fn this.toggleEnabledChatBot llm)}}
/>
</td>
<td class="column-edit">
<LinkTo
@route="adminPlugins.show.discourse-ai-llms.show"
class="btn btn-default"
@model={{llm.id}}
>
{{icon "wrench"}}
<div class="d-button-label">
{{i18n "discourse_ai.llms.edit"}}
</div>
</LinkTo>
</td>
</tr>
{{/each}}
</tbody>
</table>
</section>
{{/if}}
<section class="ai-llms-list-editor__templates">
<AdminPageSubheader @titleLabel={{this.preconfiguredTitle}} />
<AdminSectionLandingWrapper
class="ai-llms-list-editor__templates-list"
>
{{#each this.preConfiguredLlms as |llm|}}
<AdminSectionLandingItem
@titleLabelTranslated={{llm.name}}
@descriptionLabelTranslated={{this.modelDescription llm}}
@taglineLabel={{concat
"discourse_ai.llms.providers."
llm.provider
}}
data-llm-id={{llm.id}}
class="ai-llms-list-editor__templates-list-item"
>
<:buttons as |buttons|>
<buttons.Default
@action={{fn this.transitionToLlmEditor llm.id}}
@icon="gear"
@label="discourse_ai.llms.preconfigured.button"
/>
</:buttons>
</AdminSectionLandingItem>
{{/each}}
</AdminSectionLandingWrapper>
</section>
{{/if}}
</section>
</template>
}