lemonade-sdk 7.0.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.

Files changed (61) hide show
  1. lemonade/__init__.py +5 -0
  2. lemonade/api.py +125 -0
  3. lemonade/cache.py +85 -0
  4. lemonade/cli.py +135 -0
  5. lemonade/common/__init__.py +0 -0
  6. lemonade/common/analyze_model.py +26 -0
  7. lemonade/common/build.py +223 -0
  8. lemonade/common/cli_helpers.py +139 -0
  9. lemonade/common/exceptions.py +98 -0
  10. lemonade/common/filesystem.py +368 -0
  11. lemonade/common/labels.py +61 -0
  12. lemonade/common/onnx_helpers.py +176 -0
  13. lemonade/common/plugins.py +10 -0
  14. lemonade/common/printing.py +110 -0
  15. lemonade/common/status.py +490 -0
  16. lemonade/common/system_info.py +390 -0
  17. lemonade/common/tensor_helpers.py +83 -0
  18. lemonade/common/test_helpers.py +28 -0
  19. lemonade/profilers/__init__.py +1 -0
  20. lemonade/profilers/memory_tracker.py +257 -0
  21. lemonade/profilers/profiler.py +55 -0
  22. lemonade/sequence.py +363 -0
  23. lemonade/state.py +159 -0
  24. lemonade/tools/__init__.py +1 -0
  25. lemonade/tools/adapter.py +104 -0
  26. lemonade/tools/bench.py +284 -0
  27. lemonade/tools/huggingface_bench.py +267 -0
  28. lemonade/tools/huggingface_load.py +520 -0
  29. lemonade/tools/humaneval.py +258 -0
  30. lemonade/tools/llamacpp.py +261 -0
  31. lemonade/tools/llamacpp_bench.py +154 -0
  32. lemonade/tools/management_tools.py +273 -0
  33. lemonade/tools/mmlu.py +327 -0
  34. lemonade/tools/ort_genai/__init__.py +0 -0
  35. lemonade/tools/ort_genai/oga.py +1129 -0
  36. lemonade/tools/ort_genai/oga_bench.py +142 -0
  37. lemonade/tools/perplexity.py +146 -0
  38. lemonade/tools/prompt.py +228 -0
  39. lemonade/tools/quark/__init__.py +0 -0
  40. lemonade/tools/quark/quark_load.py +172 -0
  41. lemonade/tools/quark/quark_quantize.py +439 -0
  42. lemonade/tools/report/__init__.py +0 -0
  43. lemonade/tools/report/llm_report.py +203 -0
  44. lemonade/tools/report/table.py +739 -0
  45. lemonade/tools/server/__init__.py +0 -0
  46. lemonade/tools/server/serve.py +1354 -0
  47. lemonade/tools/server/tool_calls.py +146 -0
  48. lemonade/tools/tool.py +374 -0
  49. lemonade/version.py +1 -0
  50. lemonade_install/__init__.py +1 -0
  51. lemonade_install/install.py +774 -0
  52. lemonade_sdk-7.0.0.dist-info/METADATA +116 -0
  53. lemonade_sdk-7.0.0.dist-info/RECORD +61 -0
  54. lemonade_sdk-7.0.0.dist-info/WHEEL +5 -0
  55. lemonade_sdk-7.0.0.dist-info/entry_points.txt +4 -0
  56. lemonade_sdk-7.0.0.dist-info/licenses/LICENSE +201 -0
  57. lemonade_sdk-7.0.0.dist-info/licenses/NOTICE.md +21 -0
  58. lemonade_sdk-7.0.0.dist-info/top_level.txt +3 -0
  59. lemonade_server/cli.py +260 -0
  60. lemonade_server/model_manager.py +98 -0
  61. lemonade_server/server_models.json +142 -0
@@ -0,0 +1,439 @@
1
+ import argparse
2
+ import os
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ import torch
7
+ from transformers import AutoProcessor
8
+ from lemonade.state import State
9
+ from lemonade.tools import Tool
10
+ import lemonade.common.printing as printing
11
+ import lemonade.common.build as build
12
+ from lemonade_install.install import DEFAULT_QUARK_DIR
13
+
14
+
15
+ class QuarkQuantize(Tool):
16
+ """
17
+ Quantize a model using the Quark Quantization tool.
18
+
19
+ This Tool performs the following steps:
20
+ 1. Downloads and extracts necessary resources from AMD Quark Web Page.
21
+ 2. Based on the target model, it prepares the model, tokenizer, and calibration data.
22
+ 3. Optionally quantizes, freezes, and exports the model.
23
+ 4. Optionally evaluates the model.
24
+
25
+ Required Input State:
26
+ - state.model.model: Pretrained model instance to be quantized.
27
+ - state.tokenizer: Tokenizer instance from Hugging Face.
28
+ Output:
29
+ - Modifies `state` with quantized and optionally exported model.
30
+
31
+ See docs/quark.md for more details.
32
+ """
33
+
34
+ unique_name = "quark-quantize"
35
+
36
+ def __init__(self):
37
+ super().__init__(monitor_message="Quark Quantizing model")
38
+
39
+ @staticmethod
40
+ def parser(add_help: bool = True) -> argparse.ArgumentParser:
41
+ parser = __class__.helpful_parser(
42
+ short_description="Quantize a model using Quark",
43
+ add_help=add_help,
44
+ )
45
+ parser.add_argument(
46
+ "--device",
47
+ default="cpu",
48
+ choices=["cuda", "cpu"],
49
+ help="Device for running the quantizer",
50
+ )
51
+ parser.add_argument("--multi-gpu", action="store_true")
52
+ parser.add_argument(
53
+ "--data-type",
54
+ default="auto",
55
+ choices=["auto", "float16", "bfloat16", "float32"],
56
+ help="Input datatype of the model",
57
+ )
58
+ parser.add_argument(
59
+ "--seq-len", type=int, default=512, help="Sequence length of data"
60
+ )
61
+ parser.add_argument(
62
+ "--batch-size", type=int, default=1, help="Batch size for calibration."
63
+ )
64
+ parser.add_argument(
65
+ "--num-fewshot",
66
+ type=int,
67
+ default=None,
68
+ metavar="N",
69
+ help="Number of examples in few-shot context",
70
+ )
71
+ parser.add_argument(
72
+ "--output-dir", default=None, help="Output directory for exported model"
73
+ )
74
+ parser.add_argument(
75
+ "--no-weight-matrix-merge",
76
+ action="store_true",
77
+ help="If set, merges onnx model and weight \
78
+ together before export.\
79
+ By default, for onnx export, spits out a model.onnx and a model.weights",
80
+ )
81
+ parser.add_argument(
82
+ "--dataset",
83
+ default="pileval",
84
+ choices=[
85
+ "pileval",
86
+ "wikitext",
87
+ "pileval_for_awq_benchmark",
88
+ "wikitext_for_gptq_benchmark",
89
+ "HuggingFaceH4/ultrachat_200k",
90
+ ],
91
+ help="Dataset for calibration",
92
+ )
93
+ parser.add_argument(
94
+ "--num-calib-data",
95
+ type=int,
96
+ default=512,
97
+ help="Number of samples for calibration.",
98
+ )
99
+
100
+ # See docs/quark.md for more details.
101
+ parser.add_argument(
102
+ "--quant-scheme",
103
+ type=str,
104
+ default=None,
105
+ choices=[
106
+ "w_fp8_a_fp8",
107
+ "w_int4_per_channel_sym",
108
+ "w_uint4_per_group_asym",
109
+ "w_int4_per_group_sym",
110
+ "w_uint4_a_bfloat16_per_group_asym",
111
+ "w_int8_per_tensor_sym",
112
+ "w_int8_per_group_sym",
113
+ "w_uint8_per_group_asym",
114
+ "w_int8_a_int8_per_tensor_sym",
115
+ "w_int8_a_int8_per_tensor_sym_dynamic",
116
+ "w_uint8_a_uint8_per_tensor_asym",
117
+ "w_fp8_a_fp8_o_fp8",
118
+ "w_mx_fp8",
119
+ "w_mx_fp8_a_mx_fp8",
120
+ "w_int8_a_int8_per_token_dynamic",
121
+ "w_bfp16",
122
+ "w_bfp16_a_bfp16",
123
+ "w_mx6",
124
+ "w_mx6_a_mx6",
125
+ "w_fp8_per_channel_sym",
126
+ "w_int4_per_channel_asym",
127
+ "w_int4_per_group_asym",
128
+ "w_uint4_per_group_sym",
129
+ "w_uint4_per_channel_sym",
130
+ "w_uint4_per_channel_asym",
131
+ "w_int8_per_tensor_percentile",
132
+ "w_int8_per_tensor_mse",
133
+ "w_uint8_per_tensor_percentile",
134
+ "w_uint8_per_tensor_mse",
135
+ "w_mx_fp4_per_group_sym",
136
+ "w_mx_fp6_e3m2_per_group_sym",
137
+ "w_mx_fp6_e2m3_per_group_sym",
138
+ "w_mx_int8_per_group_sym",
139
+ "w_uint4_per_channel_a_int8_per_tensor",
140
+ "w_uint4_per_group_a_int8_per_tensor",
141
+ "w_bfp16_per_group_sym",
142
+ None,
143
+ ],
144
+ help="Supported quantization schemes in Quark",
145
+ )
146
+ parser.add_argument(
147
+ "--quant-algo",
148
+ type=str,
149
+ default=None,
150
+ choices=["awq", "gptq", "autosmoothquant", None],
151
+ help="Support quantization algorithms in Quark",
152
+ )
153
+ parser.add_argument(
154
+ "--pre-optimization-config-file-path",
155
+ type=str,
156
+ default=None,
157
+ help="The JSON file path of pre-optimization config",
158
+ )
159
+ parser.add_argument(
160
+ "--quant-algo-config-file-path",
161
+ type=str,
162
+ default=None,
163
+ help="The JSON file path of quantization algorithm config",
164
+ )
165
+ parser.add_argument(
166
+ "--group-size",
167
+ type=int,
168
+ default=128,
169
+ help="Group size for per_group quantization",
170
+ )
171
+ parser.add_argument(
172
+ "--pack-method",
173
+ type=str,
174
+ default="reorder",
175
+ choices=["order", "reorder"],
176
+ help="Pack method for awq_export",
177
+ )
178
+ parser.add_argument(
179
+ "--exclude-layers",
180
+ type=str,
181
+ nargs="*",
182
+ default=None,
183
+ help="List of layers to exclude from quantization.",
184
+ )
185
+ parser.add_argument(
186
+ "--kv-cache-dtype",
187
+ default=None,
188
+ choices=["fp8", None],
189
+ help="KV Cache dtype.",
190
+ )
191
+ parser.add_argument(
192
+ "--pre-quantization-optimization",
193
+ action="append",
194
+ default=[],
195
+ choices=["rotation", "smoothquant"],
196
+ help="Pre Quantization Optimization.",
197
+ )
198
+ parser.add_argument(
199
+ "--model-export",
200
+ default=None,
201
+ action="append",
202
+ choices=[
203
+ None,
204
+ "onnx",
205
+ "vllm_adopted_safetensors",
206
+ "quark_safetensors",
207
+ "gguf",
208
+ ],
209
+ help="Model export format",
210
+ )
211
+ parser.add_argument(
212
+ "--custom-mode",
213
+ default="quark",
214
+ type=str,
215
+ choices=["quark", "awq", "fp8"],
216
+ help="Custom mode for export \
217
+ This is especially relevant for npu/hybrid export",
218
+ )
219
+ parser.add_argument(
220
+ "--torch-compile",
221
+ action="store_true",
222
+ help="Compile the quantized model using torch.compile",
223
+ )
224
+ parser.add_argument(
225
+ "--params-save", action="store_true", help="Save model params"
226
+ )
227
+ parser.add_argument(
228
+ "--save-dir",
229
+ help="Directory to save model parameters as \
230
+ safetensors or pth, in the case when --params_save is used.",
231
+ )
232
+ parser.add_argument(
233
+ "--log-severity-level", type=int, default=3, help="DEBUG=1, INFO=2, ERROR=3"
234
+ )
235
+ parser.add_argument("--skip-quantization", action="store_true")
236
+
237
+ return parser
238
+
239
+ def run(self, state: State, **kwargs) -> State:
240
+ """
241
+ Executes the QuarkQuantize process.
242
+
243
+ Args:
244
+ state (State): The current state of the process, containing necessary
245
+ information such as cache directory and build name.
246
+ **kwargs: Additional keyword arguments that may include:
247
+ - output_dir (str): Directory to save the output model.
248
+ - safetensors_model_dir (str): Directory to save the safetensors model.
249
+ - save_dir (str): Directory to save model parameters.
250
+ - safetensors_path (str): Path to the safetensors model.
251
+ - quant_algo (str): The quantization algorithm to use.
252
+ - quant_algo_config_file_path (str): Path to the quantization algorithm
253
+ configuration file.
254
+ - model_dir (str): Directory of the model.
255
+ Returns:
256
+ State: The updated state after the quantization process.
257
+ Raises:
258
+ Exception: If an error occurs during the QuarkQuantize process
259
+ and when installation path does not exist.
260
+ """
261
+
262
+ try:
263
+
264
+ if os.path.isdir(DEFAULT_QUARK_DIR):
265
+ quark_llm_path = os.path.join(
266
+ DEFAULT_QUARK_DIR, "examples", "torch", "language_modeling"
267
+ )
268
+ sys.path.extend([quark_llm_path])
269
+ else:
270
+ raise FileNotFoundError(
271
+ f"The directory {DEFAULT_QUARK_DIR} does not exist. \
272
+ Please check your installation."
273
+ )
274
+ model_build_path = os.path.join(
275
+ build.output_dir(state.cache_dir, state.build_name)
276
+ )
277
+ model_export_path = os.path.join(
278
+ model_build_path,
279
+ "exported_model",
280
+ kwargs.get("quant_scheme"),
281
+ kwargs.get("quant_algo"),
282
+ )
283
+ # Set default paths only if current values are None
284
+ if kwargs.get("model_dir") is None:
285
+ kwargs["model_dir"] = model_build_path
286
+ if kwargs.get("output_dir") is None:
287
+ kwargs["output_dir"] = model_export_path
288
+ if kwargs.get("save_dir") is None:
289
+ kwargs["save_dir"] = os.path.join(model_export_path, "model_params")
290
+
291
+ from llm_utils.model_preparation import get_model_type
292
+
293
+ model_type = get_model_type(state.model.model)
294
+
295
+ quant_algo = kwargs.get("quant_algo")
296
+ kwargs["quant_algo_config_file_path"] = os.path.join(
297
+ quark_llm_path,
298
+ "llm_ptq",
299
+ "models",
300
+ model_type,
301
+ f"{quant_algo}_config.json",
302
+ )
303
+
304
+ self._quantize(state, **kwargs)
305
+
306
+ except Exception as e:
307
+ printing.log_error(f"Error during the QuarkQuantize process: {e}")
308
+ raise
309
+ return state
310
+
311
+ def _quantize(self, state: State, **kwargs) -> None:
312
+ """
313
+ Main quantization and export process.
314
+
315
+ This method is responsible for:
316
+ - Loading the model and tokenizer.
317
+ - Preparing the calibration dataset.
318
+ - Quantizing the model.
319
+ - Optionally exporting, compiling, and evaluating the model.
320
+ """
321
+
322
+ model = state.model.model
323
+ tokenizer = state.tokenizer
324
+
325
+ # Importing quark utils after adding to sys.path
326
+ from llm_utils.data_preparation import get_calib_dataloader
327
+ from llm_utils.model_preparation import get_model_type
328
+ from llm_ptq.configuration_preparation import get_config, get_export_config
329
+ from quark.torch import ModelQuantizer, ModelExporter, save_params
330
+
331
+ # 1. Load Model
332
+ printing.log_info("Loading model ...")
333
+ model_type = get_model_type(model)
334
+
335
+ # [mllama specifics]
336
+ if model_type == "mllama" and kwargs.get("model_export") is not None:
337
+ processor = AutoProcessor.from_pretrained(kwargs.get("model_dir"))
338
+ export_dir = Path(kwargs.get("output_dir"))
339
+ export_dir.mkdir(parents=True, exist_ok=True)
340
+ processor.save_pretrained(kwargs.get("output_dir"))
341
+
342
+ # 2. Load dataset
343
+ printing.log_info("Loading dataset ...")
344
+ main_device = model.device if kwargs.get("multi_gpu") else kwargs.get("device")
345
+ calib_dataloader = get_calib_dataloader(
346
+ dataset_name=kwargs.get("dataset"),
347
+ tokenizer=tokenizer,
348
+ batch_size=1,
349
+ num_calib_data=kwargs.get("num_calib_data"),
350
+ seqlen=kwargs.get("seq_len"),
351
+ device=main_device,
352
+ )
353
+
354
+ # 3. Quantize model
355
+ if not kwargs.get("skip_quantization"):
356
+ printing.log_info("Starting quantization process ...")
357
+ args = argparse.Namespace(**kwargs)
358
+ quant_config = get_config(args, model_type)
359
+ quant_config.log_severity_level = kwargs.get("log_severity_level", 3)
360
+ quantizer = ModelQuantizer(quant_config)
361
+ model = quantizer.quantize_model(model, calib_dataloader)
362
+ printing.log_info("Quantization completed.")
363
+
364
+ if (
365
+ kwargs.get("model_export") is not None
366
+ or kwargs.get("params_save")
367
+ or kwargs.get("torch_compile")
368
+ ):
369
+ printing.log_info("Freezing the quantized model ...")
370
+ model = quantizer.freeze(model)
371
+
372
+ # 4. Export model
373
+ if kwargs.get("model_export") is not None:
374
+ printing.log_info("Exporting the model ...")
375
+ export_path = kwargs.get("output_dir")
376
+
377
+ args = argparse.Namespace(**kwargs)
378
+ export_config = get_export_config(args, model_type)
379
+ exporter = ModelExporter(config=export_config, export_dir=export_path)
380
+ if "quark_safetensors" in kwargs.get("model_export"):
381
+ printing.log_info("Exporting quark native json and safetensors...")
382
+ with torch.no_grad():
383
+ quant_config = get_config(args, model_type)
384
+ exporter.export_model_info(
385
+ model,
386
+ quant_config=quant_config,
387
+ tokenizer=tokenizer,
388
+ custom_mode=kwargs.get("custom_mode"),
389
+ )
390
+ if "vllm_adopted_safetensors" in kwargs.get("model_export"):
391
+ printing.log_info("Exporting vllm adopted json and safetensors...")
392
+ with torch.inference_mode():
393
+ exporter.export_model_info(
394
+ model,
395
+ model_type=model_type,
396
+ model_dtype=state.dtype,
397
+ export_type="vllm-adopt",
398
+ )
399
+ if "onnx" in kwargs.get("model_export"):
400
+ printing.log_info("Exporting onnx graph...")
401
+ with torch.inference_mode():
402
+ batch_iter = iter(calib_dataloader)
403
+ input_args = next(batch_iter)
404
+ if kwargs.get("quant_scheme") in [
405
+ "w_int4_per_channel_sym",
406
+ "w_uint4_per_group_asym",
407
+ "w_int4_per_group_sym",
408
+ "w_uint4_a_bfloat16_per_group_asym",
409
+ ]:
410
+ uint4_int4_flag = True
411
+ else:
412
+ uint4_int4_flag = False
413
+ exporter.export_onnx_model(
414
+ model, input_args, uint4_int4_flag=uint4_int4_flag
415
+ )
416
+ if "gguf" in kwargs.get("model_export"):
417
+ printing.log_info("Exporting gguf model...")
418
+ with torch.inference_mode():
419
+ exporter.export_gguf_model(
420
+ model, kwargs.get("model_dir"), model_type
421
+ )
422
+
423
+ # 6. [Optional] Compile model
424
+ if kwargs.get("torch_compile"):
425
+ printing.log_info("torch.compile...")
426
+ model = torch.compile(model)
427
+
428
+ # 7. Save model parameters
429
+ if kwargs.get("params_save"):
430
+ printing.log_info("Saving model parameters ...")
431
+ save_params(model, model_type=model_type, export_dir=kwargs.get("save_dir"))
432
+
433
+ state.model.model = model
434
+ state.dtype = model.dtype
435
+ printing.log_info("QuarkQuantize process completed.")
436
+
437
+
438
+ # This file was originally licensed under Apache 2.0. It has been modified.
439
+ # Modifications Copyright (c) 2025 AMD
File without changes
@@ -0,0 +1,203 @@
1
+ import argparse
2
+ import csv
3
+ import os
4
+ from pathlib import Path
5
+ import re
6
+ from typing import List
7
+ import lemonade.common.printing as printing
8
+ import lemonade.common.filesystem as fs
9
+ from lemonade.tools.management_tools import ManagementTool
10
+ from lemonade.cache import DEFAULT_CACHE_DIR
11
+ from lemonade.tools.report.table import LemonadeTable, LemonadePerfTable
12
+
13
+
14
+ class LemonadeReport(ManagementTool):
15
+ """
16
+ Analyzes the input lemonade cache(s) and produces an aggregated report
17
+ in csv format that contains the build stats for all builds in all cache(s).
18
+
19
+ In addition, summary information is printed to the console and saved to a text file.
20
+
21
+ When the --perf flag is used, then a performance report is generated that summarizes
22
+ the performance data for different models. In this case, only the data used in the
23
+ text table is saved to the csv format file.
24
+ """
25
+
26
+ unique_name = "report"
27
+
28
+ @staticmethod
29
+ def parser(add_help: bool = True) -> argparse.ArgumentParser:
30
+ parser = __class__.helpful_parser(
31
+ short_description="Export statistics from each lemonade run to a CSV file",
32
+ add_help=add_help,
33
+ )
34
+
35
+ parser.add_argument(
36
+ "-i",
37
+ "--input-caches",
38
+ nargs="*",
39
+ default=[DEFAULT_CACHE_DIR],
40
+ help=(
41
+ "One or more lemonade cache directories to use to generate the report "
42
+ f"(defaults to {DEFAULT_CACHE_DIR})"
43
+ ),
44
+ )
45
+
46
+ parser.add_argument(
47
+ "-o",
48
+ "--output-dir",
49
+ help="Path to folder where reports will be saved "
50
+ "(defaults to current working directory)",
51
+ required=False,
52
+ default=os.getcwd(),
53
+ )
54
+
55
+ parser.add_argument(
56
+ "--no-save",
57
+ action="store_true",
58
+ help="Don't save output to TXT and CSV files",
59
+ )
60
+
61
+ parser.add_argument(
62
+ "--perf",
63
+ action="store_true",
64
+ help="Produce the performance table instead of the regular table",
65
+ )
66
+
67
+ parser.add_argument(
68
+ "--device",
69
+ default=None,
70
+ help="In the --perf table, only include output for the specified device "
71
+ "(e.g., cpu, igpu, npu, hybrid)",
72
+ )
73
+
74
+ parser.add_argument(
75
+ "--dtype",
76
+ default=None,
77
+ help="In the --perf table, only include output for the specified datatype "
78
+ "(e.g., float32, int4)",
79
+ )
80
+
81
+ parser.add_argument(
82
+ "--model",
83
+ default=None,
84
+ help="In the --perf table, only include output for builds with name that contains"
85
+ " the specified string (e.g., Llama). The string match is case-insensitive.",
86
+ )
87
+
88
+ parser.add_argument(
89
+ "--days",
90
+ "-d",
91
+ type=int,
92
+ metavar="N",
93
+ default=None,
94
+ help="In the --perf table, only include output for builds from the last N days.",
95
+ )
96
+
97
+ parser.add_argument(
98
+ "--merge",
99
+ action="store_true",
100
+ help="In the --perf table, merge results from different builds into the same row "
101
+ "as long as model, device, datatype, system info and package version are the same.",
102
+ )
103
+
104
+ parser.add_argument(
105
+ "--lean",
106
+ action="store_true",
107
+ help="In the --perf table, don't include the system info and sw package versions.",
108
+ )
109
+
110
+ return parser
111
+
112
+ def parse(self, args, known_only=True) -> argparse.Namespace:
113
+ """
114
+ Helper function to parse CLI arguments into the args expected by run()
115
+ """
116
+ parsed_args = super().parse(args, known_only)
117
+
118
+ if not parsed_args.perf:
119
+ # Check that none of the perf specific flags are set
120
+ perf_args = [
121
+ parsed_args.device,
122
+ parsed_args.dtype,
123
+ parsed_args.days,
124
+ parsed_args.merge,
125
+ parsed_args.lean,
126
+ ]
127
+ if not all(arg is None or arg is False for arg in perf_args):
128
+ raise ValueError(
129
+ "Invalid arguments for regular report. Did you miss the --perf argument?"
130
+ " See `lemonade report -h` for help."
131
+ )
132
+
133
+ return parsed_args
134
+
135
+ def run(
136
+ self,
137
+ _,
138
+ input_caches: List[str] = None,
139
+ output_dir: str = os.getcwd(),
140
+ no_save: bool = False,
141
+ perf: bool = False,
142
+ device: str = None,
143
+ dtype: str = None,
144
+ model: str = None,
145
+ days: int = None,
146
+ merge: bool = False,
147
+ lean: bool = False,
148
+ ):
149
+ # Process input arguments
150
+ cache_dirs = [os.path.expanduser(dir) for dir in input_caches]
151
+ cache_dirs = fs.expand_inputs(cache_dirs)
152
+ report_dir = os.path.expanduser(output_dir)
153
+
154
+ if perf:
155
+ table = LemonadePerfTable(device, dtype, model, days, merge, lean)
156
+ else:
157
+ table = LemonadeTable()
158
+
159
+ # Find builds and load stats
160
+ table.find_builds(cache_dirs, model)
161
+ table.load_stats()
162
+ table.sort_stats()
163
+
164
+ # Print message if there are no stats
165
+ if len(table.all_stats) == 0:
166
+ printing.log_info("No relevant cached build data found")
167
+ return
168
+
169
+ # Print table to stdout
170
+ print()
171
+ print(table)
172
+
173
+ if no_save:
174
+ return
175
+
176
+ # Name report file
177
+ report_path = os.path.join(report_dir, table.get_report_name())
178
+ txt_path = re.sub(".csv$", ".txt", report_path)
179
+ Path(report_dir).mkdir(parents=True, exist_ok=True)
180
+
181
+ # Create the report to save to CSV
182
+ report, column_headers = table.create_csv_report()
183
+
184
+ # Populate results spreadsheet
185
+ with open(report_path, "w", newline="", encoding="utf8") as spreadsheet:
186
+ writer = csv.writer(spreadsheet)
187
+ writer.writerow(column_headers)
188
+ for entry in report:
189
+ writer.writerow([entry[col] for col in column_headers])
190
+
191
+ # Save the text report
192
+ with open(txt_path, "w", encoding="utf-8") as file:
193
+ print(table, file=file)
194
+
195
+ # Print message with the output file path
196
+ printing.log("Report text saved at ")
197
+ printing.logn(str(txt_path), printing.Colors.OKGREEN)
198
+ printing.log("Report spreadsheet saved at ")
199
+ printing.logn(str(report_path), printing.Colors.OKGREEN)
200
+
201
+
202
+ # This file was originally licensed under Apache 2.0. It has been modified.
203
+ # Modifications Copyright (c) 2025 AMD