lemonade-sdk 8.1.8__py3-none-any.whl → 8.1.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of lemonade-sdk might be problematic. Click here for more details.
- lemonade/cli.py +47 -1
- lemonade/common/inference_engines.py +13 -4
- lemonade/common/system_info.py +570 -1
- lemonade/profilers/agt_power.py +437 -0
- lemonade/profilers/hwinfo_power.py +429 -0
- lemonade/tools/llamacpp/utils.py +16 -4
- lemonade/tools/oga/load.py +15 -2
- lemonade/tools/server/llamacpp.py +3 -12
- lemonade/tools/server/serve.py +32 -0
- lemonade/tools/server/static/js/chat.js +481 -242
- lemonade/tools/server/static/js/models.js +106 -29
- lemonade/tools/server/static/js/shared.js +4 -2
- lemonade/tools/server/static/styles.css +114 -68
- lemonade/tools/server/static/webapp.html +19 -23
- lemonade/tools/server/tray.py +64 -0
- lemonade/version.py +1 -1
- {lemonade_sdk-8.1.8.dist-info → lemonade_sdk-8.1.10.dist-info}/METADATA +2 -2
- {lemonade_sdk-8.1.8.dist-info → lemonade_sdk-8.1.10.dist-info}/RECORD +26 -24
- lemonade_server/cli.py +2 -0
- lemonade_server/server_models.json +24 -6
- lemonade_server/settings.py +39 -39
- {lemonade_sdk-8.1.8.dist-info → lemonade_sdk-8.1.10.dist-info}/WHEEL +0 -0
- {lemonade_sdk-8.1.8.dist-info → lemonade_sdk-8.1.10.dist-info}/entry_points.txt +0 -0
- {lemonade_sdk-8.1.8.dist-info → lemonade_sdk-8.1.10.dist-info}/licenses/LICENSE +0 -0
- {lemonade_sdk-8.1.8.dist-info → lemonade_sdk-8.1.10.dist-info}/licenses/NOTICE.md +0 -0
- {lemonade_sdk-8.1.8.dist-info → lemonade_sdk-8.1.10.dist-info}/top_level.txt +0 -0
|
@@ -42,53 +42,90 @@ async function checkModelHealth() {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Populate the model dropdown with all installed models
|
|
46
|
+
function populateModelDropdown() {
|
|
47
|
+
const indicator = document.getElementById('model-status-indicator');
|
|
48
|
+
const select = document.getElementById('model-select');
|
|
49
|
+
select.innerHTML = '';
|
|
50
|
+
|
|
51
|
+
// Add the default option
|
|
52
|
+
const defaultOption = document.createElement('option');
|
|
53
|
+
defaultOption.value = '';
|
|
54
|
+
defaultOption.textContent = 'Click to select a model ▼';
|
|
55
|
+
select.appendChild(defaultOption);
|
|
56
|
+
|
|
57
|
+
// Add the hidden 'Server Offline' option
|
|
58
|
+
const hiddenOption = document.createElement('option');
|
|
59
|
+
hiddenOption.value = 'server-offline';
|
|
60
|
+
hiddenOption.textContent = 'Server Offline';
|
|
61
|
+
hiddenOption.hidden = true;
|
|
62
|
+
select.appendChild(hiddenOption);
|
|
63
|
+
|
|
64
|
+
// Get all installed models from the global set
|
|
65
|
+
const sortedModels = Array.from(installedModels).sort();
|
|
66
|
+
|
|
67
|
+
// Add options for each installed model
|
|
68
|
+
sortedModels.forEach(modelId => {
|
|
69
|
+
const option = document.createElement('option');
|
|
70
|
+
option.value = modelId;
|
|
71
|
+
option.textContent = modelId;
|
|
72
|
+
select.appendChild(option);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
45
76
|
// Update model status indicator
|
|
46
77
|
async function updateModelStatusIndicator() {
|
|
47
78
|
const indicator = document.getElementById('model-status-indicator');
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
79
|
+
const select = document.getElementById('model-select');
|
|
80
|
+
const buttonIcons = document.querySelectorAll('button');
|
|
81
|
+
|
|
51
82
|
// Fetch both health and installed models
|
|
52
83
|
const [health] = await Promise.all([
|
|
53
84
|
checkModelHealth(),
|
|
54
85
|
fetchInstalledModels()
|
|
55
86
|
]);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Update system message when model status changes
|
|
63
|
-
if (window.displaySystemMessage) {
|
|
64
|
-
window.displaySystemMessage();
|
|
65
|
-
}
|
|
66
|
-
|
|
87
|
+
|
|
88
|
+
// Populate the dropdown with the newly fetched installed models
|
|
89
|
+
populateModelDropdown();
|
|
90
|
+
|
|
67
91
|
// Refresh model management UI if we're on the models tab
|
|
68
92
|
const modelsTab = document.getElementById('content-models');
|
|
69
93
|
if (modelsTab && modelsTab.classList.contains('active')) {
|
|
70
94
|
// Use the display-only version to avoid re-fetching data we just fetched
|
|
71
95
|
refreshModelMgmtUIDisplay();
|
|
72
96
|
}
|
|
73
|
-
|
|
74
|
-
// Remove any click handlers
|
|
75
|
-
indicator.onclick = null;
|
|
76
|
-
|
|
97
|
+
|
|
77
98
|
if (health && health.model_loaded) {
|
|
78
99
|
// Model is loaded - show model name with online status
|
|
100
|
+
indicator.classList.remove('online', 'offline', 'loading');
|
|
79
101
|
currentLoadedModel = health.model_loaded;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
102
|
+
indicator.classList.add('loaded');
|
|
103
|
+
select.value = currentLoadedModel;
|
|
104
|
+
select.disabled = false;
|
|
105
|
+
buttonIcons.forEach(btn => btn.disabled = false);
|
|
106
|
+
} else if (health !== null) {
|
|
83
107
|
// Server is online but no model loaded
|
|
108
|
+
indicator.classList.remove('loaded', 'offline', 'loading');
|
|
84
109
|
currentLoadedModel = null;
|
|
85
|
-
|
|
86
|
-
|
|
110
|
+
indicator.classList.add('online');
|
|
111
|
+
select.value = ''; // Set to the "Click to select a model ▼" option
|
|
112
|
+
select.disabled = false;
|
|
113
|
+
buttonIcons.forEach(btn => btn.disabled = false);
|
|
87
114
|
} else {
|
|
88
115
|
// Server is offline
|
|
116
|
+
indicator.classList.remove('loaded', 'online', 'loading');
|
|
89
117
|
currentLoadedModel = null;
|
|
90
|
-
|
|
91
|
-
|
|
118
|
+
// Add the hidden 'Server Offline' option
|
|
119
|
+
const hiddenOption = document.createElement('option');
|
|
120
|
+
hiddenOption.value = 'server-offline';
|
|
121
|
+
hiddenOption.textContent = 'Server Offline';
|
|
122
|
+
hiddenOption.hidden = true;
|
|
123
|
+
select.appendChild(hiddenOption);
|
|
124
|
+
indicator.classList.add('offline');
|
|
125
|
+
select.value = 'server-offline';
|
|
126
|
+
select.disabled = true;
|
|
127
|
+
buttonIcons.forEach(btn => btn.disabled = true);
|
|
128
|
+
return;
|
|
92
129
|
}
|
|
93
130
|
}
|
|
94
131
|
|
|
@@ -97,9 +134,18 @@ async function unloadModel() {
|
|
|
97
134
|
if (!currentLoadedModel) return;
|
|
98
135
|
|
|
99
136
|
try {
|
|
137
|
+
// Set loading state
|
|
138
|
+
const indicator = document.getElementById('model-status-indicator');
|
|
139
|
+
const select = document.getElementById('model-select');
|
|
140
|
+
indicator.classList.remove('loaded', 'online', 'offline');
|
|
141
|
+
indicator.classList.add('loading');
|
|
142
|
+
select.disabled = true;
|
|
143
|
+
select.value = currentLoadedModel; // Keep the selected model visible during unload
|
|
144
|
+
|
|
100
145
|
await httpRequest(getServerBaseUrl() + '/api/v1/unload', {
|
|
101
146
|
method: 'POST'
|
|
102
147
|
});
|
|
148
|
+
|
|
103
149
|
await updateModelStatusIndicator();
|
|
104
150
|
|
|
105
151
|
// Refresh model list to show updated button states
|
|
@@ -109,6 +155,7 @@ async function unloadModel() {
|
|
|
109
155
|
} catch (error) {
|
|
110
156
|
console.error('Error unloading model:', error);
|
|
111
157
|
showErrorBanner('Failed to unload model: ' + error.message);
|
|
158
|
+
await updateModelStatusIndicator(); // Revert state on error
|
|
112
159
|
}
|
|
113
160
|
}
|
|
114
161
|
|
|
@@ -326,10 +373,14 @@ function createModelItem(modelId, modelData, container) {
|
|
|
326
373
|
actions.appendChild(unloadBtn);
|
|
327
374
|
} else {
|
|
328
375
|
const loadBtn = document.createElement('button');
|
|
376
|
+
const modelSelect = document.getElementById('model-select');
|
|
329
377
|
loadBtn.className = 'model-item-btn load';
|
|
330
378
|
loadBtn.textContent = '🚀';
|
|
331
379
|
loadBtn.title = 'Load';
|
|
332
|
-
loadBtn.onclick = () =>
|
|
380
|
+
loadBtn.onclick = () => {
|
|
381
|
+
modelSelect.value = modelId;
|
|
382
|
+
modelSelect.dispatchEvent(new Event('change', { bubbles: true }));
|
|
383
|
+
};
|
|
333
384
|
actions.appendChild(loadBtn);
|
|
334
385
|
}
|
|
335
386
|
|
|
@@ -399,6 +450,15 @@ async function installModel(modelId) {
|
|
|
399
450
|
|
|
400
451
|
// Load model
|
|
401
452
|
async function loadModel(modelId) {
|
|
453
|
+
const indicator = document.getElementById('model-status-indicator');
|
|
454
|
+
const select = document.getElementById('model-select');
|
|
455
|
+
|
|
456
|
+
// Set loading state for indicator
|
|
457
|
+
modelSelect.value = 'loading-model';
|
|
458
|
+
indicator.classList.remove('loaded', 'online', 'offline');
|
|
459
|
+
indicator.classList.add('loading');
|
|
460
|
+
select.disabled = true;
|
|
461
|
+
|
|
402
462
|
// Find the load button and show loading state
|
|
403
463
|
const modelItems = document.querySelectorAll('.model-item');
|
|
404
464
|
let loadBtn = null;
|
|
@@ -409,6 +469,12 @@ async function loadModel(modelId) {
|
|
|
409
469
|
loadBtn = item.querySelector('.model-item-btn.load');
|
|
410
470
|
}
|
|
411
471
|
});
|
|
472
|
+
|
|
473
|
+
if (loadBtn) {
|
|
474
|
+
loadBtn.disabled = true;
|
|
475
|
+
loadBtn.textContent = '⏳';
|
|
476
|
+
loadBtn.classList.add('loading');
|
|
477
|
+
}
|
|
412
478
|
|
|
413
479
|
// Use the standardized load function
|
|
414
480
|
const success = await loadModelStandardized(modelId, {
|
|
@@ -494,6 +560,8 @@ function createModelNameWithLabels(modelId, serverModels) {
|
|
|
494
560
|
labelClass = 'reranking';
|
|
495
561
|
} else if (labelLower === 'coding') {
|
|
496
562
|
labelClass = 'coding';
|
|
563
|
+
} else if (labelLower === 'tool-calling') {
|
|
564
|
+
labelClass = 'tool-calling';
|
|
497
565
|
}
|
|
498
566
|
labelSpan.className = `model-label ${labelClass}`;
|
|
499
567
|
labelSpan.textContent = label;
|
|
@@ -514,11 +582,21 @@ document.addEventListener('DOMContentLoaded', async function() {
|
|
|
514
582
|
unloadBtn.onclick = unloadModel;
|
|
515
583
|
}
|
|
516
584
|
|
|
585
|
+
const modelSelect = document.getElementById('model-select');
|
|
586
|
+
if (modelSelect) {
|
|
587
|
+
modelSelect.addEventListener('change', async function() {
|
|
588
|
+
const modelId = this.value;
|
|
589
|
+
if (modelId) {
|
|
590
|
+
await loadModel(modelId);
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
|
|
517
595
|
// Initial fetch of model data - this will populate installedModels
|
|
518
596
|
await updateModelStatusIndicator();
|
|
519
597
|
|
|
520
598
|
// Set up periodic refresh of model status
|
|
521
|
-
setInterval(updateModelStatusIndicator,
|
|
599
|
+
setInterval(updateModelStatusIndicator, 1000); // Check every 1 seconds
|
|
522
600
|
|
|
523
601
|
// Initialize model browser with hot models
|
|
524
602
|
displayHotModels();
|
|
@@ -683,7 +761,6 @@ async function refreshModelMgmtUI() {
|
|
|
683
761
|
}
|
|
684
762
|
};
|
|
685
763
|
tdBtn.appendChild(btn);
|
|
686
|
-
|
|
687
764
|
tr.appendChild(tdName);
|
|
688
765
|
tr.appendChild(tdBtn);
|
|
689
766
|
installedTbody.appendChild(tr);
|
|
@@ -873,4 +950,4 @@ window.showAddModelForm = showAddModelForm;
|
|
|
873
950
|
window.unloadModel = unloadModel;
|
|
874
951
|
window.installModel = installModel;
|
|
875
952
|
window.loadModel = loadModel;
|
|
876
|
-
window.deleteModel = deleteModel;
|
|
953
|
+
window.deleteModel = deleteModel;
|
|
@@ -258,7 +258,7 @@ async function loadModelStandardized(modelId, options = {}) {
|
|
|
258
258
|
// Reset the default option text
|
|
259
259
|
const defaultOption = modelSelect.querySelector('option[value=""]');
|
|
260
260
|
if (defaultOption) {
|
|
261
|
-
defaultOption.textContent = '
|
|
261
|
+
defaultOption.textContent = 'Click to select a model ▼';
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
264
|
|
|
@@ -302,7 +302,7 @@ async function loadModelStandardized(modelId, options = {}) {
|
|
|
302
302
|
// Reset the default option text
|
|
303
303
|
const defaultOption = modelSelect.querySelector('option[value=""]');
|
|
304
304
|
if (defaultOption) {
|
|
305
|
-
defaultOption.textContent = '
|
|
305
|
+
defaultOption.textContent = 'Click to select a model ▼';
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
308
|
|
|
@@ -482,6 +482,8 @@ function createModelNameWithLabels(modelId, allModels) {
|
|
|
482
482
|
labelClass = 'reranking';
|
|
483
483
|
} else if (labelLower === 'coding') {
|
|
484
484
|
labelClass = 'coding';
|
|
485
|
+
} else if (labelLower === 'tool-calling') {
|
|
486
|
+
labelClass = 'tool-calling';
|
|
485
487
|
}
|
|
486
488
|
labelSpan.className = `model-label ${labelClass}`;
|
|
487
489
|
labelSpan.textContent = label;
|
|
@@ -35,6 +35,12 @@
|
|
|
35
35
|
--purple-hover: #744f7e;
|
|
36
36
|
--purple-light: #f5f1f6;
|
|
37
37
|
|
|
38
|
+
/* Status Colors */
|
|
39
|
+
--status-green: #28a745;
|
|
40
|
+
--status-red: #dc3545;
|
|
41
|
+
--status-yellow: #ffc107;
|
|
42
|
+
--status-gray: #6c757d;
|
|
43
|
+
|
|
38
44
|
/* Transitions */
|
|
39
45
|
--transition-fast: 0.2s ease;
|
|
40
46
|
--transition-medium: 0.3s ease;
|
|
@@ -229,49 +235,44 @@ body::before {
|
|
|
229
235
|
animation: pulse-glow 2s infinite;
|
|
230
236
|
}
|
|
231
237
|
|
|
232
|
-
/* Online status (
|
|
238
|
+
/* Online model unloaded status (yellow) */
|
|
233
239
|
.model-status-indicator.online .status-light {
|
|
234
|
-
background:
|
|
240
|
+
background: var(--status-yellow);
|
|
235
241
|
box-shadow: 0 0 8px rgba(40, 167, 69, 0.6);
|
|
236
242
|
}
|
|
237
243
|
|
|
238
244
|
.model-status-indicator.online .status-light::before {
|
|
239
|
-
background:
|
|
245
|
+
background: var(--status-yellow);
|
|
240
246
|
}
|
|
241
247
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
248
|
+
/* Online model loading status (yellow) */
|
|
249
|
+
.model-status-indicator.loading .status-light {
|
|
250
|
+
background: var(--status-yellow);
|
|
251
|
+
box-shadow: 0 0 8px rgba(40, 167, 69, 0.6);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.model-status-indicator.loading .status-light::before {
|
|
255
|
+
background: var(--status-yellow);
|
|
245
256
|
}
|
|
246
257
|
|
|
247
258
|
/* Offline status (red) */
|
|
248
259
|
.model-status-indicator.offline .status-light {
|
|
249
|
-
background:
|
|
260
|
+
background: var(--status-red);
|
|
250
261
|
box-shadow: 0 0 8px rgba(220, 53, 69, 0.6);
|
|
251
262
|
}
|
|
252
263
|
|
|
253
264
|
.model-status-indicator.offline .status-light::before {
|
|
254
|
-
background:
|
|
265
|
+
background: var(--status-red);
|
|
255
266
|
}
|
|
256
267
|
|
|
257
|
-
|
|
258
|
-
color: #dc3545;
|
|
259
|
-
font-weight: 600;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/* Model loaded status (same as online but with model name) */
|
|
268
|
+
/* Online model loaded status (with model name) */
|
|
263
269
|
.model-status-indicator.loaded .status-light {
|
|
264
|
-
background:
|
|
270
|
+
background: var(--status-green);
|
|
265
271
|
box-shadow: 0 0 8px rgba(40, 167, 69, 0.6);
|
|
266
272
|
}
|
|
267
273
|
|
|
268
274
|
.model-status-indicator.loaded .status-light::before {
|
|
269
|
-
background:
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
.model-status-indicator.loaded .model-status-text {
|
|
273
|
-
color: #28a745;
|
|
274
|
-
font-weight: 600;
|
|
275
|
+
background: var(--status-green);
|
|
275
276
|
}
|
|
276
277
|
|
|
277
278
|
@keyframes pulse-glow {
|
|
@@ -285,25 +286,99 @@ body::before {
|
|
|
285
286
|
}
|
|
286
287
|
}
|
|
287
288
|
|
|
288
|
-
|
|
289
|
-
|
|
289
|
+
/* Base styles for the select element */
|
|
290
|
+
.model-select {
|
|
291
|
+
padding: 0.5rem 0.75rem;
|
|
292
|
+
border: 1px solid #ddd;
|
|
293
|
+
border-radius: 6px;
|
|
294
|
+
background: #fafafa;
|
|
290
295
|
font-size: 0.9rem;
|
|
296
|
+
min-width: 180px;
|
|
297
|
+
cursor: pointer;
|
|
298
|
+
transition: all var(--transition-fast);
|
|
299
|
+
text-align-last: center;
|
|
300
|
+
-moz-appearance: none;
|
|
301
|
+
-webkit-appearance: none;
|
|
302
|
+
appearance: none;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.model-select:focus {
|
|
306
|
+
outline: none;
|
|
307
|
+
border-color: var(--accent-gold);
|
|
308
|
+
box-shadow: 0 0 0 2px rgba(255, 193, 7, 0.2);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* Existing styles for selective coloring based on status */
|
|
312
|
+
.model-status-indicator.loaded .model-select {
|
|
313
|
+
color: var(--status-green); /* Green for loaded model */
|
|
314
|
+
font-weight: bold;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.model-status-indicator.loading .model-select {
|
|
318
|
+
color: var(--status-gray); /* Gray for loading */
|
|
319
|
+
font-style: italic;
|
|
320
|
+
cursor: wait;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.model-status-indicator.online .model-select {
|
|
324
|
+
color: var(--status-yellow); /* Yellow when online but no model loaded */
|
|
325
|
+
font-weight: bold;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.model-status-indicator.offline .model-select {
|
|
329
|
+
color: var(--status-red); /* Red when offline */
|
|
330
|
+
font-weight: bold;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* Ensure options in the list are not affected by the select's styling */
|
|
334
|
+
.model-select option {
|
|
335
|
+
color: var(--text-primary);
|
|
336
|
+
font-weight: normal;
|
|
337
|
+
font-style: normal;
|
|
338
|
+
background-color: white;
|
|
339
|
+
padding: 0.5rem;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/* Highlight the selected option ONLY in the open list (if desired) */
|
|
343
|
+
.model-select option:checked {
|
|
344
|
+
background-color: #f0f0f0;
|
|
345
|
+
color: var(--text-primary);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.model-select option:checked.server-offline {
|
|
349
|
+
background-color: #f0f0f0;
|
|
350
|
+
color: var(--status-red);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.model-select:disabled {
|
|
354
|
+
background: #f5f5f5;
|
|
355
|
+
cursor: not-allowed;
|
|
356
|
+
opacity: 0.7;
|
|
357
|
+
}
|
|
358
|
+
/* When server is offline all buttons are disabled and appear muted */
|
|
359
|
+
button:disabled {
|
|
360
|
+
cursor: not-allowed;
|
|
361
|
+
opacity: 0.6;
|
|
362
|
+
background-color: #cccccc;
|
|
363
|
+
color: #666666;
|
|
291
364
|
}
|
|
292
365
|
|
|
293
366
|
.model-action-btn {
|
|
294
|
-
display:
|
|
367
|
+
display: none; /* Hide by default */
|
|
295
368
|
align-items: center;
|
|
296
369
|
justify-content: center;
|
|
297
|
-
width:
|
|
298
|
-
height:
|
|
370
|
+
width: 1.1em;
|
|
371
|
+
height: 1.1em;
|
|
299
372
|
background: transparent;
|
|
300
373
|
border: none;
|
|
301
374
|
border-radius: 50%;
|
|
302
375
|
cursor: pointer;
|
|
303
376
|
transition: all var(--transition-fast);
|
|
304
|
-
font-size:
|
|
377
|
+
font-size: 1.5rem;
|
|
305
378
|
color: #666;
|
|
306
|
-
margin-left: 0.
|
|
379
|
+
margin-left: 0.1rem;
|
|
380
|
+
margin-right: 0.2rem;
|
|
381
|
+
line-height: 1;
|
|
307
382
|
}
|
|
308
383
|
|
|
309
384
|
.model-action-btn:hover {
|
|
@@ -311,6 +386,11 @@ body::before {
|
|
|
311
386
|
color: #333;
|
|
312
387
|
}
|
|
313
388
|
|
|
389
|
+
/* Unload button is only visible when a model is loaded */
|
|
390
|
+
.model-status-indicator.loaded .model-action-btn {
|
|
391
|
+
display: flex;
|
|
392
|
+
}
|
|
393
|
+
|
|
314
394
|
.tab-content {
|
|
315
395
|
display: none;
|
|
316
396
|
padding: 2em;
|
|
@@ -874,6 +954,10 @@ body::before {
|
|
|
874
954
|
background-color: var(--info-primary);
|
|
875
955
|
}
|
|
876
956
|
|
|
957
|
+
.model-label.tool-calling {
|
|
958
|
+
background-color: #FFB74D;
|
|
959
|
+
}
|
|
960
|
+
|
|
877
961
|
.model-label.other {
|
|
878
962
|
background-color: var(--success-primary);
|
|
879
963
|
}
|
|
@@ -1106,6 +1190,7 @@ body::before {
|
|
|
1106
1190
|
|
|
1107
1191
|
#register-model-name:focus {
|
|
1108
1192
|
border-color: #e6b800;
|
|
1193
|
+
box-shadow: 0 2px 12px rgba(230,184,0,0.25);
|
|
1109
1194
|
}
|
|
1110
1195
|
|
|
1111
1196
|
.form-input-wrapper {
|
|
@@ -2190,43 +2275,4 @@ body::before {
|
|
|
2190
2275
|
0 8px 25px rgba(200, 88, 108, 0.2),
|
|
2191
2276
|
0 3px 10px rgba(0, 0, 0, 0.1),
|
|
2192
2277
|
inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
/* Model select dropdown in chat */
|
|
2196
|
-
.model-select {
|
|
2197
|
-
padding: 0.5rem 0.75rem;
|
|
2198
|
-
border: 1px solid #ddd;
|
|
2199
|
-
border-radius: 6px;
|
|
2200
|
-
background: white;
|
|
2201
|
-
font-size: 0.9rem;
|
|
2202
|
-
min-width: 180px;
|
|
2203
|
-
margin-right: 0.75rem;
|
|
2204
|
-
cursor: pointer;
|
|
2205
|
-
transition: all var(--transition-fast);
|
|
2206
|
-
}
|
|
2207
|
-
|
|
2208
|
-
.model-select:focus {
|
|
2209
|
-
outline: none;
|
|
2210
|
-
border-color: var(--accent-gold);
|
|
2211
|
-
box-shadow: 0 0 0 2px rgba(255, 193, 7, 0.2);
|
|
2212
|
-
}
|
|
2213
|
-
|
|
2214
|
-
.model-select:disabled {
|
|
2215
|
-
background: #f5f5f5;
|
|
2216
|
-
cursor: not-allowed;
|
|
2217
|
-
opacity: 0.7;
|
|
2218
|
-
}
|
|
2219
|
-
|
|
2220
|
-
.model-select option {
|
|
2221
|
-
padding: 0.5rem;
|
|
2222
|
-
}
|
|
2223
|
-
|
|
2224
|
-
.chat-input-row {
|
|
2225
|
-
display: flex;
|
|
2226
|
-
align-items: center;
|
|
2227
|
-
gap: 0.5rem;
|
|
2228
|
-
}
|
|
2229
|
-
|
|
2230
|
-
.input-with-indicator {
|
|
2231
|
-
flex: 1;
|
|
2232
|
-
}
|
|
2278
|
+
}
|
|
@@ -32,25 +32,23 @@
|
|
|
32
32
|
<div class="tab-container">
|
|
33
33
|
<div class="tabs">
|
|
34
34
|
<div class="tab-group">
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
<div class="chat-
|
|
51
|
-
|
|
52
|
-
<option value="">Pick a model</option>
|
|
53
|
-
</select>
|
|
35
|
+
<button class="tab active" id="tab-chat" onclick="showTab('chat')">LLM Chat</button>
|
|
36
|
+
<button class="tab" id="tab-model-settings" onclick="showTab('settings')">Model Settings</button>
|
|
37
|
+
<button class="tab" id="tab-models" onclick="showTab('models')">Model Management</button>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div class="model-status-indicator" id="model-status-indicator">
|
|
41
|
+
<div class="status-light" id="status-light"></div>
|
|
42
|
+
<select id="model-select" class="model-select">
|
|
43
|
+
<option value="">Pick a model</option>
|
|
44
|
+
</select>
|
|
45
|
+
<button class="model-action-btn" id="model-unload-btn" title="Unload model" style="display: flex;">⏏</button>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="tab-content active" id="content-chat">
|
|
49
|
+
<div class="chat-container">
|
|
50
|
+
<div class="chat-history" id="chat-history"></div>
|
|
51
|
+
<div class="chat-input-row">
|
|
54
52
|
<div class="input-with-indicator">
|
|
55
53
|
<textarea id="chat-input" placeholder="Type your message..." rows="1"></textarea>
|
|
56
54
|
</div>
|
|
@@ -142,7 +140,6 @@
|
|
|
142
140
|
</div>
|
|
143
141
|
<div class="category-content expanded" id="category-hot"></div>
|
|
144
142
|
</div>
|
|
145
|
-
|
|
146
143
|
<div class="model-category-section">
|
|
147
144
|
<div class="section-header">
|
|
148
145
|
<span class="section-icon">🔧</span>
|
|
@@ -155,7 +152,6 @@
|
|
|
155
152
|
<div class="subcategory" data-recipe="oga-cpu" onclick="selectRecipe('oga-cpu')">OGA CPU</div>
|
|
156
153
|
</div>
|
|
157
154
|
</div>
|
|
158
|
-
|
|
159
155
|
<div class="model-category-section">
|
|
160
156
|
<div class="section-header">
|
|
161
157
|
<span class="section-icon">🏷️</span>
|
|
@@ -165,12 +161,12 @@
|
|
|
165
161
|
<div class="subcategory" data-label="coding" onclick="selectLabel('coding')">Coding</div>
|
|
166
162
|
<div class="subcategory" data-label="vision" onclick="selectLabel('vision')">Vision</div>
|
|
167
163
|
<div class="subcategory" data-label="reasoning" onclick="selectLabel('reasoning')">Reasoning</div>
|
|
164
|
+
<div class="subcategory" data-label="tool-calling" onclick="selectLabel('tool-calling')">Tool Calling</div>
|
|
168
165
|
<div class="subcategory" data-label="reranking" onclick="selectLabel('reranking')">Reranking</div>
|
|
169
166
|
<div class="subcategory" data-label="embeddings" onclick="selectLabel('embeddings')">Embeddings</div>
|
|
170
167
|
<div class="subcategory" data-label="custom" onclick="selectLabel('custom')">Custom</div>
|
|
171
168
|
</div>
|
|
172
169
|
</div>
|
|
173
|
-
|
|
174
170
|
<div class="model-category" data-category="add">
|
|
175
171
|
<div class="category-header" onclick="showAddModelForm()">
|
|
176
172
|
<span class="category-icon">➕</span>
|
|
@@ -178,7 +174,6 @@
|
|
|
178
174
|
</div>
|
|
179
175
|
</div>
|
|
180
176
|
</div>
|
|
181
|
-
|
|
182
177
|
<div class="model-browser-main">
|
|
183
178
|
<div class="model-list" id="model-list"></div>
|
|
184
179
|
|
|
@@ -255,3 +250,4 @@
|
|
|
255
250
|
<script src="/static/js/chat.js"></script>
|
|
256
251
|
</body>
|
|
257
252
|
</html>
|
|
253
|
+
</html>
|