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.
Files changed (84) hide show
  1. lemonade/__init__.py +5 -0
  2. lemonade/api.py +180 -0
  3. lemonade/cache.py +92 -0
  4. lemonade/cli.py +173 -0
  5. lemonade/common/__init__.py +0 -0
  6. lemonade/common/build.py +176 -0
  7. lemonade/common/cli_helpers.py +139 -0
  8. lemonade/common/exceptions.py +98 -0
  9. lemonade/common/filesystem.py +368 -0
  10. lemonade/common/inference_engines.py +408 -0
  11. lemonade/common/network.py +93 -0
  12. lemonade/common/printing.py +110 -0
  13. lemonade/common/status.py +471 -0
  14. lemonade/common/system_info.py +1411 -0
  15. lemonade/common/test_helpers.py +28 -0
  16. lemonade/profilers/__init__.py +1 -0
  17. lemonade/profilers/agt_power.py +437 -0
  18. lemonade/profilers/hwinfo_power.py +429 -0
  19. lemonade/profilers/memory_tracker.py +259 -0
  20. lemonade/profilers/profiler.py +58 -0
  21. lemonade/sequence.py +363 -0
  22. lemonade/state.py +159 -0
  23. lemonade/tools/__init__.py +1 -0
  24. lemonade/tools/accuracy.py +432 -0
  25. lemonade/tools/adapter.py +114 -0
  26. lemonade/tools/bench.py +302 -0
  27. lemonade/tools/flm/__init__.py +1 -0
  28. lemonade/tools/flm/utils.py +305 -0
  29. lemonade/tools/huggingface/bench.py +187 -0
  30. lemonade/tools/huggingface/load.py +235 -0
  31. lemonade/tools/huggingface/utils.py +359 -0
  32. lemonade/tools/humaneval.py +264 -0
  33. lemonade/tools/llamacpp/bench.py +255 -0
  34. lemonade/tools/llamacpp/load.py +222 -0
  35. lemonade/tools/llamacpp/utils.py +1260 -0
  36. lemonade/tools/management_tools.py +319 -0
  37. lemonade/tools/mmlu.py +319 -0
  38. lemonade/tools/oga/__init__.py +0 -0
  39. lemonade/tools/oga/bench.py +120 -0
  40. lemonade/tools/oga/load.py +804 -0
  41. lemonade/tools/oga/migration.py +403 -0
  42. lemonade/tools/oga/utils.py +462 -0
  43. lemonade/tools/perplexity.py +147 -0
  44. lemonade/tools/prompt.py +263 -0
  45. lemonade/tools/report/__init__.py +0 -0
  46. lemonade/tools/report/llm_report.py +203 -0
  47. lemonade/tools/report/table.py +899 -0
  48. lemonade/tools/server/__init__.py +0 -0
  49. lemonade/tools/server/flm.py +133 -0
  50. lemonade/tools/server/llamacpp.py +320 -0
  51. lemonade/tools/server/serve.py +2123 -0
  52. lemonade/tools/server/static/favicon.ico +0 -0
  53. lemonade/tools/server/static/index.html +279 -0
  54. lemonade/tools/server/static/js/chat.js +1059 -0
  55. lemonade/tools/server/static/js/model-settings.js +183 -0
  56. lemonade/tools/server/static/js/models.js +1395 -0
  57. lemonade/tools/server/static/js/shared.js +556 -0
  58. lemonade/tools/server/static/logs.html +191 -0
  59. lemonade/tools/server/static/styles.css +2654 -0
  60. lemonade/tools/server/static/webapp.html +321 -0
  61. lemonade/tools/server/tool_calls.py +153 -0
  62. lemonade/tools/server/tray.py +664 -0
  63. lemonade/tools/server/utils/macos_tray.py +226 -0
  64. lemonade/tools/server/utils/port.py +77 -0
  65. lemonade/tools/server/utils/thread.py +85 -0
  66. lemonade/tools/server/utils/windows_tray.py +408 -0
  67. lemonade/tools/server/webapp.py +34 -0
  68. lemonade/tools/server/wrapped_server.py +559 -0
  69. lemonade/tools/tool.py +374 -0
  70. lemonade/version.py +1 -0
  71. lemonade_install/__init__.py +1 -0
  72. lemonade_install/install.py +239 -0
  73. lemonade_sdk-9.1.1.dist-info/METADATA +276 -0
  74. lemonade_sdk-9.1.1.dist-info/RECORD +84 -0
  75. lemonade_sdk-9.1.1.dist-info/WHEEL +5 -0
  76. lemonade_sdk-9.1.1.dist-info/entry_points.txt +5 -0
  77. lemonade_sdk-9.1.1.dist-info/licenses/LICENSE +201 -0
  78. lemonade_sdk-9.1.1.dist-info/licenses/NOTICE.md +47 -0
  79. lemonade_sdk-9.1.1.dist-info/top_level.txt +3 -0
  80. lemonade_server/cli.py +805 -0
  81. lemonade_server/model_manager.py +758 -0
  82. lemonade_server/pydantic_models.py +159 -0
  83. lemonade_server/server_models.json +643 -0
  84. 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)