lemonade-sdk 7.0.3__py3-none-any.whl → 8.0.0__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/api.py +3 -3
- lemonade/cli.py +11 -17
- lemonade/common/build.py +0 -47
- lemonade/common/network.py +50 -0
- lemonade/common/status.py +2 -21
- lemonade/common/system_info.py +19 -4
- lemonade/profilers/memory_tracker.py +3 -1
- lemonade/tools/accuracy.py +3 -4
- lemonade/tools/adapter.py +1 -2
- lemonade/tools/{huggingface_bench.py → huggingface/bench.py} +2 -87
- lemonade/tools/huggingface/load.py +235 -0
- lemonade/tools/{huggingface_load.py → huggingface/utils.py} +87 -255
- lemonade/tools/humaneval.py +9 -3
- lemonade/tools/{llamacpp_bench.py → llamacpp/bench.py} +1 -1
- lemonade/tools/{llamacpp.py → llamacpp/load.py} +18 -2
- lemonade/tools/mmlu.py +7 -15
- lemonade/tools/{ort_genai/oga.py → oga/load.py} +31 -422
- lemonade/tools/oga/utils.py +423 -0
- lemonade/tools/perplexity.py +4 -3
- lemonade/tools/prompt.py +2 -1
- lemonade/tools/quark/quark_load.py +2 -1
- lemonade/tools/quark/quark_quantize.py +5 -5
- lemonade/tools/report/table.py +3 -3
- lemonade/tools/server/llamacpp.py +159 -34
- lemonade/tools/server/serve.py +169 -147
- lemonade/tools/server/static/favicon.ico +0 -0
- lemonade/tools/server/static/styles.css +568 -0
- lemonade/tools/server/static/webapp.html +439 -0
- lemonade/tools/server/tray.py +458 -0
- lemonade/tools/server/{port_utils.py → utils/port.py} +22 -3
- lemonade/tools/server/utils/system_tray.py +395 -0
- lemonade/tools/server/{instructions.py → webapp.py} +4 -10
- lemonade/version.py +1 -1
- lemonade_install/install.py +46 -28
- {lemonade_sdk-7.0.3.dist-info → lemonade_sdk-8.0.0.dist-info}/METADATA +84 -22
- lemonade_sdk-8.0.0.dist-info/RECORD +70 -0
- lemonade_server/cli.py +182 -27
- lemonade_server/model_manager.py +192 -20
- lemonade_server/pydantic_models.py +9 -4
- lemonade_server/server_models.json +5 -3
- lemonade/common/analyze_model.py +0 -26
- lemonade/common/labels.py +0 -61
- lemonade/common/onnx_helpers.py +0 -176
- lemonade/common/plugins.py +0 -10
- lemonade/common/tensor_helpers.py +0 -83
- lemonade/tools/server/static/instructions.html +0 -262
- lemonade_sdk-7.0.3.dist-info/RECORD +0 -69
- /lemonade/tools/{ort_genai → oga}/__init__.py +0 -0
- /lemonade/tools/{ort_genai/oga_bench.py → oga/bench.py} +0 -0
- /lemonade/tools/server/{thread_utils.py → utils/thread.py} +0 -0
- {lemonade_sdk-7.0.3.dist-info → lemonade_sdk-8.0.0.dist-info}/WHEEL +0 -0
- {lemonade_sdk-7.0.3.dist-info → lemonade_sdk-8.0.0.dist-info}/entry_points.txt +0 -0
- {lemonade_sdk-7.0.3.dist-info → lemonade_sdk-8.0.0.dist-info}/licenses/LICENSE +0 -0
- {lemonade_sdk-7.0.3.dist-info → lemonade_sdk-8.0.0.dist-info}/licenses/NOTICE.md +0 -0
- {lemonade_sdk-7.0.3.dist-info → lemonade_sdk-8.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
# Add pywin32_system32 dlls (missing from embeddable Python executable)
|
|
5
|
+
pywin32_system32 = os.path.join(sys.prefix, "Lib", "site-packages", "pywin32_system32")
|
|
6
|
+
os.add_dll_directory(pywin32_system32)
|
|
7
|
+
|
|
8
|
+
from win32 import win32gui, win32api
|
|
9
|
+
import win32.lib.win32con as win32con
|
|
10
|
+
import ctypes
|
|
11
|
+
import psutil
|
|
12
|
+
from typing import Dict, Set, Callable, List, Optional, Tuple, Any
|
|
13
|
+
|
|
14
|
+
# Windows message constants
|
|
15
|
+
WM_USER = 0x0400
|
|
16
|
+
WM_TRAYICON = WM_USER + 1
|
|
17
|
+
WM_COMMAND = 0x0111
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class MenuItem:
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
text: str,
|
|
24
|
+
callback: Optional[Callable] = None,
|
|
25
|
+
enabled: bool = True,
|
|
26
|
+
submenu=None,
|
|
27
|
+
bitmap_path: Optional[str] = None,
|
|
28
|
+
):
|
|
29
|
+
self.text = text
|
|
30
|
+
self.callback = callback
|
|
31
|
+
self.enabled = enabled
|
|
32
|
+
self.submenu = submenu
|
|
33
|
+
self.bitmap_path = bitmap_path
|
|
34
|
+
self.id = None # Will be set when menu is created
|
|
35
|
+
self.bitmap_handle = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Menu:
|
|
39
|
+
SEPARATOR = "SEPARATOR"
|
|
40
|
+
|
|
41
|
+
def __init__(self, *items):
|
|
42
|
+
self.items = list(items)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SystemTray:
|
|
46
|
+
"""
|
|
47
|
+
Generic system tray implementation for Windows.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(self, app_name: str, icon_path: str):
|
|
51
|
+
self.app_name = app_name
|
|
52
|
+
self.icon_path = icon_path
|
|
53
|
+
self.hwnd = None
|
|
54
|
+
self.hinst = win32api.GetModuleHandle(None)
|
|
55
|
+
self.class_atom = None
|
|
56
|
+
self.notify_id = None
|
|
57
|
+
self.menu_handle = None
|
|
58
|
+
self.menu_items = [] # Store menu items with their IDs
|
|
59
|
+
self.next_menu_id = 1000 # Starting ID for menu items
|
|
60
|
+
|
|
61
|
+
# Message map for window procedure
|
|
62
|
+
self.message_map = {
|
|
63
|
+
win32con.WM_DESTROY: self.on_destroy,
|
|
64
|
+
win32con.WM_COMMAND: self.on_command,
|
|
65
|
+
WM_TRAYICON: self.on_tray_icon,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
def register_window_class(self):
|
|
69
|
+
"""
|
|
70
|
+
Register the window class for the tray icon.
|
|
71
|
+
"""
|
|
72
|
+
window_class = win32gui.WNDCLASS()
|
|
73
|
+
window_class.hInstance = self.hinst
|
|
74
|
+
window_class.lpszClassName = f"{self.app_name}TrayClass"
|
|
75
|
+
window_class.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
|
|
76
|
+
window_class.hCursor = win32api.LoadCursor(0, win32con.IDC_ARROW)
|
|
77
|
+
window_class.hbrBackground = win32con.COLOR_WINDOW
|
|
78
|
+
window_class.lpfnWndProc = self.window_proc
|
|
79
|
+
|
|
80
|
+
self.class_atom = win32gui.RegisterClass(window_class)
|
|
81
|
+
return self.class_atom
|
|
82
|
+
|
|
83
|
+
def create_window(self):
|
|
84
|
+
"""
|
|
85
|
+
Create a hidden window to receive messages.
|
|
86
|
+
"""
|
|
87
|
+
style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
|
|
88
|
+
self.hwnd = win32gui.CreateWindow(
|
|
89
|
+
self.class_atom,
|
|
90
|
+
f"{self.app_name} Tray",
|
|
91
|
+
style,
|
|
92
|
+
0,
|
|
93
|
+
0,
|
|
94
|
+
win32con.CW_USEDEFAULT,
|
|
95
|
+
win32con.CW_USEDEFAULT,
|
|
96
|
+
0,
|
|
97
|
+
0,
|
|
98
|
+
self.hinst,
|
|
99
|
+
None,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Set the AppUserModelID to identify our app to Windows
|
|
103
|
+
try:
|
|
104
|
+
SetCurrentProcessExplicitAppUserModelID = (
|
|
105
|
+
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID
|
|
106
|
+
)
|
|
107
|
+
SetCurrentProcessExplicitAppUserModelID(ctypes.c_wchar_p(self.app_name))
|
|
108
|
+
except Exception as e:
|
|
109
|
+
print(f"Failed to set AppUserModelID: {e}")
|
|
110
|
+
|
|
111
|
+
win32gui.UpdateWindow(self.hwnd)
|
|
112
|
+
return self.hwnd
|
|
113
|
+
|
|
114
|
+
def add_tray_icon(self):
|
|
115
|
+
"""
|
|
116
|
+
Add the tray icon to the system tray.
|
|
117
|
+
"""
|
|
118
|
+
# Load the .ico file
|
|
119
|
+
icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
|
|
120
|
+
hicon = win32gui.LoadImage(
|
|
121
|
+
self.hinst, str(self.icon_path), win32con.IMAGE_ICON, 0, 0, icon_flags
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP
|
|
125
|
+
nid = (self.hwnd, 0, flags, WM_TRAYICON, hicon, self.app_name)
|
|
126
|
+
win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, nid)
|
|
127
|
+
self.notify_id = nid
|
|
128
|
+
|
|
129
|
+
def show_balloon_notification(self, title, message, timeout=5000):
|
|
130
|
+
"""
|
|
131
|
+
Show a balloon notification from the tray icon.
|
|
132
|
+
"""
|
|
133
|
+
if self.notify_id:
|
|
134
|
+
# The notify_id is a tuple, we need to extract its elements
|
|
135
|
+
hwnd, id, flags, callback_msg, hicon, tip = self.notify_id
|
|
136
|
+
|
|
137
|
+
# Create a new notification with the balloon info
|
|
138
|
+
flags |= win32gui.NIF_INFO
|
|
139
|
+
|
|
140
|
+
# NIIF_USER (0x4) tells Windows to use our custom icon in the notification
|
|
141
|
+
info_flags = 0x4 # NIIF_USER
|
|
142
|
+
|
|
143
|
+
# Create the notification data structure with our custom icon flag
|
|
144
|
+
nid = (
|
|
145
|
+
hwnd,
|
|
146
|
+
id,
|
|
147
|
+
flags,
|
|
148
|
+
callback_msg,
|
|
149
|
+
hicon,
|
|
150
|
+
self.app_name,
|
|
151
|
+
message,
|
|
152
|
+
timeout,
|
|
153
|
+
title,
|
|
154
|
+
info_flags,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Show the notification
|
|
158
|
+
win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, nid)
|
|
159
|
+
|
|
160
|
+
def window_proc(self, hwnd, message, wparam, lparam):
|
|
161
|
+
"""
|
|
162
|
+
Window procedure to handle window messages.
|
|
163
|
+
"""
|
|
164
|
+
if message in self.message_map:
|
|
165
|
+
return self.message_map[message](hwnd, message, wparam, lparam)
|
|
166
|
+
return win32gui.DefWindowProc(hwnd, message, wparam, lparam)
|
|
167
|
+
|
|
168
|
+
def on_destroy(self, hwnd, message, wparam, lparam):
|
|
169
|
+
"""
|
|
170
|
+
Handle window destruction.
|
|
171
|
+
"""
|
|
172
|
+
if self.notify_id:
|
|
173
|
+
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, self.notify_id)
|
|
174
|
+
|
|
175
|
+
# Clean up bitmap resources
|
|
176
|
+
for item_id, item in self.menu_items:
|
|
177
|
+
if hasattr(item, "bitmap_handle") and item.bitmap_handle:
|
|
178
|
+
win32gui.DeleteObject(item.bitmap_handle)
|
|
179
|
+
|
|
180
|
+
win32gui.PostQuitMessage(0)
|
|
181
|
+
return 0
|
|
182
|
+
|
|
183
|
+
def on_command(self, hwnd, message, wparam, lparam):
|
|
184
|
+
"""
|
|
185
|
+
Handle menu commands.
|
|
186
|
+
"""
|
|
187
|
+
menu_id = win32gui.LOWORD(wparam)
|
|
188
|
+
|
|
189
|
+
# Find the menu item with this ID and execute its callback
|
|
190
|
+
for item_id, item_obj in self.menu_items:
|
|
191
|
+
if item_id == menu_id and item_obj.callback:
|
|
192
|
+
item_obj.callback(None, item_obj)
|
|
193
|
+
break
|
|
194
|
+
|
|
195
|
+
return 0
|
|
196
|
+
|
|
197
|
+
def on_tray_icon(self, hwnd, message, wparam, lparam):
|
|
198
|
+
"""
|
|
199
|
+
Handle tray icon events.
|
|
200
|
+
"""
|
|
201
|
+
if lparam == win32con.WM_RBUTTONUP or lparam == win32con.WM_LBUTTONUP:
|
|
202
|
+
# Show context menu on right-click and left-click
|
|
203
|
+
self.show_menu()
|
|
204
|
+
return 0
|
|
205
|
+
|
|
206
|
+
def create_menu_item(self, menu_handle, item, pos):
|
|
207
|
+
"""
|
|
208
|
+
Create a menu item and add it to the menu.
|
|
209
|
+
"""
|
|
210
|
+
if item == Menu.SEPARATOR:
|
|
211
|
+
win32gui.AppendMenu(menu_handle, win32con.MF_SEPARATOR, 0, "")
|
|
212
|
+
return pos + 1
|
|
213
|
+
|
|
214
|
+
# Assign a unique ID to this menu item
|
|
215
|
+
item.id = self.next_menu_id
|
|
216
|
+
self.next_menu_id += 1
|
|
217
|
+
|
|
218
|
+
# Store the menu item with its ID for later lookup
|
|
219
|
+
self.menu_items.append((item.id, item))
|
|
220
|
+
|
|
221
|
+
# Create the menu item
|
|
222
|
+
flags = win32con.MF_STRING
|
|
223
|
+
|
|
224
|
+
if not item.enabled:
|
|
225
|
+
flags |= win32con.MF_GRAYED
|
|
226
|
+
|
|
227
|
+
# Add checkmark if this is a selected item
|
|
228
|
+
if hasattr(item, "checked") and item.checked:
|
|
229
|
+
flags |= win32con.MF_CHECKED
|
|
230
|
+
|
|
231
|
+
# Handle submenu
|
|
232
|
+
if item.submenu:
|
|
233
|
+
submenu_handle = win32gui.CreatePopupMenu()
|
|
234
|
+
submenu_pos = 0
|
|
235
|
+
for submenu_item in item.submenu.items:
|
|
236
|
+
submenu_pos = self.create_menu_item(
|
|
237
|
+
submenu_handle, submenu_item, submenu_pos
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Add submenu to parent menu
|
|
241
|
+
win32gui.AppendMenu(
|
|
242
|
+
menu_handle, win32con.MF_POPUP | flags, submenu_handle, item.text
|
|
243
|
+
)
|
|
244
|
+
else:
|
|
245
|
+
# Add regular menu item first
|
|
246
|
+
win32gui.AppendMenu(menu_handle, flags, item.id, item.text)
|
|
247
|
+
|
|
248
|
+
# Then add bitmap if provided (as a separate step)
|
|
249
|
+
if item.bitmap_path:
|
|
250
|
+
try:
|
|
251
|
+
# Load the bitmap
|
|
252
|
+
icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
|
|
253
|
+
item.bitmap_handle = win32gui.LoadImage(
|
|
254
|
+
self.hinst,
|
|
255
|
+
str(item.bitmap_path),
|
|
256
|
+
win32con.IMAGE_BITMAP,
|
|
257
|
+
0,
|
|
258
|
+
0,
|
|
259
|
+
icon_flags,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
if item.bitmap_handle:
|
|
263
|
+
# Set the bitmap for checked/unchecked states
|
|
264
|
+
# Using the same bitmap for both states
|
|
265
|
+
win32gui.SetMenuItemBitmaps(
|
|
266
|
+
menu_handle,
|
|
267
|
+
item.id,
|
|
268
|
+
win32con.MF_BYCOMMAND,
|
|
269
|
+
item.bitmap_handle,
|
|
270
|
+
item.bitmap_handle,
|
|
271
|
+
)
|
|
272
|
+
else:
|
|
273
|
+
print(f"Failed to load bitmap from {item.bitmap_path}")
|
|
274
|
+
except Exception as e:
|
|
275
|
+
print(f"Error adding bitmap to menu item: {e}")
|
|
276
|
+
|
|
277
|
+
return pos + 1
|
|
278
|
+
|
|
279
|
+
def build_menu(self, menu_items):
|
|
280
|
+
"""
|
|
281
|
+
Build a menu from a list of menu items.
|
|
282
|
+
"""
|
|
283
|
+
# Create a new menu
|
|
284
|
+
menu_handle = win32gui.CreatePopupMenu()
|
|
285
|
+
pos = 0
|
|
286
|
+
|
|
287
|
+
# Clear previous menu items
|
|
288
|
+
self.menu_items = []
|
|
289
|
+
self.next_menu_id = 1000
|
|
290
|
+
|
|
291
|
+
# Add each item to the menu
|
|
292
|
+
for item in menu_items:
|
|
293
|
+
pos = self.create_menu_item(menu_handle, item, pos)
|
|
294
|
+
|
|
295
|
+
return menu_handle
|
|
296
|
+
|
|
297
|
+
def show_menu(self):
|
|
298
|
+
"""
|
|
299
|
+
Show the context menu.
|
|
300
|
+
"""
|
|
301
|
+
# Create menu based on current state
|
|
302
|
+
menu = self.create_menu()
|
|
303
|
+
menu_handle = self.build_menu(menu.items)
|
|
304
|
+
|
|
305
|
+
# Get cursor position
|
|
306
|
+
pos = win32gui.GetCursorPos()
|
|
307
|
+
|
|
308
|
+
# Make our window the foreground window
|
|
309
|
+
try:
|
|
310
|
+
win32gui.SetForegroundWindow(self.hwnd)
|
|
311
|
+
except Exception:
|
|
312
|
+
# Ignore errors when setting foreground window
|
|
313
|
+
# Those are common when the tray icon is already open
|
|
314
|
+
pass
|
|
315
|
+
|
|
316
|
+
# Display the menu
|
|
317
|
+
win32gui.TrackPopupMenu(
|
|
318
|
+
menu_handle,
|
|
319
|
+
win32con.TPM_LEFTALIGN | win32con.TPM_RIGHTBUTTON,
|
|
320
|
+
pos[0],
|
|
321
|
+
pos[1],
|
|
322
|
+
0,
|
|
323
|
+
self.hwnd,
|
|
324
|
+
None,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
# Required by Windows
|
|
328
|
+
win32gui.PostMessage(self.hwnd, win32con.WM_NULL, 0, 0)
|
|
329
|
+
|
|
330
|
+
def create_menu(self):
|
|
331
|
+
"""
|
|
332
|
+
Create the context menu based on current state. Override in subclass.
|
|
333
|
+
"""
|
|
334
|
+
return Menu(MenuItem("Exit", self.exit_app))
|
|
335
|
+
|
|
336
|
+
def exit_app(self, _, __):
|
|
337
|
+
"""Exit the application."""
|
|
338
|
+
win32gui.DestroyWindow(self.hwnd)
|
|
339
|
+
|
|
340
|
+
def update_menu(self):
|
|
341
|
+
"""
|
|
342
|
+
Update the menu (will be shown on next right-click).
|
|
343
|
+
"""
|
|
344
|
+
if self.hwnd:
|
|
345
|
+
win32gui.InvalidateRect(self.hwnd, None, True)
|
|
346
|
+
|
|
347
|
+
def message_loop(self):
|
|
348
|
+
"""
|
|
349
|
+
Run the Windows message loop.
|
|
350
|
+
"""
|
|
351
|
+
win32gui.PumpMessages()
|
|
352
|
+
|
|
353
|
+
def run(self):
|
|
354
|
+
"""
|
|
355
|
+
Run the tray application.
|
|
356
|
+
"""
|
|
357
|
+
# Register window class and create window
|
|
358
|
+
self.register_window_class()
|
|
359
|
+
self.create_window()
|
|
360
|
+
|
|
361
|
+
# Add tray icon
|
|
362
|
+
self.add_tray_icon()
|
|
363
|
+
|
|
364
|
+
# Run the message loop in the main thread
|
|
365
|
+
self.message_loop()
|
|
366
|
+
|
|
367
|
+
def setup_console_control_handler(self, logger=None):
|
|
368
|
+
"""
|
|
369
|
+
Set up Windows console control handler for CTRL+C events.
|
|
370
|
+
This handles graceful shutdown when the console window is closed or CTRL+C is pressed.
|
|
371
|
+
"""
|
|
372
|
+
|
|
373
|
+
def console_ctrl_handler(ctrl_type):
|
|
374
|
+
if ctrl_type in (0, 2): # CTRL_C_EVENT or CTRL_CLOSE_EVENT
|
|
375
|
+
if logger:
|
|
376
|
+
logger.info(
|
|
377
|
+
"Received console control event, shutting down gracefully"
|
|
378
|
+
)
|
|
379
|
+
# Post a quit message to the main thread instead of calling exit_app directly
|
|
380
|
+
# This avoids thread access issues
|
|
381
|
+
if self.hwnd:
|
|
382
|
+
win32gui.PostMessage(self.hwnd, win32con.WM_CLOSE, 0, 0)
|
|
383
|
+
return True
|
|
384
|
+
return False
|
|
385
|
+
|
|
386
|
+
# Define the handler function type
|
|
387
|
+
HANDLER_ROUTINE = ctypes.WINFUNCTYPE(
|
|
388
|
+
ctypes.wintypes.BOOL, ctypes.wintypes.DWORD
|
|
389
|
+
)
|
|
390
|
+
handler = HANDLER_ROUTINE(console_ctrl_handler)
|
|
391
|
+
|
|
392
|
+
# Set the console control handler
|
|
393
|
+
ctypes.windll.kernel32.SetConsoleCtrlHandler(handler, True)
|
|
394
|
+
|
|
395
|
+
return handler # Return handler to keep it in scope
|
|
@@ -4,18 +4,12 @@ from fastapi.responses import HTMLResponse
|
|
|
4
4
|
from lemonade_server.model_manager import ModelManager
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def
|
|
7
|
+
def get_webapp_html(port=8000):
|
|
8
8
|
"""
|
|
9
|
-
Show
|
|
9
|
+
Show Lemonade Web App for LLM chat and model management.
|
|
10
10
|
"""
|
|
11
11
|
# Load server models from JSON
|
|
12
|
-
|
|
13
|
-
Path(__file__).parent.parent.parent.parent
|
|
14
|
-
/ "lemonade_server"
|
|
15
|
-
/ "server_models.json"
|
|
16
|
-
)
|
|
17
|
-
with open(server_models_path, "r", encoding="utf-8") as f:
|
|
18
|
-
server_models = json.load(f)
|
|
12
|
+
server_models = ModelManager().supported_models
|
|
19
13
|
|
|
20
14
|
# Use shared filter function from model_manager.py
|
|
21
15
|
filtered_models = ModelManager().filter_models_by_backend(server_models)
|
|
@@ -26,7 +20,7 @@ def get_instructions_html(port=8000):
|
|
|
26
20
|
)
|
|
27
21
|
|
|
28
22
|
# Load HTML template
|
|
29
|
-
template_path = Path(__file__).parent / "static" / "
|
|
23
|
+
template_path = Path(__file__).parent / "static" / "webapp.html"
|
|
30
24
|
with open(template_path, "r", encoding="utf-8") as f:
|
|
31
25
|
html_template = f.read()
|
|
32
26
|
|
lemonade/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "
|
|
1
|
+
__version__ = "8.0.0"
|
lemonade_install/install.py
CHANGED
|
@@ -52,7 +52,6 @@ import subprocess
|
|
|
52
52
|
import sys
|
|
53
53
|
from typing import Optional
|
|
54
54
|
import zipfile
|
|
55
|
-
import requests
|
|
56
55
|
|
|
57
56
|
DEFAULT_RYZEN_AI_VERSION = "1.4.0"
|
|
58
57
|
version_info_filename = "version_info.json"
|
|
@@ -192,6 +191,8 @@ def get_oga_hybrid_dir():
|
|
|
192
191
|
|
|
193
192
|
def download_lfs_file(token, file, output_filename):
|
|
194
193
|
"""Downloads a file from LFS"""
|
|
194
|
+
import requests
|
|
195
|
+
|
|
195
196
|
# Set up the headers for the request
|
|
196
197
|
headers = {
|
|
197
198
|
"Authorization": f"token {token}",
|
|
@@ -229,6 +230,8 @@ def download_lfs_file(token, file, output_filename):
|
|
|
229
230
|
|
|
230
231
|
|
|
231
232
|
def download_file(url: str, output_filename: str, description: str = None):
|
|
233
|
+
import requests
|
|
234
|
+
|
|
232
235
|
try:
|
|
233
236
|
response = requests.get(url)
|
|
234
237
|
if response.status_code != 200:
|
|
@@ -265,55 +268,67 @@ def check_ryzen_ai_processor():
|
|
|
265
268
|
"Ryzen AI installation is only supported on Windows."
|
|
266
269
|
)
|
|
267
270
|
|
|
271
|
+
skip_check = os.getenv("RYZENAI_SKIP_PROCESSOR_CHECK", "").lower() in {
|
|
272
|
+
"1",
|
|
273
|
+
"true",
|
|
274
|
+
"yes",
|
|
275
|
+
}
|
|
276
|
+
if skip_check:
|
|
277
|
+
print("[WARNING]: Processor check skipped.")
|
|
278
|
+
return
|
|
279
|
+
|
|
268
280
|
is_supported = False
|
|
269
281
|
cpu_name = ""
|
|
270
282
|
|
|
271
283
|
try:
|
|
272
|
-
# Use
|
|
284
|
+
# Use PowerShell command to get processor name
|
|
285
|
+
powershell_cmd = [
|
|
286
|
+
"powershell",
|
|
287
|
+
"-ExecutionPolicy",
|
|
288
|
+
"Bypass",
|
|
289
|
+
"-Command",
|
|
290
|
+
"Get-WmiObject -Class Win32_Processor | Select-Object -ExpandProperty Name",
|
|
291
|
+
]
|
|
292
|
+
|
|
273
293
|
result = subprocess.run(
|
|
274
|
-
|
|
275
|
-
"reg",
|
|
276
|
-
"query",
|
|
277
|
-
"HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
|
|
278
|
-
"/v",
|
|
279
|
-
"ProcessorNameString",
|
|
280
|
-
],
|
|
294
|
+
powershell_cmd,
|
|
281
295
|
capture_output=True,
|
|
282
296
|
text=True,
|
|
283
297
|
check=True,
|
|
284
298
|
)
|
|
285
299
|
|
|
286
|
-
#
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
break
|
|
300
|
+
# Extract the CPU name from PowerShell output
|
|
301
|
+
cpu_name = result.stdout.strip()
|
|
302
|
+
if not cpu_name:
|
|
303
|
+
print(
|
|
304
|
+
"[WARNING]: Could not detect processor name. Proceeding with installation."
|
|
305
|
+
)
|
|
306
|
+
return
|
|
294
307
|
|
|
295
308
|
# Check for any supported series
|
|
296
309
|
for series in SUPPORTED_RYZEN_AI_SERIES:
|
|
297
310
|
# Look for the series number pattern - matches any processor in the supported series
|
|
298
311
|
pattern = rf"ryzen ai.*\b{series[0]}\d{{2}}\b"
|
|
299
312
|
match = re.search(pattern, cpu_name.lower(), re.IGNORECASE)
|
|
313
|
+
|
|
300
314
|
if match:
|
|
301
315
|
is_supported = True
|
|
302
316
|
break
|
|
303
317
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
318
|
+
if not is_supported:
|
|
319
|
+
print(
|
|
320
|
+
f"[WARNING]: Processor '{cpu_name}' may not be officially supported for Ryzen AI hybrid execution."
|
|
321
|
+
)
|
|
322
|
+
print(
|
|
323
|
+
"[WARNING]: Installation will proceed, but hybrid features may not work correctly."
|
|
324
|
+
)
|
|
325
|
+
print("[WARNING]: Officially supported processors: Ryzen AI 300-series")
|
|
310
326
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
f"Ryzen AI installation requires a Ryzen AI {supported_series_str} "
|
|
315
|
-
f"series processor. Your current processor ({cpu_name}) is not supported."
|
|
327
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
328
|
+
print(
|
|
329
|
+
f"[WARNING]: Could not detect processor ({e}). Proceeding with installation."
|
|
316
330
|
)
|
|
331
|
+
print("[WARNING]: Hybrid features may not work if processor is not supported.")
|
|
317
332
|
|
|
318
333
|
|
|
319
334
|
def download_and_extract_package(
|
|
@@ -334,6 +349,8 @@ def download_and_extract_package(
|
|
|
334
349
|
Returns:
|
|
335
350
|
str: Path where package was extracted (renamed to package-version)
|
|
336
351
|
"""
|
|
352
|
+
import requests
|
|
353
|
+
|
|
337
354
|
zip_filename = f"{package_name}-{version}.zip"
|
|
338
355
|
zip_path = os.path.join(install_dir, zip_filename)
|
|
339
356
|
target_folder = os.path.join(install_dir, f"{package_name}-{version}")
|
|
@@ -691,6 +708,7 @@ class Install:
|
|
|
691
708
|
raise ValueError(
|
|
692
709
|
f"Value passed to ryzenai argument is not supported: {ryzenai}"
|
|
693
710
|
)
|
|
711
|
+
|
|
694
712
|
if build_model:
|
|
695
713
|
model_prep_file = Install._install_ryzenai_model_artifacts(
|
|
696
714
|
ryzen_ai_folder, version
|