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

Commit 331e68f

Browse files
committed
Move persona/llm selector to component
1 parent 86f82ea commit 331e68f

File tree

2 files changed

+193
-169
lines changed

2 files changed

+193
-169
lines changed
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import Component from "@glimmer/component";
2+
import { tracked } from "@glimmer/tracking";
3+
import { hash } from "@ember/helper";
4+
import { next } from "@ember/runloop";
5+
import { service } from "@ember/service";
6+
import KeyValueStore from "discourse/lib/key-value-store";
7+
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
8+
9+
export default class AiPersonaLlmSelector extends Component {
10+
@service currentUser;
11+
12+
@tracked llm;
13+
@tracked allowLLMSelector = true;
14+
15+
STORE_NAMESPACE = "discourse_ai_persona_selector_";
16+
LLM_STORE_NAMESPACE = "discourse_ai_llm_selector_";
17+
18+
preferredPersonaStore = new KeyValueStore(this.STORE_NAMESPACE);
19+
preferredLlmStore = new KeyValueStore(this.LLM_STORE_NAMESPACE);
20+
21+
constructor() {
22+
super(...arguments);
23+
24+
if (this.botOptions && this.botOptions.length) {
25+
let personaId = this.preferredPersonaStore.getObject("id");
26+
27+
this._value = this.botOptions[0].id;
28+
if (personaId) {
29+
personaId = parseInt(personaId, 10);
30+
if (this.botOptions.any((bot) => bot.id === personaId)) {
31+
this._value = personaId;
32+
}
33+
}
34+
35+
this.args.setPersonaId(this._value);
36+
this.setAllowLLMSelector();
37+
38+
if (this.hasLlmSelector) {
39+
let llm = this.preferredLlmStore.getObject("id");
40+
41+
const llmOption =
42+
this.llmOptions.find((innerLlmOption) => innerLlmOption.id === llm) ||
43+
this.llmOptions[0];
44+
45+
if (llmOption) {
46+
llm = llmOption.id;
47+
} else {
48+
llm = "";
49+
}
50+
51+
if (llm) {
52+
next(() => {
53+
this.currentLlm = llm;
54+
});
55+
}
56+
}
57+
58+
next(() => {
59+
this.resetTargetRecipients();
60+
});
61+
}
62+
}
63+
64+
get composer() {
65+
return this.args?.outletArgs?.model;
66+
}
67+
68+
get hasLlmSelector() {
69+
return this.currentUser.ai_enabled_chat_bots.any((bot) => !bot.is_persona);
70+
}
71+
72+
get botOptions() {
73+
if (this.currentUser.ai_enabled_personas) {
74+
let enabledPersonas = this.currentUser.ai_enabled_personas;
75+
76+
if (!this.hasLlmSelector) {
77+
enabledPersonas = enabledPersonas.filter((persona) => persona.username);
78+
}
79+
80+
return enabledPersonas.map((persona) => {
81+
return {
82+
id: persona.id,
83+
name: persona.name,
84+
description: persona.description,
85+
};
86+
});
87+
}
88+
}
89+
90+
get filterable() {
91+
return this.botOptions.length > 4;
92+
}
93+
94+
get value() {
95+
return this._value;
96+
}
97+
98+
set value(newValue) {
99+
this._value = newValue;
100+
this.preferredPersonaStore.setObject({ key: "id", value: newValue });
101+
this.args.setPersonaId(newValue);
102+
this.setAllowLLMSelector();
103+
this.resetTargetRecipients();
104+
}
105+
106+
setAllowLLMSelector() {
107+
if (!this.hasLlmSelector) {
108+
this.allowLLMSelector = false;
109+
return;
110+
}
111+
112+
const persona = this.currentUser.ai_enabled_personas.find(
113+
(innerPersona) => innerPersona.id === this._value
114+
);
115+
116+
this.allowLLMSelector = !persona?.force_default_llm;
117+
}
118+
119+
get currentLlm() {
120+
return this.llm;
121+
}
122+
123+
set currentLlm(newValue) {
124+
this.llm = newValue;
125+
this.preferredLlmStore.setObject({ key: "id", value: newValue });
126+
127+
this.resetTargetRecipients();
128+
}
129+
130+
resetTargetRecipients() {
131+
if (this.allowLLMSelector) {
132+
const botUsername = this.currentUser.ai_enabled_chat_bots.find(
133+
(bot) => bot.id === this.llm
134+
).username;
135+
this.args.setTargetRecipient(botUsername);
136+
} else {
137+
const persona = this.currentUser.ai_enabled_personas.find(
138+
(innerPersona) => innerPersona.id === this._value
139+
);
140+
this.args.setTargetRecipient(persona.username || "");
141+
}
142+
}
143+
144+
get llmOptions() {
145+
const availableBots = this.currentUser.ai_enabled_chat_bots
146+
.filter((bot) => !bot.is_persona)
147+
.filter(Boolean);
148+
149+
return availableBots
150+
.map((bot) => {
151+
return {
152+
id: bot.id,
153+
name: bot.display_name,
154+
};
155+
})
156+
.sort((a, b) => a.name.localeCompare(b.name));
157+
}
158+
159+
<template>
160+
<div class="persona-llm-selector">
161+
<div class="gpt-persona">
162+
<DropdownSelectBox
163+
class="persona-llm-selector__persona-dropdown"
164+
@value={{this.value}}
165+
@content={{this.botOptions}}
166+
@options={{hash icon="robot" filterable=this.filterable}}
167+
/>
168+
</div>
169+
{{#if this.allowLLMSelector}}
170+
<div class="llm-selector">
171+
<DropdownSelectBox
172+
class="persona-llm-selector__llm-dropdown"
173+
@value={{this.currentLlm}}
174+
@content={{this.llmOptions}}
175+
@options={{hash icon="globe"}}
176+
/>
177+
</div>
178+
{{/if}}
179+
</div>
180+
</template>
181+
}
Lines changed: 12 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import Component from "@glimmer/component";
2-
import { tracked } from "@glimmer/tracking";
3-
import { hash } from "@ember/helper";
4-
import { next } from "@ember/runloop";
2+
import { action } from "@ember/object";
53
import { service } from "@ember/service";
6-
import KeyValueStore from "discourse/lib/key-value-store";
7-
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
4+
import AiPersonaLlmSelector from "discourse/plugins/discourse-ai/discourse/components/ai-persona-llm-selector";
85

96
function isBotMessage(composer, currentUser) {
107
if (
@@ -30,175 +27,21 @@ export default class BotSelector extends Component {
3027
}
3128

3229
@service currentUser;
33-
@service siteSettings;
3430

35-
@tracked llm;
36-
@tracked allowLLMSelector = true;
37-
38-
STORE_NAMESPACE = "discourse_ai_persona_selector_";
39-
LLM_STORE_NAMESPACE = "discourse_ai_llm_selector_";
40-
41-
preferredPersonaStore = new KeyValueStore(this.STORE_NAMESPACE);
42-
preferredLlmStore = new KeyValueStore(this.LLM_STORE_NAMESPACE);
43-
44-
constructor() {
45-
super(...arguments);
46-
47-
if (this.botOptions && this.botOptions.length && this.composer) {
48-
let personaId = this.preferredPersonaStore.getObject("id");
49-
50-
this._value = this.botOptions[0].id;
51-
if (personaId) {
52-
personaId = parseInt(personaId, 10);
53-
if (this.botOptions.any((bot) => bot.id === personaId)) {
54-
this._value = personaId;
55-
}
56-
}
57-
58-
this.composer.metaData = { ai_persona_id: this._value };
59-
this.setAllowLLMSelector();
60-
61-
if (this.hasLlmSelector) {
62-
let llm = this.preferredLlmStore.getObject("id");
63-
64-
const llmOption =
65-
this.llmOptions.find((innerLlmOption) => innerLlmOption.id === llm) ||
66-
this.llmOptions[0];
67-
68-
if (llmOption) {
69-
llm = llmOption.id;
70-
} else {
71-
llm = "";
72-
}
73-
74-
if (llm) {
75-
next(() => {
76-
this.currentLlm = llm;
77-
});
78-
}
79-
}
80-
81-
next(() => {
82-
this.resetTargetRecipients();
83-
});
84-
}
85-
}
86-
87-
get composer() {
88-
return this.args?.outletArgs?.model;
89-
}
90-
91-
get hasLlmSelector() {
92-
return this.currentUser.ai_enabled_chat_bots.any((bot) => !bot.is_persona);
93-
}
94-
95-
get botOptions() {
96-
if (this.currentUser.ai_enabled_personas) {
97-
let enabledPersonas = this.currentUser.ai_enabled_personas;
98-
99-
if (!this.hasLlmSelector) {
100-
enabledPersonas = enabledPersonas.filter((persona) => persona.username);
101-
}
102-
103-
return enabledPersonas.map((persona) => {
104-
return {
105-
id: persona.id,
106-
name: persona.name,
107-
description: persona.description,
108-
};
109-
});
110-
}
31+
@action
32+
setPersonaIdOnComposer(id) {
33+
this.args.outletArgs.model.metaData = { ai_persona_id: id };
11134
}
11235

113-
get filterable() {
114-
return this.botOptions.length > 4;
115-
}
116-
117-
get value() {
118-
return this._value;
119-
}
120-
121-
set value(newValue) {
122-
this._value = newValue;
123-
this.preferredPersonaStore.setObject({ key: "id", value: newValue });
124-
this.composer.metaData = { ai_persona_id: newValue };
125-
this.setAllowLLMSelector();
126-
this.resetTargetRecipients();
127-
}
128-
129-
setAllowLLMSelector() {
130-
if (!this.hasLlmSelector) {
131-
this.allowLLMSelector = false;
132-
return;
133-
}
134-
135-
const persona = this.currentUser.ai_enabled_personas.find(
136-
(innerPersona) => innerPersona.id === this._value
137-
);
138-
139-
this.allowLLMSelector = !persona?.force_default_llm;
140-
}
141-
142-
get currentLlm() {
143-
return this.llm;
144-
}
145-
146-
set currentLlm(newValue) {
147-
this.llm = newValue;
148-
this.preferredLlmStore.setObject({ key: "id", value: newValue });
149-
150-
this.resetTargetRecipients();
151-
}
152-
153-
resetTargetRecipients() {
154-
if (this.allowLLMSelector) {
155-
const botUsername = this.currentUser.ai_enabled_chat_bots.find(
156-
(bot) => bot.id === this.llm
157-
).username;
158-
this.composer.set("targetRecipients", botUsername);
159-
} else {
160-
const persona = this.currentUser.ai_enabled_personas.find(
161-
(innerPersona) => innerPersona.id === this._value
162-
);
163-
this.composer.set("targetRecipients", persona.username || "");
164-
}
165-
}
166-
167-
get llmOptions() {
168-
const availableBots = this.currentUser.ai_enabled_chat_bots
169-
.filter((bot) => !bot.is_persona)
170-
.filter(Boolean);
171-
172-
return availableBots
173-
.map((bot) => {
174-
return {
175-
id: bot.id,
176-
name: bot.display_name,
177-
};
178-
})
179-
.sort((a, b) => a.name.localeCompare(b.name));
36+
@action
37+
setTargetRecipientsOnComposer(username) {
38+
this.args.outletArgs.model.set("targetRecipients", username);
18039
}
18140

18241
<template>
183-
<div class="persona-llm-selector">
184-
<div class="gpt-persona">
185-
<DropdownSelectBox
186-
class="persona-llm-selector__persona-dropdown"
187-
@value={{this.value}}
188-
@content={{this.botOptions}}
189-
@options={{hash icon="robot" filterable=this.filterable}}
190-
/>
191-
</div>
192-
{{#if this.allowLLMSelector}}
193-
<div class="llm-selector">
194-
<DropdownSelectBox
195-
class="persona-llm-selector__llm-dropdown"
196-
@value={{this.currentLlm}}
197-
@content={{this.llmOptions}}
198-
@options={{hash icon="globe"}}
199-
/>
200-
</div>
201-
{{/if}}
202-
</div>
42+
<AiPersonaLlmSelector
43+
@setPersonaId={{this.setPersonaIdOnComposer}}
44+
@setTargetRecipient={{this.setTargetRecipientsOnComposer}}
45+
/>
20346
</template>
20447
}

0 commit comments

Comments
 (0)