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.

Files changed (53) hide show
  1. lemonade/cache.py +6 -1
  2. lemonade/cli.py +47 -5
  3. lemonade/common/inference_engines.py +13 -4
  4. lemonade/common/status.py +4 -4
  5. lemonade/common/system_info.py +544 -1
  6. lemonade/profilers/agt_power.py +437 -0
  7. lemonade/profilers/hwinfo_power.py +429 -0
  8. lemonade/tools/accuracy.py +143 -48
  9. lemonade/tools/adapter.py +6 -1
  10. lemonade/tools/bench.py +26 -8
  11. lemonade/tools/flm/__init__.py +1 -0
  12. lemonade/tools/flm/utils.py +303 -0
  13. lemonade/tools/huggingface/bench.py +6 -1
  14. lemonade/tools/llamacpp/bench.py +146 -27
  15. lemonade/tools/llamacpp/load.py +30 -2
  16. lemonade/tools/llamacpp/utils.py +393 -33
  17. lemonade/tools/oga/bench.py +5 -26
  18. lemonade/tools/oga/load.py +60 -121
  19. lemonade/tools/oga/migration.py +403 -0
  20. lemonade/tools/report/table.py +76 -8
  21. lemonade/tools/server/flm.py +133 -0
  22. lemonade/tools/server/llamacpp.py +220 -553
  23. lemonade/tools/server/serve.py +684 -168
  24. lemonade/tools/server/static/js/chat.js +666 -342
  25. lemonade/tools/server/static/js/model-settings.js +24 -3
  26. lemonade/tools/server/static/js/models.js +597 -73
  27. lemonade/tools/server/static/js/shared.js +79 -14
  28. lemonade/tools/server/static/logs.html +191 -0
  29. lemonade/tools/server/static/styles.css +491 -66
  30. lemonade/tools/server/static/webapp.html +83 -31
  31. lemonade/tools/server/tray.py +158 -38
  32. lemonade/tools/server/utils/macos_tray.py +226 -0
  33. lemonade/tools/server/utils/{system_tray.py → windows_tray.py} +13 -0
  34. lemonade/tools/server/webapp.py +4 -1
  35. lemonade/tools/server/wrapped_server.py +559 -0
  36. lemonade/version.py +1 -1
  37. lemonade_install/install.py +54 -611
  38. {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/METADATA +29 -72
  39. lemonade_sdk-8.2.2.dist-info/RECORD +83 -0
  40. lemonade_server/cli.py +145 -37
  41. lemonade_server/model_manager.py +521 -37
  42. lemonade_server/pydantic_models.py +28 -1
  43. lemonade_server/server_models.json +246 -92
  44. lemonade_server/settings.py +39 -39
  45. lemonade/tools/quark/__init__.py +0 -0
  46. lemonade/tools/quark/quark_load.py +0 -173
  47. lemonade/tools/quark/quark_quantize.py +0 -439
  48. lemonade_sdk-8.1.4.dist-info/RECORD +0 -77
  49. {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/WHEEL +0 -0
  50. {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/entry_points.txt +0 -0
  51. {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/licenses/LICENSE +0 -0
  52. {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/licenses/NOTICE.md +0 -0
  53. {lemonade_sdk-8.1.4.dist-info → lemonade_sdk-8.2.2.dist-info}/top_level.txt +0 -0
@@ -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 Ryzen AI software artifacts (libraries, wheels, etc.).
7
- # The Ryzen AI hybrid artifacts directory hierarchy is:
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.7.0.2.1", "0.7.0.2"):
166
- return "1.5.0", oga_path
55
+ if og.__version__ in ("0.9.2", "0.9.2.1"):
56
+ return "1.6.0", oga_path
167
57
  else:
168
- return "1.4.0", oga_path
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
- if device == "npu":
171
- oga_path, version = get_oga_npu_dir()
172
- else:
173
- oga_path, version = get_oga_hybrid_dir()
174
- return version, oga_path
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://lemonade_server.ai/install_options.html for details"
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
- "--ryzenai",
414
- help="Install Ryzen AI software for LLMs. Requires an authentication token. "
415
- "The 'npu' and 'hybrid' choices install the default "
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
- "-y",
435
- "--yes",
178
+ "--flm",
436
179
  action="store_true",
437
- help="Answer 'yes' to all questions. "
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 _get_license_acceptance(version, license_file, license_tag, yes):
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
- Installs the Ryzen AI model prep artifacts if the build-model flag is provided.
188
+ Install llama.cpp binaries with the specified backend.
540
189
 
541
190
  Args:
542
- ryzen_ai_folder (str): Path to the Ryzen AI installation folder.
543
- version (str): Version of the Ryzen AI artifacts.
191
+ backend: The backend to use ('rocm' or 'vulkan')
544
192
  """
545
193
 
546
- # Check if model prep artifacts are available for the given version
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
- @staticmethod
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 _install_ryzenai_hybrid(
614
- ryzen_ai_folder, version, yes, token, skip_wheels=False
615
- ):
616
- # Check version is valid
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
- warning_msg = (
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
- Args:
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
- yes: bool = False,
768
- token: Optional[str] = None,
214
+ flm: Optional[bool] = None,
769
215
  ):
770
- if ryzenai is None and quark is None and llamacpp is None:
216
+ if llamacpp is None and flm is None:
771
217
  raise ValueError(
772
218
  "You must select something to install, "
773
- "for example `--ryzenai`, `--quark`, or `--llamacpp`"
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()