simplify preset editing screen
Nicolas Leão committed
Mar 31, 2026 at 14:08 UTC
b531e5ee42481e45155ea875609451edba282a1c
2 files changed
+183
-147
plugins/_model_config/webui/main.html
-1
@@ -110,7 +110,6 @@
110
.preset-card {
111
border: 1px solid var(--color-border);
112
border-radius: 6px;
113
- overflow: hidden;
113
}
114
.preset-card-header {
115
display: flex;
plugins/_model_config/webui/model-field.html
+183
-146
@@ -71,180 +71,194 @@
71
</div>
72
</div>
73
74
- <!-- API key (store mode: config.html) -->
75
- <template x-if="apiKeyMode === 'store'">
76
- <div class="field">
77
- <div class="field-label">
78
- <div class="field-title">API key</div>
79
- <div class="field-description">Authentication key for this provider. Shared across all model slots using the same provider.</div>
80
- </div>
81
- <div class="field-control" style="position:relative;" x-data="{ showKey: false }">
82
- <input :type="showKey ? 'text' : 'password'"
83
- :value="$store.modelConfig.apiKeyValues[_prov]"
84
- :placeholder="$store.modelConfig.apiKeyStatus[_prov] ? '••••••••••••' : ''"
85
- autocomplete="off"
86
- @input="$store.modelConfig.setApiKeyValue(_prov, $el.value)"
87
- style="padding-right:32px;" />
88
- <span class="material-symbols-outlined eye-toggle"
89
- @click="
90
- showKey = !showKey;
91
- const prov = _prov;
92
- if (showKey && !$store.modelConfig.apiKeyValues[prov] && $store.modelConfig.apiKeyStatus[prov]) {
93
- $store.modelConfig.revealApiKey(prov).then(v => { if (v) $store.modelConfig.apiKeyValues[prov] = v; });
94
- }
95
- "
96
- x-text="showKey ? 'visibility' : 'visibility_off'"></span>
97
- </div>
74
+ <!-- Advanced Settings (collapsed by default) -->
75
+ <div class="advanced-section" x-data="{ advOpen: false }">
76
+ <div class="advanced-toggle" @click="advOpen = !advOpen">
77
+ <span class="material-symbols-outlined advanced-toggle-icon"
78
+ :style="advOpen ? 'transform:rotate(90deg)' : ''"
79
+ >chevron_right</span>
80
+ <span>Advanced Settings</span>
81
</div>
99
- </template>
82
101
- <!-- API key (inline mode: presets) -->
102
- <template x-if="apiKeyMode === 'inline'">
103
- <div class="field">
104
- <div class="field-label">
105
- <div class="field-title">API key</div>
106
- <div class="field-description">Authentication key for this provider. Shared across all model slots using the same provider.</div>
107
- </div>
108
- <div class="field-control" style="position:relative;" x-data="{ showKey: false, _revealed: '' }">
109
- <input :type="showKey ? 'text' : 'password'" x-model="model.api_key" autocomplete="off"
110
- :placeholder="$store.modelConfig.apiKeyStatus[_prov] ? '••••••••••••' : ''"
111
- style="padding-right:32px;" />
112
- <span class="material-symbols-outlined eye-toggle"
113
- @click="
114
- showKey = !showKey;
115
- if (showKey && !model.api_key && $store.modelConfig.apiKeyStatus[_prov]) {
116
- $store.modelConfig.revealApiKey(_prov).then(v => { if (v) { model.api_key = v; _revealed = v; } });
117
- }
118
- if (!showKey && _revealed && model.api_key === _revealed) {
119
- model.api_key = ''; _revealed = '';
120
- }
121
- "
122
- x-text="showKey ? 'visibility' : 'visibility_off'"></span>
123
- </div>
124
- </div>
125
- </template>
83
+ <div class="advanced-body" x-show="advOpen" x-transition.opacity>
84
127
- <!-- API base URL -->
128
- <div class="field">
129
- <div class="field-label">
130
- <div class="field-title">API base URL</div>
131
- <div class="field-description">Custom endpoint URL. Leave empty to use the provider's default.</div>
132
- </div>
133
- <div class="field-control">
134
- <input type="text" x-model="model.api_base" />
135
- </div>
136
- </div>
85
+ <!-- API key (store mode: config.html) -->
86
+ <template x-if="apiKeyMode === 'store'">
87
+ <div class="field">
88
+ <div class="field-label">
89
+ <div class="field-title">API key</div>
90
+ <div class="field-description">Authentication key for this provider. Shared across all model slots using the same provider.</div>
91
+ </div>
92
+ <div class="field-control" style="position:relative;" x-data="{ showKey: false }">
93
+ <input :type="showKey ? 'text' : 'password'"
94
+ :value="$store.modelConfig.apiKeyValues[_prov]"
95
+ :placeholder="$store.modelConfig.apiKeyStatus[_prov] ? '••••••••••••' : ''"
96
+ autocomplete="off"
97
+ @input="$store.modelConfig.setApiKeyValue(_prov, $el.value)"
98
+ style="padding-right:32px;" />
99
+ <span class="material-symbols-outlined eye-toggle"
100
+ @click="
101
+ showKey = !showKey;
102
+ const prov = _prov;
103
+ if (showKey && !$store.modelConfig.apiKeyValues[prov] && $store.modelConfig.apiKeyStatus[prov]) {
104
+ $store.modelConfig.revealApiKey(prov).then(v => { if (v) $store.modelConfig.apiKeyValues[prov] = v; });
105
+ }
106
+ "
107
+ x-text="showKey ? 'visibility' : 'visibility_off'"></span>
108
+ </div>
109
+ </div>
110
+ </template>
111
138
- <!-- Context length (not for embedding) -->
139
- <template x-if="modelType !== 'embedding'">
140
- <div class="field">
141
- <div class="field-label">
142
- <div class="field-title">Context length</div>
143
- <div class="field-description">Maximum number of tokens in the context window. System prompt, chat history, RAG and response all count towards this limit.</div>
144
- </div>
145
- <div class="field-control">
146
- <input type="number" x-model.number="model.ctx_length" />
112
+ <!-- API key (inline mode: presets) -->
113
+ <template x-if="apiKeyMode === 'inline'">
114
+ <div class="field">
115
+ <div class="field-label">
116
+ <div class="field-title">API key</div>
117
+ <div class="field-description">Authentication key for this provider. Shared across all model slots using the same provider.</div>
118
+ </div>
119
+ <div class="field-control" style="position:relative;" x-data="{ showKey: false, _revealed: '' }">
120
+ <input :type="showKey ? 'text' : 'password'" x-model="model.api_key" autocomplete="off"
121
+ :placeholder="$store.modelConfig.apiKeyStatus[_prov] ? '••••••••••••' : ''"
122
+ style="padding-right:32px;" />
123
+ <span class="material-symbols-outlined eye-toggle"
124
+ @click="
125
+ showKey = !showKey;
126
+ if (showKey && !model.api_key && $store.modelConfig.apiKeyStatus[_prov]) {
127
+ $store.modelConfig.revealApiKey(_prov).then(v => { if (v) { model.api_key = v; _revealed = v; } });
128
+ }
129
+ if (!showKey && _revealed && model.api_key === _revealed) {
130
+ model.api_key = ''; _revealed = '';
131
+ }
132
+ "
133
+ x-text="showKey ? 'visibility' : 'visibility_off'"></span>
134
+ </div>
135
+ </div>
136
+ </template>
137
+
138
+ <!-- API base URL -->
139
+ <div class="field">
140
+ <div class="field-label">
141
+ <div class="field-title">API base URL</div>
142
+ <div class="field-description">Custom endpoint URL. Leave empty to use the provider's default.</div>
143
+ </div>
144
+ <div class="field-control">
145
+ <input type="text" x-model="model.api_base" />
146
+ </div>
147
</div>
148
- </div>
149
- </template>
148
151
- <!-- Chat-specific: ctx_history, vision, max_embeds -->
152
- <template x-if="modelType === 'chat'">
153
- <div>
149
+ <!-- Context length (not for embedding) -->
150
+ <template x-if="modelType !== 'embedding'">
151
+ <div class="field">
152
+ <div class="field-label">
153
+ <div class="field-title">Context length</div>
154
+ <div class="field-description">Maximum number of tokens in the context window. System prompt, chat history, RAG and response all count towards this limit.</div>
155
+ </div>
156
+ <div class="field-control">
157
+ <input type="number" x-model.number="model.ctx_length" />
158
+ </div>
159
+ </div>
160
+ </template>
161
+
162
+ <!-- Chat-specific: ctx_history, vision, max_embeds -->
163
+ <template x-if="modelType === 'chat'">
164
+ <div>
165
+ <div class="field">
166
+ <div class="field-label">
167
+ <div class="field-title">Context window space for chat history</div>
168
+ <div class="field-description">Portion of context window dedicated to chat history visible to the agent. Smaller size will result in shorter and more summarized history.</div>
169
+ </div>
170
+ <div class="field-control">
171
+ <input type="range" min="0.01" max="1" step="0.01" x-model.number="model.ctx_history" />
172
+ <span class="range-value" x-text="model.ctx_history"></span>
173
+ </div>
174
+ </div>
175
+ <div class="field">
176
+ <div class="field-label">
177
+ <div class="field-title">Supports Vision</div>
178
+ <div class="field-description">Models capable of Vision can for example natively see the content of image attachments.</div>
179
+ </div>
180
+ <div class="field-control">
181
+ <label class="toggle">
182
+ <input type="checkbox" x-model="model.vision" />
183
+ <span class="toggler"></span>
184
+ </label>
185
+ </div>
186
+ </div>
187
+ <template x-if="model.vision">
188
+ <div class="field">
189
+ <div class="field-label">
190
+ <div class="field-title">Max embeds</div>
191
+ <div class="field-description">Maximum number of embedded images used by the chat model. Set to 0 for unlimited.</div>
192
+ </div>
193
+ <div class="field-control">
194
+ <input type="number" min="0" x-model.number="model.max_embeds" x-init="if (!model.max_embeds) model.max_embeds = 10" />
195
+ </div>
196
+ </div>
197
+ </template>
198
+ </div>
199
+ </template>
200
+
201
+ <!-- Utility-specific: ctx_input slider -->
202
+ <template x-if="modelType === 'utility'">
203
+ <div class="field">
204
+ <div class="field-label">
205
+ <div class="field-title">Context window space for utility model input</div>
206
+ <div class="field-description">Portion of context window used for utility model input messages.</div>
207
+ </div>
208
+ <div class="field-control">
209
+ <input type="range" min="0.01" max="1" step="0.01" x-model.number="model.ctx_input" />
210
+ <span class="range-value" x-text="model.ctx_input"></span>
211
+ </div>
212
+ </div>
213
+ </template>
214
+
215
+ <!-- Rate limits -->
216
<div class="field">
217
<div class="field-label">
156
- <div class="field-title">Context window space for chat history</div>
157
- <div class="field-description">Portion of context window dedicated to chat history visible to the agent. Smaller size will result in shorter and more summarized history.</div>
218
+ <div class="field-title">Requests per minute limit</div>
219
+ <div class="field-description">Limits the number of requests per minute. Waits if the limit is exceeded. Set to 0 to disable.</div>
220
</div>
221
<div class="field-control">
160
- <input type="range" min="0.01" max="1" step="0.01" x-model.number="model.ctx_history" />
161
- <span class="range-value" x-text="model.ctx_history"></span>
222
+ <input type="number" x-model.number="model.rl_requests" />
223
</div>
224
</div>
225
+
226
<div class="field">
227
<div class="field-label">
166
- <div class="field-title">Supports Vision</div>
167
- <div class="field-description">Models capable of Vision can for example natively see the content of image attachments.</div>
228
+ <div class="field-title">Input tokens per minute limit</div>
229
+ <div class="field-description">Limits the number of input tokens per minute. Waits if the limit is exceeded. Set to 0 to disable.</div>
230
</div>
231
<div class="field-control">
170
- <label class="toggle">
171
- <input type="checkbox" x-model="model.vision" />
172
- <span class="toggler"></span>
173
- </label>
232
+ <input type="number" x-model.number="model.rl_input" />
233
</div>
234
</div>
176
- <template x-if="model.vision">
235
+
236
+ <template x-if="modelType !== 'embedding'">
237
<div class="field">
238
<div class="field-label">
179
- <div class="field-title">Max embeds</div>
180
- <div class="field-description">Maximum number of embedded images used by the chat model. Set to 0 for unlimited.</div>
239
+ <div class="field-title">Output tokens per minute limit</div>
240
+ <div class="field-description">Limits the number of output tokens per minute. Waits if the limit is exceeded. Set to 0 to disable.</div>
241
</div>
242
<div class="field-control">
183
- <input type="number" min="0" x-model.number="model.max_embeds" x-init="if (!model.max_embeds) model.max_embeds = 10" />
243
+ <input type="number" x-model.number="model.rl_output" />
244
</div>
245
</div>
246
</template>
187
- </div>
188
- </template>
189
-
190
- <!-- Utility-specific: ctx_input slider -->
191
- <template x-if="modelType === 'utility'">
192
- <div class="field">
193
- <div class="field-label">
194
- <div class="field-title">Context window space for utility model input</div>
195
- <div class="field-description">Portion of context window used for utility model input messages.</div>
196
- </div>
197
- <div class="field-control">
198
- <input type="range" min="0.01" max="1" step="0.01" x-model.number="model.ctx_input" />
199
- <span class="range-value" x-text="model.ctx_input"></span>
200
- </div>
201
- </div>
202
- </template>
247
204
- <!-- Rate limits -->
205
- <div class="field">
206
- <div class="field-label">
207
- <div class="field-title">Requests per minute limit</div>
208
- <div class="field-description">Limits the number of requests per minute. Waits if the limit is exceeded. Set to 0 to disable.</div>
209
- </div>
210
- <div class="field-control">
211
- <input type="number" x-model.number="model.rl_requests" />
212
- </div>
213
- </div>
214
-
215
- <div class="field">
216
- <div class="field-label">
217
- <div class="field-title">Input tokens per minute limit</div>
218
- <div class="field-description">Limits the number of input tokens per minute. Waits if the limit is exceeded. Set to 0 to disable.</div>
219
- </div>
220
- <div class="field-control">
221
- <input type="number" x-model.number="model.rl_input" />
222
- </div>
223
- </div>
224
-
225
- <template x-if="modelType !== 'embedding'">
226
- <div class="field">
227
- <div class="field-label">
228
- <div class="field-title">Output tokens per minute limit</div>
229
- <div class="field-description">Limits the number of output tokens per minute. Waits if the limit is exceeded. Set to 0 to disable.</div>
230
- </div>
231
- <div class="field-control">
232
- <input type="number" x-model.number="model.rl_output" />
248
+ <!-- Additional parameters -->
249
+ <div class="field field-full">
250
+ <div class="field-label">
251
+ <div class="field-title">Additional parameters</div>
252
+ <div class="field-description">
253
+ Any other parameters supported by <a href='https://docs.litellm.ai/docs/set_keys' target='_blank'>LiteLLM</a>. Format is KEY=VALUE on individual lines. Value can be JSON objects; unquoted is treated as object/number, quoted as string.
254
+ </div>
255
+ </div>
256
+ <div class="field-control">
257
+ <textarea x-model="model._kwargs_text"
258
+ @change="model.kwargs = $store.modelConfig.textToKwargs(model._kwargs_text)"></textarea>
259
+ </div>
260
</div>
234
- </div>
235
- </template>
261
237
- <!-- Additional parameters -->
238
- <div class="field field-full">
239
- <div class="field-label">
240
- <div class="field-title">Additional parameters</div>
241
- <div class="field-description">
242
- Any other parameters supported by <a href='https://docs.litellm.ai/docs/set_keys' target='_blank'>LiteLLM</a>. Format is KEY=VALUE on individual lines. Value can be JSON objects; unquoted is treated as object/number, quoted as string.
243
- </div>
244
- </div>
245
- <div class="field-control">
246
- <textarea x-model="model._kwargs_text"
247
- @change="model.kwargs = $store.modelConfig.textToKwargs(model._kwargs_text)"></textarea>
262
</div>
263
</div>
264
</div>
@@ -334,6 +348,29 @@
348
.model-search-item.disabled:hover {
349
background: transparent;
350
}
351
+
352
+ .advanced-section {
353
+ margin-top: 4px;
354
+ }
355
+ .advanced-toggle {
356
+ display: flex;
357
+ align-items: center;
358
+ gap: 4px;
359
+ font-size: 0.78rem;
360
+ opacity: 0.6;
361
+ cursor: pointer;
362
+ user-select: none;
363
+ padding: 4px 0;
364
+ }
365
+ .advanced-toggle:hover {
366
+ opacity: 1;
367
+ }
368
+ .advanced-toggle-icon {
369
+ font-size: 16px;
370
+ transition: transform 0.15s ease;
371
+ }
372
+ .advanced-body {
373
+ }
374
</style>
375
</body>
376
</html>