import Component from "@glimmer/component"; import { tracked } from "@glimmer/tracking"; import { Input, Textarea } from "@ember/component"; import { concat, fn, get } from "@ember/helper"; import { on } from "@ember/modifier"; import { action, computed } from "@ember/object"; import didInsert from "@ember/render-modifiers/modifiers/did-insert"; import didUpdate from "@ember/render-modifiers/modifiers/did-update"; import { later } from "@ember/runloop"; import { service } from "@ember/service"; import BackButton from "discourse/components/back-button"; import DButton from "discourse/components/d-button"; import icon from "discourse/helpers/d-icon"; import { popupAjaxError } from "discourse/lib/ajax-error"; import { i18n } from "discourse-i18n"; import AdminSectionLandingItem from "admin/components/admin-section-landing-item"; import AdminSectionLandingWrapper from "admin/components/admin-section-landing-wrapper"; import ComboBox from "select-kit/components/combo-box"; import DTooltip from "float-kit/components/d-tooltip"; import not from "truth-helpers/helpers/not"; export default class AiEmbeddingEditor extends Component { @service toasts; @service router; @service dialog; @service store; @tracked isSaving = false; @tracked selectedPreset = null; @tracked testRunning = false; @tracked testResult = null; @tracked testError = null; @tracked apiKeySecret = true; @tracked editingModel = null; get selectedProviders() { const t = (provName) => { return i18n(`discourse_ai.embeddings.providers.${provName}`); }; return this.args.embeddings.resultSetMeta.providers.map((prov) => { return { id: prov, name: t(prov) }; }); } get distanceFunctions() { const t = (df) => { return i18n(`discourse_ai.embeddings.distance_functions.${df}`); }; return this.args.embeddings.resultSetMeta.distance_functions.map((df) => { let iconName; if (df === "<=>") { iconName = "discourse-spaceship-operator"; } else if (df === "<#>") { iconName = "discourse-negative-inner-product"; } return { id: df, name: t(df), icon: iconName, }; }); } get presets() { const presets = this.args.embeddings.resultSetMeta.presets.map((preset) => { return { name: preset.display_name, id: preset.preset_id, provider: preset.provider, }; }); presets.unshiftObject({ name: i18n("discourse_ai.embeddings.configure_manually"), id: "manual", provider: "fake", }); return presets; } get showPresets() { return !this.selectedPreset && this.args.model.isNew; } @computed("editingModel.provider") get metaProviderParams() { return ( this.args.embeddings.resultSetMeta.provider_params[ this.editingModel?.provider ] || {} ); } get testErrorMessage() { return i18n("discourse_ai.llms.tests.failure", { error: this.testError }); } get displayTestResult() { return this.testRunning || this.testResult !== null; } @action configurePreset(preset) { this.selectedPreset = this.args.embeddings.resultSetMeta.presets.findBy( "preset_id", preset.id ) || {}; this.editingModel = this.store .createRecord("ai-embedding", this.selectedPreset) .workingCopy(); } @action updateModel() { this.editingModel = this.args.model.workingCopy(); } @action makeApiKeySecret() { this.apiKeySecret = true; } @action toggleApiKeySecret() { this.apiKeySecret = !this.apiKeySecret; } @action async save() { this.isSaving = true; const isNew = this.args.model.isNew; try { await this.editingModel.save(); if (isNew) { this.args.embeddings.addObject(this.editingModel); this.router.transitionTo( "adminPlugins.show.discourse-ai-embeddings.index" ); } else { this.toasts.success({ data: { message: i18n("discourse_ai.embeddings.saved") }, duration: 2000, }); } } catch (e) { popupAjaxError(e); } finally { later(() => { this.isSaving = false; }, 1000); } } @action async test() { this.testRunning = true; try { const configTestResult = await this.editingModel.testConfig(); this.testResult = configTestResult.success; if (this.testResult) { this.testError = null; } else { this.testError = configTestResult.error; } } catch (e) { popupAjaxError(e); } finally { later(() => { this.testRunning = false; }, 1000); } } @action delete() { return this.dialog.confirm({ message: i18n("discourse_ai.embeddings.confirm_delete"), didConfirm: () => { return this.args.model .destroyRecord() .then(() => { this.args.llms.removeObject(this.args.model); this.router.transitionTo( "adminPlugins.show.discourse-ai-embeddings.index" ); }) .catch(popupAjaxError); }, }); } @action resetForm() { this.selectedPreset = null; this.editingModel = null; }