litert-cli 0.1.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.
Files changed (67) hide show
  1. examples/litert_cli.ipynb +313 -0
  2. examples/models/presets/default.py +19 -0
  3. examples/run_cli_demo.sh +38 -0
  4. examples/run_cli_npu.sh +89 -0
  5. examples/run_commands.sh +67 -0
  6. examples/run_models.sh +63 -0
  7. examples/run_smoke_tests.sh +58 -0
  8. examples/utils.ps1 +163 -0
  9. examples/utils.sh +184 -0
  10. litert_cli/__init__.py +15 -0
  11. litert_cli/commands/benchmark/__init__.py +16 -0
  12. litert_cli/commands/benchmark/android.py +212 -0
  13. litert_cli/commands/benchmark/cli.py +294 -0
  14. litert_cli/commands/benchmark/desktop.py +228 -0
  15. litert_cli/commands/benchmark/gcp.py +336 -0
  16. litert_cli/commands/clean.py +73 -0
  17. litert_cli/commands/compile.py +211 -0
  18. litert_cli/commands/convert/__init__.py +20 -0
  19. litert_cli/commands/convert/cli.py +255 -0
  20. litert_cli/commands/convert/generic.py +211 -0
  21. litert_cli/commands/convert/huggingface.py +175 -0
  22. litert_cli/commands/delete.py +56 -0
  23. litert_cli/commands/download.py +274 -0
  24. litert_cli/commands/import.py +124 -0
  25. litert_cli/commands/list.py +132 -0
  26. litert_cli/commands/lm.py +74 -0
  27. litert_cli/commands/quantize.py +193 -0
  28. litert_cli/commands/run/__init__.py +16 -0
  29. litert_cli/commands/run/android.py +394 -0
  30. litert_cli/commands/run/cli.py +297 -0
  31. litert_cli/commands/run/desktop.py +340 -0
  32. litert_cli/commands/visualize.py +234 -0
  33. litert_cli/core/android_utils.py +304 -0
  34. litert_cli/core/android_utils_test.py +236 -0
  35. litert_cli/core/constants.py +131 -0
  36. litert_cli/core/deps.py +180 -0
  37. litert_cli/core/deps_test.py +101 -0
  38. litert_cli/core/inputs.py +203 -0
  39. litert_cli/core/inputs_test.py +176 -0
  40. litert_cli/core/log_filters.py +50 -0
  41. litert_cli/core/models.py +96 -0
  42. litert_cli/core/npu_utils.py +382 -0
  43. litert_cli/core/targets_manager.py +192 -0
  44. litert_cli/core/utils.py +58 -0
  45. litert_cli/litert.py +119 -0
  46. litert_cli/litert_help_test.py +51 -0
  47. litert_cli/litert_test.py +88 -0
  48. litert_cli/models/__init__.py +145 -0
  49. litert_cli/models/asr/__init__.py +15 -0
  50. litert_cli/models/asr/asr_model.py +108 -0
  51. litert_cli/models/asr/parakeet_ctc.py +165 -0
  52. litert_cli/models/asr/runner.py +482 -0
  53. litert_cli/models/base.py +57 -0
  54. litert_cli/test_data/dummy_calib_data.py +26 -0
  55. litert_cli/test_data/dummy_cv_model.py +52 -0
  56. litert_cli/test_data/dummy_cv_model.tflite +0 -0
  57. litert_cli/test_data/generate_test_inputs.py +51 -0
  58. litert_cli/test_data/mobilenet_v3_calib_data.py +25 -0
  59. litert_cli/test_data/quantize_recipe.json +16 -0
  60. litert_cli/test_data/resnet18.py +31 -0
  61. litert_cli-0.1.0.dist-info/METADATA +38 -0
  62. litert_cli-0.1.0.dist-info/RECORD +67 -0
  63. litert_cli-0.1.0.dist-info/WHEEL +5 -0
  64. litert_cli-0.1.0.dist-info/entry_points.txt +2 -0
  65. litert_cli-0.1.0.dist-info/licenses/LICENSE +202 -0
  66. litert_cli-0.1.0.dist-info/top_level.txt +3 -0
  67. tools/build_wheels.py +122 -0
@@ -0,0 +1,294 @@
1
+ # Copyright 2026 The LiteRT CLI Authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+
16
+ """CLI Benchmark Module providing commands to benchmark LiteRT models.
17
+
18
+ This module defines the `benchmark` command using Click. It supports:
19
+ 1. Benchmarking on a local Android device via adb (Default).
20
+ 2. Benchmarking on Google Cloud Platform (GCP) via AI Edge Portal Cloud API.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import pathlib
26
+ import textwrap
27
+
28
+ import click
29
+ from litert_cli.core import constants
30
+ from litert_cli.core import utils
31
+
32
+
33
+ @click.command(
34
+ "benchmark",
35
+ help="""\b
36
+ Benchmark LiteRT models on different platforms.
37
+ \b
38
+ MODEL: Path to the LiteRT model file.
39
+ \b
40
+ Examples:
41
+ \b
42
+ # Benchmark on Desktop with CPU (Default) or GPU
43
+ \b
44
+ $ litert benchmark model.tflite
45
+ $ litert benchmark model.tflite --gpu
46
+ \b
47
+ # Benchmark on Android with CPU (Default) or GPU or NPU
48
+ $ litert benchmark model.tflite --android
49
+ $ litert benchmark model.tflite --android --gpu
50
+ $ litert benchmark model.tflite --android --npu
51
+ \b
52
+ # Benchmark on Google AI Edge Portal in Google Cloud. Prerequisites:
53
+ # - Set up your Google AI Edge Portal account by following up the instructions at:
54
+ # https://ai.google.dev/edge/ai-edge-portal
55
+ # - Set up authentication by running: gcloud auth login
56
+ # - You can set the default GCP project by setting the environment variable LITERT_GCP_PROJECT,
57
+ # or by providing the --gcp-project option.
58
+ #
59
+ $ litert benchmark model.tflite --gcp --device "pixel 7"
60
+ $ litert benchmark model.tflite --gcp --device "pixel 7" --gcp-project "your-gcp-project-id"
61
+ $ litert benchmark model.tflite --gcp --devices "pixel 7, sm-s931u1" --gpu
62
+ """,
63
+ )
64
+ @click.argument("model", type=str)
65
+ @click.option(
66
+ "--android",
67
+ "target",
68
+ flag_value="android",
69
+ default=True,
70
+ help="Benchmark on Android device (Default)",
71
+ )
72
+ @click.option(
73
+ "--gcp",
74
+ "target",
75
+ flag_value="gcp",
76
+ help="Benchmark on Google AI Edge Portal in Google Cloud.",
77
+ )
78
+ @click.option(
79
+ "--desktop",
80
+ "target",
81
+ flag_value="desktop",
82
+ help="Benchmark on local Desktop machine.",
83
+ )
84
+ @click.option(
85
+ "--cpu",
86
+ "accelerator",
87
+ flag_value="cpu",
88
+ default=True,
89
+ help="Use CPU accelerator (Default)",
90
+ )
91
+ @click.option(
92
+ "--gpu",
93
+ "accelerator",
94
+ flag_value="gpu",
95
+ help="Use GPU accelerator",
96
+ )
97
+ @click.option(
98
+ "--npu",
99
+ "accelerator",
100
+ flag_value="npu",
101
+ help="Use NPU accelerator",
102
+ )
103
+ @click.option(
104
+ "--jit",
105
+ "compilation_mode",
106
+ flag_value="jit",
107
+ default=True,
108
+ help="Use JIT (Just-in-time) compilation mode for NPU (Default).",
109
+ )
110
+ @click.option(
111
+ "--aot",
112
+ "compilation_mode",
113
+ flag_value="aot",
114
+ help="Use AOT (Ahead-of-time) compilation mode for NPU.",
115
+ )
116
+ @click.option(
117
+ "--soc-model",
118
+ type=str,
119
+ default="SM8750",
120
+ help="Target SoC model name for NPU AOT mode (e.g., 'SM8750').",
121
+ )
122
+ @click.option(
123
+ "--device",
124
+ "--devices",
125
+ "devices",
126
+ type=str,
127
+ multiple=True,
128
+ default=["pixel 7"],
129
+ help=(
130
+ "Target device model name(s) (e.g., 'pixel 7'). Can be specified"
131
+ " --device multiple times or use --devices 'pixel 7, sm-s931u1'."
132
+ " Default is 'pixel 7'"
133
+ ),
134
+ )
135
+ @click.option(
136
+ "--gcp-project",
137
+ type=str,
138
+ help="GCP project ID for benchmarking (Only for GCP target).",
139
+ )
140
+ @click.option(
141
+ "--gcp-bucket",
142
+ type=str,
143
+ help="GCS bucket name for uploading model (Only for GCP target).",
144
+ )
145
+ @click.option(
146
+ "--num-runs",
147
+ type=int,
148
+ default=50,
149
+ help="Target number of benchmark iterations. Default is 50.",
150
+ )
151
+ @click.option(
152
+ "--warmup-runs",
153
+ type=int,
154
+ default=1,
155
+ help="Number of warmup iterations before benchmarking. Default is 1.",
156
+ )
157
+ @click.option(
158
+ "--min-secs",
159
+ type=float,
160
+ default=1.0,
161
+ help="Minimum seconds to run. Default is 1.0.",
162
+ )
163
+ @click.option(
164
+ "--max-secs",
165
+ type=float,
166
+ default=150.0,
167
+ help="Maximum seconds to run. Default is 150.0.",
168
+ )
169
+ @click.option(
170
+ "--warmup-min-secs",
171
+ type=float,
172
+ default=0.5,
173
+ help="Minimum warmup duration in seconds. Default is 0.5.",
174
+ )
175
+ @click.option(
176
+ "--input-layer-value-range",
177
+ type=str,
178
+ help=(
179
+ "A map-like string representing value range for input layers (e.g."
180
+ " input1,1.0,2.0:input2,0,254)."
181
+ ),
182
+ )
183
+ @click.option(
184
+ "--signature-key",
185
+ type=str,
186
+ help=(
187
+ "The signature key to benchmark. If not specified, the default"
188
+ " signature is used."
189
+ ),
190
+ )
191
+ def benchmark_cmd(
192
+ model: str,
193
+ target: str,
194
+ accelerator: str,
195
+ devices: tuple[str, ...],
196
+ compilation_mode: str,
197
+ soc_model: str,
198
+ gcp_project: str | None = None,
199
+ gcp_bucket: str | None = None,
200
+ num_runs: int = 50,
201
+ warmup_runs: int = 1,
202
+ min_secs: float = 1.0,
203
+ max_secs: float = 150.0,
204
+ warmup_min_secs: float = 0.5,
205
+ input_layer_value_range: str | None = None,
206
+ signature_key: str | None = None,
207
+ ) -> None:
208
+ """Benchmarks LiteRT models on different platforms.
209
+
210
+ Args:
211
+ model: Path to the LiteRT model file or Model Reference.
212
+ target: Target platform for benchmark (android, gcp, desktop).
213
+ accelerator: Accelerator to use (cpu, gpu, npu).
214
+ devices: Target device model(s) (e.g., 'pixel 7').
215
+ compilation_mode: Compilation mode for NPU (jit, aot).
216
+ soc_model: Target SoC model for NPU AOT mode.
217
+ gcp_project: GCP project ID for benchmarking.
218
+ gcp_bucket: GCS bucket name for uploading model.
219
+ num_runs: Target number of benchmark iterations.
220
+ warmup_runs: Number of warmup iterations before benchmarking.
221
+ min_secs: Minimum seconds to run.
222
+ max_secs: Maximum seconds to run.
223
+ warmup_min_secs: Minimum warmup duration in seconds.
224
+ input_layer_value_range: Value range for input layers.
225
+ signature_key: The signature key to benchmark.
226
+ """
227
+ from litert_cli.core import models as core_models
228
+
229
+ # Quiet if default is true
230
+ if constants.DEFAULT_QUIET:
231
+ utils.enable_quiet_mode()
232
+
233
+ resolved_model_path, _ = core_models.resolve_model_reference(model)
234
+
235
+ if resolved_model_path != model:
236
+ click.echo(f"Resolved model '{model}' to '{resolved_model_path}'")
237
+
238
+ model_path = pathlib.Path(resolved_model_path)
239
+
240
+ if target == "android":
241
+ # pylint: disable=g-import-not-at-top
242
+ from litert_cli.commands.benchmark import android
243
+
244
+ if not model_path.exists():
245
+ raise click.ClickException(f"Local model file not found: {model_path}")
246
+
247
+ android.run_android(
248
+ model_path=model_path,
249
+ accelerator=accelerator,
250
+ num_runs=num_runs,
251
+ warmup_runs=warmup_runs,
252
+ min_secs=min_secs,
253
+ max_secs=max_secs,
254
+ warmup_min_secs=warmup_min_secs,
255
+ input_layer_value_range=input_layer_value_range,
256
+ signature_key=signature_key,
257
+ )
258
+ elif target == "desktop":
259
+ # pylint: disable=g-import-not-at-top
260
+ from litert_cli.commands.benchmark import desktop
261
+
262
+ if not model_path.exists():
263
+ raise click.ClickException(f"Local model file not found: {model_path}")
264
+
265
+ desktop.run_desktop(
266
+ model_path=model_path,
267
+ accelerator=accelerator,
268
+ num_runs=num_runs,
269
+ warmup_runs=warmup_runs,
270
+ min_secs=min_secs,
271
+ max_secs=max_secs,
272
+ warmup_min_secs=warmup_min_secs,
273
+ input_layer_value_range=input_layer_value_range,
274
+ signature_key=signature_key,
275
+ )
276
+ elif target == "gcp":
277
+ # pylint: disable=g-import-not-at-top
278
+ from litert_cli.commands.benchmark import gcp
279
+
280
+ if accelerator != "npu":
281
+ compilation_mode = None
282
+ soc_model = None
283
+
284
+ gcp.run_gcp(
285
+ str(model_path),
286
+ accelerator,
287
+ devices,
288
+ gcp_project,
289
+ gcp_bucket,
290
+ compilation_mode,
291
+ soc_model,
292
+ )
293
+ else:
294
+ click.secho(f"Target '{target}' is not yet supported.", fg="red")
@@ -0,0 +1,228 @@
1
+ # Copyright 2026 The LiteRT CLI Authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+
16
+ """Desktop Benchmark Module."""
17
+
18
+ from __future__ import annotations
19
+
20
+ import pathlib
21
+ import platform
22
+ import subprocess
23
+ import sys
24
+
25
+ import click
26
+ from litert_cli.core import constants
27
+ import requests
28
+
29
+
30
+ def _ensure_desktop_binary(tool_name: str) -> pathlib.Path:
31
+ """Downloads the pre-built binary for the current desktop platform if not cached."""
32
+ system = sys.platform.lower()
33
+ machine = platform.machine().lower()
34
+
35
+ if system.startswith("linux"):
36
+ if machine in ("arm64", "aarch64"):
37
+ plat = "linux_arm64"
38
+ elif machine in ("x86_64", "amd64"):
39
+ plat = "linux_x86_64"
40
+ else:
41
+ raise click.ClickException(f"Unsupported Linux architecture: {machine}")
42
+ elif system == "darwin":
43
+ if machine in ("arm64", "aarch64"):
44
+ plat = "macos_arm64"
45
+ else:
46
+ raise click.ClickException(
47
+ f"Unsupported macOS architecture: {machine}. Only macos_arm64 is"
48
+ " currently supported."
49
+ )
50
+ elif system == "win32":
51
+ raise click.ClickException("Windows benchmarking is not yet supported.")
52
+ else:
53
+ raise click.ClickException(
54
+ f"Unsupported desktop operating system: {system}"
55
+ )
56
+
57
+ base_url = f"{constants.LITERT_BINARIES_BASE_URL}/{plat}"
58
+ download_url = f"{base_url}/{tool_name}"
59
+
60
+ cache_dir = pathlib.Path(constants.LITERT_CLI_CACHE_DIR) / "binaries" / plat
61
+ cache_dir.mkdir(parents=True, exist_ok=True)
62
+
63
+ cached_file_path = cache_dir / tool_name
64
+ if cached_file_path.exists():
65
+ if system == "darwin":
66
+ subprocess.run(
67
+ ["xattr", "-c", str(cached_file_path)],
68
+ check=False,
69
+ stdout=subprocess.DEVNULL,
70
+ stderr=subprocess.DEVNULL,
71
+ )
72
+ subprocess.run(
73
+ ["codesign", "-s", "-", str(cached_file_path)],
74
+ check=False,
75
+ stdout=subprocess.DEVNULL,
76
+ stderr=subprocess.DEVNULL,
77
+ )
78
+ return cached_file_path
79
+
80
+ click.secho(
81
+ f"Downloading {tool_name!r} for desktop platform {plat!r}...", fg="cyan"
82
+ )
83
+ tmp_cached_file = cached_file_path.with_suffix(".tmp")
84
+ try:
85
+ with requests.get(download_url, stream=True, timeout=10) as response:
86
+ response.raise_for_status()
87
+ content_length = response.headers.get("Content-Length")
88
+ total_size = int(content_length) if content_length else 0
89
+
90
+ bar_length = total_size if total_size > 0 else None
91
+ bar_label = f"Downloading {tool_name}"
92
+ if bar_length is None:
93
+ click.secho(
94
+ f"Content-Length header not found for {tool_name!r}, using"
95
+ " indeterminate progress bar.",
96
+ fg="yellow",
97
+ )
98
+ bar_label += " (size unknown)"
99
+
100
+ with click.progressbar(length=bar_length, label=bar_label) as bar:
101
+ with open(tmp_cached_file, "wb") as f:
102
+ for buffer in response.iter_content(chunk_size=8192):
103
+ f.write(buffer)
104
+ bar.update(len(buffer))
105
+
106
+ tmp_cached_file.rename(cached_file_path)
107
+ cached_file_path.chmod(0o755)
108
+ if system == "darwin":
109
+ subprocess.run(
110
+ ["xattr", "-c", str(cached_file_path)],
111
+ check=False,
112
+ stdout=subprocess.DEVNULL,
113
+ stderr=subprocess.DEVNULL,
114
+ )
115
+ subprocess.run(
116
+ ["codesign", "-s", "-", str(cached_file_path)],
117
+ check=False,
118
+ stdout=subprocess.DEVNULL,
119
+ stderr=subprocess.DEVNULL,
120
+ )
121
+ return cached_file_path
122
+ except (requests.exceptions.RequestException, OSError) as e:
123
+ if cached_file_path.exists():
124
+ cached_file_path.unlink()
125
+ if tmp_cached_file.exists():
126
+ tmp_cached_file.unlink()
127
+ raise click.ClickException(
128
+ f"Failed to download {tool_name!r} from {download_url!r}: {e}"
129
+ ) from e
130
+
131
+
132
+ def run_desktop(
133
+ *,
134
+ model_path: pathlib.Path,
135
+ accelerator: str,
136
+ num_runs: int = 50,
137
+ warmup_runs: int = 1,
138
+ min_secs: float = 1.0,
139
+ max_secs: float = 150.0,
140
+ warmup_min_secs: float = 0.5,
141
+ input_layer_value_range: str | None = None,
142
+ signature_key: str | None = None,
143
+ ) -> None:
144
+ """Runs the benchmark_model binary on the local desktop machine.
145
+
146
+ Args:
147
+ model_path: Path to the local LiteRT model file.
148
+ accelerator: Hardware accelerator to use (cpu, gpu, npu).
149
+ num_runs: Target number of benchmark iterations.
150
+ warmup_runs: Number of warmup iterations before benchmarking.
151
+ min_secs: Minimum seconds to run.
152
+ max_secs: Maximum seconds to run.
153
+ warmup_min_secs: Minimum warmup duration in seconds.
154
+ input_layer_value_range: Value range for input layers.
155
+ signature_key: The signature key to benchmark.
156
+
157
+ Raises:
158
+ click.ClickException: If execution fails.
159
+ """
160
+ click.echo("Preparing to run benchmark on local desktop...")
161
+
162
+ if not model_path.exists():
163
+ raise click.ClickException(f"Local model file not found: {model_path}")
164
+
165
+ benchmark_bin = _ensure_desktop_binary("benchmark_model")
166
+
167
+ click.echo(f"Executing benchmark locally using {benchmark_bin.name}...\n")
168
+ try:
169
+ bench_args = [
170
+ str(benchmark_bin),
171
+ f"--graph={model_path.resolve()}",
172
+ ]
173
+ if accelerator == "gpu":
174
+ bench_args.append("--use_gpu=true")
175
+ elif accelerator == "npu":
176
+ click.secho(
177
+ "Warning: NPU benchmarking via benchmark_model on desktop is not"
178
+ " fully supported yet.",
179
+ fg="yellow",
180
+ )
181
+ bench_args.append("--use_npu=true")
182
+
183
+ if num_runs != 50:
184
+ bench_args.append(f"--num_runs={num_runs}")
185
+ if warmup_runs != 1:
186
+ bench_args.append(f"--warmup_runs={warmup_runs}")
187
+ if min_secs != 1.0:
188
+ bench_args.append(f"--min_secs={min_secs}")
189
+ if max_secs != 150.0:
190
+ bench_args.append(f"--max_secs={max_secs}")
191
+ if warmup_min_secs != 0.5:
192
+ bench_args.append(f"--warmup_min_secs={warmup_min_secs}")
193
+ if input_layer_value_range:
194
+ bench_args.append(f"--input_layer_value_range={input_layer_value_range}")
195
+ if signature_key:
196
+ bench_args.append(f"--signature_to_run_for={signature_key}")
197
+
198
+ process = subprocess.Popen(
199
+ bench_args,
200
+ stdout=subprocess.PIPE,
201
+ stderr=subprocess.STDOUT,
202
+ text=True,
203
+ )
204
+
205
+ from litert_cli.core.log_filters import BenchmarkLogFilter
206
+
207
+ output_lines = []
208
+ log_filter = BenchmarkLogFilter(constants.DEFAULT_QUIET)
209
+
210
+ for line in process.stdout:
211
+ output_lines.append(line)
212
+ if log_filter.should_show(line):
213
+ click.echo(line, nl=False)
214
+
215
+ process.wait()
216
+ if process.returncode != 0:
217
+ click.secho(
218
+ f"Execution failed on desktop with exit code {process.returncode}",
219
+ fg="red",
220
+ )
221
+ click.echo("Full output for debugging:")
222
+ for line in output_lines:
223
+ click.echo(line, nl=False)
224
+ raise click.ClickException("Benchmark failed on desktop.")
225
+ except click.ClickException:
226
+ raise
227
+ except Exception as e:
228
+ raise click.ClickException(f"Failed to execute benchmark on desktop: {e}")