@@ -5,20 +5,100 @@ import { action } from "@ember/object";
55import { LinkTo } from " @ember/routing" ;
66import { inject as service } from " @ember/service" ;
77import DBreadcrumbsItem from " discourse/components/d-breadcrumbs-item" ;
8+ import DButton from " discourse/components/d-button" ;
89import DToggleSwitch from " discourse/components/d-toggle-switch" ;
910import { popupAjaxError } from " discourse/lib/ajax-error" ;
1011import icon from " discourse-common/helpers/d-icon" ;
1112import i18n from " discourse-common/helpers/i18n" ;
1213import I18n from " discourse-i18n" ;
14+ import AdminPageSubheader from " admin/components/admin-page-subheader" ;
1315import AiLlmEditor from " ./ai-llm-editor" ;
1416
1517export default class AiLlmsListEditor extends Component {
1618 @service adminPluginNavManager;
19+ @service router;
1720
18- get hasLLMElements () {
21+ @action
22+ modelDescription (llm ) {
23+ // this is a bit of an odd object, it can be an llm model or a preset model
24+ // handle both flavors
25+
26+ // in the case of model
27+ let key = " " ;
28+ if (typeof llm .id === " number" ) {
29+ key = ` ${ llm .provider } -${ llm .name } ` ;
30+ } else {
31+ // case of preset
32+ key = llm .id .replace (/ \. / g , " -" );
33+ }
34+
35+ key = ` discourse_ai.llms.model_description.${ key} ` ;
36+ if (I18n .lookup (key, { ignoreMissing: true })) {
37+ return I18n .t (key);
38+ }
39+ return " " ;
40+ }
41+
42+ sanitizedTranslationKey (id ) {
43+ return id .replace (/ \. / g , " -" );
44+ }
45+
46+ get hasLlmElements () {
1947 return this .args .llms .length !== 0 ;
2048 }
2149
50+ get preconfiguredTitle () {
51+ if (this .hasLlmElements ) {
52+ return " discourse_ai.llms.preconfigured.title" ;
53+ } else {
54+ return " discourse_ai.llms.preconfigured.title_no_llms" ;
55+ }
56+ }
57+
58+ get preConfiguredLlms () {
59+ const options = [
60+ {
61+ id: " none" ,
62+ name: I18n .t (" discourse_ai.llms.preconfigured.fake" ),
63+ provider: " fake" ,
64+ },
65+ ];
66+
67+ const llmsContent = this .args .llms .content .map ((llm ) => ({
68+ provider: llm .provider ,
69+ name: llm .name ,
70+ }));
71+
72+ this .args .llms .resultSetMeta .presets .forEach ((llm ) => {
73+ if (llm .models ) {
74+ llm .models .forEach ((model ) => {
75+ const id = ` ${ llm .id } -${ model .name } ` ;
76+ const isConfigured = llmsContent .some (
77+ (content ) =>
78+ content .provider === llm .provider && content .name === model .name
79+ );
80+
81+ if (! isConfigured) {
82+ options .push ({
83+ id,
84+ name: model .display_name ,
85+ provider: llm .provider ,
86+ });
87+ }
88+ });
89+ }
90+ });
91+
92+ return options;
93+ }
94+
95+ @action
96+ transitionToLlmEditor (llmTemplate ) {
97+ this .router .transitionTo (" adminPlugins.show.discourse-ai-llms.new" , {
98+ queryParams: { llmTemplate },
99+ });
100+ }
101+
22102 @action
23103 async toggleEnabledChatBot (llm ) {
24104 const oldValue = llm .enabled_chat_bot ;
@@ -39,60 +119,92 @@ export default class AiLlmsListEditor extends Component {
39119 @ path =" /admin/plugins/{{this .adminPluginNavManager.currentPlugin.name }} /ai-llms"
40120 @ label ={{i18n " discourse_ai.llms.short_title" }}
41121 />
42- <section class =" ai-llms-list-editor admin-detail pull-left" >
43-
122+ <section class =" ai-llm-list-editor admin-detail" >
44123 {{#if @ currentLlm }}
45- <AiLlmEditor @ model ={{@ currentLlm }} @ llms ={{@ llms }} />
124+ <AiLlmEditor
125+ @ model ={{@ currentLlm }}
126+ @ llms ={{@ llms }}
127+ @ llmTemplate ={{@ llmTemplate }}
128+ />
46129 {{else }}
47- <div class =" ai-llms-list-editor__header" >
48- <h3 >{{i18n " discourse_ai.llms.short_title" }} </h3 >
49- {{#unless @ currentLlm.isNew }}
50- <LinkTo
51- @ route =" adminPlugins.show.discourse-ai-llms.new"
52- class =" btn btn-small btn-primary ai-llms-list-editor__new"
53- >
54- {{icon " plus" }}
55- <span >{{I18n.t " discourse_ai.llms.new" }} </span >
56- </LinkTo >
57- {{/unless }}
58- </div >
59-
60- {{#if this . hasLLMElements }}
61- <table class =" content-list ai-persona-list-editor" >
62- <thead >
63- <tr >
64- <th >{{i18n " discourse_ai.llms.display_name" }} </th >
65- <th >{{i18n " discourse_ai.llms.provider" }} </th >
66- <th >{{i18n " discourse_ai.llms.enabled_chat_bot" }} </th >
67- <th ></th >
68- </tr >
69- </thead >
70- <tbody >
71- {{#each @ llms as | llm | }}
72- <tr data-persona-id ={{llm.id }} class =" ai-llm-list__row" >
73- <td ><strong >{{llm.display_name }} </strong ></td >
74- <td >{{i18n
75- ( concat " discourse_ai.llms.providers." llm.provider )
76- }} </td >
77- <td >
78- <DToggleSwitch
79- @ state ={{llm.enabled_chat_bot }}
80- {{on " click" ( fn this . toggleEnabledChatBot llm ) }}
81- />
82- </td >
83- <td >
84- <LinkTo
85- @ route =" adminPlugins.show.discourse-ai-llms.show"
86- current-when =" true"
87- class =" btn btn-text btn-small"
88- @ model ={{llm }}
89- >{{i18n " discourse_ai.llms.edit" }} </LinkTo >
90- </td >
130+ {{#if this . hasLlmElements }}
131+ <section class =" ai-llms-list-editor__configured" >
132+ <AdminPageSubheader
133+ @ titleLabel =" discourse_ai.llms.configured.title"
134+ />
135+ <table >
136+ <thead >
137+ <tr >
138+ <th >{{i18n " discourse_ai.llms.display_name" }} </th >
139+ <th >{{i18n " discourse_ai.llms.provider" }} </th >
140+ <th >{{i18n " discourse_ai.llms.enabled_chat_bot" }} </th >
141+ <th ></th >
91142 </tr >
92- {{/each }}
93- </tbody >
94- </table >
143+ </thead >
144+ <tbody >
145+ {{#each @ llms as | llm | }}
146+ <tr data-persona-id ={{llm.id }} class =" ai-llm-list__row" >
147+ <td class =" column-name" >
148+ <h3 >{{llm.display_name }} </h3 >
149+ <p >
150+ {{this .modelDescription llm }}
151+ </p >
152+ </td >
153+ <td >
154+ {{i18n
155+ ( concat " discourse_ai.llms.providers." llm.provider )
156+ }}
157+ </td >
158+ <td >
159+ <DToggleSwitch
160+ @ state ={{llm.enabled_chat_bot }}
161+ {{on " click" ( fn this . toggleEnabledChatBot llm ) }}
162+ />
163+ </td >
164+ <td class =" column-edit" >
165+ <LinkTo
166+ @ route =" adminPlugins.show.discourse-ai-llms.show"
167+ class =" btn btn-default"
168+ @ model ={{llm.id }}
169+ >
170+ {{icon " wrench" }}
171+ <div class =" d-button-label" >
172+ {{i18n " discourse_ai.llms.edit" }}
173+ </div >
174+ </LinkTo >
175+ </td >
176+ </tr >
177+ {{/each }}
178+ </tbody >
179+ </table >
180+ </section >
95181 {{/if }}
182+ <section class =" ai-llms-list-editor__templates" >
183+ <AdminPageSubheader @ titleLabel ={{this .preconfiguredTitle }} />
184+ <div class =" ai-llms-list-editor__templates-list" >
185+ {{#each this . preConfiguredLlms as | llm | }}
186+ <div
187+ data-llm-id ={{llm.id }}
188+ class =" ai-llms-list-editor__templates-list-item"
189+ >
190+ <h4 >
191+ {{i18n ( concat " discourse_ai.llms.providers." llm.provider ) }}
192+ </h4 >
193+ <h3 >
194+ {{llm.name }}
195+ </h3 >
196+ <p >
197+ {{this .modelDescription llm }}
198+ </p >
199+ <DButton
200+ @ action ={{fn this . transitionToLlmEditor llm.id }}
201+ @ icon =" gear"
202+ @ label =" discourse_ai.llms.preconfigured.button"
203+ />
204+ </div >
205+ {{/each }}
206+ </div >
207+ </section >
96208 {{/if }}
97209 </section >
98210 </template >
0 commit comments