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

Commit f2ab44f

Browse files
committed
This is all working now!
1 parent 476a87f commit f2ab44f

File tree

5 files changed

+216
-59
lines changed

5 files changed

+216
-59
lines changed

assets/javascripts/discourse/components/ai-llm-editor-form.gjs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,26 @@ import AdminUser from "admin/models/admin-user";
1818
import ComboBox from "select-kit/components/combo-box";
1919
import DTooltip from "float-kit/components/d-tooltip";
2020
import AiLlmQuotaEditor from "./ai-llm-quota-editor";
21+
import AiLlmQuotaModal from "./modal/ai-llm-quota-modal";
2122

2223
export default class AiLlmEditorForm extends Component {
2324
@service toasts;
2425
@service router;
2526
@service dialog;
27+
@service modal;
2628

2729
@tracked isSaving = false;
2830

2931
@tracked testRunning = false;
3032
@tracked testResult = null;
3133
@tracked testError = null;
3234
@tracked apiKeySecret = true;
35+
@tracked quotaCount = 0;
36+
37+
constructor() {
38+
super(...arguments);
39+
this.updateQuotaCount();
40+
}
3341

3442
get selectedProviders() {
3543
const t = (provName) => {
@@ -86,6 +94,26 @@ export default class AiLlmEditorForm extends Component {
8694
});
8795
}
8896

97+
get showQuotas() {
98+
return this.quotaCount > 0;
99+
}
100+
101+
get showAddQuotaButton() {
102+
return !this.showQuotas && !this.args.model.isNew;
103+
}
104+
105+
@action
106+
updateQuotaCount() {
107+
this.quotaCount = this.args.model.llm_quotas.length;
108+
}
109+
110+
@action
111+
openAddQuotaModal() {
112+
this.modal.show(AiLlmQuotaModal, {
113+
model: { llm: this.args.model, onSave: this.updateQuotaCount },
114+
});
115+
}
116+
89117
@computed("args.model.provider")
90118
get metaProviderParams() {
91119
return (
@@ -318,12 +346,16 @@ export default class AiLlmEditorForm extends Component {
318346
</div>
319347
{{/if}}
320348

321-
{{#unless @model.isNew}}
349+
{{#if this.showQuotas}}
322350
<div class="control-group">
323351
<label>{{i18n "discourse_ai.llms.quotas.title"}}</label>
324-
<AiLlmQuotaEditor @model={{@model}} @groups={{@groups}} />
352+
<AiLlmQuotaEditor
353+
@model={{@model}}
354+
@groups={{@groups}}
355+
@didUpdate={{this.updateQuotaCount}}
356+
/>
325357
</div>
326-
{{/unless}}
358+
{{/if}}
327359

328360
<div class="control-group ai-llm-editor__action_panel">
329361
<DButton
@@ -332,7 +364,13 @@ export default class AiLlmEditorForm extends Component {
332364
@disabled={{this.testRunning}}
333365
@label="discourse_ai.llms.tests.title"
334366
/>
335-
367+
{{#if this.showAddQuotaButton}}
368+
<DButton
369+
@action={{this.openAddQuotaModal}}
370+
@label="discourse_ai.llms.quotas.add"
371+
class="btn"
372+
/>
373+
{{/if}}
336374
<DButton
337375
class="btn-primary ai-llm-editor__save"
338376
@action={{this.save}}

assets/javascripts/discourse/components/ai-llm-quota-editor.gjs

Lines changed: 24 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import Component from "@glimmer/component";
22
import { tracked } from "@glimmer/tracking";
3-
import { fn, hash } from "@ember/helper";
3+
import { fn } from "@ember/helper";
44
import { on } from "@ember/modifier";
55
import { action } from "@ember/object";
66
import { service } from "@ember/service";
77
import DButton from "discourse/components/d-button";
88
import I18n from "discourse-i18n";
9-
import GroupChooser from "select-kit/components/group-chooser";
109
import DurationSelector from "./ai-quota-duration-selector";
10+
import AiLlmQuotaModal from "./modal/ai-llm-quota-modal";
1111

1212
export default class AiLlmQuotaEditor extends Component {
1313
@service store;
1414
@service dialog;
1515
@service site;
16+
@service modal;
1617

1718
@tracked newQuotaGroupIds = null;
1819
@tracked newQuotaTokens = null;
@@ -34,6 +35,13 @@ export default class AiLlmQuotaEditor extends Component {
3435
quota.duration_seconds = value;
3536
}
3637

38+
@action
39+
openAddQuotaModal() {
40+
this.modal.show(AiLlmQuotaModal, {
41+
model: { llm: this.args.model },
42+
});
43+
}
44+
3745
get canAddQuota() {
3846
return (
3947
this.newQuotaGroupId &&
@@ -73,11 +81,17 @@ export default class AiLlmQuotaEditor extends Component {
7381
duration_seconds: this.newQuotaDuration,
7482
};
7583
this.args.model.llm_quotas.pushObject(quota);
84+
if (this.args.didUpdate) {
85+
this.args.didUpdate();
86+
}
7687
}
7788

7889
@action
7990
async deleteQuota(quota) {
8091
this.args.model.llm_quotas.removeObject(quota);
92+
if (this.args.didUpdate) {
93+
this.args.didUpdate();
94+
}
8195
}
8296

8397
<template>
@@ -139,52 +153,16 @@ export default class AiLlmQuotaEditor extends Component {
139153
</td>
140154
</tr>
141155
{{/each}}
142-
<tr class="ai-llm-quotas__row ai-llm-quotas__row--new">
143-
<td class="ai-llm-quotas__cell">
144-
<GroupChooser
145-
@value={{this.newQuotaGroupIds}}
146-
@content={{this.site.groups}}
147-
@onChange={{this.updateGroups}}
148-
@options={{hash maximum=1}}
149-
/>
150-
</td>
151-
<td class="ai-llm-quotas__cell">
152-
<input
153-
type="number"
154-
value={{this.newQuotaTokens}}
155-
class="ai-llm-quotas__input"
156-
{{on "input" this.updateQuotaTokens}}
157-
min="1"
158-
/>
159-
</td>
160-
<td class="ai-llm-quotas__cell">
161-
<input
162-
type="number"
163-
value={{this.newQuotaUsages}}
164-
class="ai-llm-quotas__input"
165-
{{on "input" this.updateQuotaUsages}}
166-
min="1"
167-
/>
168-
</td>
169-
<td class="ai-llm-quotas__cell">
170-
<input
171-
type="number"
172-
value={{this.newQuotaDuration}}
173-
class="ai-llm-quotas__input"
174-
{{on "input" this.updateQuotaDuration}}
175-
min="1"
176-
/>
177-
</td>
178-
<td class="ai-llm-quotas__cell ai-llm-quotas__cell--actions">
179-
<DButton
180-
@action={{this.addQuota}}
181-
@icon="plus"
182-
class="btn-primary ai-llm-quotas__add-btn"
183-
/>
184-
</td>
185-
</tr>
186156
</tbody>
187157
</table>
158+
<div class="ai-llm-quotas__actions">
159+
<DButton
160+
@action={{this.openAddQuotaModal}}
161+
@icon="plus"
162+
@label="discourse_ai.llms.quotas.add"
163+
class="btn"
164+
/>
165+
</div>
188166
</div>
189167
</template>
190168
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import Component from "@glimmer/component";
2+
import { tracked } from "@glimmer/tracking";
3+
import { hash } from "@ember/helper";
4+
import { on } from "@ember/modifier";
5+
import { action } from "@ember/object";
6+
import { service } from "@ember/service";
7+
import { not } from "truth-helpers";
8+
import DButton from "discourse/components/d-button";
9+
import DModal from "discourse/components/d-modal";
10+
import I18n from "discourse-i18n";
11+
import GroupChooser from "select-kit/components/group-chooser";
12+
import DurationSelector from "../ai-quota-duration-selector";
13+
14+
export default class AiLlmQuotaModal extends Component {
15+
@service site;
16+
17+
@tracked groupIds = null;
18+
@tracked maxTokens = null;
19+
@tracked maxUsages = null;
20+
@tracked duration = 86400; // Default 1 day
21+
22+
get canSave() {
23+
return (
24+
this.groupIds?.length > 0 &&
25+
(this.maxTokens || this.maxUsages) &&
26+
this.duration
27+
);
28+
}
29+
30+
@action
31+
updateGroups(groups) {
32+
this.groupIds = groups;
33+
}
34+
35+
@action
36+
updateDuration(value) {
37+
this.duration = value;
38+
}
39+
40+
@action
41+
updateMaxTokens(event) {
42+
this.maxTokens = event.target.value;
43+
}
44+
45+
@action
46+
updateMaxUsages(event) {
47+
this.maxUsages = event.target.value;
48+
}
49+
50+
@action
51+
save() {
52+
const quota = {
53+
group_id: this.groupIds[0],
54+
group_name: this.site.groups.findBy("id", this.groupIds[0]).name,
55+
llm_model_id: this.args.model.id,
56+
max_tokens: this.maxTokens,
57+
max_usages: this.maxUsages,
58+
duration_seconds: this.duration,
59+
};
60+
61+
this.args.model.llm.llm_quotas.pushObject(quota);
62+
this.args.closeModal();
63+
if (this.args.model.onSave) {
64+
this.args.model.onSave();
65+
}
66+
}
67+
68+
get availableGroups() {
69+
const existingQuotaGroupIds =
70+
this.args.model.llm.llm_quotas.map((q) => q.group_id) || [];
71+
72+
return this.site.groups.filter(
73+
(group) => !existingQuotaGroupIds.includes(group.id) && group.id !== 0
74+
);
75+
}
76+
77+
<template>
78+
<DModal
79+
@title={{I18n.t "discourse_ai.llms.quotas.add_title"}}
80+
@closeModal={{@closeModal}}
81+
>
82+
<:body>
83+
<div class="control-group">
84+
<label>{{I18n.t "discourse_ai.llms.quotas.group"}}</label>
85+
<GroupChooser
86+
@value={{this.groupIds}}
87+
@content={{this.availableGroups}}
88+
@onChange={{this.updateGroups}}
89+
@options={{hash maximum=1}}
90+
/>
91+
</div>
92+
93+
<div class="control-group">
94+
<label>{{I18n.t "discourse_ai.llms.quotas.max_tokens"}}</label>
95+
<input
96+
type="number"
97+
value={{this.maxTokens}}
98+
class="input-large"
99+
min="1"
100+
{{on "input" this.updateMaxTokens}}
101+
/>
102+
</div>
103+
104+
<div class="control-group">
105+
<label>{{I18n.t "discourse_ai.llms.quotas.max_usages"}}</label>
106+
<input
107+
type="number"
108+
value={{this.maxUsages}}
109+
class="input-large"
110+
min="1"
111+
{{on "input" this.updateMaxUsages}}
112+
/>
113+
</div>
114+
115+
<div class="control-group">
116+
<label>{{I18n.t "discourse_ai.llms.quotas.duration"}}</label>
117+
<DurationSelector
118+
@value={{this.duration}}
119+
@onChange={{this.updateDuration}}
120+
/>
121+
</div>
122+
</:body>
123+
124+
<:footer>
125+
<DButton
126+
@action={{this.save}}
127+
@label="discourse_ai.llms.quotas.add"
128+
@disabled={{not this.canSave}}
129+
class="btn-primary"
130+
/>
131+
</:footer>
132+
</DModal>
133+
</template>
134+
}

assets/stylesheets/modules/llms/common/ai-llm-quotas.scss

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@
1111
background-color: var(--primary-very-low);
1212
}
1313

14+
.duration-selector {
15+
.select-kit {
16+
width: 150px;
17+
}
18+
}
19+
20+
.duration-selector__custom {
21+
margin-top: 8px;
22+
}
23+
1424
&__header {
1525
text-align: left;
1626
padding: 0.5em;
@@ -24,26 +34,21 @@
2434

2535
&__row {
2636
border-bottom: 1px solid var(--primary-low);
27-
28-
&--new {
29-
background-color: var(--primary-very-low);
30-
}
3137
}
3238

3339
&__cell {
34-
padding: 0.5em;
3540
vertical-align: middle;
41+
align-items: center;
3642

3743
&--actions {
3844
text-align: center;
3945
}
4046
}
4147

42-
&__input {
43-
width: 100px;
48+
&__input[type=number] {
49+
width: 200px;
4450
padding: 0.5em;
45-
border: 1px solid var(--primary-low);
46-
border-radius: 3px;
51+
margin-bottom: 0;
4752
}
4853

4954
&__group-select {

0 commit comments

Comments
 (0)