mirror of
https://ghfast.top/https://github.com/discourse/discourse-ai.git
synced 2026-07-17 11:46:28 +08:00
This changeset contains 4 fixes: 1. We were allowing running tests on unsaved tools, this is problematic cause uploads are not yet associated or indexed leading to confusing results. We now only show the test button when tool is saved. 2. We were not properly scoping rag document fragements, this meant that personas and ai tools could get results from other unrelated tools, just to be filtered out later 3. index.search showed options as "optional" but implementation required the second option 4. When testing tools searching through document fragments was not working at all cause we did not properly load the tool
261 lines
7.4 KiB
Text
261 lines
7.4 KiB
Text
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 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;
|
|
|
|
@tracked isSaving = false;
|
|
@tracked editingModel = null;
|
|
@tracked showDelete = false;
|
|
@tracked selectedPreset = null;
|
|
|
|
get presets() {
|
|
return this.args.presets.map((preset) => {
|
|
return {
|
|
name: preset.preset_name,
|
|
id: preset.preset_id,
|
|
};
|
|
});
|
|
}
|
|
|
|
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",
|
|
"description",
|
|
"parameters",
|
|
"script",
|
|
"summary",
|
|
"rag_uploads",
|
|
"rag_chunk_tokens",
|
|
"rag_chunk_overlap_tokens"
|
|
);
|
|
|
|
await this.args.model.save(data);
|
|
|
|
this.toasts.success({
|
|
data: { message: I18n.t("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.show",
|
|
this.args.model
|
|
);
|
|
} catch (e) {
|
|
popupAjaxError(e);
|
|
} finally {
|
|
this.isSaving = false;
|
|
}
|
|
}
|
|
|
|
@action
|
|
delete() {
|
|
return this.dialog.confirm({
|
|
message: I18n.t("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,
|
|
},
|
|
});
|
|
}
|
|
|
|
<template>
|
|
<BackButton
|
|
@route="adminPlugins.show.discourse-ai-tools"
|
|
@label="discourse_ai.tools.back"
|
|
/>
|
|
|
|
<form
|
|
{{didInsert this.updateModel @model.id}}
|
|
{{didUpdate this.updateModel @model.id}}
|
|
class="form-horizontal ai-tool-editor"
|
|
>
|
|
{{#if this.showPresets}}
|
|
<div class="control-group">
|
|
<label>{{I18n.t "discourse_ai.tools.presets"}}</label>
|
|
<ComboBox
|
|
@value={{this.presetId}}
|
|
@content={{this.presets}}
|
|
class="ai-tool-editor__presets"
|
|
/>
|
|
</div>
|
|
|
|
<div class="control-group ai-llm-editor__action_panel">
|
|
<DButton
|
|
@action={{this.configurePreset}}
|
|
@label="discourse_ai.tools.next.title"
|
|
class="ai-tool-editor__next"
|
|
/>
|
|
</div>
|
|
{{else}}
|
|
<div class="control-group">
|
|
<label>{{I18n.t "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="question-circle"
|
|
@content={{I18n.t "discourse_ai.tools.name_help"}}
|
|
/>
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
<label>{{I18n.t "discourse_ai.tools.description"}}</label>
|
|
<textarea
|
|
{{on
|
|
"input"
|
|
(withEventValue (fn (mut this.editingModel.description)))
|
|
}}
|
|
placeholder={{I18n.t "discourse_ai.tools.description_help"}}
|
|
class="ai-tool-editor__description input-xxlarge"
|
|
>{{this.editingModel.description}}</textarea>
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
<label>{{I18n.t "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="question-circle"
|
|
@content={{I18n.t "discourse_ai.tools.summary_help"}}
|
|
/>
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
<label>{{I18n.t "discourse_ai.tools.parameters"}}</label>
|
|
<AiToolParameterEditor @parameters={{this.editingModel.parameters}} />
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
<label>{{I18n.t "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}}
|
|
/>
|
|
</div>
|
|
<RagOptions @model={{this.editingModel}} />
|
|
{{/if}}
|
|
|
|
<div class="control-group ai-tool-editor__action_panel">
|
|
{{#unless @model.isNew}}
|
|
<DButton
|
|
@action={{this.openTestModal}}
|
|
@label="discourse_ai.tools.test"
|
|
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>
|
|
{{/if}}
|
|
</form>
|
|
</template>
|
|
}
|