lemonade-sdk 8.1.9__py3-none-any.whl → 8.1.11__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.

Files changed (33) hide show
  1. lemonade/common/inference_engines.py +13 -4
  2. lemonade/common/system_info.py +570 -1
  3. lemonade/tools/flm/__init__.py +1 -0
  4. lemonade/tools/flm/utils.py +255 -0
  5. lemonade/tools/llamacpp/utils.py +62 -13
  6. lemonade/tools/server/flm.py +137 -0
  7. lemonade/tools/server/llamacpp.py +23 -5
  8. lemonade/tools/server/serve.py +292 -135
  9. lemonade/tools/server/static/js/chat.js +165 -82
  10. lemonade/tools/server/static/js/models.js +87 -54
  11. lemonade/tools/server/static/js/shared.js +5 -3
  12. lemonade/tools/server/static/logs.html +47 -0
  13. lemonade/tools/server/static/styles.css +159 -8
  14. lemonade/tools/server/static/webapp.html +28 -10
  15. lemonade/tools/server/tray.py +158 -38
  16. lemonade/tools/server/utils/macos_tray.py +226 -0
  17. lemonade/tools/server/utils/{system_tray.py → windows_tray.py} +13 -0
  18. lemonade/tools/server/webapp.py +4 -1
  19. lemonade/tools/server/wrapped_server.py +91 -25
  20. lemonade/version.py +1 -1
  21. lemonade_install/install.py +25 -2
  22. {lemonade_sdk-8.1.9.dist-info → lemonade_sdk-8.1.11.dist-info}/METADATA +9 -6
  23. {lemonade_sdk-8.1.9.dist-info → lemonade_sdk-8.1.11.dist-info}/RECORD +33 -28
  24. lemonade_server/cli.py +105 -14
  25. lemonade_server/model_manager.py +186 -45
  26. lemonade_server/pydantic_models.py +25 -1
  27. lemonade_server/server_models.json +162 -62
  28. lemonade_server/settings.py +39 -39
  29. {lemonade_sdk-8.1.9.dist-info → lemonade_sdk-8.1.11.dist-info}/WHEEL +0 -0
  30. {lemonade_sdk-8.1.9.dist-info → lemonade_sdk-8.1.11.dist-info}/entry_points.txt +0 -0
  31. {lemonade_sdk-8.1.9.dist-info → lemonade_sdk-8.1.11.dist-info}/licenses/LICENSE +0 -0
  32. {lemonade_sdk-8.1.9.dist-info → lemonade_sdk-8.1.11.dist-info}/licenses/NOTICE.md +0 -0
  33. {lemonade_sdk-8.1.9.dist-info → lemonade_sdk-8.1.11.dist-info}/top_level.txt +0 -0
@@ -181,7 +181,7 @@ async function loadModelStandardized(modelId, options = {}) {
181
181
  // Update load button if provided
182
182
  if (loadButton) {
183
183
  loadButton.disabled = true;
184
- loadButton.textContent = '';
184
+ loadButton.textContent = '';
185
185
  }
186
186
 
187
187
  // Update status indicator to show loading state
@@ -246,7 +246,8 @@ async function loadModelStandardized(modelId, options = {}) {
246
246
  // Reset load button if provided
247
247
  if (loadButton) {
248
248
  loadButton.disabled = false;
249
- loadButton.textContent = 'Load';
249
+ loadButton.textContent = '🚀';
250
+ loadButton.classList.remove('loading');
250
251
  }
251
252
 
252
253
  // Reset chat controls
@@ -280,7 +281,8 @@ async function loadModelStandardized(modelId, options = {}) {
280
281
  // Reset load button if provided
281
282
  if (loadButton) {
282
283
  loadButton.disabled = false;
283
- loadButton.textContent = 'Load';
284
+ loadButton.textContent = '🚀';
285
+ loadButton.classList.remove('loading');
284
286
  }
285
287
 
286
288
  // Reset status indicator on error
@@ -0,0 +1,47 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Lemonade Server Logs</title>
6
+ <style>
7
+ body {
8
+ font-family: monospace;
9
+ background: #1e1e1e;
10
+ color: #d4d4d4;
11
+ margin: 0;
12
+ padding: 0;
13
+ }
14
+ #log-container {
15
+ padding: 10px;
16
+ height: 100vh;
17
+ overflow-y: auto;
18
+ white-space: pre-wrap;
19
+ }
20
+ </style>
21
+ </head>
22
+ <body>
23
+ <div id="log-container"></div>
24
+
25
+ <script>
26
+ function stripAnsi(str) {
27
+ return str.replace(/\x1B\[[0-9;]*[A-Za-z]/g, '');
28
+ }
29
+ const logContainer = document.getElementById("log-container");
30
+ const ws = new WebSocket(`ws://${location.host}/api/v1/logs/ws`);
31
+
32
+ ws.onmessage = (event) => {
33
+ const line = document.createElement("div");
34
+ line.textContent = stripAnsi(event.data);
35
+ logContainer.appendChild(line);
36
+ logContainer.scrollTop = logContainer.scrollHeight; // auto scroll
37
+ };
38
+
39
+ ws.onclose = () => {
40
+ const msg = document.createElement("div");
41
+ msg.textContent = "[Disconnected from log stream]";
42
+ msg.style.color = "red";
43
+ logContainer.appendChild(msg);
44
+ };
45
+ </script>
46
+ </body>
47
+ </html>
@@ -99,6 +99,15 @@ body::before {
99
99
  .brand-title a {
100
100
  color: inherit;
101
101
  text-decoration: none;
102
+ display: flex;
103
+ align-items: center;
104
+ gap: 0.5rem;
105
+ }
106
+
107
+ .brand-icon {
108
+ width: 1.5rem;
109
+ height: 1.5rem;
110
+ vertical-align: middle;
102
111
  }
103
112
 
104
113
  .navbar-links {
@@ -167,6 +176,9 @@ body::before {
167
176
  width: 100%;
168
177
  margin-left: 1rem;
169
178
  margin-right: 1rem;
179
+ /* Removing only the bottom border and shadow for the content tab gap to look nicer */
180
+ box-shadow: 0 -2px 8px rgba(0, 0, 0, 0);
181
+ border-bottom: none;
170
182
  }
171
183
 
172
184
  .tabs {
@@ -213,14 +225,24 @@ body::before {
213
225
  position: relative;
214
226
  }
215
227
 
228
+ /* Wrapper for the select element with embedded status light */
229
+ .model-select-wrapper {
230
+ position: relative;
231
+ display: flex;
232
+ align-items: center;
233
+ }
234
+
216
235
  .status-light {
217
236
  width: 8px;
218
237
  height: 8px;
219
238
  border-radius: 50%;
220
- position: relative;
239
+ position: absolute;
240
+ left: 8px;
241
+ top: 50%;
242
+ transform: translateY(-50%);
221
243
  transition: all var(--transition-fast);
222
244
  flex-shrink: 0;
223
- align-self: center;
245
+ z-index: 10;
224
246
  }
225
247
 
226
248
  .status-light::before {
@@ -288,7 +310,7 @@ body::before {
288
310
 
289
311
  /* Base styles for the select element */
290
312
  .model-select {
291
- padding: 0.5rem 0.75rem;
313
+ padding: 0.5rem 0.75rem 0.5rem 1.5rem;
292
314
  border: 1px solid #ddd;
293
315
  border-radius: 6px;
294
316
  background: #fafafa;
@@ -391,11 +413,18 @@ button:disabled {
391
413
  display: flex;
392
414
  }
393
415
 
416
+ .tab-content-wrapper {
417
+ width: 85%;
418
+ }
419
+
394
420
  .tab-content {
395
421
  display: none;
396
422
  padding: 2em;
397
423
  background: #fafafa;
398
424
  border-radius: 0 0 8px 8px;
425
+ /* adding border and shadow that was removed for the gap look from higher div */
426
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
427
+ border: 1px solid #e0e0e0;
399
428
  }
400
429
 
401
430
  .tab-content.active {
@@ -406,7 +435,9 @@ button:disabled {
406
435
  .chat-container {
407
436
  display: flex;
408
437
  flex-direction: column;
409
- min-height: 300px;
438
+ /* Max space available in viewport
439
+ This also prevents the chat-history section to resize lower than a certain point */
440
+ min-height: calc(100vh - 550px);
410
441
  min-width: 300px;
411
442
  max-width: 100%;
412
443
  width: 100%;
@@ -414,18 +445,37 @@ button:disabled {
414
445
  border: 1px solid #e0e0e0;
415
446
  border-radius: 8px;
416
447
  background: #fff;
417
- resize: both;
418
- overflow: auto;
448
+ /* Use a semi-fixed chat 'window' height so streaming content never expands the page.
449
+ The chat-history area inside will scroll. The CSS variable allows easy tuning. */
450
+ --chat-height: 520px;
451
+ /* Allow vertical resizing by the user while keeping horizontal size fixed.
452
+ Constrain the resize with min/max heights so it stays usable and doesn't overflow the viewport. */
453
+ resize: vertical;
454
+ height: var(--chat-height);
455
+ max-height: calc(100vh - 120px);
456
+ overflow: hidden; /* hide overflow at container level; chat-history will scroll */
457
+ }
458
+
459
+ /* Responsive fallback: if the viewport height is small, cap the chat window to fit */
460
+ @media (max-height: 700px) {
461
+ .chat-container {
462
+ height: calc(100vh - 180px); /* leave space for navbar/footer */
463
+ }
419
464
  }
420
465
 
421
466
  .chat-history {
422
- flex: 1;
467
+ /* Make chat history take remaining space in the chat container and scroll when content
468
+ exceeds this space. This prevents streaming text from expanding the overall layout. */
469
+ flex: 1 1 auto;
423
470
  overflow-y: auto;
471
+ -webkit-overflow-scrolling: touch;
424
472
  padding: 1em;
425
473
  border-bottom: 1px solid #e0e0e0;
426
474
  display: flex;
427
475
  flex-direction: column;
428
476
  gap: 0.5em;
477
+ /* Optional visual hint for scrollable content */
478
+ scrollbar-width: thin;
429
479
  }
430
480
 
431
481
  .chat-message {
@@ -1492,6 +1542,11 @@ button:disabled {
1492
1542
  font-size: 1.3rem;
1493
1543
  }
1494
1544
 
1545
+ .brand-icon {
1546
+ width: 1.3rem;
1547
+ height: 1.3rem;
1548
+ }
1549
+
1495
1550
  .navbar-links {
1496
1551
  gap: 1.5rem;
1497
1552
  font-size: 1rem;
@@ -1507,6 +1562,11 @@ button:disabled {
1507
1562
  font-size: 1.2rem;
1508
1563
  }
1509
1564
 
1565
+ .brand-icon {
1566
+ width: 1.2rem;
1567
+ height: 1.2rem;
1568
+ }
1569
+
1510
1570
  .navbar-links {
1511
1571
  gap: 1rem;
1512
1572
  font-size: 0.9rem;
@@ -1838,6 +1898,53 @@ button:disabled {
1838
1898
  align-items: center;
1839
1899
  }
1840
1900
 
1901
+ /* FastFlowLM notice styles */
1902
+ .flm-notice {
1903
+ background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%);
1904
+ border: 1px solid #ffeaa7;
1905
+ border-left: 4px solid #f39c12;
1906
+ border-radius: 8px;
1907
+ margin-bottom: 1.5rem;
1908
+ box-shadow: 0 2px 8px rgba(243, 156, 18, 0.1);
1909
+ animation: fadeIn 0.3s ease;
1910
+ }
1911
+
1912
+ .flm-notice-content {
1913
+ display: flex;
1914
+ align-items: flex-start;
1915
+ gap: 0.75rem;
1916
+ padding: 1rem 1.25rem;
1917
+ }
1918
+
1919
+ .flm-notice-icon {
1920
+ font-size: 1.2rem;
1921
+ flex-shrink: 0;
1922
+ margin-top: 0.1rem;
1923
+ }
1924
+
1925
+ .flm-notice-text {
1926
+ flex: 1;
1927
+ font-size: 0.95rem;
1928
+ line-height: 1.5;
1929
+ color: #856404;
1930
+ }
1931
+
1932
+ .flm-notice-text strong {
1933
+ color: #6c5ce7;
1934
+ font-weight: 600;
1935
+ }
1936
+
1937
+ .flm-notice-text a {
1938
+ color: var(--info-primary);
1939
+ text-decoration: none;
1940
+ font-weight: 500;
1941
+ }
1942
+
1943
+ .flm-notice-text a:hover {
1944
+ text-decoration: underline;
1945
+ color: var(--info-hover);
1946
+ }
1947
+
1841
1948
  .error-banner .close-btn {
1842
1949
  background: none;
1843
1950
  border: none;
@@ -2275,4 +2382,48 @@ button:disabled {
2275
2382
  0 8px 25px rgba(200, 88, 108, 0.2),
2276
2383
  0 3px 10px rgba(0, 0, 0, 0.1),
2277
2384
  inset 0 1px 0 rgba(255, 255, 255, 0.9);
2278
- }
2385
+ }
2386
+
2387
+
2388
+ /* Dropdown styling */
2389
+ .dropdown {
2390
+ position: relative;
2391
+ display: inline-block;
2392
+ }
2393
+
2394
+ .dropbtn {
2395
+ background-color: transparent;
2396
+ border: none;
2397
+ font-size: 16px;
2398
+ cursor: pointer;
2399
+ }
2400
+
2401
+ .dropdown-content {
2402
+ display: none;
2403
+ position: absolute;
2404
+ right: 0; /* align to the right edge of the parent */
2405
+ left: auto; /* prevent left alignment */
2406
+ top: calc(100% + 1px); /* opens 8px below the button */
2407
+ background-color: #ffe76c;
2408
+ border-radius: 8px;
2409
+ min-width: 140px;
2410
+ box-shadow: 0px 8px 16px rgba(0,0,0,0.2);
2411
+ z-index: 1000;
2412
+ overflow: hidden;
2413
+ }
2414
+
2415
+ .dropdown-content a {
2416
+ color: black;
2417
+ font-size: 12px; /* smaller font */
2418
+ padding: 8px 12px;
2419
+ text-decoration: none;
2420
+ display: block;
2421
+ }
2422
+
2423
+ .dropdown-content a:hover {
2424
+ background-color: #f6c146;
2425
+ }
2426
+
2427
+ .dropdown:hover .dropdown-content {
2428
+ display: block;
2429
+ }
@@ -10,12 +10,13 @@
10
10
  window.SERVER_PORT = {{SERVER_PORT}};
11
11
  </script>
12
12
  {{SERVER_MODELS_JS}}
13
+ {{PLATFORM_JS}}
13
14
  </head>
14
15
  <body>
15
16
  <nav class="navbar" id="navbar">
16
17
  <div class="navbar-brand">
17
- <span class="brand-title"><a href="https://lemonade-server.ai">🍋 Lemonade Server</a></span>
18
- </div>
18
+ <span class="brand-title"><a href="https://lemonade-server.ai"><img src="/static/favicon.ico" alt="🍋" class="brand-icon"> Lemonade Server</a></span>
19
+ </div>
19
20
  <div class="navbar-links">
20
21
  <a href="https://github.com/lemonade-sdk/lemonade" target="_blank">GitHub</a>
21
22
  <a href="https://lemonade-server.ai/docs/" target="_blank">Docs</a>
@@ -29,6 +30,7 @@
29
30
  <button class="close-btn" onclick="hideErrorBanner()">&times;</button>
30
31
  </div>
31
32
  <main class="main">
33
+ <div class="tab-content-wrapper">
32
34
  <div class="tab-container">
33
35
  <div class="tabs">
34
36
  <div class="tab-group">
@@ -38,23 +40,32 @@
38
40
  </div>
39
41
 
40
42
  <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>
43
+ <div class="model-select-wrapper">
44
+ <div class="status-light" id="status-light"></div>
45
+ <select id="model-select" class="model-select">
46
+ <option value="">Pick a model</option>
47
+ </select>
48
+ </div>
45
49
  <button class="model-action-btn" id="model-unload-btn" title="Unload model" style="display: flex;">⏏</button>
50
+ <!-- Dropdown -->
51
+ <div class="dropdown">
52
+ <button class="dropbtn" aria-label="Dropdown" style="display: flex;">🛠️</button>
53
+ <div class="dropdown-content">
54
+ <a href="/static/logs.html" target="_blank">View Logs</a>
55
+ </div>
56
+ </div>
46
57
  </div>
47
- </div>
58
+ </div>
48
59
  <div class="tab-content active" id="content-chat">
49
60
  <div class="chat-container">
50
- <div class="chat-history" id="chat-history"></div>
61
+ <div class="chat-history" id="chat-history" style="overflow-y: auto;"></div>
51
62
  <div class="chat-input-row">
52
63
  <div class="input-with-indicator">
53
64
  <textarea id="chat-input" placeholder="Type your message..." rows="1"></textarea>
54
65
  </div>
55
66
  <input type="file" id="file-attachment" style="display: none;" multiple accept="image/*">
56
67
  <button id="attachment-btn" title="Attach files">&#x1F4CE;</button>
57
- <button id="send-btn">Send</button>
68
+ <button id="toggle-btn" title="Start">Start</button>
58
69
  </div>
59
70
  <div class="attachments-preview-container" id="attachments-preview-container">
60
71
  <div class="attachments-preview-row" id="attachments-preview-row"></div>
@@ -150,6 +161,7 @@
150
161
  <div class="subcategory" data-recipe="oga-hybrid" onclick="selectRecipe('oga-hybrid')">OGA Hybrid</div>
151
162
  <div class="subcategory" data-recipe="oga-npu" onclick="selectRecipe('oga-npu')">OGA NPU</div>
152
163
  <div class="subcategory" data-recipe="oga-cpu" onclick="selectRecipe('oga-cpu')">OGA CPU</div>
164
+ <div class="subcategory" data-recipe="flm" onclick="selectRecipe('flm')">FastFlowLM NPU</div>
153
165
  </div>
154
166
  </div>
155
167
  <div class="model-category-section">
@@ -204,6 +216,7 @@
204
216
  </label>
205
217
  <select id="register-recipe" name="recipe" required>
206
218
  <option value="llamacpp">llamacpp</option>
219
+ <option value="flm">flm</option>
207
220
  <option value="oga-npu">oga-npu</option>
208
221
  <option value="oga-hybrid">oga-hybrid</option>
209
222
  <option value="oga-cpu">oga-cpu</option>
@@ -221,6 +234,11 @@
221
234
  Reasoning
222
235
  <span class="tooltip-icon" data-tooltip="Enable to inform Lemonade Server that the model has reasoning capabilities that will use thinking tokens.">ⓘ</span>
223
236
  </label>
237
+ <label class="register-label reasoning-inline">
238
+ <input type="checkbox" id="register-vision" name="vision">
239
+ Vision
240
+ <span class="tooltip-icon" data-tooltip="Enable to inform Lemonade Server that the model has vision capabilities for processing images.">ⓘ</span>
241
+ </label>
224
242
  </div>
225
243
  <div class="register-form-row register-form-row-tight">
226
244
  <button type="submit" id="register-submit">Install</button>
@@ -231,6 +249,7 @@
231
249
  </div>
232
250
  </div>
233
251
  </div>
252
+ </div>
234
253
  </div>
235
254
  </main>
236
255
  <footer class="site-footer">
@@ -250,4 +269,3 @@
250
269
  <script src="/static/js/chat.js"></script>
251
270
  </body>
252
271
  </html>
253
- </html>