lemonade-sdk 9.1.1__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.
- lemonade/__init__.py +5 -0
- lemonade/api.py +180 -0
- lemonade/cache.py +92 -0
- lemonade/cli.py +173 -0
- lemonade/common/__init__.py +0 -0
- lemonade/common/build.py +176 -0
- lemonade/common/cli_helpers.py +139 -0
- lemonade/common/exceptions.py +98 -0
- lemonade/common/filesystem.py +368 -0
- lemonade/common/inference_engines.py +408 -0
- lemonade/common/network.py +93 -0
- lemonade/common/printing.py +110 -0
- lemonade/common/status.py +471 -0
- lemonade/common/system_info.py +1411 -0
- lemonade/common/test_helpers.py +28 -0
- lemonade/profilers/__init__.py +1 -0
- lemonade/profilers/agt_power.py +437 -0
- lemonade/profilers/hwinfo_power.py +429 -0
- lemonade/profilers/memory_tracker.py +259 -0
- lemonade/profilers/profiler.py +58 -0
- lemonade/sequence.py +363 -0
- lemonade/state.py +159 -0
- lemonade/tools/__init__.py +1 -0
- lemonade/tools/accuracy.py +432 -0
- lemonade/tools/adapter.py +114 -0
- lemonade/tools/bench.py +302 -0
- lemonade/tools/flm/__init__.py +1 -0
- lemonade/tools/flm/utils.py +305 -0
- lemonade/tools/huggingface/bench.py +187 -0
- lemonade/tools/huggingface/load.py +235 -0
- lemonade/tools/huggingface/utils.py +359 -0
- lemonade/tools/humaneval.py +264 -0
- lemonade/tools/llamacpp/bench.py +255 -0
- lemonade/tools/llamacpp/load.py +222 -0
- lemonade/tools/llamacpp/utils.py +1260 -0
- lemonade/tools/management_tools.py +319 -0
- lemonade/tools/mmlu.py +319 -0
- lemonade/tools/oga/__init__.py +0 -0
- lemonade/tools/oga/bench.py +120 -0
- lemonade/tools/oga/load.py +804 -0
- lemonade/tools/oga/migration.py +403 -0
- lemonade/tools/oga/utils.py +462 -0
- lemonade/tools/perplexity.py +147 -0
- lemonade/tools/prompt.py +263 -0
- lemonade/tools/report/__init__.py +0 -0
- lemonade/tools/report/llm_report.py +203 -0
- lemonade/tools/report/table.py +899 -0
- lemonade/tools/server/__init__.py +0 -0
- lemonade/tools/server/flm.py +133 -0
- lemonade/tools/server/llamacpp.py +320 -0
- lemonade/tools/server/serve.py +2123 -0
- lemonade/tools/server/static/favicon.ico +0 -0
- lemonade/tools/server/static/index.html +279 -0
- lemonade/tools/server/static/js/chat.js +1059 -0
- lemonade/tools/server/static/js/model-settings.js +183 -0
- lemonade/tools/server/static/js/models.js +1395 -0
- lemonade/tools/server/static/js/shared.js +556 -0
- lemonade/tools/server/static/logs.html +191 -0
- lemonade/tools/server/static/styles.css +2654 -0
- lemonade/tools/server/static/webapp.html +321 -0
- lemonade/tools/server/tool_calls.py +153 -0
- lemonade/tools/server/tray.py +664 -0
- lemonade/tools/server/utils/macos_tray.py +226 -0
- lemonade/tools/server/utils/port.py +77 -0
- lemonade/tools/server/utils/thread.py +85 -0
- lemonade/tools/server/utils/windows_tray.py +408 -0
- lemonade/tools/server/webapp.py +34 -0
- lemonade/tools/server/wrapped_server.py +559 -0
- lemonade/tools/tool.py +374 -0
- lemonade/version.py +1 -0
- lemonade_install/__init__.py +1 -0
- lemonade_install/install.py +239 -0
- lemonade_sdk-9.1.1.dist-info/METADATA +276 -0
- lemonade_sdk-9.1.1.dist-info/RECORD +84 -0
- lemonade_sdk-9.1.1.dist-info/WHEEL +5 -0
- lemonade_sdk-9.1.1.dist-info/entry_points.txt +5 -0
- lemonade_sdk-9.1.1.dist-info/licenses/LICENSE +201 -0
- lemonade_sdk-9.1.1.dist-info/licenses/NOTICE.md +47 -0
- lemonade_sdk-9.1.1.dist-info/top_level.txt +3 -0
- lemonade_server/cli.py +805 -0
- lemonade_server/model_manager.py +758 -0
- lemonade_server/pydantic_models.py +159 -0
- lemonade_server/server_models.json +643 -0
- lemonade_server/settings.py +39 -0
|
@@ -0,0 +1,408 @@
|
|
|
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
|
+
checked: bool = False,
|
|
29
|
+
):
|
|
30
|
+
self.text = text
|
|
31
|
+
self.callback = callback
|
|
32
|
+
self.enabled = enabled
|
|
33
|
+
self.submenu = submenu
|
|
34
|
+
self.bitmap_path = bitmap_path
|
|
35
|
+
self.checked = checked
|
|
36
|
+
self.id = None # Will be set when menu is created
|
|
37
|
+
self.bitmap_handle = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Menu:
|
|
41
|
+
SEPARATOR = "SEPARATOR"
|
|
42
|
+
|
|
43
|
+
def __init__(self, *items):
|
|
44
|
+
self.items = list(items)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class SystemTray:
|
|
48
|
+
"""
|
|
49
|
+
Generic system tray implementation for Windows.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, app_name: str, icon_path: str):
|
|
53
|
+
self.app_name = app_name
|
|
54
|
+
self.icon_path = icon_path
|
|
55
|
+
self.hwnd = None
|
|
56
|
+
self.hinst = win32api.GetModuleHandle(None)
|
|
57
|
+
self.class_atom = None
|
|
58
|
+
self.notify_id = None
|
|
59
|
+
self.menu_handle = None
|
|
60
|
+
self.menu_items = [] # Store menu items with their IDs
|
|
61
|
+
self.next_menu_id = 1000 # Starting ID for menu items
|
|
62
|
+
|
|
63
|
+
# Message map for window procedure
|
|
64
|
+
self.message_map = {
|
|
65
|
+
win32con.WM_DESTROY: self.on_destroy,
|
|
66
|
+
win32con.WM_COMMAND: self.on_command,
|
|
67
|
+
WM_TRAYICON: self.on_tray_icon,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def register_window_class(self):
|
|
71
|
+
"""
|
|
72
|
+
Register the window class for the tray icon.
|
|
73
|
+
"""
|
|
74
|
+
window_class = win32gui.WNDCLASS()
|
|
75
|
+
window_class.hInstance = self.hinst
|
|
76
|
+
window_class.lpszClassName = f"{self.app_name}TrayClass"
|
|
77
|
+
window_class.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
|
|
78
|
+
window_class.hCursor = win32api.LoadCursor(0, win32con.IDC_ARROW)
|
|
79
|
+
window_class.hbrBackground = win32con.COLOR_WINDOW
|
|
80
|
+
window_class.lpfnWndProc = self.window_proc
|
|
81
|
+
|
|
82
|
+
self.class_atom = win32gui.RegisterClass(window_class)
|
|
83
|
+
return self.class_atom
|
|
84
|
+
|
|
85
|
+
def create_window(self):
|
|
86
|
+
"""
|
|
87
|
+
Create a hidden window to receive messages.
|
|
88
|
+
"""
|
|
89
|
+
style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
|
|
90
|
+
self.hwnd = win32gui.CreateWindow(
|
|
91
|
+
self.class_atom,
|
|
92
|
+
f"{self.app_name} Tray",
|
|
93
|
+
style,
|
|
94
|
+
0,
|
|
95
|
+
0,
|
|
96
|
+
win32con.CW_USEDEFAULT,
|
|
97
|
+
win32con.CW_USEDEFAULT,
|
|
98
|
+
0,
|
|
99
|
+
0,
|
|
100
|
+
self.hinst,
|
|
101
|
+
None,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Set the AppUserModelID to identify our app to Windows
|
|
105
|
+
try:
|
|
106
|
+
SetCurrentProcessExplicitAppUserModelID = (
|
|
107
|
+
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID
|
|
108
|
+
)
|
|
109
|
+
SetCurrentProcessExplicitAppUserModelID(ctypes.c_wchar_p(self.app_name))
|
|
110
|
+
except Exception as e:
|
|
111
|
+
print(f"Failed to set AppUserModelID: {e}")
|
|
112
|
+
|
|
113
|
+
win32gui.UpdateWindow(self.hwnd)
|
|
114
|
+
return self.hwnd
|
|
115
|
+
|
|
116
|
+
def add_tray_icon(self):
|
|
117
|
+
"""
|
|
118
|
+
Add the tray icon to the system tray.
|
|
119
|
+
"""
|
|
120
|
+
# Load the .ico file
|
|
121
|
+
icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
|
|
122
|
+
hicon = win32gui.LoadImage(
|
|
123
|
+
self.hinst, str(self.icon_path), win32con.IMAGE_ICON, 0, 0, icon_flags
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP
|
|
127
|
+
nid = (self.hwnd, 0, flags, WM_TRAYICON, hicon, self.app_name)
|
|
128
|
+
win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, nid)
|
|
129
|
+
self.notify_id = nid
|
|
130
|
+
|
|
131
|
+
def show_balloon_notification(self, title, message, timeout=5000):
|
|
132
|
+
"""
|
|
133
|
+
Show a balloon notification from the tray icon.
|
|
134
|
+
"""
|
|
135
|
+
if self.notify_id:
|
|
136
|
+
# The notify_id is a tuple, we need to extract its elements
|
|
137
|
+
hwnd, id, flags, callback_msg, hicon, tip = self.notify_id
|
|
138
|
+
|
|
139
|
+
# Create a new notification with the balloon info
|
|
140
|
+
flags |= win32gui.NIF_INFO
|
|
141
|
+
|
|
142
|
+
# NIIF_USER (0x4) tells Windows to use our custom icon in the notification
|
|
143
|
+
info_flags = 0x4 # NIIF_USER
|
|
144
|
+
|
|
145
|
+
# Create the notification data structure with our custom icon flag
|
|
146
|
+
nid = (
|
|
147
|
+
hwnd,
|
|
148
|
+
id,
|
|
149
|
+
flags,
|
|
150
|
+
callback_msg,
|
|
151
|
+
hicon,
|
|
152
|
+
self.app_name,
|
|
153
|
+
message,
|
|
154
|
+
timeout,
|
|
155
|
+
title,
|
|
156
|
+
info_flags,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Show the notification
|
|
160
|
+
win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, nid)
|
|
161
|
+
|
|
162
|
+
def window_proc(self, hwnd, message, wparam, lparam):
|
|
163
|
+
"""
|
|
164
|
+
Window procedure to handle window messages.
|
|
165
|
+
"""
|
|
166
|
+
if message in self.message_map:
|
|
167
|
+
return self.message_map[message](hwnd, message, wparam, lparam)
|
|
168
|
+
return win32gui.DefWindowProc(hwnd, message, wparam, lparam)
|
|
169
|
+
|
|
170
|
+
def on_destroy(self, hwnd, message, wparam, lparam):
|
|
171
|
+
"""
|
|
172
|
+
Handle window destruction.
|
|
173
|
+
"""
|
|
174
|
+
if self.notify_id:
|
|
175
|
+
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, self.notify_id)
|
|
176
|
+
|
|
177
|
+
# Clean up bitmap resources
|
|
178
|
+
for item_id, item in self.menu_items:
|
|
179
|
+
if hasattr(item, "bitmap_handle") and item.bitmap_handle:
|
|
180
|
+
win32gui.DeleteObject(item.bitmap_handle)
|
|
181
|
+
|
|
182
|
+
win32gui.PostQuitMessage(0)
|
|
183
|
+
return 0
|
|
184
|
+
|
|
185
|
+
def on_command(self, hwnd, message, wparam, lparam):
|
|
186
|
+
"""
|
|
187
|
+
Handle menu commands.
|
|
188
|
+
"""
|
|
189
|
+
menu_id = win32gui.LOWORD(wparam)
|
|
190
|
+
|
|
191
|
+
# Find the menu item with this ID and execute its callback
|
|
192
|
+
for item_id, item_obj in self.menu_items:
|
|
193
|
+
if item_id == menu_id and item_obj.callback:
|
|
194
|
+
item_obj.callback(None, item_obj)
|
|
195
|
+
break
|
|
196
|
+
|
|
197
|
+
return 0
|
|
198
|
+
|
|
199
|
+
def on_tray_icon(self, hwnd, message, wparam, lparam):
|
|
200
|
+
"""
|
|
201
|
+
Handle tray icon events.
|
|
202
|
+
"""
|
|
203
|
+
if lparam == win32con.WM_RBUTTONUP or lparam == win32con.WM_LBUTTONUP:
|
|
204
|
+
# Show context menu on right-click and left-click
|
|
205
|
+
self.show_menu()
|
|
206
|
+
return 0
|
|
207
|
+
|
|
208
|
+
def create_menu_item(self, menu_handle, item, pos):
|
|
209
|
+
"""
|
|
210
|
+
Create a menu item and add it to the menu.
|
|
211
|
+
"""
|
|
212
|
+
if item == Menu.SEPARATOR:
|
|
213
|
+
win32gui.AppendMenu(menu_handle, win32con.MF_SEPARATOR, 0, "")
|
|
214
|
+
return pos + 1
|
|
215
|
+
|
|
216
|
+
# Assign a unique ID to this menu item
|
|
217
|
+
item.id = self.next_menu_id
|
|
218
|
+
self.next_menu_id += 1
|
|
219
|
+
|
|
220
|
+
# Store the menu item with its ID for later lookup
|
|
221
|
+
self.menu_items.append((item.id, item))
|
|
222
|
+
|
|
223
|
+
# Create the menu item
|
|
224
|
+
flags = win32con.MF_STRING
|
|
225
|
+
|
|
226
|
+
if not item.enabled:
|
|
227
|
+
flags |= win32con.MF_GRAYED
|
|
228
|
+
|
|
229
|
+
# Add checkmark if this is a selected item
|
|
230
|
+
if hasattr(item, "checked") and item.checked:
|
|
231
|
+
flags |= win32con.MF_CHECKED
|
|
232
|
+
|
|
233
|
+
# Handle submenu
|
|
234
|
+
if item.submenu:
|
|
235
|
+
submenu_handle = win32gui.CreatePopupMenu()
|
|
236
|
+
submenu_pos = 0
|
|
237
|
+
for submenu_item in item.submenu.items:
|
|
238
|
+
submenu_pos = self.create_menu_item(
|
|
239
|
+
submenu_handle, submenu_item, submenu_pos
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
# Add submenu to parent menu
|
|
243
|
+
win32gui.AppendMenu(
|
|
244
|
+
menu_handle, win32con.MF_POPUP | flags, submenu_handle, item.text
|
|
245
|
+
)
|
|
246
|
+
else:
|
|
247
|
+
# Add regular menu item first
|
|
248
|
+
win32gui.AppendMenu(menu_handle, flags, item.id, item.text)
|
|
249
|
+
|
|
250
|
+
# Then add bitmap if provided (as a separate step)
|
|
251
|
+
if item.bitmap_path:
|
|
252
|
+
try:
|
|
253
|
+
# Load the bitmap
|
|
254
|
+
icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
|
|
255
|
+
item.bitmap_handle = win32gui.LoadImage(
|
|
256
|
+
self.hinst,
|
|
257
|
+
str(item.bitmap_path),
|
|
258
|
+
win32con.IMAGE_BITMAP,
|
|
259
|
+
0,
|
|
260
|
+
0,
|
|
261
|
+
icon_flags,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
if item.bitmap_handle:
|
|
265
|
+
# Set the bitmap for checked/unchecked states
|
|
266
|
+
# Using the same bitmap for both states
|
|
267
|
+
win32gui.SetMenuItemBitmaps(
|
|
268
|
+
menu_handle,
|
|
269
|
+
item.id,
|
|
270
|
+
win32con.MF_BYCOMMAND,
|
|
271
|
+
item.bitmap_handle,
|
|
272
|
+
item.bitmap_handle,
|
|
273
|
+
)
|
|
274
|
+
else:
|
|
275
|
+
print(f"Failed to load bitmap from {item.bitmap_path}")
|
|
276
|
+
except Exception as e:
|
|
277
|
+
print(f"Error adding bitmap to menu item: {e}")
|
|
278
|
+
|
|
279
|
+
return pos + 1
|
|
280
|
+
|
|
281
|
+
def build_menu(self, menu_items):
|
|
282
|
+
"""
|
|
283
|
+
Build a menu from a list of menu items.
|
|
284
|
+
"""
|
|
285
|
+
# Create a new menu
|
|
286
|
+
menu_handle = win32gui.CreatePopupMenu()
|
|
287
|
+
pos = 0
|
|
288
|
+
|
|
289
|
+
# Clear previous menu items
|
|
290
|
+
self.menu_items = []
|
|
291
|
+
self.next_menu_id = 1000
|
|
292
|
+
|
|
293
|
+
# Add each item to the menu
|
|
294
|
+
for item in menu_items:
|
|
295
|
+
pos = self.create_menu_item(menu_handle, item, pos)
|
|
296
|
+
|
|
297
|
+
return menu_handle
|
|
298
|
+
|
|
299
|
+
def show_menu(self):
|
|
300
|
+
"""
|
|
301
|
+
Show the context menu.
|
|
302
|
+
"""
|
|
303
|
+
# Create menu based on current state
|
|
304
|
+
menu = self.create_menu()
|
|
305
|
+
menu_handle = self.build_menu(menu.items)
|
|
306
|
+
|
|
307
|
+
# Get cursor position
|
|
308
|
+
pos = win32gui.GetCursorPos()
|
|
309
|
+
|
|
310
|
+
# Make our window the foreground window
|
|
311
|
+
try:
|
|
312
|
+
win32gui.SetForegroundWindow(self.hwnd)
|
|
313
|
+
except Exception:
|
|
314
|
+
# Ignore errors when setting foreground window
|
|
315
|
+
# Those are common when the tray icon is already open
|
|
316
|
+
pass
|
|
317
|
+
|
|
318
|
+
# Display the menu
|
|
319
|
+
win32gui.TrackPopupMenu(
|
|
320
|
+
menu_handle,
|
|
321
|
+
win32con.TPM_LEFTALIGN | win32con.TPM_RIGHTBUTTON,
|
|
322
|
+
pos[0],
|
|
323
|
+
pos[1],
|
|
324
|
+
0,
|
|
325
|
+
self.hwnd,
|
|
326
|
+
None,
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
# Required by Windows
|
|
330
|
+
win32gui.PostMessage(self.hwnd, win32con.WM_NULL, 0, 0)
|
|
331
|
+
|
|
332
|
+
def create_menu(self):
|
|
333
|
+
"""
|
|
334
|
+
Create the context menu based on current state. Override in subclass.
|
|
335
|
+
"""
|
|
336
|
+
return Menu(MenuItem("Exit", self.exit_app))
|
|
337
|
+
|
|
338
|
+
def exit_app(self, _, __):
|
|
339
|
+
"""Exit the application."""
|
|
340
|
+
win32gui.DestroyWindow(self.hwnd)
|
|
341
|
+
|
|
342
|
+
def update_menu(self):
|
|
343
|
+
"""
|
|
344
|
+
Update the menu (will be shown on next right-click).
|
|
345
|
+
"""
|
|
346
|
+
if self.hwnd:
|
|
347
|
+
win32gui.InvalidateRect(self.hwnd, None, True)
|
|
348
|
+
|
|
349
|
+
def message_loop(self):
|
|
350
|
+
"""
|
|
351
|
+
Run the Windows message loop.
|
|
352
|
+
"""
|
|
353
|
+
win32gui.PumpMessages()
|
|
354
|
+
|
|
355
|
+
def run(self):
|
|
356
|
+
"""
|
|
357
|
+
Run the tray application.
|
|
358
|
+
"""
|
|
359
|
+
# Register window class and create window
|
|
360
|
+
self.register_window_class()
|
|
361
|
+
self.create_window()
|
|
362
|
+
|
|
363
|
+
# Set up Windows console control handler for CTRL+C
|
|
364
|
+
self.console_handler = self.setup_console_control_handler(self.logger)
|
|
365
|
+
|
|
366
|
+
# Add tray icon
|
|
367
|
+
self.add_tray_icon()
|
|
368
|
+
|
|
369
|
+
# Notify subclasses that the tray is ready (hwnd and icon created)
|
|
370
|
+
# Allows showing initial notifications after initialization
|
|
371
|
+
try:
|
|
372
|
+
if hasattr(self, "on_ready") and callable(getattr(self, "on_ready")):
|
|
373
|
+
self.on_ready()
|
|
374
|
+
except Exception:
|
|
375
|
+
pass
|
|
376
|
+
|
|
377
|
+
# Run the message loop in the main thread
|
|
378
|
+
self.message_loop()
|
|
379
|
+
|
|
380
|
+
def setup_console_control_handler(self, logger=None):
|
|
381
|
+
"""
|
|
382
|
+
Set up Windows console control handler for CTRL+C events.
|
|
383
|
+
This handles graceful shutdown when the console window is closed or CTRL+C is pressed.
|
|
384
|
+
"""
|
|
385
|
+
|
|
386
|
+
def console_ctrl_handler(ctrl_type):
|
|
387
|
+
if ctrl_type in (0, 2): # CTRL_C_EVENT or CTRL_CLOSE_EVENT
|
|
388
|
+
if logger:
|
|
389
|
+
logger.info(
|
|
390
|
+
"Received console control event, shutting down gracefully"
|
|
391
|
+
)
|
|
392
|
+
# Post a quit message to the main thread instead of calling exit_app directly
|
|
393
|
+
# This avoids thread access issues
|
|
394
|
+
if self.hwnd:
|
|
395
|
+
win32gui.PostMessage(self.hwnd, win32con.WM_CLOSE, 0, 0)
|
|
396
|
+
return True
|
|
397
|
+
return False
|
|
398
|
+
|
|
399
|
+
# Define the handler function type
|
|
400
|
+
HANDLER_ROUTINE = ctypes.WINFUNCTYPE(
|
|
401
|
+
ctypes.wintypes.BOOL, ctypes.wintypes.DWORD
|
|
402
|
+
)
|
|
403
|
+
handler = HANDLER_ROUTINE(console_ctrl_handler)
|
|
404
|
+
|
|
405
|
+
# Set the console control handler
|
|
406
|
+
ctypes.windll.kernel32.SetConsoleCtrlHandler(handler, True)
|
|
407
|
+
|
|
408
|
+
return handler # Return handler to keep it in scope
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import json
|
|
3
|
+
import platform
|
|
4
|
+
from fastapi.responses import HTMLResponse
|
|
5
|
+
from lemonade_server.model_manager import ModelManager
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_webapp_html(port=8000):
|
|
9
|
+
"""
|
|
10
|
+
Show Lemonade Web App for LLM chat and model management.
|
|
11
|
+
"""
|
|
12
|
+
# Load server models from JSON
|
|
13
|
+
server_models = ModelManager().supported_models
|
|
14
|
+
|
|
15
|
+
# Use shared filter function from model_manager.py
|
|
16
|
+
filtered_models = ModelManager().filter_models_by_backend(server_models)
|
|
17
|
+
|
|
18
|
+
# Pass filtered server_models and platform info to JS
|
|
19
|
+
server_models_js = (
|
|
20
|
+
f"<script>window.SERVER_MODELS = {json.dumps(filtered_models)};</script>"
|
|
21
|
+
)
|
|
22
|
+
platform_js = f"<script>window.PLATFORM = '{platform.system()}';</script>"
|
|
23
|
+
|
|
24
|
+
# Load HTML template
|
|
25
|
+
template_path = Path(__file__).parent / "static" / "webapp.html"
|
|
26
|
+
with open(template_path, "r", encoding="utf-8") as f:
|
|
27
|
+
html_template = f.read()
|
|
28
|
+
|
|
29
|
+
# Replace template variables
|
|
30
|
+
html_content = html_template.replace("{{SERVER_PORT}}", str(port))
|
|
31
|
+
html_content = html_content.replace("{{SERVER_MODELS_JS}}", server_models_js)
|
|
32
|
+
html_content = html_content.replace("{{PLATFORM_JS}}", platform_js)
|
|
33
|
+
|
|
34
|
+
return HTMLResponse(content=html_content)
|