mirror of
https://github.com/discourse/discourse-ai.git
synced 2025-03-06 09:20:14 +00:00
WIP
This commit is contained in:
parent
0bb920194b
commit
72af05cad0
@ -16,6 +16,12 @@ import RagOptions from "./rag-options";
|
||||
import RagUploader from "./rag-uploader";
|
||||
|
||||
export default class AiToolEditorForm extends Component {
|
||||
@service siteSettings;
|
||||
@service dialog;
|
||||
@service router;
|
||||
|
||||
@tracked uploadedFiles = [];
|
||||
|
||||
PARAMETER_TYPES = [
|
||||
{ name: "string", id: "string" },
|
||||
{ name: "number", id: "number" },
|
||||
@ -23,6 +29,15 @@ export default class AiToolEditorForm extends Component {
|
||||
{ name: "array", id: "array" },
|
||||
];
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
console.log(
|
||||
"editingModel",
|
||||
this.args.editingModel,
|
||||
this.args.selectedPreset
|
||||
);
|
||||
}
|
||||
|
||||
@cached
|
||||
get formData() {
|
||||
// todo
|
||||
@ -33,6 +48,7 @@ export default class AiToolEditorForm extends Component {
|
||||
summary: "",
|
||||
parameters: [],
|
||||
script: "",
|
||||
rag_uploads: [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -41,6 +57,36 @@ export default class AiToolEditorForm extends Component {
|
||||
console.log(data, "is saved!");
|
||||
}
|
||||
|
||||
@action
|
||||
delete() {
|
||||
return this.dialog.confirm({
|
||||
message: i18n("discourse_ai.tools.confirm_delete"),
|
||||
didConfirm: async () => {
|
||||
// ! TODO: handle deletion properly.
|
||||
// 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);
|
||||
}
|
||||
|
||||
currentParameterSelection(data, index) {
|
||||
return data.parameters[index].type;
|
||||
}
|
||||
@ -199,29 +245,43 @@ export default class AiToolEditorForm extends Component {
|
||||
</form.Field>
|
||||
|
||||
{{! Uploads }}
|
||||
<form.Field
|
||||
@name="uploads"
|
||||
@title={{i18n "discourse_ai.rag.uploads.title"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Custom>
|
||||
{{! TODO: props for RagUploader and RagOptions }}
|
||||
<RagUploader
|
||||
@target={{this.editingModel}}
|
||||
@updateUploads={{this.updateUploads}}
|
||||
@onRemove={{this.removeUpload}}
|
||||
@allowImages={{@settings.rag_images_enabled}}
|
||||
/>
|
||||
<RagOptions
|
||||
@model={{this.editingModel}}
|
||||
@llms={{@llms}}
|
||||
@allowImages={{@settings.rag_images_enabled}}
|
||||
/>
|
||||
</field.Custom>
|
||||
</form.Field>
|
||||
{{#if this.siteSettings.ai_embeddings_enabled}}
|
||||
<form.Field
|
||||
@name="rag_uploads"
|
||||
@title={{i18n "discourse_ai.rag.uploads.title"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Custom>
|
||||
{{! TODO: remove this.editingModel (target not needed?) }}
|
||||
<RagUploader
|
||||
@target={{this.editingModel}}
|
||||
@updateUploads={{fn this.updateUploads form.addItemToCollection}}
|
||||
@onRemove={{fn this.removeUpload form}}
|
||||
@allowImages={{@settings.rag_images_enabled}}
|
||||
/>
|
||||
<RagOptions
|
||||
@model={{this.editingModel}}
|
||||
@llms={{@llms}}
|
||||
@allowImages={{@settings.rag_images_enabled}}
|
||||
/>
|
||||
</field.Custom>
|
||||
</form.Field>
|
||||
{{/if}}
|
||||
|
||||
<form.Actions>
|
||||
{{! TODO add delete and test actions when /edit }}
|
||||
|
||||
{{#unless @isNew}}
|
||||
Run Test
|
||||
|
||||
<form.Button
|
||||
@label="discourse_ai.tools.delete"
|
||||
@icon="trash-can"
|
||||
@action={{this.delete}}
|
||||
class="btn-danger ai-tool-editor__delete"
|
||||
/>
|
||||
{{/unless}}
|
||||
|
||||
<form.Submit @label="discourse_ai.tools.save" />
|
||||
</form.Actions>
|
||||
</Form>
|
||||
|
@ -1,26 +1,16 @@
|
||||
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 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 AiToolEditorForm from "./ai-tool-editor-form";
|
||||
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;
|
||||
@ -32,39 +22,27 @@ export default class AiToolEditor extends Component {
|
||||
|
||||
@tracked isSaving = false;
|
||||
@tracked editingModel = null;
|
||||
@tracked showDelete = false;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.updateModel();
|
||||
}
|
||||
|
||||
get selectedPreset() {
|
||||
if (!this.args.selectedPreset) {
|
||||
return null;
|
||||
return this.args.presets.findBy("preset_id", "empty_tool");
|
||||
}
|
||||
|
||||
return this.args.presets.findBy("preset_id", this.args.selectedPreset);
|
||||
}
|
||||
|
||||
@action
|
||||
updateModel() {
|
||||
if (this.args.model.isNew) {
|
||||
this.editingModel = this.store
|
||||
.createRecord("ai-tool", this.selectedPreset)
|
||||
.workingCopy();
|
||||
this.showDelete = false;
|
||||
} else {
|
||||
this.editingModel = this.args.model.workingCopy();
|
||||
this.showDelete = !this.args.model.isNew;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
updateUploads(uploads) {
|
||||
this.editingModel.rag_uploads = uploads;
|
||||
}
|
||||
|
||||
@action
|
||||
removeUpload(upload) {
|
||||
this.editingModel.rag_uploads.removeObject(upload);
|
||||
if (!this.args.model.isNew) {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,102 +113,17 @@ export default class AiToolEditor extends Component {
|
||||
@label="discourse_ai.tools.back"
|
||||
/>
|
||||
|
||||
<AiToolEditorForm />
|
||||
<AiToolEditorForm
|
||||
@editingModel={{this.editingModel}}
|
||||
@isNew={{@model.isNew}}
|
||||
@selectedPreset={{this.selectedPreset}}
|
||||
/>
|
||||
|
||||
<hr />
|
||||
<hr />
|
||||
<hr />
|
||||
|
||||
<form
|
||||
{{didInsert this.updateModel @model.id}}
|
||||
{{didUpdate this.updateModel @model.id}}
|
||||
class="form-horizontal ai-tool-editor"
|
||||
>
|
||||
<div class="control-group">
|
||||
<label>{{i18n "discourse_ai.tools.name"}}</label>
|
||||
<input
|
||||
{{on "input" (withEventValue (fn (mut this.editingModel.name)))}}
|
||||
value={{this.editingModel.name}}
|
||||
type="text"
|
||||
class="ai-tool-editor__name"
|
||||
/>
|
||||
<DTooltip
|
||||
@icon="circle-question"
|
||||
@content={{i18n "discourse_ai.tools.name_help"}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label>{{i18n "discourse_ai.tools.tool_name"}}</label>
|
||||
<input
|
||||
{{on "input" (withEventValue (fn (mut this.editingModel.tool_name)))}}
|
||||
value={{this.editingModel.tool_name}}
|
||||
type="text"
|
||||
class="ai-tool-editor__tool_name"
|
||||
/>
|
||||
<DTooltip
|
||||
@icon="circle-question"
|
||||
@content={{i18n "discourse_ai.tools.tool_name_help"}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label>{{i18n "discourse_ai.tools.description"}}</label>
|
||||
<textarea
|
||||
{{on
|
||||
"input"
|
||||
(withEventValue (fn (mut this.editingModel.description)))
|
||||
}}
|
||||
placeholder={{i18n "discourse_ai.tools.description_help"}}
|
||||
class="ai-tool-editor__description input-xxlarge"
|
||||
>{{this.editingModel.description}}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label>{{i18n "discourse_ai.tools.summary"}}</label>
|
||||
<input
|
||||
{{on "input" (withEventValue (fn (mut this.editingModel.summary)))}}
|
||||
value={{this.editingModel.summary}}
|
||||
type="text"
|
||||
class="ai-tool-editor__summary input-xxlarge"
|
||||
/>
|
||||
<DTooltip
|
||||
@icon="circle-question"
|
||||
@content={{i18n "discourse_ai.tools.summary_help"}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label>{{i18n "discourse_ai.tools.parameters"}}</label>
|
||||
<AiToolParameterEditor @parameters={{this.editingModel.parameters}} />
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label>{{i18n "discourse_ai.tools.script"}}</label>
|
||||
<AceEditor
|
||||
@content={{this.editingModel.script}}
|
||||
@onChange={{fn (mut this.editingModel.script)}}
|
||||
@mode={{ACE_EDITOR_MODE}}
|
||||
@theme={{ACE_EDITOR_THEME}}
|
||||
@editorId="ai-tool-script-editor"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{{#if this.siteSettings.ai_embeddings_enabled}}
|
||||
<div class="control-group">
|
||||
<RagUploader
|
||||
@target={{this.editingModel}}
|
||||
@updateUploads={{this.updateUploads}}
|
||||
@onRemove={{this.removeUpload}}
|
||||
@allowImages={{@settings.rag_images_enabled}}
|
||||
/>
|
||||
</div>
|
||||
<RagOptions
|
||||
@model={{this.editingModel}}
|
||||
@llms={{@llms}}
|
||||
@allowImages={{@settings.rag_images_enabled}}
|
||||
/>
|
||||
{{/if}}
|
||||
<form class="form-horizontal ai-tool-editor">
|
||||
|
||||
<div class="control-group ai-tool-editor__action_panel">
|
||||
{{#unless @model.isNew}}
|
||||
@ -240,21 +133,6 @@ export default class AiToolEditor extends Component {
|
||||
class="ai-tool-editor__test-button"
|
||||
/>
|
||||
{{/unless}}
|
||||
|
||||
<DButton
|
||||
@action={{this.save}}
|
||||
@label="discourse_ai.tools.save"
|
||||
@disabled={{this.isSaving}}
|
||||
class="btn-primary ai-tool-editor__save"
|
||||
/>
|
||||
|
||||
{{#if this.showDelete}}
|
||||
<DButton
|
||||
@action={{this.delete}}
|
||||
@label="discourse_ai.tools.delete"
|
||||
class="btn-danger ai-tool-editor__delete"
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
@ -1,155 +0,0 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { fn } from "@ember/helper";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import { TrackedArray, TrackedObject } from "@ember-compat/tracked-built-ins";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import withEventValue from "discourse/helpers/with-event-value";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import ComboBox from "select-kit/components/combo-box";
|
||||
|
||||
const PARAMETER_TYPES = [
|
||||
{ name: "string", id: "string" },
|
||||
{ name: "number", id: "number" },
|
||||
{ name: "boolean", id: "boolean" },
|
||||
{ name: "array", id: "array" },
|
||||
];
|
||||
|
||||
export default class AiToolParameterEditor extends Component {
|
||||
@action
|
||||
addParameter() {
|
||||
this.args.parameters.push(
|
||||
new TrackedObject({
|
||||
name: "",
|
||||
description: "",
|
||||
type: "string",
|
||||
required: false,
|
||||
enum: null,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
removeParameter(parameter) {
|
||||
const index = this.args.parameters.indexOf(parameter);
|
||||
this.args.parameters.splice(index, 1);
|
||||
}
|
||||
|
||||
@action
|
||||
toggleRequired(parameter, event) {
|
||||
parameter.required = event.target.checked;
|
||||
}
|
||||
|
||||
@action
|
||||
toggleEnum(parameter) {
|
||||
if (parameter.enum) {
|
||||
parameter.enum = null;
|
||||
} else {
|
||||
this.addEnumValue(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
addEnumValue(parameter) {
|
||||
parameter.enum ||= new TrackedArray();
|
||||
parameter.enum.push("");
|
||||
}
|
||||
|
||||
@action
|
||||
removeEnumValue(parameter, index) {
|
||||
parameter.enum.splice(index, 1);
|
||||
if (parameter.enum.length === 0) {
|
||||
parameter.enum = null;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
updateEnumValue(parameter, index, event) {
|
||||
parameter.enum[index] = event.target.value;
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#each @parameters as |parameter|}}
|
||||
<div class="ai-tool-parameter">
|
||||
<div class="parameter-row">
|
||||
<input
|
||||
{{on "input" (withEventValue (fn (mut parameter.name)))}}
|
||||
value={{parameter.name}}
|
||||
type="text"
|
||||
placeholder={{i18n "discourse_ai.tools.parameter_name"}}
|
||||
/>
|
||||
<ComboBox @value={{parameter.type}} @content={{PARAMETER_TYPES}} />
|
||||
</div>
|
||||
|
||||
<div class="parameter-row">
|
||||
<input
|
||||
{{on "input" (withEventValue (fn (mut parameter.description)))}}
|
||||
value={{parameter.description}}
|
||||
type="text"
|
||||
placeholder={{i18n "discourse_ai.tools.parameter_description"}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="parameter-row">
|
||||
<label>
|
||||
<input
|
||||
{{on "input" (fn this.toggleRequired parameter)}}
|
||||
checked={{parameter.required}}
|
||||
type="checkbox"
|
||||
class="parameter-row__required-toggle"
|
||||
/>
|
||||
{{i18n "discourse_ai.tools.parameter_required"}}
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input
|
||||
{{on "input" (fn this.toggleEnum parameter)}}
|
||||
checked={{parameter.enum}}
|
||||
type="checkbox"
|
||||
class="parameter-row__enum-toggle"
|
||||
/>
|
||||
{{i18n "discourse_ai.tools.parameter_enum"}}
|
||||
</label>
|
||||
|
||||
<DButton
|
||||
@action={{fn this.removeParameter parameter}}
|
||||
@icon="trash-can"
|
||||
class="btn-danger"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{{#if parameter.enum}}
|
||||
<div class="parameter-enum-values">
|
||||
{{#each parameter.enum as |enumValue enumIndex|}}
|
||||
<div class="enum-value-row">
|
||||
<input
|
||||
{{on "change" (fn this.updateEnumValue parameter enumIndex)}}
|
||||
value={{enumValue}}
|
||||
type="text"
|
||||
placeholder={{i18n "discourse_ai.tools.enum_value"}}
|
||||
/>
|
||||
<DButton
|
||||
@action={{fn this.removeEnumValue parameter enumIndex}}
|
||||
@icon="trash-can"
|
||||
class="btn-danger"
|
||||
/>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
<DButton
|
||||
@action={{fn this.addEnumValue parameter}}
|
||||
@label="discourse_ai.tools.add_enum_value"
|
||||
@icon="plus"
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
<DButton
|
||||
@action={{this.addParameter}}
|
||||
@label="discourse_ai.tools.add_parameter"
|
||||
@icon="plus"
|
||||
/>
|
||||
</template>
|
||||
}
|
@ -74,6 +74,7 @@ export default class RagUploader extends Component {
|
||||
}
|
||||
|
||||
_updateTargetWithUploads() {
|
||||
console.log(this.updateUploads);
|
||||
this.updateUploads(this.ragUploads);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user