Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.

Commit fd09595

Browse files
committed
DEV: migrates tools form to form-kit
1 parent 269f169 commit fd09595

File tree

11 files changed

+431
-490
lines changed

11 files changed

+431
-490
lines changed
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
import DiscourseRoute from "discourse/routes/discourse";
22

33
export default class DiscourseAiToolsNewRoute extends DiscourseRoute {
4+
beforeModel(transition) {
5+
this.preset = transition.to.queryParams.presetId || "empty_tool";
6+
}
7+
48
async model() {
59
return this.store.createRecord("ai-tool");
610
}
711

812
setupController(controller) {
913
super.setupController(...arguments);
1014
const toolsModel = this.modelFor("adminPlugins.show.discourse-ai-tools");
11-
1215
controller.set("allTools", toolsModel);
1316
controller.set("presets", toolsModel.resultSetMeta.presets);
1417
controller.set("llms", toolsModel.resultSetMeta.llms);
1518
controller.set("settings", toolsModel.resultSetMeta.settings);
19+
controller.set("selectedPreset", this.preset);
1620
}
1721
}

admin/assets/javascripts/discourse/templates/admin-plugins/show/discourse-ai-tools/new.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
@presets={{this.presets}}
66
@llms={{this.llms}}
77
@settings={{this.settings}}
8+
@selectedPreset={{this.selectedPreset}}
89
/>
910
</section>
Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
import Component from "@glimmer/component";
2+
import { tracked } from "@glimmer/tracking";
3+
import { fn, hash } from "@ember/helper";
4+
import { action } from "@ember/object";
5+
import { service } from "@ember/service";
6+
import Form from "discourse/components/form";
7+
import { popupAjaxError } from "discourse/lib/ajax-error";
8+
import { i18n } from "discourse-i18n";
9+
import AiToolTestModal from "./modal/ai-tool-test-modal";
10+
import RagOptions from "./rag-options";
11+
import RagUploader from "./rag-uploader";
12+
13+
export default class AiToolEditorForm extends Component {
14+
@service modal;
15+
@service siteSettings;
16+
@service dialog;
17+
@service router;
18+
@service toasts;
19+
20+
@tracked uploadedFiles = [];
21+
@tracked isSaving = false;
22+
23+
PARAMETER_TYPES = [
24+
{ name: "string", id: "string" },
25+
{ name: "number", id: "number" },
26+
{ name: "boolean", id: "boolean" },
27+
{ name: "array", id: "array" },
28+
];
29+
30+
get formData() {
31+
return {
32+
name: this.args.editingModel.name || "",
33+
tool_name: this.args.editingModel.tool_name || "",
34+
description: this.args.editingModel.description || "",
35+
summary: this.args.editingModel.summary || "",
36+
parameters: this.args.editingModel.parameters || [],
37+
script: this.args.editingModel.script || "",
38+
rag_uploads: this.args.editingModel.rag_uploads || [],
39+
};
40+
}
41+
42+
@action
43+
async save(data) {
44+
this.isSaving = true;
45+
46+
try {
47+
await this.args.model.save(data);
48+
49+
this.toasts.success({
50+
data: { message: i18n("discourse_ai.tools.saved") },
51+
duration: 2000,
52+
});
53+
54+
if (!this.args.tools.any((tool) => tool.id === this.args.model.id)) {
55+
this.args.tools.pushObject(this.args.model);
56+
}
57+
58+
this.router.transitionTo(
59+
"adminPlugins.show.discourse-ai-tools.edit",
60+
this.args.model
61+
);
62+
} catch (e) {
63+
popupAjaxError(e);
64+
} finally {
65+
this.isSaving = false;
66+
}
67+
}
68+
69+
@action
70+
delete() {
71+
return this.dialog.confirm({
72+
message: i18n("discourse_ai.tools.confirm_delete"),
73+
74+
didConfirm: async () => {
75+
await this.args.model.destroyRecord();
76+
this.args.tools.removeObject(this.args.model);
77+
this.router.transitionTo("adminPlugins.show.discourse-ai-tools.index");
78+
},
79+
});
80+
}
81+
82+
@action
83+
updateUploads(addItemToCollection, uploads) {
84+
const uniqueUploads = uploads.filter(
85+
(upload) => !this.uploadedFiles.some((file) => file.id === upload.id)
86+
);
87+
addItemToCollection("rag_uploads", uniqueUploads);
88+
this.uploadedFiles = [...this.uploadedFiles, ...uniqueUploads];
89+
}
90+
91+
@action
92+
removeUpload(form, upload) {
93+
this.uploadedFiles = this.uploadedFiles.filter(
94+
(file) => file.id !== upload.id
95+
);
96+
form.set("rag_uploads", this.uploadedFiles);
97+
}
98+
99+
@action
100+
openTestModal() {
101+
this.modal.show(AiToolTestModal, {
102+
model: {
103+
tool: this.args.editingModel,
104+
},
105+
});
106+
}
107+
108+
currentParameterSelection(data, index) {
109+
return data.parameters[index].type;
110+
}
111+
112+
get ragUploadsDescription() {
113+
return this.siteSettings.rag_images_enabled
114+
? i18n("discourse_ai.rag.uploads.description_with_images")
115+
: i18n("discourse_ai.rag.uploads.description");
116+
}
117+
118+
<template>
119+
<Form
120+
@onSubmit={{this.save}}
121+
@data={{this.formData}}
122+
class="ai-tool-editor"
123+
as |form|
124+
>
125+
{{! NAME }}
126+
<form.Field
127+
@name="name"
128+
@title={{i18n "discourse_ai.tools.name"}}
129+
@validation="required|length:1,100"
130+
@format="large"
131+
@tooltip={{i18n "discourse_ai.tools.name_help"}}
132+
as |field|
133+
>
134+
<field.Input class="ai-tool-editor__name" />
135+
</form.Field>
136+
137+
{{! TOOL NAME }}
138+
<form.Field
139+
@name="tool_name"
140+
@title={{i18n "discourse_ai.tools.tool_name"}}
141+
@validation="required|length:1,100"
142+
@format="large"
143+
@tooltip={{i18n "discourse_ai.tools.tool_name_help"}}
144+
as |field|
145+
>
146+
<field.Input class="ai-tool-editor__tool_name" />
147+
</form.Field>
148+
149+
{{! DESCRIPTION }}
150+
<form.Field
151+
@name="description"
152+
@title={{i18n "discourse_ai.tools.description"}}
153+
@validation="required|length:1,1000"
154+
@format="full"
155+
@tooltip={{i18n "discourse_ai.tools.description_help"}}
156+
as |field|
157+
>
158+
<field.Textarea
159+
@height={{60}}
160+
class="ai-tool-editor__description"
161+
placeholder={{i18n "discourse_ai.tools.description_help"}}
162+
/>
163+
</form.Field>
164+
165+
{{! SUMMARY }}
166+
<form.Field
167+
@name="summary"
168+
@title={{i18n "discourse_ai.tools.summary"}}
169+
@validation="required|length:1,255"
170+
@format="large"
171+
@tooltip={{i18n "discourse_ai.tools.summary_help"}}
172+
as |field|
173+
>
174+
<field.Input class="ai-tool-editor__summary" />
175+
</form.Field>
176+
177+
{{! PARAMETERS }}
178+
<form.Collection @name="parameters" as |collection index collectionData|>
179+
<form.Container class="ai-tool-parameter">
180+
<form.Row as |row|>
181+
<row.Col @size={{6}}>
182+
<collection.Field
183+
@name="name"
184+
@title={{i18n "discourse_ai.tools.parameter_name"}}
185+
@validation="required|length:1,100"
186+
@format="full"
187+
as |field|
188+
>
189+
<field.Input />
190+
</collection.Field>
191+
</row.Col>
192+
193+
<row.Col @size={{6}}>
194+
<collection.Field
195+
@name="type"
196+
@title={{i18n "discourse_ai.tools.parameter_type"}}
197+
@validation="required"
198+
@format="full"
199+
as |field|
200+
>
201+
<field.Select as |select|>
202+
{{#each this.PARAMETER_TYPES as |type|}}
203+
<select.Option
204+
@value={{type.id}}
205+
>{{type.name}}</select.Option>
206+
{{/each}}
207+
</field.Select>
208+
</collection.Field>
209+
</row.Col>
210+
</form.Row>
211+
212+
<form.Row as |row|>
213+
<row.Col @size={{12}}>
214+
<collection.Field
215+
@name="description"
216+
@title={{i18n "discourse_ai.tools.parameter_description"}}
217+
@validation="required|length:1,1000"
218+
@format="full"
219+
as |field|
220+
>
221+
<field.Input class="ai-tool-editor__parameter-description" />
222+
</collection.Field>
223+
</row.Col>
224+
</form.Row>
225+
226+
<form.Row as |row|>
227+
<row.Col>
228+
<collection.Field @name="required" @title="Required" as |field|>
229+
<field.Checkbox />
230+
</collection.Field>
231+
</row.Col>
232+
233+
<row.Col>
234+
<collection.Field @name="enum" @title="Enum" as |field|>
235+
<field.Checkbox />
236+
</collection.Field>
237+
</row.Col>
238+
239+
{{#if collectionData.enum}}
240+
HANDLE IS ENUM
241+
{{/if}}
242+
</form.Row>
243+
<form.Row as |row|>
244+
<row.Col class="ai-tool-parameter-actions">
245+
<form.Button
246+
@label="discourse_ai.tools.remove_parameter"
247+
@icon="trash-can"
248+
@action={{fn collection.remove index}}
249+
class="btn-danger"
250+
/>
251+
</row.Col>
252+
</form.Row>
253+
</form.Container>
254+
</form.Collection>
255+
256+
<form.Button
257+
@icon="plus"
258+
@label="discourse_ai.tools.add_parameter"
259+
@action={{fn
260+
form.addItemToCollection
261+
"parameters"
262+
(hash name="" type="string" description="" required=false enum=false)
263+
}}
264+
/>
265+
266+
{{! SCRIPT }}
267+
<form.Field
268+
@name="script"
269+
@title={{i18n "discourse_ai.tools.script"}}
270+
@validation="required|length:1,100000"
271+
@format="full"
272+
as |field|
273+
>
274+
<field.Code @lang="javascript" @height={{600}} />
275+
</form.Field>
276+
277+
{{! UPLOADS }}
278+
{{#if this.siteSettings.ai_embeddings_enabled}}
279+
<form.Field
280+
@name="rag_uploads"
281+
@title={{i18n "discourse_ai.rag.uploads.title"}}
282+
@tooltip={{this.ragUploadsDescription}}
283+
as |field|
284+
>
285+
<field.Custom>
286+
<RagUploader
287+
@target={{@editingModel}}
288+
@updateUploads={{fn this.updateUploads form.addItemToCollection}}
289+
@onRemove={{fn this.removeUpload form}}
290+
@allowImages={{@settings.rag_images_enabled}}
291+
/>
292+
<RagOptions
293+
@model={{@editingModel}}
294+
@llms={{@llms}}
295+
@allowImages={{@settings.rag_images_enabled}}
296+
/>
297+
</field.Custom>
298+
</form.Field>
299+
{{/if}}
300+
301+
<form.Actions>
302+
{{#unless @isNew}}
303+
<form.Button
304+
@label="discourse_ai.tools.test"
305+
@action={{this.openTestModal}}
306+
class="ai-tool-editor__test-button"
307+
/>
308+
309+
<form.Button
310+
@label="discourse_ai.tools.delete"
311+
@icon="trash-can"
312+
@action={{this.delete}}
313+
class="btn-danger ai-tool-editor__delete"
314+
/>
315+
{{/unless}}
316+
317+
<form.Submit
318+
@label="discourse_ai.tools.save"
319+
class="ai-tool-editor__save"
320+
/>
321+
</form.Actions>
322+
</Form>
323+
</template>
324+
}

0 commit comments

Comments
 (0)