lemonade-sdk 8.1.4__py3-none-any.whl → 8.2.2__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/cache.py +6 -1
- lemonade/cli.py +47 -5
- lemonade/common/inference_engines.py +13 -4
- lemonade/common/status.py +4 -4
- lemonade/common/system_info.py +544 -1
- lemonade/profilers/agt_power.py +437 -0
- lemonade/profilers/hwinfo_power.py +429 -0
- lemonade/tools/accuracy.py +143 -48
- lemonade/tools/adapter.py +6 -1
- lemonade/tools/bench.py +26 -8
- lemonade/tools/flm/__init__.py +1 -0
- lemonade/tools/flm/utils.py +303 -0
- lemonade/tools/huggingface/bench.py +6 -1
- lemonade/tools/llamacpp/bench.py +146 -27
- lemonade/tools/llamacpp/load.py +30 -2
- lemonade/tools/llamacpp/utils.py +393 -33
- lemonade/tools/oga/bench.py +5 -26
- lemonade/tools/oga/load.py +60 -121
- lemonade/tools/oga/migration.py +403 -0
- lemonade/tools/report/table.py +76 -8
- lemonade/tools/server/flm.py +133 -0
- lemonade/tools/server/llamacpp.py +220 -553
- lemonade/tools/server/serve.py +684 -168
- lemonade/tools/server/static/js/chat.js +666 -342
- lemonade/tools/server/static/js/model-settings.js +24 -3
- lemonade/tools/server/static/js/models.js +597 -73
- lemonade/tools/server/static/js/shared.js +79 -14
- lemonade/tools/server/static/logs.html +191 -0
- lemonade/tools/server/static/styles.css +491 -66
- lemonade/tools/server/static/webapp.html +83 -31
- lemonade/tools/server/tray.py +158 -38
- lemonade/tools/server/utils/macos_tray.py +226 -0
- lemonade/tools/server/utils/{system_tray.py → windows_tray.py} +13 -0
- lemonade/tools/server/webapp.py +4 -1
- lemonade/tools/server/wrapped_server.py +559 -0
- lemonade/version.py +1 -1
- lemonade_install/install.py +54 -611
- {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/METADATA +29 -72
- lemonade_sdk-8.2.2.dist-info/RECORD +83 -0
- lemonade_server/cli.py +145 -37
- lemonade_server/model_manager.py +521 -37
- lemonade_server/pydantic_models.py +28 -1
- lemonade_server/server_models.json +246 -92
- lemonade_server/settings.py +39 -39
- lemonade/tools/quark/__init__.py +0 -0
- lemonade/tools/quark/quark_load.py +0 -173
- lemonade/tools/quark/quark_quantize.py +0 -439
- lemonade_sdk-8.1.4.dist-info/RECORD +0 -77
- {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/WHEEL +0 -0
- {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/entry_points.txt +0 -0
- {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/licenses/LICENSE +0 -0
- {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/licenses/NOTICE.md +0 -0
- {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/top_level.txt +0 -0
|
@@ -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"
|
|
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>
|
|
@@ -28,35 +29,48 @@
|
|
|
28
29
|
<span id="error-banner-msg"></span>
|
|
29
30
|
<button class="close-btn" onclick="hideErrorBanner()">×</button>
|
|
30
31
|
</div>
|
|
32
|
+
<div id="migration-banner" class="migration-banner" style="display:none;">
|
|
33
|
+
<span id="migration-banner-msg"></span>
|
|
34
|
+
<button class="migration-action-btn" onclick="showMigrationModal()">Clean Up Now</button>
|
|
35
|
+
<button class="close-btn" onclick="hideMigrationBanner()">×</button>
|
|
36
|
+
</div>
|
|
31
37
|
<main class="main">
|
|
38
|
+
<div class="tab-content-wrapper">
|
|
32
39
|
<div class="tab-container">
|
|
33
40
|
<div class="tabs">
|
|
34
41
|
<div class="tab-group">
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<div class="
|
|
50
|
-
|
|
51
|
-
<
|
|
52
|
-
<
|
|
53
|
-
</
|
|
42
|
+
<button class="tab active" id="tab-chat" onclick="showTab('chat')">LLM Chat</button>
|
|
43
|
+
<button class="tab" id="tab-model-settings" onclick="showTab('settings')">Model Settings</button>
|
|
44
|
+
<button class="tab" id="tab-models" onclick="showTab('models')">Model Management</button>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div class="model-status-indicator" id="model-status-indicator">
|
|
48
|
+
<div class="model-select-wrapper">
|
|
49
|
+
<div class="status-light" id="status-light"></div>
|
|
50
|
+
<select id="model-select" class="model-select">
|
|
51
|
+
<option value="">Pick a model</option>
|
|
52
|
+
</select>
|
|
53
|
+
</div>
|
|
54
|
+
<button class="model-action-btn" id="model-unload-btn" title="Unload model" style="display: flex;">⏏</button>
|
|
55
|
+
<!-- Dropdown -->
|
|
56
|
+
<div class="dropdown">
|
|
57
|
+
<button class="dropbtn" aria-label="Dropdown" style="display: flex;">🛠️</button>
|
|
58
|
+
<div class="dropdown-content">
|
|
59
|
+
<a href="/static/logs.html" target="_blank">View Logs</a>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="tab-content active" id="content-chat">
|
|
65
|
+
<div class="chat-container">
|
|
66
|
+
<div class="chat-history" id="chat-history" style="overflow-y: auto;"></div>
|
|
67
|
+
<div class="chat-input-row">
|
|
54
68
|
<div class="input-with-indicator">
|
|
55
|
-
<
|
|
69
|
+
<textarea id="chat-input" placeholder="Type your message..." rows="1"></textarea>
|
|
56
70
|
</div>
|
|
57
71
|
<input type="file" id="file-attachment" style="display: none;" multiple accept="image/*">
|
|
58
72
|
<button id="attachment-btn" title="Attach files">📎</button>
|
|
59
|
-
<button id="
|
|
73
|
+
<button id="toggle-btn" title="Start">Start</button>
|
|
60
74
|
</div>
|
|
61
75
|
<div class="attachments-preview-container" id="attachments-preview-container">
|
|
62
76
|
<div class="attachments-preview-row" id="attachments-preview-row"></div>
|
|
@@ -92,9 +106,9 @@
|
|
|
92
106
|
<img src="https://raw.githubusercontent.com/lemonade-sdk/assets/refs/heads/main/partner_logos/lm_eval.png" alt="LM-Eval" class="app-logo-img">
|
|
93
107
|
<span class="app-name">LM-Eval</span>
|
|
94
108
|
</a>
|
|
95
|
-
<a href="https://lemonade-
|
|
96
|
-
<img src="https://raw.githubusercontent.com/lemonade-sdk/
|
|
97
|
-
<span class="app-name">
|
|
109
|
+
<a href="https://github.com/lemonade-sdk/lemonade-arcade" target="_blank" class="app-logo-item" title="Lemonade Arcade">
|
|
110
|
+
<img src="https://raw.githubusercontent.com/lemonade-sdk/lemonade-arcade/refs/heads/main/docs/assets/favicon.ico" alt="Lemonade Arcade" class="app-logo-img">
|
|
111
|
+
<span class="app-name">Lemonade Arcade</span>
|
|
98
112
|
</a>
|
|
99
113
|
<a href="https://github.com/lemonade-sdk/lemonade/blob/main/docs/server/apps/ai-toolkit.md" target="_blank" class="app-logo-item" title="AI Toolkit">
|
|
100
114
|
<img src="https://raw.githubusercontent.com/lemonade-sdk/assets/refs/heads/main/partner_logos/ai_toolkit.png" alt="AI Toolkit" class="app-logo-img">
|
|
@@ -126,6 +140,12 @@
|
|
|
126
140
|
<input type="number" id="setting-repeat-penalty" min="0.5" max="2" step="0.05" placeholder="default" />
|
|
127
141
|
<span class="setting-description">Penalty for repeating tokens (1 = no penalty, >1 = less repetition)</span>
|
|
128
142
|
</div>
|
|
143
|
+
<div class="setting-field">
|
|
144
|
+
<label for="enable-thinking">Enable Thinking:</label>
|
|
145
|
+
<input type="checkbox" id="enable-thinking">
|
|
146
|
+
<br>
|
|
147
|
+
<span class="setting-description">Determines whether hybrid reasoning models, such as Qwen3, will use thinking.</span>
|
|
148
|
+
</div>
|
|
129
149
|
<div class="setting-actions">
|
|
130
150
|
<button id="reset-settings-btn" class="reset-btn">Reset to Defaults</button>
|
|
131
151
|
</div>
|
|
@@ -142,7 +162,6 @@
|
|
|
142
162
|
</div>
|
|
143
163
|
<div class="category-content expanded" id="category-hot"></div>
|
|
144
164
|
</div>
|
|
145
|
-
|
|
146
165
|
<div class="model-category-section">
|
|
147
166
|
<div class="section-header">
|
|
148
167
|
<span class="section-icon">🔧</span>
|
|
@@ -153,9 +172,9 @@
|
|
|
153
172
|
<div class="subcategory" data-recipe="oga-hybrid" onclick="selectRecipe('oga-hybrid')">OGA Hybrid</div>
|
|
154
173
|
<div class="subcategory" data-recipe="oga-npu" onclick="selectRecipe('oga-npu')">OGA NPU</div>
|
|
155
174
|
<div class="subcategory" data-recipe="oga-cpu" onclick="selectRecipe('oga-cpu')">OGA CPU</div>
|
|
175
|
+
<div class="subcategory" data-recipe="flm" onclick="selectRecipe('flm')">FastFlowLM NPU</div>
|
|
156
176
|
</div>
|
|
157
177
|
</div>
|
|
158
|
-
|
|
159
178
|
<div class="model-category-section">
|
|
160
179
|
<div class="section-header">
|
|
161
180
|
<span class="section-icon">🏷️</span>
|
|
@@ -165,12 +184,12 @@
|
|
|
165
184
|
<div class="subcategory" data-label="coding" onclick="selectLabel('coding')">Coding</div>
|
|
166
185
|
<div class="subcategory" data-label="vision" onclick="selectLabel('vision')">Vision</div>
|
|
167
186
|
<div class="subcategory" data-label="reasoning" onclick="selectLabel('reasoning')">Reasoning</div>
|
|
187
|
+
<div class="subcategory" data-label="tool-calling" onclick="selectLabel('tool-calling')">Tool Calling</div>
|
|
168
188
|
<div class="subcategory" data-label="reranking" onclick="selectLabel('reranking')">Reranking</div>
|
|
169
189
|
<div class="subcategory" data-label="embeddings" onclick="selectLabel('embeddings')">Embeddings</div>
|
|
170
190
|
<div class="subcategory" data-label="custom" onclick="selectLabel('custom')">Custom</div>
|
|
171
191
|
</div>
|
|
172
192
|
</div>
|
|
173
|
-
|
|
174
193
|
<div class="model-category" data-category="add">
|
|
175
194
|
<div class="category-header" onclick="showAddModelForm()">
|
|
176
195
|
<span class="category-icon">➕</span>
|
|
@@ -178,7 +197,6 @@
|
|
|
178
197
|
</div>
|
|
179
198
|
</div>
|
|
180
199
|
</div>
|
|
181
|
-
|
|
182
200
|
<div class="model-browser-main">
|
|
183
201
|
<div class="model-list" id="model-list"></div>
|
|
184
202
|
|
|
@@ -200,7 +218,11 @@
|
|
|
200
218
|
Checkpoint
|
|
201
219
|
<span class="tooltip-icon" data-tooltip="Specify the model checkpoint path from Hugging Face (e.g., org-name/model-name:variant).">ⓘ</span>
|
|
202
220
|
</label>
|
|
221
|
+
<div class="checkpoint-input-group">
|
|
203
222
|
<input type="text" id="register-checkpoint" name="checkpoint" placeholder="unsloth/gemma-3-12b-it-GGUF:Q4_0" class="register-textbox" autocomplete="off">
|
|
223
|
+
<button type="button" id="select-folder-btn" class="folder-select-btn" title="Select local folder">📁</button>
|
|
224
|
+
</div>
|
|
225
|
+
<input type="file" id="folder-input" webkitdirectory directory style="display: none;">
|
|
204
226
|
</div>
|
|
205
227
|
<div class="register-form-row">
|
|
206
228
|
<label class="register-label">
|
|
@@ -209,6 +231,7 @@
|
|
|
209
231
|
</label>
|
|
210
232
|
<select id="register-recipe" name="recipe" required>
|
|
211
233
|
<option value="llamacpp">llamacpp</option>
|
|
234
|
+
<option value="flm">flm</option>
|
|
212
235
|
<option value="oga-npu">oga-npu</option>
|
|
213
236
|
<option value="oga-hybrid">oga-hybrid</option>
|
|
214
237
|
<option value="oga-cpu">oga-cpu</option>
|
|
@@ -226,6 +249,11 @@
|
|
|
226
249
|
Reasoning
|
|
227
250
|
<span class="tooltip-icon" data-tooltip="Enable to inform Lemonade Server that the model has reasoning capabilities that will use thinking tokens.">ⓘ</span>
|
|
228
251
|
</label>
|
|
252
|
+
<label class="register-label reasoning-inline">
|
|
253
|
+
<input type="checkbox" id="register-vision" name="vision">
|
|
254
|
+
Vision
|
|
255
|
+
<span class="tooltip-icon" data-tooltip="Enable to inform Lemonade Server that the model has vision capabilities for processing images.">ⓘ</span>
|
|
256
|
+
</label>
|
|
229
257
|
</div>
|
|
230
258
|
<div class="register-form-row register-form-row-tight">
|
|
231
259
|
<button type="submit" id="register-submit">Install</button>
|
|
@@ -236,6 +264,7 @@
|
|
|
236
264
|
</div>
|
|
237
265
|
</div>
|
|
238
266
|
</div>
|
|
267
|
+
</div>
|
|
239
268
|
</div>
|
|
240
269
|
</main>
|
|
241
270
|
<footer class="site-footer">
|
|
@@ -244,10 +273,33 @@
|
|
|
244
273
|
</footer>
|
|
245
274
|
|
|
246
275
|
<!-- External libraries -->
|
|
247
|
-
<script src="https://cdn.jsdelivr.net/npm/openai@
|
|
276
|
+
<script src="https://cdn.jsdelivr.net/npm/openai@6.7.0/dist/openai.min.js"></script>
|
|
248
277
|
<script src="https://cdn.jsdelivr.net/npm/marked@9.1.0/marked.min.js"></script>
|
|
249
278
|
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
|
|
250
279
|
|
|
280
|
+
<!-- Migration Cleanup Modal -->
|
|
281
|
+
<div id="migration-modal" class="modal" style="display:none;">
|
|
282
|
+
<div class="modal-content">
|
|
283
|
+
<div class="modal-header">
|
|
284
|
+
<h2>Clean Up Incompatible Models</h2>
|
|
285
|
+
<button class="modal-close" onclick="hideMigrationModal()">×</button>
|
|
286
|
+
</div>
|
|
287
|
+
<div class="modal-body">
|
|
288
|
+
<p>The following RyzenAI models are incompatible with RyzenAI 1.6 and can be safely deleted:</p>
|
|
289
|
+
<p class="migration-instructions">
|
|
290
|
+
After deleting, you can re-download compatible Ryzen AI 1.6 models from the <em>OGA NPU</em> and <em>OGA Hybrid</em> tabs.</p>
|
|
291
|
+
<div id="migration-model-list" class="migration-model-list"></div>
|
|
292
|
+
<div class="migration-summary">
|
|
293
|
+
<strong>Total space to free: <span id="migration-total-size"></span></strong>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
<div class="modal-footer">
|
|
297
|
+
<button class="cancel-btn" onclick="hideMigrationModal()">Cancel</button>
|
|
298
|
+
<button class="delete-btn" onclick="deleteIncompatibleModels()">Delete All</button>
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
|
|
251
303
|
<!-- Application JavaScript -->
|
|
252
304
|
<script src="/static/js/shared.js"></script>
|
|
253
305
|
<script src="/static/js/models.js"></script>
|
lemonade/tools/server/tray.py
CHANGED
|
@@ -7,11 +7,28 @@ import webbrowser
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
import logging
|
|
9
9
|
import tempfile
|
|
10
|
+
import platform
|
|
11
|
+
|
|
10
12
|
import requests
|
|
11
13
|
from packaging.version import parse as parse_version
|
|
12
14
|
|
|
15
|
+
from lemonade_server.pydantic_models import DEFAULT_CTX_SIZE
|
|
16
|
+
|
|
13
17
|
from lemonade.version import __version__
|
|
14
|
-
|
|
18
|
+
|
|
19
|
+
# Import the appropriate tray implementation based on platform
|
|
20
|
+
if platform.system() == "Darwin": # macOS
|
|
21
|
+
from lemonade.tools.server.utils.macos_tray import (
|
|
22
|
+
MacOSSystemTray as SystemTray,
|
|
23
|
+
Menu,
|
|
24
|
+
MenuItem,
|
|
25
|
+
)
|
|
26
|
+
else: # Windows/Linux
|
|
27
|
+
from lemonade.tools.server.utils.windows_tray import (
|
|
28
|
+
SystemTray,
|
|
29
|
+
Menu,
|
|
30
|
+
MenuItem,
|
|
31
|
+
)
|
|
15
32
|
|
|
16
33
|
|
|
17
34
|
class OutputDuplicator:
|
|
@@ -57,6 +74,7 @@ class LemonadeTray(SystemTray):
|
|
|
57
74
|
self.executor = ThreadPoolExecutor(max_workers=1)
|
|
58
75
|
self.log_file = log_file
|
|
59
76
|
self.port = port
|
|
77
|
+
self.ctx_size = DEFAULT_CTX_SIZE
|
|
60
78
|
self.server_factory = server_factory
|
|
61
79
|
self.debug_logs_enabled = log_level == "debug"
|
|
62
80
|
|
|
@@ -83,6 +101,9 @@ class LemonadeTray(SystemTray):
|
|
|
83
101
|
self.version_check_thread = None
|
|
84
102
|
self.stop_version_check = threading.Event()
|
|
85
103
|
|
|
104
|
+
# Hook function for platform-specific initialization callback
|
|
105
|
+
self.on_ready = None
|
|
106
|
+
|
|
86
107
|
def get_latest_version(self):
|
|
87
108
|
"""
|
|
88
109
|
Update the latest version information.
|
|
@@ -187,15 +208,38 @@ class LemonadeTray(SystemTray):
|
|
|
187
208
|
Show the log file in a new window.
|
|
188
209
|
"""
|
|
189
210
|
try:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
211
|
+
system = platform.system().lower()
|
|
212
|
+
if system == "darwin":
|
|
213
|
+
# Use Terminal.app to show live logs on macOS
|
|
214
|
+
try:
|
|
215
|
+
subprocess.Popen(
|
|
216
|
+
[
|
|
217
|
+
"osascript",
|
|
218
|
+
"-e",
|
|
219
|
+
f'tell application "Terminal" to do script "tail -f {self.log_file}"',
|
|
220
|
+
]
|
|
221
|
+
)
|
|
222
|
+
except (subprocess.CalledProcessError, FileNotFoundError) as e:
|
|
223
|
+
self.logger.error(f"Failed to open Terminal for logs: {e}")
|
|
224
|
+
self.show_balloon_notification(
|
|
225
|
+
"Error",
|
|
226
|
+
f"Failed to open logs in Terminal. Log file: {self.log_file}",
|
|
227
|
+
)
|
|
228
|
+
elif system == "windows":
|
|
229
|
+
# Use PowerShell on Windows
|
|
230
|
+
subprocess.Popen(
|
|
231
|
+
[
|
|
232
|
+
"powershell",
|
|
233
|
+
"Start-Process",
|
|
234
|
+
"powershell",
|
|
235
|
+
"-ArgumentList",
|
|
236
|
+
f'"-NoExit", "Get-Content -Wait {self.log_file}"',
|
|
237
|
+
]
|
|
238
|
+
)
|
|
239
|
+
else:
|
|
240
|
+
# Unsupported platform
|
|
241
|
+
self.logger.error(f"Log viewing not supported on platform: {system}")
|
|
242
|
+
|
|
199
243
|
except Exception as e: # pylint: disable=broad-exception-caught
|
|
200
244
|
self.logger.error(f"Error opening logs: {str(e)}")
|
|
201
245
|
|
|
@@ -224,7 +268,7 @@ class LemonadeTray(SystemTray):
|
|
|
224
268
|
try:
|
|
225
269
|
response = requests.get(
|
|
226
270
|
f"http://localhost:{self.port}/api/v0/health",
|
|
227
|
-
timeout=0.1,
|
|
271
|
+
timeout=0.1,
|
|
228
272
|
)
|
|
229
273
|
response.raise_for_status()
|
|
230
274
|
response_data = response.json()
|
|
@@ -253,7 +297,9 @@ class LemonadeTray(SystemTray):
|
|
|
253
297
|
"""
|
|
254
298
|
Change the server port and restart the server.
|
|
255
299
|
"""
|
|
300
|
+
|
|
256
301
|
try:
|
|
302
|
+
|
|
257
303
|
# Stop the current server
|
|
258
304
|
if self.server_thread and self.server_thread.is_alive():
|
|
259
305
|
# Set should_exit flag on the uvicorn server instance
|
|
@@ -266,22 +312,58 @@ class LemonadeTray(SystemTray):
|
|
|
266
312
|
|
|
267
313
|
# Update the port in both the tray and the server instance
|
|
268
314
|
self.port = new_port
|
|
269
|
-
if self.server:
|
|
270
|
-
self.server.port = new_port
|
|
271
315
|
|
|
272
|
-
#
|
|
316
|
+
# Clear the old server instance to ensure a fresh start
|
|
317
|
+
# This prevents middleware conflicts when restarting
|
|
318
|
+
self.server = None
|
|
319
|
+
|
|
273
320
|
self.server_thread = threading.Thread(target=self.start_server, daemon=True)
|
|
274
321
|
self.server_thread.start()
|
|
275
322
|
|
|
276
|
-
# Show notification
|
|
277
323
|
self.show_balloon_notification(
|
|
278
|
-
"Port Changed",
|
|
324
|
+
"Port Changed",
|
|
325
|
+
f"Lemonade Server is now running on port {self.port}",
|
|
279
326
|
)
|
|
280
327
|
|
|
281
328
|
except Exception as e: # pylint: disable=broad-exception-caught
|
|
282
329
|
self.logger.error(f"Error changing port: {str(e)}")
|
|
283
330
|
self.show_balloon_notification("Error", f"Failed to change port: {str(e)}")
|
|
284
331
|
|
|
332
|
+
def change_context_size(self, _, __, new_ctx_size):
|
|
333
|
+
"""
|
|
334
|
+
Change the server context size and restart the server.
|
|
335
|
+
"""
|
|
336
|
+
try:
|
|
337
|
+
# Stop the current server
|
|
338
|
+
if self.server_thread and self.server_thread.is_alive():
|
|
339
|
+
# Set should_exit flag on the uvicorn server instance
|
|
340
|
+
if (
|
|
341
|
+
hasattr(self.server, "uvicorn_server")
|
|
342
|
+
and self.server.uvicorn_server
|
|
343
|
+
):
|
|
344
|
+
self.server.uvicorn_server.should_exit = True
|
|
345
|
+
self.server_thread.join(timeout=2)
|
|
346
|
+
# Update the context size in both the tray and the server instance
|
|
347
|
+
self.ctx_size = new_ctx_size
|
|
348
|
+
if self.server:
|
|
349
|
+
self.server.ctx_size = new_ctx_size
|
|
350
|
+
# Restart the server
|
|
351
|
+
self.server_thread = threading.Thread(target=self.start_server, daemon=True)
|
|
352
|
+
self.server_thread.start()
|
|
353
|
+
# Show notification
|
|
354
|
+
ctx_size_label = (
|
|
355
|
+
f"{new_ctx_size//1024}K" if new_ctx_size >= 1024 else str(new_ctx_size)
|
|
356
|
+
)
|
|
357
|
+
self.show_balloon_notification(
|
|
358
|
+
"Context Size Changed",
|
|
359
|
+
f"Lemonade Server context size is now {ctx_size_label}",
|
|
360
|
+
)
|
|
361
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
362
|
+
self.logger.error(f"Error changing context size: {str(e)}")
|
|
363
|
+
self.show_balloon_notification(
|
|
364
|
+
"Error", f"Failed to change context size: {str(e)}"
|
|
365
|
+
)
|
|
366
|
+
|
|
285
367
|
def _using_installer(self):
|
|
286
368
|
"""
|
|
287
369
|
Check if the user is using the NSIS installer by checking for embeddable python
|
|
@@ -438,6 +520,30 @@ class LemonadeTray(SystemTray):
|
|
|
438
520
|
|
|
439
521
|
port_submenu = Menu(*port_menu_items)
|
|
440
522
|
|
|
523
|
+
# Create context size selection submenu with 6 options
|
|
524
|
+
ctx_size_menu_items = []
|
|
525
|
+
ctx_size_options = [
|
|
526
|
+
("4K", 4096),
|
|
527
|
+
("8K", 8192),
|
|
528
|
+
("16K", 16384),
|
|
529
|
+
("32K", 32768),
|
|
530
|
+
("64K", 65536),
|
|
531
|
+
("128K", 131072),
|
|
532
|
+
]
|
|
533
|
+
|
|
534
|
+
for ctx_label, ctx_value in ctx_size_options:
|
|
535
|
+
# Create a function that returns the lambda to properly capture the ctx_size variable
|
|
536
|
+
def create_ctx_handler(ctx_size):
|
|
537
|
+
return lambda icon, item: self.change_context_size(icon, item, ctx_size)
|
|
538
|
+
|
|
539
|
+
ctx_item = MenuItem(
|
|
540
|
+
f"Context size {ctx_label}", create_ctx_handler(ctx_value)
|
|
541
|
+
)
|
|
542
|
+
ctx_item.checked = ctx_value == self.ctx_size
|
|
543
|
+
ctx_size_menu_items.append(ctx_item)
|
|
544
|
+
|
|
545
|
+
ctx_size_submenu = Menu(*ctx_size_menu_items)
|
|
546
|
+
|
|
441
547
|
# Create the Logs submenu
|
|
442
548
|
debug_log_text = "Enable Debug Logs"
|
|
443
549
|
debug_log_item = MenuItem(debug_log_text, self.toggle_debug_logs)
|
|
@@ -452,6 +558,7 @@ class LemonadeTray(SystemTray):
|
|
|
452
558
|
if status_successfully_checked:
|
|
453
559
|
items.append(MenuItem("Load Model", None, submenu=load_submenu))
|
|
454
560
|
items.append(MenuItem("Port", None, submenu=port_submenu))
|
|
561
|
+
items.append(MenuItem("Context Size", None, submenu=ctx_size_submenu))
|
|
455
562
|
items.append(Menu.SEPARATOR)
|
|
456
563
|
|
|
457
564
|
# Only show upgrade option if newer version is available
|
|
@@ -475,6 +582,11 @@ class LemonadeTray(SystemTray):
|
|
|
475
582
|
Start the uvicorn server.
|
|
476
583
|
"""
|
|
477
584
|
self.server = self.server_factory()
|
|
585
|
+
|
|
586
|
+
# Ensure the server uses the current port from the tray
|
|
587
|
+
# This is important when changing ports
|
|
588
|
+
self.server.port = self.port
|
|
589
|
+
|
|
478
590
|
self.server.uvicorn_server = self.server.run_in_thread(self.server.host)
|
|
479
591
|
self.server.uvicorn_server.run()
|
|
480
592
|
|
|
@@ -483,16 +595,6 @@ class LemonadeTray(SystemTray):
|
|
|
483
595
|
Run the Lemonade tray application.
|
|
484
596
|
"""
|
|
485
597
|
|
|
486
|
-
# Register window class and create window
|
|
487
|
-
self.register_window_class()
|
|
488
|
-
self.create_window()
|
|
489
|
-
|
|
490
|
-
# Set up Windows console control handler for CTRL+C
|
|
491
|
-
self.console_handler = self.setup_console_control_handler(self.logger)
|
|
492
|
-
|
|
493
|
-
# Add tray icon
|
|
494
|
-
self.add_tray_icon()
|
|
495
|
-
|
|
496
598
|
# Start the background model mapping update thread
|
|
497
599
|
self.model_update_thread = threading.Thread(
|
|
498
600
|
target=self.update_downloaded_models_background, daemon=True
|
|
@@ -509,17 +611,27 @@ class LemonadeTray(SystemTray):
|
|
|
509
611
|
self.server_thread = threading.Thread(target=self.start_server, daemon=True)
|
|
510
612
|
self.server_thread.start()
|
|
511
613
|
|
|
512
|
-
#
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
(
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
614
|
+
# Provide an on_ready hook that Windows base tray will call after
|
|
615
|
+
# the HWND/icon are created. macOS will call it immediately after run.
|
|
616
|
+
def _on_ready():
|
|
617
|
+
system = platform.system().lower()
|
|
618
|
+
if system == "darwin":
|
|
619
|
+
message = (
|
|
620
|
+
"Lemonade Server is running! "
|
|
621
|
+
"Click the tray icon above to access options."
|
|
622
|
+
)
|
|
623
|
+
else: # Windows/Linux
|
|
624
|
+
message = (
|
|
625
|
+
"Lemonade Server is running! "
|
|
626
|
+
"Right-click the tray icon below to access options."
|
|
627
|
+
)
|
|
628
|
+
self.show_balloon_notification("Woohoo!", message)
|
|
520
629
|
|
|
521
|
-
#
|
|
522
|
-
self.
|
|
630
|
+
# Attach hook for both implementations to invoke after init
|
|
631
|
+
self.on_ready = _on_ready
|
|
632
|
+
|
|
633
|
+
# Call the parent run method which handles platform-specific initialization
|
|
634
|
+
super().run()
|
|
523
635
|
|
|
524
636
|
def exit_app(self, icon, item):
|
|
525
637
|
"""
|
|
@@ -534,8 +646,16 @@ class LemonadeTray(SystemTray):
|
|
|
534
646
|
if self.version_check_thread and self.version_check_thread.is_alive():
|
|
535
647
|
self.version_check_thread.join(timeout=1)
|
|
536
648
|
|
|
537
|
-
#
|
|
538
|
-
|
|
649
|
+
# Platform-specific exit handling
|
|
650
|
+
system = platform.system().lower()
|
|
651
|
+
if system == "darwin": # macOS
|
|
652
|
+
# For macOS, quit the rumps application
|
|
653
|
+
import rumps
|
|
654
|
+
|
|
655
|
+
rumps.quit_application()
|
|
656
|
+
else:
|
|
657
|
+
# Call parent exit method for Windows
|
|
658
|
+
super().exit_app(icon, item)
|
|
539
659
|
|
|
540
660
|
# Stop the server using the CLI stop command to ensure a rigorous cleanup
|
|
541
661
|
# This must be a subprocess to ensure the cleanup doesnt kill itself
|