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