From b6483e416dadcbad69aed301e8f4660f622b109b Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 18 Mar 2025 18:07:04 +1100 Subject: [PATCH] Revert "DEV: Convert tool editor to form kit (#1135)" (#1201) This reverts commit 107f14456b0e4b51fd2b934ee7cceced78b2e0cc. enum was not handled, so reverting for now --- ...min-plugins-show-discourse-ai-tools-new.js | 6 +- .../show/discourse-ai-tools/new.hbs | 1 - .../components/ai-tool-editor-form.gjs | 317 ------------------ .../discourse/components/ai-tool-editor.gjs | 282 +++++++++++++++- .../components/ai-tool-list-editor.gjs | 55 +-- .../components/ai-tool-parameter-editor.gjs | 155 +++++++++ .../discourse/components/rag-uploader.gjs | 5 +- .../modules/ai-bot/common/ai-persona.scss | 9 +- .../modules/ai-bot/common/ai-tools.scss | 53 ++- config/locales/client.en.yml | 7 +- spec/system/ai_bot/tool_spec.rb | 19 +- 11 files changed, 491 insertions(+), 418 deletions(-) delete mode 100644 assets/javascripts/discourse/components/ai-tool-editor-form.gjs create mode 100644 assets/javascripts/discourse/components/ai-tool-parameter-editor.gjs diff --git a/admin/assets/javascripts/discourse/routes/admin-plugins-show-discourse-ai-tools-new.js b/admin/assets/javascripts/discourse/routes/admin-plugins-show-discourse-ai-tools-new.js index 463ae21a..1e645097 100644 --- a/admin/assets/javascripts/discourse/routes/admin-plugins-show-discourse-ai-tools-new.js +++ b/admin/assets/javascripts/discourse/routes/admin-plugins-show-discourse-ai-tools-new.js @@ -1,10 +1,6 @@ import DiscourseRoute from "discourse/routes/discourse"; export default class DiscourseAiToolsNewRoute extends DiscourseRoute { - beforeModel(transition) { - this.preset = transition.to.queryParams.presetId || "empty_tool"; - } - async model() { return this.store.createRecord("ai-tool"); } @@ -12,10 +8,10 @@ export default class DiscourseAiToolsNewRoute extends DiscourseRoute { setupController(controller) { super.setupController(...arguments); const toolsModel = this.modelFor("adminPlugins.show.discourse-ai-tools"); + controller.set("allTools", toolsModel); controller.set("presets", toolsModel.resultSetMeta.presets); controller.set("llms", toolsModel.resultSetMeta.llms); controller.set("settings", toolsModel.resultSetMeta.settings); - controller.set("selectedPreset", this.preset); } } diff --git a/admin/assets/javascripts/discourse/templates/admin-plugins/show/discourse-ai-tools/new.hbs b/admin/assets/javascripts/discourse/templates/admin-plugins/show/discourse-ai-tools/new.hbs index cc036e76..444f6966 100644 --- a/admin/assets/javascripts/discourse/templates/admin-plugins/show/discourse-ai-tools/new.hbs +++ b/admin/assets/javascripts/discourse/templates/admin-plugins/show/discourse-ai-tools/new.hbs @@ -5,6 +5,5 @@ @presets={{this.presets}} @llms={{this.llms}} @settings={{this.settings}} - @selectedPreset={{this.selectedPreset}} /> \ No newline at end of file diff --git a/assets/javascripts/discourse/components/ai-tool-editor-form.gjs b/assets/javascripts/discourse/components/ai-tool-editor-form.gjs deleted file mode 100644 index aed8e30d..00000000 --- a/assets/javascripts/discourse/components/ai-tool-editor-form.gjs +++ /dev/null @@ -1,317 +0,0 @@ -import Component from "@glimmer/component"; -import { tracked } from "@glimmer/tracking"; -import { fn, hash } from "@ember/helper"; -import { action } from "@ember/object"; -import { service } from "@ember/service"; -import Form from "discourse/components/form"; -import { popupAjaxError } from "discourse/lib/ajax-error"; -import { i18n } from "discourse-i18n"; -import AiToolTestModal from "./modal/ai-tool-test-modal"; -import RagOptions from "./rag-options"; -import RagUploader from "./rag-uploader"; - -export default class AiToolEditorForm extends Component { - @service modal; - @service siteSettings; - @service dialog; - @service router; - @service toasts; - - @tracked uploadedFiles = []; - @tracked isSaving = false; - - PARAMETER_TYPES = [ - { name: "string", id: "string" }, - { name: "number", id: "number" }, - { name: "boolean", id: "boolean" }, - { name: "array", id: "array" }, - ]; - - get formData() { - return { - name: this.args.editingModel.name || "", - tool_name: this.args.editingModel.tool_name || "", - description: this.args.editingModel.description || "", - summary: this.args.editingModel.summary || "", - parameters: this.args.editingModel.parameters || [], - script: this.args.editingModel.script || "", - rag_uploads: this.args.editingModel.rag_uploads || [], - }; - } - - @action - async save(data) { - this.isSaving = true; - - try { - await this.args.model.save(data); - - this.toasts.success({ - data: { message: i18n("discourse_ai.tools.saved") }, - duration: 2000, - }); - - if (!this.args.tools.any((tool) => tool.id === this.args.model.id)) { - this.args.tools.pushObject(this.args.model); - } - - this.router.transitionTo( - "adminPlugins.show.discourse-ai-tools.edit", - this.args.model - ); - } catch (e) { - popupAjaxError(e); - } finally { - this.isSaving = false; - } - } - - @action - delete() { - return this.dialog.confirm({ - message: i18n("discourse_ai.tools.confirm_delete"), - - didConfirm: async () => { - await this.args.model.destroyRecord(); - this.args.tools.removeObject(this.args.model); - this.router.transitionTo("adminPlugins.show.discourse-ai-tools.index"); - }, - }); - } - - @action - updateUploads(addItemToCollection, uploads) { - const uniqueUploads = uploads.filter( - (upload) => !this.uploadedFiles.some((file) => file.id === upload.id) - ); - addItemToCollection("rag_uploads", uniqueUploads); - this.uploadedFiles = [...this.uploadedFiles, ...uniqueUploads]; - } - - @action - removeUpload(form, upload) { - this.uploadedFiles = this.uploadedFiles.filter( - (file) => file.id !== upload.id - ); - form.set("rag_uploads", this.uploadedFiles); - } - - @action - openTestModal() { - this.modal.show(AiToolTestModal, { - model: { - tool: this.args.editingModel, - }, - }); - } - - currentParameterSelection(data, index) { - return data.parameters[index].type; - } - - get ragUploadsDescription() { - return this.siteSettings.rag_images_enabled - ? i18n("discourse_ai.rag.uploads.description_with_images") - : i18n("discourse_ai.rag.uploads.description"); - } - - -} diff --git a/assets/javascripts/discourse/components/ai-tool-editor.gjs b/assets/javascripts/discourse/components/ai-tool-editor.gjs index 98fffe5b..0a3b4922 100644 --- a/assets/javascripts/discourse/components/ai-tool-editor.gjs +++ b/assets/javascripts/discourse/components/ai-tool-editor.gjs @@ -1,39 +1,285 @@ import Component from "@glimmer/component"; +import { tracked } from "@glimmer/tracking"; +import { fn } from "@ember/helper"; +import { on } from "@ember/modifier"; +import { action } from "@ember/object"; +import didInsert from "@ember/render-modifiers/modifiers/did-insert"; +import didUpdate from "@ember/render-modifiers/modifiers/did-update"; import { service } from "@ember/service"; +import AceEditor from "discourse/components/ace-editor"; import BackButton from "discourse/components/back-button"; -import AiToolEditorForm from "./ai-tool-editor-form"; +import DButton from "discourse/components/d-button"; +import DTooltip from "discourse/components/d-tooltip"; +import withEventValue from "discourse/helpers/with-event-value"; +import { popupAjaxError } from "discourse/lib/ajax-error"; +import { i18n } from "discourse-i18n"; +import ComboBox from "select-kit/components/combo-box"; +import AiToolParameterEditor from "./ai-tool-parameter-editor"; +import AiToolTestModal from "./modal/ai-tool-test-modal"; +import RagOptions from "./rag-options"; +import RagUploader from "./rag-uploader"; + +const ACE_EDITOR_MODE = "javascript"; +const ACE_EDITOR_THEME = "chrome"; export default class AiToolEditor extends Component { + @service router; + @service dialog; + @service modal; + @service toasts; @service store; + @service siteSettings; - get selectedPreset() { - if (!this.args.selectedPreset) { - return this.args.presets.findBy("preset_id", "empty_tool"); - } + @tracked isSaving = false; + @tracked editingModel = null; + @tracked showDelete = false; + @tracked selectedPreset = null; - return this.args.presets.findBy("preset_id", this.args.selectedPreset); + get presets() { + return this.args.presets.map((preset) => { + return { + name: preset.preset_name, + id: preset.preset_id, + }; + }); } - get editingModel() { - if (this.args.model.isNew) { - return this.store.createRecord("ai-tool", this.selectedPreset); - } else { - return this.args.model; + get showPresets() { + return !this.selectedPreset && this.args.model.isNew; + } + + @action + updateModel() { + this.editingModel = this.args.model.workingCopy(); + this.showDelete = !this.args.model.isNew; + } + + @action + configurePreset() { + this.selectedPreset = this.args.presets.findBy("preset_id", this.presetId); + this.editingModel = this.store + .createRecord("ai-tool", this.selectedPreset) + .workingCopy(); + this.showDelete = false; + } + + @action + updateUploads(uploads) { + this.editingModel.rag_uploads = uploads; + } + + @action + removeUpload(upload) { + this.editingModel.rag_uploads.removeObject(upload); + if (!this.args.model.isNew) { + this.save(); } } + @action + async save() { + this.isSaving = true; + + try { + const data = this.editingModel.getProperties( + "name", + "tool_name", + "description", + "parameters", + "script", + "summary", + "rag_uploads", + "rag_chunk_tokens", + "rag_chunk_overlap_tokens", + "rag_llm_model_id" + ); + + await this.args.model.save(data); + + this.toasts.success({ + data: { message: i18n("discourse_ai.tools.saved") }, + duration: 2000, + }); + if (!this.args.tools.any((tool) => tool.id === this.args.model.id)) { + this.args.tools.pushObject(this.args.model); + } + + this.router.transitionTo( + "adminPlugins.show.discourse-ai-tools.edit", + this.args.model + ); + } catch (e) { + popupAjaxError(e); + } finally { + this.isSaving = false; + } + } + + @action + delete() { + return this.dialog.confirm({ + message: i18n("discourse_ai.tools.confirm_delete"), + didConfirm: async () => { + await this.args.model.destroyRecord(); + + this.args.tools.removeObject(this.args.model); + this.router.transitionTo("adminPlugins.show.discourse-ai-tools.index"); + }, + }); + } + + @action + openTestModal() { + this.modal.show(AiToolTestModal, { + model: { + tool: this.editingModel, + }, + }); + } + } diff --git a/assets/javascripts/discourse/components/ai-tool-list-editor.gjs b/assets/javascripts/discourse/components/ai-tool-list-editor.gjs index e5bd276c..77fbcc22 100644 --- a/assets/javascripts/discourse/components/ai-tool-list-editor.gjs +++ b/assets/javascripts/discourse/components/ai-tool-list-editor.gjs @@ -1,36 +1,13 @@ import Component from "@glimmer/component"; -import { fn } from "@ember/helper"; -import { action } from "@ember/object"; import { LinkTo } from "@ember/routing"; import { service } from "@ember/service"; -import { eq } from "truth-helpers"; import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item"; -import DButton from "discourse/components/d-button"; import DPageSubheader from "discourse/components/d-page-subheader"; -import DropdownMenu from "discourse/components/dropdown-menu"; import { i18n } from "discourse-i18n"; import AdminConfigAreaEmptyList from "admin/components/admin-config-area-empty-list"; -import DMenu from "float-kit/components/d-menu"; export default class AiToolListEditor extends Component { @service adminPluginNavManager; - @service router; - - get lastIndexOfPresets() { - return this.args.tools.resultSetMeta.presets.length - 1; - } - - @action - routeToNewTool(preset) { - return this.router.transitionTo( - "adminPlugins.show.discourse-ai-tools.new", - { - queryParams: { - presetId: preset.preset_id, - }, - } - ); - }