Follow up to b863ddc94b
Ruby:
* Validate `summary` (the column is `not null`)
* Fix `name` validation (the column has `max_length` 100)
* Fix table annotations
* Accept missing `parameter` attributes (`required, `enum`, `enum_values`)
JS:
* Use native classes
* Don't use ember's array extensions
* Add explicit service injections
* Correct class names
* Use `||=` operator
* Use `store` service to create records
* Remove unused service injections
* Extract consts
* Group actions together
* Use `async`/`await`
* Use `withEventValue`
* Sort html attributes
* Use DButtons `@label` arg
* Use `input` elements instead of Ember's `Input` component (same w/ textarea)
* Remove `btn-default` class (automatically applied by DButton)
* Don't mix `I18n.t` and `i18n` in the same template
* Don't track props that aren't used in a template
* Correct invalid `target.value` code
* Remove unused/invalid `this.parameter`/`onChange` code
* Whitespace
* Use the new service import `inject as service` -> `service`
* Use `Object.entries()`
* Add missing i18n strings
* Fix an error in `addEnumValue` (calling `pushObject` on `undefined`)
* Use `TrackedArray`/`TrackedObject`
* Transform tool `parameters` keys (`enumValues` -> `enum_values`)
This commit is contained in:
parent
a708d4dfa2
commit
a5a39dd2ee
|
@ -1,16 +1,15 @@
|
|||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
export default class DiscourseAiToolsNewRoute extends DiscourseRoute {
|
||||
async model() {
|
||||
const record = this.store.createRecord("ai-tool");
|
||||
return record;
|
||||
},
|
||||
return this.store.createRecord("ai-tool");
|
||||
}
|
||||
|
||||
setupController(controller, model) {
|
||||
this._super(controller, model);
|
||||
setupController(controller) {
|
||||
super.setupController(...arguments);
|
||||
const toolsModel = this.modelFor("adminPlugins.show.discourse-ai-tools");
|
||||
|
||||
controller.set("allTools", toolsModel);
|
||||
controller.set("presets", toolsModel.resultSetMeta.presets);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
export default class DiscourseAiToolsShowRoute extends DiscourseRoute {
|
||||
async model(params) {
|
||||
const allTools = this.modelFor("adminPlugins.show.discourse-ai-tools");
|
||||
const id = parseInt(params.id, 10);
|
||||
return allTools.findBy("id", id);
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
this._super(controller, model);
|
||||
return allTools.find((tool) => tool.id === id);
|
||||
}
|
||||
|
||||
setupController(controller) {
|
||||
super.setupController(...arguments);
|
||||
const toolsModel = this.modelFor("adminPlugins.show.discourse-ai-tools");
|
||||
|
||||
controller.set("allTools", toolsModel);
|
||||
controller.set("presets", toolsModel.resultSetMeta.presets);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import { service } from "@ember/service";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default class DiscourseAiToolsRoute extends DiscourseRoute {
|
||||
@service store;
|
||||
|
||||
model() {
|
||||
return this.store.findAll("ai-tool");
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ module DiscourseAi
|
|||
:description,
|
||||
:script,
|
||||
:summary,
|
||||
parameters: %i[name type description],
|
||||
parameters: [:name, :type, :description, :required, :enum, enum_values: []],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AiTool < ActiveRecord::Base
|
||||
validates :name, presence: true, length: { maximum: 255 }
|
||||
validates :name, presence: true, length: { maximum: 100 }
|
||||
validates :description, presence: true, length: { maximum: 1000 }
|
||||
validates :summary, presence: true, length: { maximum: 255 }
|
||||
validates :script, presence: true, length: { maximum: 100_000 }
|
||||
validates :created_by_id, presence: true
|
||||
belongs_to :created_by, class_name: "User"
|
||||
|
@ -174,10 +175,12 @@ end
|
|||
#
|
||||
# id :bigint not null, primary key
|
||||
# name :string not null
|
||||
# description :text not null
|
||||
# description :string not null
|
||||
# summary :string not null
|
||||
# parameters :jsonb not null
|
||||
# script :text not null
|
||||
# created_by_id :integer not null
|
||||
# enabled :boolean default(TRUE), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import RestAdapter from "discourse/adapters/rest";
|
||||
|
||||
export default class Adapter extends RestAdapter {
|
||||
export default class AiToolAdapter extends RestAdapter {
|
||||
jsonMode = true;
|
||||
|
||||
basePath() {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { TrackedArray, TrackedObject } from "@ember-compat/tracked-built-ins";
|
||||
import RestModel from "discourse/models/rest";
|
||||
|
||||
const CREATE_ATTRIBUTES = [
|
||||
|
@ -20,8 +21,21 @@ export default class AiTool extends RestModel {
|
|||
}
|
||||
|
||||
workingCopy() {
|
||||
let attrs = this.getProperties(CREATE_ATTRIBUTES);
|
||||
attrs.parameters = attrs.parameters || [];
|
||||
return AiTool.create(attrs);
|
||||
const attrs = this.getProperties(CREATE_ATTRIBUTES);
|
||||
|
||||
attrs.parameters = new TrackedArray(
|
||||
attrs.parameters?.map((p) => {
|
||||
const parameter = new TrackedObject(p);
|
||||
|
||||
if (parameter.enum_values) {
|
||||
parameter.enumValues = new TrackedArray(parameter.enum_values);
|
||||
delete parameter.enum_values;
|
||||
}
|
||||
|
||||
return parameter;
|
||||
})
|
||||
);
|
||||
|
||||
return this.store.createRecord("ai-tool", attrs);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { Input } from "@ember/component";
|
||||
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 { inject as service } from "@ember/service";
|
||||
import { service } from "@ember/service";
|
||||
import BackButton from "discourse/components/back-button";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import Textarea from "discourse/components/d-textarea";
|
||||
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 AceEditor from "admin/components/ace-editor";
|
||||
|
@ -16,9 +17,11 @@ import ComboBox from "select-kit/components/combo-box";
|
|||
import AiToolParameterEditor from "./ai-tool-parameter-editor";
|
||||
import AiToolTestModal from "./modal/ai-tool-test-modal";
|
||||
|
||||
const ACE_EDITOR_MODE = "javascript";
|
||||
const ACE_EDITOR_THEME = "chrome";
|
||||
|
||||
export default class AiToolEditor extends Component {
|
||||
@service router;
|
||||
@service store;
|
||||
@service dialog;
|
||||
@service modal;
|
||||
@service toasts;
|
||||
|
@ -26,18 +29,8 @@ export default class AiToolEditor extends Component {
|
|||
@tracked isSaving = false;
|
||||
@tracked editingModel = null;
|
||||
@tracked showDelete = false;
|
||||
|
||||
@tracked selectedPreset = null;
|
||||
|
||||
aceEditorMode = "javascript";
|
||||
aceEditorTheme = "chrome";
|
||||
|
||||
@action
|
||||
updateModel() {
|
||||
this.editingModel = this.args.model.workingCopy();
|
||||
this.showDelete = !this.args.model.isNew;
|
||||
}
|
||||
|
||||
get presets() {
|
||||
return this.args.presets.map((preset) => {
|
||||
return {
|
||||
|
@ -51,6 +44,12 @@ export default class AiToolEditor extends Component {
|
|||
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);
|
||||
|
@ -64,16 +63,23 @@ export default class AiToolEditor extends Component {
|
|||
this.isSaving = true;
|
||||
|
||||
try {
|
||||
await this.args.model.save(
|
||||
this.editingModel.getProperties(
|
||||
const data = this.editingModel.getProperties(
|
||||
"name",
|
||||
"description",
|
||||
"parameters",
|
||||
"script",
|
||||
"summary"
|
||||
)
|
||||
);
|
||||
|
||||
for (const p of data.parameters) {
|
||||
if (p.enumValues) {
|
||||
p.enum_values = p.enumValues;
|
||||
delete p.enumValues;
|
||||
}
|
||||
}
|
||||
|
||||
await this.args.model.save(data);
|
||||
|
||||
this.toasts.success({
|
||||
data: { message: I18n.t("discourse_ai.tools.saved") },
|
||||
duration: 2000,
|
||||
|
@ -97,22 +103,15 @@ export default class AiToolEditor extends Component {
|
|||
delete() {
|
||||
return this.dialog.confirm({
|
||||
message: I18n.t("discourse_ai.tools.confirm_delete"),
|
||||
didConfirm: () => {
|
||||
return this.args.model.destroyRecord().then(() => {
|
||||
didConfirm: async () => {
|
||||
await this.args.model.destroyRecord();
|
||||
|
||||
this.args.tools.removeObject(this.args.model);
|
||||
this.router.transitionTo(
|
||||
"adminPlugins.show.discourse-ai-tools.index"
|
||||
);
|
||||
});
|
||||
this.router.transitionTo("adminPlugins.show.discourse-ai-tools.index");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
updateScript(script) {
|
||||
this.editingModel.script = script;
|
||||
}
|
||||
|
||||
@action
|
||||
openTestModal() {
|
||||
this.modal.show(AiToolTestModal, {
|
||||
|
@ -129,9 +128,9 @@ export default class AiToolEditor extends Component {
|
|||
/>
|
||||
|
||||
<form
|
||||
class="form-horizontal ai-tool-editor"
|
||||
{{didUpdate this.updateModel @model.id}}
|
||||
{{didInsert this.updateModel @model.id}}
|
||||
{{didUpdate this.updateModel @model.id}}
|
||||
class="form-horizontal ai-tool-editor"
|
||||
>
|
||||
{{#if this.showPresets}}
|
||||
<div class="control-group">
|
||||
|
@ -145,18 +144,18 @@ export default class AiToolEditor extends Component {
|
|||
|
||||
<div class="control-group ai-llm-editor__action_panel">
|
||||
<DButton
|
||||
class="ai-tool-editor__next"
|
||||
@action={{this.configurePreset}}
|
||||
>
|
||||
{{I18n.t "discourse_ai.tools.next.title"}}
|
||||
</DButton>
|
||||
@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
|
||||
@type="text"
|
||||
@value={{this.editingModel.name}}
|
||||
<input
|
||||
{{on "input" (withEventValue (fn (mut this.editingModel.name)))}}
|
||||
value={{this.editingModel.name}}
|
||||
type="text"
|
||||
class="ai-tool-editor__name"
|
||||
/>
|
||||
<DTooltip
|
||||
|
@ -164,19 +163,25 @@ export default class AiToolEditor extends Component {
|
|||
@content={{I18n.t "discourse_ai.tools.name_help"}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label>{{I18n.t "discourse_ai.tools.description"}}</label>
|
||||
<Textarea
|
||||
@value={{this.editingModel.description}}
|
||||
class="ai-tool-editor__description input-xxlarge"
|
||||
<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
|
||||
@type="text"
|
||||
@value={{this.editingModel.summary}}
|
||||
<input
|
||||
{{on "input" (withEventValue (fn (mut this.editingModel.summary)))}}
|
||||
value={{this.editingModel.summary}}
|
||||
type="text"
|
||||
class="ai-tool-editor__summary input-xxlarge"
|
||||
/>
|
||||
<DTooltip
|
||||
|
@ -184,37 +189,43 @@ export default class AiToolEditor extends Component {
|
|||
@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}}
|
||||
@mode={{this.aceEditorMode}}
|
||||
@theme={{this.aceEditorTheme}}
|
||||
@onChange={{this.updateScript}}
|
||||
@onChange={{withEventValue (fn (mut this.editingModel.script))}}
|
||||
@mode={{ACE_EDITOR_MODE}}
|
||||
@theme={{ACE_EDITOR_THEME}}
|
||||
@editorId="ai-tool-script-editor"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="control-group ai-tool-editor__action_panel">
|
||||
<DButton
|
||||
@action={{this.openTestModal}}
|
||||
class="btn-default ai-tool-editor__test-button"
|
||||
>{{I18n.t "discourse_ai.tools.test"}}</DButton>
|
||||
@label="discourse_ai.tools.test"
|
||||
class="ai-tool-editor__test-button"
|
||||
/>
|
||||
|
||||
<DButton
|
||||
class="btn-primary ai-tool-editor__save"
|
||||
@action={{this.save}}
|
||||
@label="discourse_ai.tools.save"
|
||||
@disabled={{this.isSaving}}
|
||||
>{{I18n.t "discourse_ai.tools.save"}}</DButton>
|
||||
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"
|
||||
>
|
||||
{{I18n.t "discourse_ai.tools.delete"}}
|
||||
</DButton>
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { LinkTo } from "@ember/routing";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
<template>
|
||||
<section class="ai-tool-list-editor__current admin-detail pull-left">
|
||||
<div class="ai-tool-list-editor__header">
|
||||
<h3>{{i18n "discourse_ai.tools.short_title"}}</h3>
|
||||
<h3>{{I18n.t "discourse_ai.tools.short_title"}}</h3>
|
||||
<LinkTo
|
||||
@route="adminPlugins.show.discourse-ai-tools.new"
|
||||
class="btn btn-small btn-primary ai-tool-list-editor__new-button"
|
||||
|
@ -37,7 +36,7 @@ import I18n from "discourse-i18n";
|
|||
@route="adminPlugins.show.discourse-ai-tools.show"
|
||||
@model={{tool}}
|
||||
class="btn btn-text btn-small"
|
||||
>{{i18n "discourse_ai.tools.edit"}} </LinkTo>
|
||||
>{{I18n.t "discourse_ai.tools.edit"}}</LinkTo>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { Input } from "@ember/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";
|
||||
|
||||
|
@ -16,145 +16,135 @@ const PARAMETER_TYPES = [
|
|||
];
|
||||
|
||||
export default class AiToolParameterEditor extends Component {
|
||||
@tracked parameters = [];
|
||||
|
||||
@action
|
||||
addParameter() {
|
||||
this.args.parameters.pushObject({
|
||||
this.args.parameters.push(
|
||||
new TrackedObject({
|
||||
name: "",
|
||||
description: "",
|
||||
type: "string",
|
||||
required: false,
|
||||
enum: false,
|
||||
enumValues: [],
|
||||
});
|
||||
enumValues: null,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
removeParameter(parameter) {
|
||||
this.args.parameters.removeObject(parameter);
|
||||
const index = this.args.parameters.indexOf(parameter);
|
||||
this.args.parameters.splice(index, 1);
|
||||
}
|
||||
|
||||
@action
|
||||
updateParameter(parameter, field, value) {
|
||||
parameter[field] = value;
|
||||
toggleRequired(parameter, event) {
|
||||
parameter.required = event.target.checked;
|
||||
}
|
||||
|
||||
@action
|
||||
toggleEnum(parameter) {
|
||||
parameter.enum = !parameter.enum;
|
||||
if (!parameter.enum) {
|
||||
parameter.enumValues = [];
|
||||
parameter.enumValues = null;
|
||||
}
|
||||
this.args.onChange(this.parameters);
|
||||
}
|
||||
|
||||
@action
|
||||
addEnumValue(parameter) {
|
||||
parameter.enumValues.pushObject("");
|
||||
parameter.enumValues ||= new TrackedArray();
|
||||
parameter.enumValues.push("");
|
||||
}
|
||||
|
||||
@action
|
||||
removeEnumValue(parameter, index) {
|
||||
parameter.enumValues.removeAt(index);
|
||||
parameter.enumValues.splice(index, 1);
|
||||
}
|
||||
|
||||
@action
|
||||
updateEnumValue(parameter, index, event) {
|
||||
parameter.enumValues[index] = event.target.value;
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#each @parameters as |parameter|}}
|
||||
<div class="ai-tool-parameter">
|
||||
<div class="parameter-row">
|
||||
<Input
|
||||
@type="text"
|
||||
@value={{parameter.name}}
|
||||
<input
|
||||
{{on "input" (withEventValue (fn (mut parameter.name)))}}
|
||||
value={{parameter.name}}
|
||||
type="text"
|
||||
placeholder={{I18n.t "discourse_ai.tools.parameter_name"}}
|
||||
/>
|
||||
<ComboBox @value={{parameter.type}} @content={{PARAMETER_TYPES}} />
|
||||
</div>
|
||||
|
||||
<div class="parameter-row">
|
||||
<Input
|
||||
@type="text"
|
||||
@value={{parameter.description}}
|
||||
<input
|
||||
{{on "input" (withEventValue (fn (mut parameter.description)))}}
|
||||
value={{parameter.description}}
|
||||
type="text"
|
||||
placeholder={{I18n.t "discourse_ai.tools.parameter_description"}}
|
||||
{{on
|
||||
"input"
|
||||
(fn
|
||||
this.updateParameter
|
||||
parameter
|
||||
"description"
|
||||
value="target.value"
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="parameter-row">
|
||||
<label>
|
||||
<Input
|
||||
@type="checkbox"
|
||||
@checked={{parameter.required}}
|
||||
{{on
|
||||
"change"
|
||||
(fn
|
||||
this.updateParameter
|
||||
parameter
|
||||
"required"
|
||||
value="target.checked"
|
||||
)
|
||||
}}
|
||||
<input
|
||||
{{on "input" (fn this.toggleRequired parameter)}}
|
||||
checked={{parameter.required}}
|
||||
type="checkbox"
|
||||
/>
|
||||
{{I18n.t "discourse_ai.tools.parameter_required"}}
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<Input
|
||||
@type="checkbox"
|
||||
@checked={{parameter.enum}}
|
||||
{{on "change" (fn this.toggleEnum parameter)}}
|
||||
<input
|
||||
{{on "input" (fn this.toggleEnum parameter)}}
|
||||
checked={{parameter.enum}}
|
||||
type="checkbox"
|
||||
/>
|
||||
{{I18n.t "discourse_ai.tools.parameter_enum"}}
|
||||
</label>
|
||||
|
||||
<DButton
|
||||
@icon="trash-alt"
|
||||
@action={{fn this.removeParameter parameter}}
|
||||
@icon="trash-alt"
|
||||
class="btn-danger"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{{#if parameter.enum}}
|
||||
<div class="parameter-enum-values">
|
||||
{{#each parameter.enumValues as |enumValue enumIndex|}}
|
||||
<div class="enum-value-row">
|
||||
<Input
|
||||
@type="text"
|
||||
@value={{enumValue}}
|
||||
<input
|
||||
{{on "change" (fn this.updateEnumValue parameter enumIndex)}}
|
||||
value={{enumValue}}
|
||||
type="text"
|
||||
placeholder={{I18n.t "discourse_ai.tools.enum_value"}}
|
||||
{{on
|
||||
"input"
|
||||
(fn
|
||||
this.updateParameter
|
||||
parameter.enumValues
|
||||
enumIndex
|
||||
value="target.value"
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<DButton
|
||||
@icon="trash-alt"
|
||||
@action={{fn this.removeEnumValue parameter enumIndex}}
|
||||
@icon="trash-alt"
|
||||
class="btn-danger"
|
||||
/>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
<DButton
|
||||
@icon="plus"
|
||||
@action={{fn this.addEnumValue parameter}}
|
||||
@label="discourse_ai.tools.add_enum_value"
|
||||
@icon="plus"
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
<DButton
|
||||
@icon="plus"
|
||||
@action={{this.addParameter}}
|
||||
@label="discourse_ai.tools.add_parameter"
|
||||
@icon="plus"
|
||||
/>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@ import I18n from "discourse-i18n";
|
|||
import { jsonToHtml } from "../../lib/utilities";
|
||||
|
||||
export default class AiToolTestModal extends Component {
|
||||
@tracked testResult = null;
|
||||
@tracked testResult;
|
||||
@tracked isLoading = false;
|
||||
@tracked parameterValues = {};
|
||||
parameterValues = {};
|
||||
|
||||
@action
|
||||
updateParameter(name, event) {
|
||||
|
@ -24,16 +24,14 @@ export default class AiToolTestModal extends Component {
|
|||
async runTest() {
|
||||
this.isLoading = true;
|
||||
try {
|
||||
const data = JSON.stringify({
|
||||
ai_tool: this.args.model.tool,
|
||||
parameters: this.parameterValues,
|
||||
}); // required given this is a POST
|
||||
|
||||
const response = await ajax(
|
||||
"/admin/plugins/discourse-ai/ai-tools/test.json",
|
||||
{
|
||||
type: "POST",
|
||||
data,
|
||||
data: JSON.stringify({
|
||||
ai_tool: this.args.model.tool,
|
||||
parameters: this.parameterValues,
|
||||
}),
|
||||
contentType: "application/json",
|
||||
}
|
||||
);
|
||||
|
@ -49,20 +47,21 @@ export default class AiToolTestModal extends Component {
|
|||
<DModal
|
||||
@title={{I18n.t "discourse_ai.tools.test_modal.title"}}
|
||||
@closeModal={{@closeModal}}
|
||||
class="ai-tool-test-modal"
|
||||
@bodyClass="ai-tool-test-modal__body"
|
||||
class="ai-tool-test-modal"
|
||||
>
|
||||
<:body>
|
||||
{{#each @model.tool.parameters as |param|}}
|
||||
<div class="control-group">
|
||||
<label>{{param.name}}</label>
|
||||
<input
|
||||
type="text"
|
||||
name={{param.name}}
|
||||
{{on "input" (fn this.updateParameter param.name)}}
|
||||
name={{param.name}}
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
{{#if this.testResult}}
|
||||
<div class="ai-tool-test-modal__test-result">
|
||||
<h3>{{I18n.t "discourse_ai.tools.test_modal.result"}}</h3>
|
||||
|
@ -70,6 +69,7 @@ export default class AiToolTestModal extends Component {
|
|||
</div>
|
||||
{{/if}}
|
||||
</:body>
|
||||
|
||||
<:footer>
|
||||
<DButton
|
||||
@action={{this.runTest}}
|
||||
|
|
|
@ -8,30 +8,27 @@ export function jsonToHtml(json) {
|
|||
if (typeof json !== "object") {
|
||||
return escapeExpression(json);
|
||||
}
|
||||
|
||||
let html = "<ul>";
|
||||
for (let key in json) {
|
||||
if (!json.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let [key, value] of Object.entries(json)) {
|
||||
html += "<li>";
|
||||
if (typeof json[key] === "object" && Array.isArray(json[key])) {
|
||||
html += `<strong>${escapeExpression(key)}:</strong> ${jsonToHtml(
|
||||
json[key]
|
||||
)}`;
|
||||
} else if (typeof json[key] === "object") {
|
||||
html += `<strong>${escapeExpression(key)}:</strong> <ul><li>${jsonToHtml(
|
||||
json[key]
|
||||
)}</li></ul>`;
|
||||
key = escapeExpression(key);
|
||||
|
||||
if (typeof value === "object" && Array.isArray(value)) {
|
||||
html += `<strong>${key}:</strong> ${jsonToHtml(value)}`;
|
||||
} else if (typeof value === "object") {
|
||||
html += `<strong>${key}:</strong> <ul><li>${jsonToHtml(value)}</li></ul>`;
|
||||
} else {
|
||||
let value = json[key];
|
||||
if (typeof value === "string") {
|
||||
value = escapeExpression(value);
|
||||
value = value.replace(/\n/g, "<br>");
|
||||
value = escapeExpression(value).replace(/\n/g, "<br>");
|
||||
}
|
||||
html += `<strong>${escapeExpression(key)}:</strong> ${value}`;
|
||||
html += `<strong>${key}:</strong> ${value}`;
|
||||
}
|
||||
|
||||
html += "</li>";
|
||||
}
|
||||
|
||||
html += "</ul>";
|
||||
return htmlSafe(html);
|
||||
}
|
||||
|
|
|
@ -200,6 +200,8 @@ en:
|
|||
parameter_enum: "Enum"
|
||||
parameter_name: "Parameter Name"
|
||||
parameter_description: "Parameter Description"
|
||||
enum_value: "Enum value"
|
||||
add_enum_value: "Add enum value"
|
||||
edit: "Edit"
|
||||
test: "Run Test"
|
||||
delete: "Delete"
|
||||
|
|
Loading…
Reference in New Issue