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
lemonade_install/install.py
CHANGED
|
@@ -3,42 +3,16 @@
|
|
|
3
3
|
# import any lemonade modules in order to avoid any installation
|
|
4
4
|
# collisions on imported modules.
|
|
5
5
|
#
|
|
6
|
-
# This tool can install
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# RYZEN_AI\hybrid
|
|
10
|
-
# libs
|
|
11
|
-
# wheels
|
|
12
|
-
#
|
|
13
|
-
# The Ryzen AI npu artifacts directory hierarchy is:
|
|
14
|
-
#
|
|
15
|
-
# RYZEN_AI\npu
|
|
16
|
-
# libs
|
|
17
|
-
# wheels
|
|
18
|
-
#
|
|
19
|
-
# In the above, RYZEN_AI is the path to the folder ryzen_ai that is contained in the
|
|
20
|
-
# same folder as the executable python.exe.
|
|
21
|
-
# The folder RYZEN_AI also contains a file `version_info.json` that contains information
|
|
22
|
-
# about the installed Ryzen AI artifacts.
|
|
23
|
-
#
|
|
24
|
-
# In any python environment, only one set of artifacts can be installed at a time.
|
|
25
|
-
# Python environments created by Lemonade v6.1.x or earlier will need to be recreated.
|
|
26
|
-
#
|
|
6
|
+
# This tool can install llama.cpp and FLM (FastFlowLM) for local LLM inference.
|
|
7
|
+
# For RyzenAI support, use PyPI installation:
|
|
8
|
+
# pip install lemonade-sdk[oga-ryzenai] --extra-index-url https://pypi.amd.com/simple
|
|
27
9
|
|
|
28
10
|
import argparse
|
|
29
|
-
import glob
|
|
30
|
-
import json
|
|
31
11
|
import os
|
|
32
|
-
from pathlib import Path
|
|
33
12
|
import re
|
|
34
|
-
import shutil
|
|
35
13
|
import subprocess
|
|
36
14
|
import sys
|
|
37
15
|
from typing import Optional
|
|
38
|
-
import zipfile
|
|
39
|
-
|
|
40
|
-
DEFAULT_RYZEN_AI_VERSION = "1.4.0"
|
|
41
|
-
version_info_filename = "version_info.json"
|
|
42
16
|
|
|
43
17
|
# NPU Driver configuration
|
|
44
18
|
NPU_DRIVER_DOWNLOAD_URL = (
|
|
@@ -47,108 +21,9 @@ NPU_DRIVER_DOWNLOAD_URL = (
|
|
|
47
21
|
)
|
|
48
22
|
REQUIRED_NPU_DRIVER_VERSION = "32.0.203.280"
|
|
49
23
|
|
|
50
|
-
lemonade_install_dir = Path(__file__).parent.parent.parent
|
|
51
|
-
DEFAULT_QUARK_VERSION = "quark-0.6.0"
|
|
52
|
-
DEFAULT_QUARK_DIR = os.path.join(
|
|
53
|
-
lemonade_install_dir, "install", "quark", DEFAULT_QUARK_VERSION
|
|
54
|
-
)
|
|
55
|
-
|
|
56
24
|
# List of supported Ryzen AI processor series (can be extended in the future)
|
|
57
25
|
SUPPORTED_RYZEN_AI_SERIES = ["300"]
|
|
58
26
|
|
|
59
|
-
npu_install_data = {
|
|
60
|
-
"1.4.0": {
|
|
61
|
-
"artifacts_zipfile": (
|
|
62
|
-
"https://www.xilinx.com/bin/public/openDownload?"
|
|
63
|
-
"filename=npu-llm-artifacts_1.4.0_032925.zip"
|
|
64
|
-
),
|
|
65
|
-
"license_file": (
|
|
66
|
-
"https://account.amd.com/content/dam/account/en/licenses/download/"
|
|
67
|
-
"amd-end-user-license-agreement.pdf"
|
|
68
|
-
),
|
|
69
|
-
"license_tag": "",
|
|
70
|
-
},
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
hybrid_install_data = {
|
|
74
|
-
"1.4.0": {
|
|
75
|
-
"artifacts_zipfile": (
|
|
76
|
-
"https://www.xilinx.com/bin/public/openDownload?"
|
|
77
|
-
"filename=hybrid-llm-artifacts_1.4.0_032925.zip"
|
|
78
|
-
),
|
|
79
|
-
"license_file": (
|
|
80
|
-
"https://account.amd.com/content/dam/account/en/licenses/download/"
|
|
81
|
-
"amd-end-user-license-agreement.pdf"
|
|
82
|
-
),
|
|
83
|
-
"license_tag": "",
|
|
84
|
-
},
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
model_prep_install_data = {
|
|
88
|
-
"1.4.0": {
|
|
89
|
-
"model_prep_artifacts_zipfile": (
|
|
90
|
-
"https://www.xilinx.com/bin/public/openDownload?"
|
|
91
|
-
"filename=model-prep-artifacts-1.4.0-0331.zip"
|
|
92
|
-
)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def get_ryzen_ai_path(check_exists=True):
|
|
98
|
-
python_executable_path = sys.executable
|
|
99
|
-
python_executable_dir = os.path.dirname(python_executable_path)
|
|
100
|
-
ryzen_ai_folder = os.path.join(python_executable_dir, "ryzen_ai")
|
|
101
|
-
if check_exists:
|
|
102
|
-
if not os.path.isdir(ryzen_ai_folder):
|
|
103
|
-
raise RuntimeError(
|
|
104
|
-
"Please use `lemonade-install --ryzenai` to install Ryzen AI into the current "
|
|
105
|
-
"python environment. For more info, see "
|
|
106
|
-
"https://ryzenai.docs.amd.com/en/latest/llm/high_level_python.html."
|
|
107
|
-
)
|
|
108
|
-
return ryzen_ai_folder
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def get_ryzen_ai_version_info():
|
|
112
|
-
ryzen_ai_folder = get_ryzen_ai_path()
|
|
113
|
-
version_info_path = os.path.join(ryzen_ai_folder, version_info_filename)
|
|
114
|
-
if not os.path.isfile(version_info_path):
|
|
115
|
-
raise RuntimeError(
|
|
116
|
-
f"The file {version_info_filename} is missing from the Ryzen AI "
|
|
117
|
-
f"folder {ryzen_ai_folder}. Please run `lemonade-install` to fix. For more info, see "
|
|
118
|
-
"https://ryzenai.docs.amd.com/en/latest/llm/high_level_python.html."
|
|
119
|
-
)
|
|
120
|
-
with open(version_info_path, encoding="utf-8") as file:
|
|
121
|
-
version_info = json.load(file)
|
|
122
|
-
return version_info
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def get_oga_npu_dir():
|
|
126
|
-
version_info = get_ryzen_ai_version_info()
|
|
127
|
-
version = version_info["version"]
|
|
128
|
-
ryzen_ai_folder = get_ryzen_ai_path()
|
|
129
|
-
npu_dir = os.path.join(ryzen_ai_folder, "npu")
|
|
130
|
-
if not os.path.isdir(npu_dir):
|
|
131
|
-
raise RuntimeError(
|
|
132
|
-
f"The npu artifacts are missing from the Ryzen AI folder {ryzen_ai_folder}. "
|
|
133
|
-
" Please run `lemonade-install --ryzenai npu` again to fix. For more info, see"
|
|
134
|
-
"https://ryzenai.docs.amd.com/en/latest/llm/high_level_python.html."
|
|
135
|
-
)
|
|
136
|
-
return npu_dir, version
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def get_oga_hybrid_dir():
|
|
140
|
-
version_info = get_ryzen_ai_version_info()
|
|
141
|
-
version = version_info["version"]
|
|
142
|
-
ryzen_ai_folder = get_ryzen_ai_path()
|
|
143
|
-
hybrid_dir = os.path.join(ryzen_ai_folder, "hybrid")
|
|
144
|
-
if not os.path.isdir(hybrid_dir):
|
|
145
|
-
raise RuntimeError(
|
|
146
|
-
f"The hybrid artifacts are missing from the Ryzen AI folder {ryzen_ai_folder}. "
|
|
147
|
-
" Please run `lemonade-install --ryzenai hybrid` again to fix. For more info, see "
|
|
148
|
-
"https://ryzenai.docs.amd.com/en/latest/llm/high_level_python.html."
|
|
149
|
-
)
|
|
150
|
-
return hybrid_dir, version
|
|
151
|
-
|
|
152
27
|
|
|
153
28
|
def _get_ryzenai_version_info(device=None):
|
|
154
29
|
"""
|
|
@@ -158,96 +33,49 @@ def _get_ryzenai_version_info(device=None):
|
|
|
158
33
|
try:
|
|
159
34
|
# Lazy import to avoid errors when OGA is not installed
|
|
160
35
|
from packaging.version import Version
|
|
36
|
+
|
|
37
|
+
# For embedded Python on Windows, add DLL directory before importing onnxruntime_genai
|
|
38
|
+
# This is required to find DirectML.dll and other dependencies
|
|
39
|
+
if sys.platform.startswith("win"):
|
|
40
|
+
import site
|
|
41
|
+
|
|
42
|
+
site_packages = site.getsitepackages()
|
|
43
|
+
for sp in site_packages:
|
|
44
|
+
oga_dir = os.path.join(sp, "onnxruntime_genai")
|
|
45
|
+
if os.path.exists(oga_dir):
|
|
46
|
+
# Add the onnxruntime_genai directory to DLL search path
|
|
47
|
+
# This ensures DirectML.dll and onnxruntime.dll can be found
|
|
48
|
+
os.add_dll_directory(oga_dir)
|
|
49
|
+
break
|
|
50
|
+
|
|
161
51
|
import onnxruntime_genai as og
|
|
162
52
|
|
|
163
53
|
if Version(og.__version__) >= Version("0.7.0"):
|
|
164
54
|
oga_path = os.path.dirname(og.__file__)
|
|
165
|
-
if og.__version__ in ("0.
|
|
166
|
-
return "1.
|
|
55
|
+
if og.__version__ in ("0.9.2", "0.9.2.1"):
|
|
56
|
+
return "1.6.0", oga_path
|
|
167
57
|
else:
|
|
168
|
-
|
|
58
|
+
raise ValueError(
|
|
59
|
+
f"Unsupported onnxruntime-genai-directml-ryzenai version: {og.__version__}\n"
|
|
60
|
+
"Only RyzenAI 1.6.0 is currently supported. Please upgrade:\n"
|
|
61
|
+
"pip install --upgrade lemonade-sdk[oga-ryzenai] --extra-index-url https://pypi.amd.com/simple"
|
|
62
|
+
)
|
|
169
63
|
else:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
64
|
+
# Legacy lemonade-install approach is no longer supported
|
|
65
|
+
raise ValueError(
|
|
66
|
+
"Legacy RyzenAI installation detected (version < 0.7.0).\n"
|
|
67
|
+
"RyzenAI 1.4.0 and 1.5.0 are no longer supported. Please upgrade to 1.6.0:\n"
|
|
68
|
+
"pip install lemonade-sdk[oga-ryzenai] --extra-index-url https://pypi.amd.com/simple"
|
|
69
|
+
)
|
|
175
70
|
except ImportError as e:
|
|
176
71
|
raise ImportError(
|
|
177
72
|
f"{e}\n Please install lemonade-sdk with "
|
|
178
73
|
"one of the oga extras, for example:\n"
|
|
179
74
|
"pip install lemonade-sdk[dev,oga-cpu]\n"
|
|
180
|
-
"See https://
|
|
75
|
+
"See https://lemonade-server.ai/install_options.html for details"
|
|
181
76
|
) from e
|
|
182
77
|
|
|
183
78
|
|
|
184
|
-
def download_lfs_file(token, file, output_filename):
|
|
185
|
-
"""Downloads a file from LFS"""
|
|
186
|
-
import requests
|
|
187
|
-
|
|
188
|
-
# Set up the headers for the request
|
|
189
|
-
headers = {
|
|
190
|
-
"Authorization": f"token {token}",
|
|
191
|
-
"Accept": "application/vnd.github.v3+json",
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
response = requests.get(
|
|
195
|
-
f"https://api.github.com/repos/aigdat/ryzenai-sw-ea/contents/{file}",
|
|
196
|
-
headers=headers,
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
# Check if the request was successful
|
|
200
|
-
if response.status_code == 200:
|
|
201
|
-
# Parse the JSON response to get the download URL
|
|
202
|
-
content = response.json()
|
|
203
|
-
download_url = content.get("download_url")
|
|
204
|
-
|
|
205
|
-
if download_url:
|
|
206
|
-
# Download the file from the download URL
|
|
207
|
-
file_response = requests.get(download_url)
|
|
208
|
-
|
|
209
|
-
# Write the content to a file
|
|
210
|
-
with open(output_filename, "wb") as file:
|
|
211
|
-
file.write(file_response.content)
|
|
212
|
-
else:
|
|
213
|
-
print("Download URL not found in the response.")
|
|
214
|
-
else:
|
|
215
|
-
raise ValueError(
|
|
216
|
-
"Failed to fetch the content from GitHub API. "
|
|
217
|
-
f"Status code: {response.status_code}, Response: {response.json()}"
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
if not os.path.isfile(output_filename):
|
|
221
|
-
raise ValueError(f"Error: {output_filename} does not exist.")
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
def download_file(url: str, output_filename: str, description: str = None):
|
|
225
|
-
import requests
|
|
226
|
-
|
|
227
|
-
try:
|
|
228
|
-
response = requests.get(url)
|
|
229
|
-
if response.status_code != 200:
|
|
230
|
-
raise Exception(
|
|
231
|
-
f"Failed to fetch the content from GitHub API. \
|
|
232
|
-
Status code: {response.status_code}, Response: {response.json()}"
|
|
233
|
-
)
|
|
234
|
-
|
|
235
|
-
with open(output_filename, "wb") as file:
|
|
236
|
-
file.write(response.content)
|
|
237
|
-
|
|
238
|
-
if not os.path.isfile(output_filename):
|
|
239
|
-
raise Exception(f"\nError: Failed to write to {output_filename}")
|
|
240
|
-
|
|
241
|
-
except Exception as e:
|
|
242
|
-
raise Exception(f"\nError downloading {description or 'file'}: {str(e)}")
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
def unzip_file(zip_path, extract_to):
|
|
246
|
-
"""Unzips the specified zip file to the given directory."""
|
|
247
|
-
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
|
248
|
-
zip_ref.extractall(extract_to)
|
|
249
|
-
|
|
250
|
-
|
|
251
79
|
def check_ryzen_ai_processor():
|
|
252
80
|
"""
|
|
253
81
|
Checks if the current system has a supported Ryzen AI processor.
|
|
@@ -323,75 +151,6 @@ def check_ryzen_ai_processor():
|
|
|
323
151
|
print("[WARNING]: Hybrid features may not work if processor is not supported.")
|
|
324
152
|
|
|
325
153
|
|
|
326
|
-
def download_and_extract_package(
|
|
327
|
-
url: str,
|
|
328
|
-
version: str,
|
|
329
|
-
install_dir: str,
|
|
330
|
-
package_name: str,
|
|
331
|
-
) -> str:
|
|
332
|
-
"""
|
|
333
|
-
Downloads, Extracts and Renames the folder
|
|
334
|
-
|
|
335
|
-
Args:
|
|
336
|
-
url: Download URL for the package
|
|
337
|
-
version: Version string
|
|
338
|
-
install_dir: Directory to install to
|
|
339
|
-
package_name: Name of the package
|
|
340
|
-
|
|
341
|
-
Returns:
|
|
342
|
-
str: Path where package was extracted (renamed to package-version)
|
|
343
|
-
"""
|
|
344
|
-
import requests
|
|
345
|
-
|
|
346
|
-
zip_filename = f"{package_name}-{version}.zip"
|
|
347
|
-
zip_path = os.path.join(install_dir, zip_filename)
|
|
348
|
-
target_folder = os.path.join(install_dir, f"{package_name}-{version}")
|
|
349
|
-
|
|
350
|
-
print(f"\nDownloading {package_name} from {url}")
|
|
351
|
-
response = requests.get(url)
|
|
352
|
-
if response.status_code == 200:
|
|
353
|
-
with open(zip_path, "wb") as f:
|
|
354
|
-
f.write(response.content)
|
|
355
|
-
else:
|
|
356
|
-
raise Exception(
|
|
357
|
-
f"Failed to download {package_name}. Status code: {response.status_code}"
|
|
358
|
-
)
|
|
359
|
-
|
|
360
|
-
print("\n[INFO]: Extracting zip file ...")
|
|
361
|
-
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
|
362
|
-
zip_ref.extractall(install_dir)
|
|
363
|
-
print("\n[INFO]: Extraction completed.")
|
|
364
|
-
|
|
365
|
-
os.remove(zip_path)
|
|
366
|
-
|
|
367
|
-
extracted_folder = None
|
|
368
|
-
for folder in os.listdir(install_dir):
|
|
369
|
-
folder_path = os.path.join(install_dir, folder)
|
|
370
|
-
if os.path.isdir(folder_path) and folder.startswith(f"{package_name}-"):
|
|
371
|
-
extracted_folder = folder_path
|
|
372
|
-
break
|
|
373
|
-
|
|
374
|
-
if extracted_folder is None:
|
|
375
|
-
raise ValueError(
|
|
376
|
-
f"Error: Extracted folder for {package_name} version {version} not found."
|
|
377
|
-
)
|
|
378
|
-
|
|
379
|
-
# Rename extracted folder to package-version
|
|
380
|
-
if extracted_folder != target_folder:
|
|
381
|
-
if os.path.exists(target_folder):
|
|
382
|
-
shutil.rmtree(target_folder) # Remove if already exists
|
|
383
|
-
os.rename(extracted_folder, target_folder)
|
|
384
|
-
print(f"\n[INFO]: Renamed folder to {target_folder}")
|
|
385
|
-
|
|
386
|
-
return target_folder
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
class LicenseRejected(Exception):
|
|
390
|
-
"""
|
|
391
|
-
Raise an exception if the user rejects the license prompt.
|
|
392
|
-
"""
|
|
393
|
-
|
|
394
|
-
|
|
395
154
|
class UnsupportedPlatformError(Exception):
|
|
396
155
|
"""
|
|
397
156
|
Raise an exception if the hardware is not supported.
|
|
@@ -410,378 +169,62 @@ class Install:
|
|
|
410
169
|
)
|
|
411
170
|
|
|
412
171
|
parser.add_argument(
|
|
413
|
-
"--
|
|
414
|
-
help="Install
|
|
415
|
-
"
|
|
416
|
-
f"{DEFAULT_RYZEN_AI_VERSION} version.",
|
|
417
|
-
choices=[
|
|
418
|
-
"npu",
|
|
419
|
-
"hybrid",
|
|
420
|
-
"unified",
|
|
421
|
-
"npu-1.4.0",
|
|
422
|
-
"hybrid-1.4.0",
|
|
423
|
-
"unified-1.4.0",
|
|
424
|
-
],
|
|
425
|
-
)
|
|
426
|
-
|
|
427
|
-
parser.add_argument(
|
|
428
|
-
"--build-model",
|
|
429
|
-
action="store_true",
|
|
430
|
-
help="If specified, installs additional model prep artifacts for Ryzen AI.",
|
|
172
|
+
"--llamacpp",
|
|
173
|
+
help="Install llama.cpp binaries with specified backend",
|
|
174
|
+
choices=["rocm", "vulkan"],
|
|
431
175
|
)
|
|
432
176
|
|
|
433
177
|
parser.add_argument(
|
|
434
|
-
"
|
|
435
|
-
"--yes",
|
|
178
|
+
"--flm",
|
|
436
179
|
action="store_true",
|
|
437
|
-
help="
|
|
438
|
-
"Make sure to review all legal agreements before selecting this option.",
|
|
439
|
-
)
|
|
440
|
-
|
|
441
|
-
parser.add_argument(
|
|
442
|
-
"--token",
|
|
443
|
-
help="Some software requires an authentication token to download. "
|
|
444
|
-
"If this argument is not provided, the token can come from an environment "
|
|
445
|
-
"variable (e.g., Ryzen AI uses environment variable OGA_TOKEN).",
|
|
446
|
-
)
|
|
447
|
-
|
|
448
|
-
parser.add_argument(
|
|
449
|
-
"--quark",
|
|
450
|
-
help="Install Quark Quantization tool for LLMs",
|
|
451
|
-
choices=["0.6.0"],
|
|
452
|
-
)
|
|
453
|
-
|
|
454
|
-
parser.add_argument(
|
|
455
|
-
"--llamacpp",
|
|
456
|
-
help="Install llama.cpp binaries with specified backend",
|
|
457
|
-
choices=["rocm", "vulkan"],
|
|
180
|
+
help="Install FLM (FastFlowLM) for running local language models",
|
|
458
181
|
)
|
|
459
182
|
|
|
460
183
|
return parser
|
|
461
184
|
|
|
462
185
|
@staticmethod
|
|
463
|
-
def
|
|
464
|
-
if yes:
|
|
465
|
-
print(
|
|
466
|
-
f"\nYou have accepted the AMD {license_tag}Software End User License "
|
|
467
|
-
f"Agreement for Ryzen AI {version} by providing the `--yes` option. "
|
|
468
|
-
"The license file is available for your review at "
|
|
469
|
-
f"{license_file}\n"
|
|
470
|
-
)
|
|
471
|
-
else:
|
|
472
|
-
print(
|
|
473
|
-
f"\nYou must accept the AMD {license_tag}Software End User License "
|
|
474
|
-
"Agreement in order to install this software. To continue, type the word "
|
|
475
|
-
"yes to assert that you agree and are authorized to agree "
|
|
476
|
-
"on behalf of your organization, to the terms and "
|
|
477
|
-
f"conditions, in the {license_tag}Software End User License Agreement, "
|
|
478
|
-
"which terms and conditions may be reviewed, downloaded and "
|
|
479
|
-
"printed from this link: "
|
|
480
|
-
f"{license_file}\n"
|
|
481
|
-
)
|
|
482
|
-
|
|
483
|
-
response = input("Would you like to accept the license (yes/No)? ")
|
|
484
|
-
if response.lower() == "yes" or response.lower() == "y":
|
|
485
|
-
pass
|
|
486
|
-
else:
|
|
487
|
-
raise LicenseRejected("Exiting because the license was not accepted.")
|
|
488
|
-
|
|
489
|
-
@staticmethod
|
|
490
|
-
def _install_artifacts(version, install_dir, token, file, wheels_full_path):
|
|
491
|
-
archive_file_name = "artifacts.zip"
|
|
492
|
-
archive_file_path = os.path.join(install_dir, archive_file_name)
|
|
493
|
-
|
|
494
|
-
if token:
|
|
495
|
-
token_to_use = token
|
|
496
|
-
else:
|
|
497
|
-
token_to_use = os.environ.get("OGA_TOKEN")
|
|
498
|
-
|
|
499
|
-
# Retrieve the installation artifacts
|
|
500
|
-
if os.path.exists(install_dir):
|
|
501
|
-
# Remove any artifacts from a previous installation attempt
|
|
502
|
-
shutil.rmtree(install_dir)
|
|
503
|
-
os.makedirs(install_dir)
|
|
504
|
-
if any(proto in file for proto in ["https:", "http:"]):
|
|
505
|
-
print(f"\nDownloading {file}\n")
|
|
506
|
-
download_file(file, archive_file_path)
|
|
507
|
-
elif "file:" in file:
|
|
508
|
-
local_file = file.replace("file://", "C:/")
|
|
509
|
-
print(f"\nCopying {local_file}\n")
|
|
510
|
-
shutil.copy(local_file, archive_file_path)
|
|
511
|
-
else:
|
|
512
|
-
print(f"\nDownloading {file} from GitHub LFS to {install_dir}\n")
|
|
513
|
-
download_lfs_file(token_to_use, file, archive_file_path)
|
|
514
|
-
|
|
515
|
-
# Unzip the file
|
|
516
|
-
print(f"\nUnzipping archive {archive_file_path}\n")
|
|
517
|
-
unzip_file(archive_file_path, install_dir)
|
|
518
|
-
print(f"\nDLLs installed\n")
|
|
519
|
-
|
|
520
|
-
# Install all whl files in the specified wheels folder
|
|
521
|
-
if wheels_full_path is not None:
|
|
522
|
-
print(f"\nInstalling wheels from {wheels_full_path}\n")
|
|
523
|
-
# Install all the wheel files together, allowing pip to work out the dependencies
|
|
524
|
-
wheel_files = glob.glob(os.path.join(wheels_full_path, "*.whl"))
|
|
525
|
-
install_cmd = [sys.executable, "-m", "pip", "install"] + wheel_files
|
|
526
|
-
subprocess.run(
|
|
527
|
-
install_cmd,
|
|
528
|
-
check=True,
|
|
529
|
-
shell=True,
|
|
530
|
-
)
|
|
531
|
-
|
|
532
|
-
# Delete the zip file
|
|
533
|
-
print(f"\nCleaning up, removing {archive_file_path}\n")
|
|
534
|
-
os.remove(archive_file_path)
|
|
535
|
-
|
|
536
|
-
@staticmethod
|
|
537
|
-
def _install_ryzenai_model_artifacts(ryzen_ai_folder, version):
|
|
186
|
+
def _install_llamacpp(backend):
|
|
538
187
|
"""
|
|
539
|
-
|
|
188
|
+
Install llama.cpp binaries with the specified backend.
|
|
540
189
|
|
|
541
190
|
Args:
|
|
542
|
-
|
|
543
|
-
version (str): Version of the Ryzen AI artifacts.
|
|
191
|
+
backend: The backend to use ('rocm' or 'vulkan')
|
|
544
192
|
"""
|
|
545
193
|
|
|
546
|
-
|
|
547
|
-
if version not in model_prep_install_data:
|
|
548
|
-
raise ValueError(
|
|
549
|
-
"Model prep artifacts are only available "
|
|
550
|
-
"for version 1.4.0 and above, "
|
|
551
|
-
"and only for 'hybrid' and 'npu-only' targets."
|
|
552
|
-
)
|
|
553
|
-
|
|
554
|
-
# Get the model prep artifacts zipfile URL
|
|
555
|
-
file = model_prep_install_data[version].get(
|
|
556
|
-
"model_prep_artifacts_zipfile", None
|
|
557
|
-
)
|
|
558
|
-
if not file:
|
|
559
|
-
raise ValueError(
|
|
560
|
-
"Model prep artifacts zipfile URL is missing for version " f"{version}."
|
|
561
|
-
)
|
|
562
|
-
|
|
563
|
-
# Download and extract the model prep artifacts
|
|
564
|
-
print(f"\nDownloading model prep artifacts for Ryzen AI {version}.")
|
|
565
|
-
model_prep_dir = download_and_extract_package(
|
|
566
|
-
url=file,
|
|
567
|
-
version=version,
|
|
568
|
-
install_dir=ryzen_ai_folder,
|
|
569
|
-
package_name="model-prep",
|
|
570
|
-
)
|
|
571
|
-
|
|
572
|
-
# Install all .whl files from the extracted model prep artifacts
|
|
573
|
-
wheels_full_path = os.path.join(model_prep_dir)
|
|
574
|
-
print(f"\nInstalling model prep wheels from {wheels_full_path}\n")
|
|
575
|
-
wheel_files = glob.glob(os.path.join(wheels_full_path, "*.whl"))
|
|
576
|
-
if not wheel_files:
|
|
577
|
-
raise ValueError(
|
|
578
|
-
f"No .whl files found in the model prep artifacts directory: {wheels_full_path}"
|
|
579
|
-
)
|
|
580
|
-
|
|
581
|
-
install_cmd = [sys.executable, "-m", "pip", "install"] + wheel_files
|
|
582
|
-
subprocess.run(install_cmd, check=True, shell=True)
|
|
583
|
-
|
|
584
|
-
print(f"\nModel prep artifacts installed successfully from {model_prep_dir}.")
|
|
585
|
-
return file
|
|
194
|
+
from lemonade.tools.llamacpp.utils import install_llamacpp
|
|
586
195
|
|
|
587
|
-
|
|
588
|
-
def _install_ryzenai_npu(ryzen_ai_folder, version, yes, token, skip_wheels=False):
|
|
589
|
-
# Check version is valid
|
|
590
|
-
if version not in npu_install_data:
|
|
591
|
-
raise ValueError(
|
|
592
|
-
"Invalid Ryzen AI version for NPU. Valid options are "
|
|
593
|
-
f"{list(npu_install_data.keys())}."
|
|
594
|
-
)
|
|
595
|
-
file = npu_install_data[version].get("artifacts_zipfile", None)
|
|
596
|
-
license_file = npu_install_data[version].get("license_file", None)
|
|
597
|
-
license_tag = npu_install_data[version].get("license_tag", None)
|
|
598
|
-
install_dir = os.path.join(ryzen_ai_folder, "npu")
|
|
599
|
-
wheels_full_path = os.path.join(install_dir, "wheels")
|
|
600
|
-
|
|
601
|
-
if license_file:
|
|
602
|
-
Install._get_license_acceptance(version, license_file, license_tag, yes)
|
|
603
|
-
|
|
604
|
-
print(f"Downloading NPU artifacts for Ryzen AI {version}.")
|
|
605
|
-
if skip_wheels:
|
|
606
|
-
wheels_full_path = None
|
|
607
|
-
else:
|
|
608
|
-
print("Wheels will be added to current activated environment.")
|
|
609
|
-
Install._install_artifacts(version, install_dir, token, file, wheels_full_path)
|
|
610
|
-
return file
|
|
196
|
+
install_llamacpp(backend)
|
|
611
197
|
|
|
612
198
|
@staticmethod
|
|
613
|
-
def
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
if version not in hybrid_install_data:
|
|
618
|
-
raise ValueError(
|
|
619
|
-
"Invalid version for hybrid. Valid options are "
|
|
620
|
-
f"{list(hybrid_install_data.keys())}."
|
|
621
|
-
)
|
|
622
|
-
file = hybrid_install_data[version].get("artifacts_zipfile", None)
|
|
623
|
-
license_file = hybrid_install_data[version].get("license_file", None)
|
|
624
|
-
license_tag = hybrid_install_data[version].get("license_tag", None)
|
|
625
|
-
install_dir = os.path.join(ryzen_ai_folder, "hybrid")
|
|
626
|
-
wheels_full_path = os.path.join(install_dir, "wheels")
|
|
627
|
-
|
|
628
|
-
if license_file:
|
|
629
|
-
Install._get_license_acceptance(version, license_file, license_tag, yes)
|
|
630
|
-
|
|
631
|
-
print(f"Downloading hybrid artifacts for Ryzen AI {version}.")
|
|
632
|
-
if skip_wheels:
|
|
633
|
-
wheels_full_path = None
|
|
634
|
-
else:
|
|
635
|
-
print("Wheels will be added to current activated environment.")
|
|
636
|
-
Install._install_artifacts(version, install_dir, token, file, wheels_full_path)
|
|
637
|
-
return file
|
|
199
|
+
def _install_flm():
|
|
200
|
+
"""
|
|
201
|
+
Install FLM (FastFlowLM) for running local language models.
|
|
202
|
+
"""
|
|
638
203
|
|
|
639
|
-
@staticmethod
|
|
640
|
-
def _install_ryzenai(ryzenai, build_model, yes, token):
|
|
641
204
|
# Check if the processor is supported before proceeding
|
|
642
205
|
check_ryzen_ai_processor()
|
|
643
206
|
|
|
644
|
-
|
|
645
|
-
"\n" + "=" * 80 + "\n"
|
|
646
|
-
"WARNING: IMPORTANT: NEW RYZEN AI 1.5.0 INSTALLATION PROCESS\n"
|
|
647
|
-
+ "=" * 80
|
|
648
|
-
+ "\n"
|
|
649
|
-
"Starting with Ryzen AI 1.5.0, installation is now available through PyPI.\n"
|
|
650
|
-
"For new installations, consider using:\n\n"
|
|
651
|
-
"pip install lemonade-sdk[oga-ryzenai] --extra-index-url https://pypi.amd.com/simple\n\n"
|
|
652
|
-
"This legacy installation method (lemonade-install --ryzenai) is still\n"
|
|
653
|
-
"supported for version 1.4.0, but may be deprecated in future releases.\n"
|
|
654
|
-
+ "=" * 80
|
|
655
|
-
+ "\n"
|
|
656
|
-
)
|
|
657
|
-
print(warning_msg)
|
|
658
|
-
|
|
659
|
-
# Delete any previous Ryzen AI installation in this environment
|
|
660
|
-
ryzen_ai_folder = get_ryzen_ai_path(check_exists=False)
|
|
661
|
-
if os.path.exists(ryzen_ai_folder):
|
|
662
|
-
print("Deleting previous Ryzen AI installation in this environment.")
|
|
663
|
-
shutil.rmtree(ryzen_ai_folder)
|
|
664
|
-
|
|
665
|
-
# Determine Ryzen AI version to install
|
|
666
|
-
version = DEFAULT_RYZEN_AI_VERSION
|
|
667
|
-
if "-" in ryzenai:
|
|
668
|
-
# ryzenai is: (npu|hybrid)-(<VERSION>)
|
|
669
|
-
parts = ryzenai.split("-")
|
|
670
|
-
ryzenai = parts[0]
|
|
671
|
-
version = parts[1]
|
|
672
|
-
|
|
673
|
-
# Install artifacts needed for npu, hybrid, or unified (both) inference
|
|
674
|
-
npu_file = None
|
|
675
|
-
hybrid_file = None
|
|
676
|
-
model_prep_file = None
|
|
677
|
-
if ryzenai == "npu":
|
|
678
|
-
npu_file = Install._install_ryzenai_npu(
|
|
679
|
-
ryzen_ai_folder, version, yes, token
|
|
680
|
-
)
|
|
681
|
-
elif ryzenai == "hybrid":
|
|
682
|
-
hybrid_file = Install._install_ryzenai_hybrid(
|
|
683
|
-
ryzen_ai_folder, version, yes, token
|
|
684
|
-
)
|
|
685
|
-
elif ryzenai == "unified":
|
|
686
|
-
# Download NPU artifacts and use the NPU wheels
|
|
687
|
-
npu_file = Install._install_ryzenai_npu(
|
|
688
|
-
ryzen_ai_folder, version, yes, token
|
|
689
|
-
)
|
|
690
|
-
# Download Hybrid artifacts (and skip wheels as they are already loaded)
|
|
691
|
-
hybrid_file = Install._install_ryzenai_hybrid(
|
|
692
|
-
ryzen_ai_folder, version, yes, token, skip_wheels=True
|
|
693
|
-
)
|
|
694
|
-
else:
|
|
695
|
-
raise ValueError(
|
|
696
|
-
f"Value passed to ryzenai argument is not supported: {ryzenai}"
|
|
697
|
-
)
|
|
698
|
-
|
|
699
|
-
if build_model:
|
|
700
|
-
model_prep_file = Install._install_ryzenai_model_artifacts(
|
|
701
|
-
ryzen_ai_folder, version
|
|
702
|
-
)
|
|
703
|
-
|
|
704
|
-
# Write version_info file
|
|
705
|
-
version_info = {
|
|
706
|
-
"version": version,
|
|
707
|
-
"hybrid_artifacts": hybrid_file,
|
|
708
|
-
"npu_artifacts": npu_file,
|
|
709
|
-
"model_prep_artifacts": model_prep_file,
|
|
710
|
-
}
|
|
711
|
-
version_info_path = os.path.join(ryzen_ai_folder, version_info_filename)
|
|
712
|
-
try:
|
|
713
|
-
with open(version_info_path, "w", encoding="utf-8") as file:
|
|
714
|
-
json.dump(version_info, file, indent=4)
|
|
715
|
-
except IOError as e:
|
|
716
|
-
print(f"An error occurred while writing {version_info_path}: {e}")
|
|
717
|
-
|
|
718
|
-
@staticmethod
|
|
719
|
-
def _install_quark(quark):
|
|
720
|
-
quark_install_dir = os.path.join(lemonade_install_dir, "install", "quark")
|
|
721
|
-
os.makedirs(quark_install_dir, exist_ok=True)
|
|
722
|
-
|
|
723
|
-
# Install Quark utilities
|
|
724
|
-
quark_url = (
|
|
725
|
-
f"https://www.xilinx.com/bin/public/openDownload?filename=quark-{quark}.zip"
|
|
726
|
-
)
|
|
727
|
-
quark_path = download_and_extract_package(
|
|
728
|
-
url=quark_url,
|
|
729
|
-
version=quark,
|
|
730
|
-
install_dir=quark_install_dir,
|
|
731
|
-
package_name="quark",
|
|
732
|
-
)
|
|
733
|
-
# Install Quark wheel
|
|
734
|
-
wheel_url = (
|
|
735
|
-
"https://www.xilinx.com/bin/public/openDownload?"
|
|
736
|
-
f"filename=quark-{quark}-py3-none-any.whl"
|
|
737
|
-
)
|
|
738
|
-
wheel_path = os.path.join(quark_install_dir, f"quark-{quark}-py3-none-any.whl")
|
|
739
|
-
print(f"\nInstalling Quark wheel from {wheel_url}")
|
|
740
|
-
download_file(wheel_url, wheel_path, "wheel file")
|
|
741
|
-
|
|
742
|
-
install_cmd = f"{sys.executable} -m pip install --no-deps {wheel_path}"
|
|
743
|
-
subprocess.run(install_cmd, check=True, shell=True)
|
|
744
|
-
os.remove(wheel_path)
|
|
745
|
-
|
|
746
|
-
print(f"\nQuark installed successfully at: {quark_path}")
|
|
747
|
-
|
|
748
|
-
@staticmethod
|
|
749
|
-
def _install_llamacpp(backend):
|
|
750
|
-
"""
|
|
751
|
-
Install llama.cpp binaries with the specified backend.
|
|
207
|
+
from lemonade.tools.flm.utils import install_flm
|
|
752
208
|
|
|
753
|
-
|
|
754
|
-
backend: The backend to use ('rocm' or 'vulkan')
|
|
755
|
-
"""
|
|
756
|
-
|
|
757
|
-
from lemonade.tools.llamacpp.utils import install_llamacpp
|
|
758
|
-
|
|
759
|
-
install_llamacpp(backend)
|
|
209
|
+
install_flm()
|
|
760
210
|
|
|
761
211
|
def run(
|
|
762
212
|
self,
|
|
763
|
-
ryzenai: Optional[str] = None,
|
|
764
|
-
build_model: Optional[str] = None,
|
|
765
|
-
quark: Optional[str] = None,
|
|
766
213
|
llamacpp: Optional[str] = None,
|
|
767
|
-
|
|
768
|
-
token: Optional[str] = None,
|
|
214
|
+
flm: Optional[bool] = None,
|
|
769
215
|
):
|
|
770
|
-
if
|
|
216
|
+
if llamacpp is None and flm is None:
|
|
771
217
|
raise ValueError(
|
|
772
218
|
"You must select something to install, "
|
|
773
|
-
"for example `--
|
|
219
|
+
"for example `--llamacpp` or `--flm`"
|
|
774
220
|
)
|
|
775
221
|
|
|
776
|
-
if ryzenai is not None:
|
|
777
|
-
self._install_ryzenai(ryzenai, build_model, yes, token)
|
|
778
|
-
|
|
779
|
-
if quark is not None:
|
|
780
|
-
self._install_quark(quark)
|
|
781
|
-
|
|
782
222
|
if llamacpp is not None:
|
|
783
223
|
self._install_llamacpp(llamacpp)
|
|
784
224
|
|
|
225
|
+
if flm:
|
|
226
|
+
self._install_flm()
|
|
227
|
+
|
|
785
228
|
|
|
786
229
|
def main():
|
|
787
230
|
installer = Install()
|