[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6BY5F447DRN3YDJVRWDG9MZ":3,"skill-same:01M6BY5F447DRN3YDJVRWDG9MZ":155},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":43,"commitSha":44,"skillDirectory":45,"forksCount":46,"repositoryHtmlUrl":47,"repositoryHomepageUrl":48,"ownerHtmlUrl":49,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":50,"install":51,"resources":64},"01M6BY5F447DRN3YDJVRWDG9MZ","litert-google-tensor",null,"Compiles LiteRT (.tflite) models ahead-of-time for Google Tensor TPUs (Tensor G5 on Pixel 10, Tensor G6 on Pixel 11) locally on Linux x86_64 or remotely via Google Colab (--colab on macOS, Windows, or ARM hosts), benchmarks them on a USB-attached Pixel with LiteRT's benchmark_model (NPU latency, peak memory, TPU tile time, TPU energy, optional CPU baseline), and packages compiled models for an Android Gradle app as a Google Play AI pack with the Tensor NPU runtime module and a Kotlin loader, applies it to the app and verifies it on a Pixel. Use when the user asks to compile, AOT-compile, or prepare a .tflite model for the Pixel TPU\u002FNPU, check whether a .tflite is already Tensor-compiled, measure on-device NPU performance of a compiled model, or package or deploy a compiled model into their Android app (or replace the one it ships). Don't use for Qualcomm, MediaTek, or Samsung NPUs, GPU-only benchmarking, LiteRT-LM or LLM (.litertlm) models, on-device JIT compilation, non-Gradle builds, or publishing to Google Play.","google-ai-edge\u002Flitert-samples","google-ai-edge","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F150697620?v=4","01M6BY4NHMZF88PJAC1E1QJY8N","01M6BY4NNXP8PC95Y6CABCJXP9",495,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"litert google tensor","litert","tensor","google","benchmark","kotlin","model","baseline","gpu","package","compile","play","packages","benchmarking","windows","tpu","performance","memory","linux","android","2026-11-10 10:12:30",false,"---\nname: litert-google-tensor\ndescription: >-\n  Compiles LiteRT (.tflite) models ahead-of-time for Google Tensor TPUs\n  (Tensor G5 on Pixel 10, Tensor G6 on Pixel 11) locally on Linux x86_64 or\n  remotely via Google Colab (--colab on macOS, Windows, or ARM hosts),\n  benchmarks them on a USB-attached Pixel with LiteRT's benchmark_model\n  (NPU latency, peak memory, TPU tile time, TPU energy, optional CPU baseline),\n  and packages compiled models for an Android Gradle app as a Google Play AI\n  pack with the Tensor NPU runtime module and a Kotlin loader, applies it to\n  the app and verifies it on a Pixel. Use when the\n  user asks to compile, AOT-compile, or prepare a .tflite model for the Pixel\n  TPU\u002FNPU, check whether a .tflite is already Tensor-compiled, measure\n  on-device NPU performance of a compiled model, or package or deploy a\n  compiled model into their Android app (or replace the one it ships). Don't\n  use for Qualcomm, MediaTek, or Samsung NPUs, GPU-only benchmarking,\n  LiteRT-LM or LLM (.litertlm) models, on-device JIT compilation, non-Gradle\n  builds, or publishing to Google Play.\n---\n\n# LiteRT on Google Tensor: compile, benchmark, package for apps\n\nThree scriptable workflows for Pixel TPUs:\n\n1.  **Compile** a plain `.tflite` into a Google Tensor AOT-compiled `.tflite`\n    with the public LiteRT Python API (`ai_edge_litert.aot`) - either locally on\n    Linux `x86_64` or remotely on a Google Colab VM (`--colab` via\n    `google-colab-cli`) when the user is on macOS\u002FWindows or lacks a Linux box.\n2.  **Benchmark** a compiled model on a connected Pixel with LiteRT's\n    `benchmark_model` binary, NPU-only, optionally next to a CPU baseline,\n    including TPU tile time (pure TPU compute time) and TPU energy per\n    inference, both from the Tensor runtime's hooks.\n3.  **Package** compiled builds for an Android (Gradle) app: a Google Play AI\n    pack written by LiteRT's own exporter (`ai_edge_litert.aot.ai_pack`) from\n    the files as they are, the Google Tensor NPU runtime as a feature module\n    (from Google Maven), a Kotlin loader and a step-by-step `INTEGRATION.md`.\n    For an app that already ships Google Tensor builds, it prepares only the\n    files to replace, under the app's own paths, with an `UPDATE.md`. When\n    the user agrees, the agent applies the plan to the project (one approval,\n    separate OKs for risky changes) and checks the app on a Pixel.\n\nEverything runs synchronously in the user's terminal; the scripts never start\nbackground processes and always clean up what they pushed to the phone or\nallocated on Colab. All paths below are relative to this skill's directory\n(`SKILL_DIR`).\n\n```text\nSKILL_DIR\u002F\n  versions.json                    # LiteRT versions the scripts use (single source)\n  scripts\u002Fcheck_compiled.py        # is this .tflite already Tensor-compiled?\n  scripts\u002Fcompile_model.py         # AOT compile (local Linux x86_64 or --colab)\n  scripts\u002Fbenchmark_on_device.py   # adb push -> benchmark -> parse -> cleanup\n  scripts\u002Fpackage_ai_pack.py       # compiled builds -> AI pack + Gradle modules for an app, or an update of one\n  scripts\u002Finstall_and_verify.py    # build the app, install it like Google Play, read the NPU\u002FCPU result\n  templates\u002F                       # Gradle, Kotlin, INTEGRATION.md and UPDATE.md templates (package_ai_pack.py)\n  references\u002Fcompile-flags.md      # API, compiler flags, and --colab reference\n  references\u002Fbenchmark-flags.md    # benchmark_model flags, output, binaries\n  references\u002Fapp-integration.md    # packaging for an app: contents, delivery modes, checks\n  references\u002Ftroubleshooting.md    # symptom -> fix tables\n  references\u002Fcontributing.md       # maintainers: adopting a new LiteRT release\n```\n\nEvery script supports `--help` and `--dry-run` (`check_compiled.py` is\nread-only, so its `--dry-run` is a no-op) and is Python 3 standard library only.\n`compile_model.py` imports LiteRT only for a local compile: its `--help`,\n`--dry-run`, `--print-install` (`[--colab]`), and `--colab` runs work with any\nlocal `python3`, while a local compile must use the virtualenv's interpreter\n(`\u003Cvenv>\u002Fbin\u002Fpython SKILL_DIR\u002Fscripts\u002Fcompile_model.py ...`, or activate the\nvenv first). `package_ai_pack.py` likewise imports LiteRT only for a real\nrun, which needs a Python with `ai-edge-litert` (the compile venv works; the\nBeta SDK is not needed); its `--check-project` mode and the other scripts run\nwith any `python3`.\n\n## Prerequisites (tell the user up front)\n\n| Need | Compile (local) | Compile (`--colab`) | Benchmark | Package (app) |\n|---|---|---|---|---|\n| **Google Tensor SDK Beta access** - sign up at https:\u002F\u002Fservices.google.com\u002Ffb\u002Fforms\u002Ftensor_ml_sdk_experimental_access (overview: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Flitert\u002Fnext\u002Ftensor-sdk). Google emails a download link for `litert_plugin_compiler.tar.gz`; the compiler cannot be installed without it. | required | required (`--sdk-tarball` or `--sdk-url`) | - | - |\n| Host OS | Linux **x86_64**, >= 16 GB RAM | any OS (`colab` CLI on PATH; runs on a remote Colab Linux `x86_64` VM) | any OS with `adb` | any OS where `ai-edge-litert` installs (the compile venv's Linux `x86_64` host works) |\n| Python environment | Local venv with `ai-edge-litert` + `ai-edge-litert-sdk-google-tensor` at the version in `versions.json` (`compile_model.py --print-install`) | Any local `python3` + `google-colab-cli` (`compile_model.py --print-install --colab`); SDK is installed on the Colab VM automatically | any `python3` (stdlib only) | a Python with `ai-edge-litert` (the compile venv works; the SDK package is not needed) |\n| `adb` on PATH, Pixel 10\u002F11 with USB debugging enabled | - | - | required | step 7 (install and check the app) |\n| A JDK the app's Gradle supports (`JAVA_HOME`), the Android SDK, the project's `.\u002Fgradlew` | - | - | - | steps 6-7 (build, install); bundletool is downloaded once |\n| `libLiteRtDispatch_GoogleTensor.so` - downloaded automatically from the public LiteRT binaries bucket (same directory as `benchmark_model`), or supplied with `--dispatch-lib` | - | - | required | - (the app gets it from Google Maven) |\n| Internet | once for `pip install` | required (Colab VM + `pip install`) | once to fetch `benchmark_model` + dispatch lib (cached in `~\u002F.cache\u002Flitert-google-tensor\u002F`) unless supplied locally | - (the app's Gradle build downloads the LiteRT Maven artifacts) |\n\nSupported targets: `Tensor_G5` (Pixel 10 family) and `Tensor_G6` (Pixel 11\nfamily) - the SoCs listed as supported by the public Tensor SDK docs.\n\n## Step 0 - Collect parameters\n\nAsk the user only for what is missing; never invent values:\n\nParameter                           | How to get it\n----------------------------------- | -------------\nModel path                          | Absolute path to the `.tflite`. Confirm it exists.\nGoal                                | check only, compile only, benchmark only, compile + benchmark, or package for an app (step 5). Infer it from the request (\"how fast is it on the TPU vs CPU\" = compile if needed + benchmark with a CPU baseline; \"put it in my app\" \u002F \"deploy it\" = package); do not add an unrequested device run.\nTarget SoC                          | For a **compile**, the user's stated device wins (Pixel 10 family = `Tensor_G5`, Pixel 11 family = `Tensor_G6`), even if a different Pixel is attached. For a **benchmark**, the attached phone decides: `adb shell getprop ro.soc.model` -> `Tensor G5` => `Tensor_G5`, `Tensor G6` => `Tensor_G6` (the benchmark script reads and enforces this itself).\nOutput path                         | Default `\u003Coutput-dir>\u002F\u003Cstem>_Google_\u003Ctarget>.tflite`. `compile_model.py` refuses to overwrite an existing file (exit 2): run `check_compiled.py` on the existing file and reuse it, or pass `--output-name`, or `--overwrite` after asking. When a second build is needed next to an existing one, name it `\u003Cstem>_Google_\u003Ctarget>.\u003CYYYYMMDD-HHMM>.tflite` (e.g. `--output-name model_Google_Tensor_G5.20260107-0930.tflite`) or, for a flag variant, `\u003Cstem>_Google_\u003Ctarget>.\u003Cflag-summary>.tflite` (`...G5.half-maxsharding.tflite`); keep the `_Google_\u003Ctarget>` part so the file stays recognisable.\nDevice serial                       | Only if `adb devices` lists more than one device.\nDispatch library \u002F benchmark binary | Optional. Both are downloaded automatically from the same public bucket directory and cached. Only ask if the user wants a specific build, or if the host is offline: then `--benchmark-bin \u003Cbenchmark_model>` plus `--dispatch-lib \u003ClibLiteRtDispatch_GoogleTensor.so \\| dir \\| .zip \\| .aar>` (also env `LITERT_GOOGLE_TENSOR_DISPATCH_LIB`) use local copies and no network is touched.\nCPU baseline                        | Optional; needs the path of the **original uncompiled** model (ask for it if only a compiled file was given; the benchmark script rejects a compiled file here with exit 2).\nRun counts \u002F mode                   | Defaults 5 warmup \u002F 50 measured, `burst` performance mode. `benchmark_model` treats counts as **minimums** and also runs at least 0.5 s warmup \u002F 1 s measured, so fast models execute more runs than requested. If the user states exact counts, say so and ask whether to disable the floors (`--extra-arg=--min_secs=0 --extra-arg=--warmup_min_secs=0`); \"steady-state\" \u002F \"sustained\" requests map to `--performance-mode sustained_performance`.\nTPU tile time                       | On by default, nothing to ask: a second, hooked NPU pass. `--no-tile-time` turns it off; the hooked pass itself (it repeats the NPU run, so slow models take about twice as long) is skipped only with `--no-tile-time --no-power`.\nTPU energy                          | On by default, nothing to ask: the power metric of the same hooked pass (the TPU rail's energy per inference). `--no-power` skips it. The script leaves it off, and says why, for a dispatch library whose power hook predates the fix.\nCompile mode                        | Local Linux `x86_64` by default. Use `--colab` when the host is not Linux `x86_64` (macOS\u002FWindows\u002FARM) or when the user asks to compile on Google Colab; ask for the Beta tarball path (`--sdk-tarball` \u002F `GOOGLE_TENSOR_SDK_BETA`) or URL (`--sdk-url` \u002F `GOOGLE_TENSOR_ML_SDK_URL`) before running.\nCompiler flags                      | Default to the compiler defaults; only set flags the user asks for or that troubleshooting requires (see `references\u002Fcompile-flags.md`).\nApp deployment level                | Packaging jobs only; nothing to ask up front. After step 5, tell the user the plan is ready (`INTEGRATION.md`, or `UPDATE.md` for an update) and ask whether you should apply it to their project (step 6). After that, offer to install the app on their Pixel and check it uses the NPU (step 7).\nNew integration or update           | Packaging jobs only. Ask whether the app already ships Google Tensor builds (an AI pack with folders like `model#group_Google_Tensor_G5` under its `src\u002Fmain\u002Fassets`). If it does, it is an **update** (step 5b): ask for the project root (the folder with `settings.gradle.kts` or `settings.gradle`). The pack, model file, device groups, delivery mode and app module then come from the project, so do not ask for them.\nBuilds to package                   | One compiled build per target SoC (`Tensor_G5`, `Tensor_G6`) plus the **original uncompiled** model they were compiled from (the app's CPU path). Compile missing targets first (step 2); a build is per SoC, so a Pixel 11 needs a `Tensor_G6` build. For an update, build every SoC the app already ships.\nDelivery mode                       | New integrations only (an update keeps the app's). When Google Play puts the model on the phone: `install-time` (default; installed with the app, ready at first launch), `fast-follow` (downloaded right after install) or `on-demand` (downloaded when the app asks). Explain each in one sentence (`references\u002Fapp-integration.md`) and use the default when the user has no preference.\nPack \u002F model names, app module      | New integrations only. Defaults: pack name = the original's file name made valid, model name = pack name, app module `app`, Kotlin package `com.example.litert`. Ask only when the user's project differs (another app module name, their package).\nQuick test (optional)               | New integrations only. Offer it when the user wants to try the model on their own Pixel before setting up bundletool: `--quick-test` adds a copy of each build and the steps for `.\u002Fgradlew installDebug -PtensorQuickTest=\u003CSoC>`, which puts the build and the NPU runtime in the debug APK. Local testing only; release builds are unaffected.\n\n## Step 1 - Check whether the model is already compiled\n\n```bash\npython3 SKILL_DIR\u002Fscripts\u002Fcheck_compiled.py \u002Fabs\u002Fpath\u002Fmodel.tflite [--expect-target Tensor_G5]\n```\n\nRun it on every model file involved (the model to compile or benchmark, the\nCPU-baseline file, and an existing output file you are about to reuse).\nFor a benchmark job read the phone first (`adb shell getprop ro.soc.model`,\nwhich prints `Tensor G5` or `Tensor G6`) and pass the underscore form as\n`--expect-target` (`Tensor G5` -> `--expect-target Tensor_G5`), so one check\nanswers both \"compiled?\" and \"for this phone?\" (the flag only matters for\ncompiled files; a plain model exits 1 either way); the benchmark script\nenforces the match again anyway (exit 4, before anything is downloaded or\npushed).\n\n| Exit | Meaning | Next action |\n|---|---|---|\n| 0 | compiled for Google Tensor (SoC printed) | skip compile; report the SoC (and the `Compile info` line if a `.compile.json` sidecar exists). Continue to step 3 only if the goal includes a benchmark. No sidecar (compiled elsewhere, flags unknown): for a plain \"how fast is it\" job, benchmark the file as-is and say \"compile flags unknown\"; when the user needs to know how it was built (or wants a reproducible comparison), compile the original with the defaults under a new `--output-name` and compare SHA-256 (`sha256sum` of both files, or `output_sha256` in the new sidecar): compilation is deterministic, so an identical hash proves the existing file is a default build (`references\u002Fcompile-flags.md`). Never `--overwrite` to find out. |\n| 1 | plain (uncompiled) model | go to step 2 if compiling; this is the correct file for `--cpu-baseline` |\n| 2 | compiled for a different SoC than `--expect-target` | compiled models are SoC-specific (a `Tensor_G5` file never runs on a `Tensor_G6` TPU or vice versa; there is no runtime fallback, and `--allow-mismatch` only produces dispatch errors). Tell the user which SoC the phone has, then offer, in this order: an existing `*_Google_\u003Cphone target>.tflite` build of the same model in the project (verify it with this check first; do not benchmark it unless the user says so), attaching the matching phone, or recompiling the **original** uncompiled export for the phone's target. |\n| 3 | not a `.tflite` \u002F missing | fix the path |\n| 4 | compiled by another toolchain: an Edge TPU custom op (`edgetpu-custom-op...`) instead of LiteRT's `DISPATCH_OP` | not usable here: LiteRT cannot recompile it or run it on the Google Tensor TPU, and the CPU cannot run it (so it is no `--cpu-baseline` or `--fallback` either; the scripts refuse it). Ask for the original model it was compiled from and compile that (step 2) |\n\nThe check looks for LiteRT's `DISPATCH_OP` custom op and `LiteRtStamp`\nmetadata, which the AOT compiler always writes; it needs no packages. Files\nfrom the Edge TPU compiler or older Google Tensor tooling carry an\n`edgetpu-custom-op` custom op instead (exit 4). The\nstamp is authoritative, the `_Google_Tensor_G5` file suffix is only the compile\nscript's naming convention. The stamp records the SoC but **not** the compiler\nflags - those come from the `\u003Cmodel>.compile.json` sidecar that\n`compile_model.py` writes next to its output (printed by the check when\npresent); without it, say \"flags unknown\" rather than guessing.\n\n## Step 2 - Compile (Linux x86_64 or Google Colab via `--colab`)\n\n### 2a. One-time environment\n\nThe SDK pip package contains no compiler by itself: at install time it unpacks\nthe Beta tarball that `GOOGLE_TENSOR_SDK_BETA` points to (same flow as the\npublic codelab). Ask the user for the tarball path first, then print the\ninstall commands for the LiteRT version the skill pins in `versions.json`:\n\n```bash\npython3 SKILL_DIR\u002Fscripts\u002Fcompile_model.py --print-install [--colab]\n```\n\n**Local Linux `x86_64`** (`--print-install`): runs with any `python3` and prints\ncomment lines (the pinned version, when and on what it was last verified, the\nsupported Python range, and whether this host and Python qualify) followed by:\n\n```text\npython3 -m venv ~\u002F.venvs\u002Flitert-tensor && source ~\u002F.venvs\u002Flitert-tensor\u002Fbin\u002Factivate\npip install --upgrade pip\nexport GOOGLE_TENSOR_SDK_BETA=\u002Fabs\u002Fpath\u002Flitert_plugin_compiler.tar.gz   # from the Beta e-mail\npip install --no-cache-dir ai-edge-litert==\u003Cpinned> ai-edge-litert-sdk-google-tensor==\u003Cpinned>\n```\n\n**Remote Google Colab (`--print-install --colab`)** - for macOS, Windows,\nnon-x86 Linux, or when compiling on Colab without a local Linux venv: installs\n`google-colab-cli` into `~\u002F.venvs\u002Fcolab-cli` (`references\u002Fcompile-flags.md`).\nNever ask the user to run a terminal command for OAuth: when `--colab` needs\none-time OAuth, show its printed URL in chat, ask the user for the code, and\nre-run with `--colab-auth-code \u003CCODE>`. `--colab` provisions a Colab Linux\n`x86_64` VM, installs the pinned LiteRT + SDK with the user's Beta tarball,\ncompiles, downloads `\u003Coutput>.tflite` + `\u003Coutput>.compile.json`, and stops the\nVM. Run the printed commands with the real tarball path; never type a LiteRT\nversion yourself.\n\n*   Any venv location works; a local compile needs only its interpreter. On\n    Debian\u002FUbuntu `python3 -m venv` may need `sudo apt install python3-venv`. If\n    `--print-install` warns about Python version, use `python3.N -m venv ...` in\n    the supported range.\n*   Without `GOOGLE_TENSOR_SDK_BETA` pip aborts with `SDK location is not set.\n    Aborting installation.`; with a wrong path, `Google Tensor SDK beta file not\n    found`. The e-mailed URL works via `GOOGLE_TENSOR_ML_SDK_URL=\u003Curl>` (or\n    `--sdk-url \u003Curl>` with `--colab`) instead of a local tarball.\n*   `--no-cache-dir` matters when re-installing (add `--force-reinstall` then):\n    a wheel built earlier **without** the compiler - on another OS, or with\n    `SKIP_SDK_DOWNLOAD` - stays in pip's cache and fails with\n    `liblitert_plugin_compiler.so: cannot open shared object file` (exit 5).\n*   Tell the user before installing anything. Install the exact pair\n    `--print-install` prints: the runtime and SDK packages must be one version\n    (`WARNING` when they differ; `NOTE` when they match each other but not the\n    pin). The pin changes only via `references\u002Fcontributing.md` (compatibility:\n    https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Ftensor-sdk\u002Frelease-notes#compatibility).\n    Use `-nightly` packages only when the user explicitly asks for nightlies.\n\n**If the user has no Beta access yet:** say so plainly - the compiler cannot be\ninstalled until Google e-mails the tarball link (sign-up form above) - and do\nnot install anything or allocate a Colab VM meanwhile (`--colab` checks for the\ntarball before `colab new`). Still useful now: run `check_compiled.py` on their\nfiles, confirm the host and phone SoC, and run `--dry-run` \u002F `--print-install`.\n\n### 2b. Compile\n\n```bash\n# Local Linux x86_64 (using the virtualenv's interpreter):\npython3 SKILL_DIR\u002Fscripts\u002Fcompile_model.py \u002Fabs\u002Fpath\u002Fmodel.tflite \\\n  --target Tensor_G5 --output-dir \u002Fabs\u002Fpath\u002Fcompiled\n\n# Remote Google Colab VM (any OS, any local python3 + google-colab-cli):\npython3 SKILL_DIR\u002Fscripts\u002Fcompile_model.py \u002Fabs\u002Fpath\u002Fmodel.tflite \\\n  --target Tensor_G5 --colab --sdk-tarball \u002Fabs\u002Fpath\u002Flitert_plugin_compiler.tar.gz \\\n  --output-dir \u002Fabs\u002Fpath\u002Fcompiled\n# -> \u002Fabs\u002Fpath\u002Fcompiled\u002Fmodel_Google_Tensor_G5.tflite\n```\n\n*   Sets `GOOGLE_TENSOR_BACKEND_ENABLED=1` (required by the stable package),\n    checks the installed LiteRT package versions against each other and\n    `versions.json`, calls `aot_compile(..., target=Target(SocModel.TENSOR_G5),\n    keep_going=False, google_tensor_*=...)`, prints the compilation report,\n    exports to `--output-dir`, re-verifies the Tensor stamp and writes\n    `\u003Coutput>.compile.json` (flags, `ops offloaded \u002F total`, package versions,\n    SHA-256 of input\u002Foutput\u002Fcompiler lib, elapsed time, plus `compiled_via:\n    \"colab\"` with `--colab`). Never overwrites an existing output (exit 2)\n    unless `--overwrite` is given.\n*   Optional compiler flags (`references\u002Fcompile-flags.md`): `--truncation-type\n    {auto,bfloat16,half,no_truncation}`, `--sharding-intensity\n    {minimal,moderate,extensive,maximum}`, `--int64-to-int32`,\n    `--large-model-support`, `--enable-4bit`, `--extra-options-path FILE`.\n    Script options: `--output-name`, `--overwrite`, `--keep-going`,\n    `--keep-intermediates`, `--no-report-file`, `--json`, `--dry-run`. Colab\n    options: `--colab`, `--sdk-tarball`, `--sdk-url`, `--colab-session`,\n    `--keep-colab-session`, `--colab-high-mem`, `--colab-timeout`,\n    `--colab-auth`, `--colab-auth-code`, `--colab-bin`.\n* If the compiler crashes with an internal error - or the user reports that it\n  did with the defaults (no need to reproduce the crash) - use\n  `--truncation-type half --sharding-intensity maximum` (public FAQ guidance)\n  and tell the user which flags produced the artifact. `half` changes the\n  numerics (16-bit floats on the TPU), so tell the user to validate the\n  model's outputs against the original before relying on it.\n* Read the printed compilation report: ops left on the CPU mean the strict\n  NPU benchmark will fail (see step 3, `--allow-cpu-fallback`).\n* Report a compile-only job from the script's output and sidecar, nothing\n  else:\n\n  ```markdown\n  **Compiled:** \u003Coutput path> (\u003Cbytes from \"Compiled model written to\" line>)\n  **Target \u002F flags:** \u003Ccli_flags from the sidecar, or \"--target X (compiler defaults)\">\n  **Ops offloaded:** \u003CN \u002F M from the \"Ops offloaded to the TPU\" line>\n  **LiteRT:** \u003C\"packages\" from the sidecar, plus the script's WARNING\u002FNOTE line if it printed one>\n  **Verified:** check_compiled.py -> \u003Cits Status line>\n  ```\n\nExit codes: 0 ok, 2 bad input, 3 already compiled, 4 unsupported host,\n5 LiteRT packages or Beta compiler library missing (message includes the\ninstall fix), 6 compile failed (compiler log errors are printed),\n7 output verification failed.\n\n## Step 3 - Benchmark on the device\n\n```bash\npython3 SKILL_DIR\u002Fscripts\u002Fbenchmark_on_device.py \\\n  --model \u002Fabs\u002Fpath\u002Fcompiled\u002Fmodel_Google_Tensor_G5.tflite \\\n  [--cpu-baseline \u002Fabs\u002Fpath\u002Fmodel.tflite] [--serial SERIAL] \\\n  [--dispatch-lib \u002Fabs\u002Fpath\u002FlibLiteRtDispatch_GoogleTensor.so] \\\n  [--output-dir \u002Fabs\u002Fpath\u002Fresults]\n```\n\nThe script, in order:\n\n1. Selects the device, reads `ro.soc.model`, and refuses to run a model\n   compiled for another SoC (`--allow-mismatch` overrides). `--dry-run`\n   skips this - it never contacts the device - so a dry run proves nothing\n   about the SoC match.\n2. Resolves the two host-side binaries, both cached under\n   `~\u002F.cache\u002Flitert-google-tensor\u002F` (file names `\u003Crelease>_android_arm64_\u003Cfile>`,\n   `\u003Crelease>` being the bucket directory) after the first download:\n   * `benchmark_model`: `--benchmark-bin`, else a one-time download of\n     `--benchmark-url` (default\n     `https:\u002F\u002Fstorage.googleapis.com\u002Flitert\u002Fbinaries\u002F\u003Crelease>\u002Fandroid_arm64\u002Fbenchmark_model`\n     with `\u003Crelease>` = `binaries_release` from `versions.json`; `--help`\n     prints the resolved URL).\n   * `libLiteRtDispatch_GoogleTensor.so`: `--dispatch-lib` (a `.so`, or a\n     `.zip`\u002F`.aar` it is extracted from), else env\n     `LITERT_GOOGLE_TENSOR_DISPATCH_LIB`, else a one-time download of\n     `--dispatch-url` (default: the same directory as `--benchmark-url`).\n   The script prints where each binary came from; `downloaded from \u003Curl>`\n   means this was the first time - tell the user what was fetched. Downloads\n   end with `(fetched \u003CUTC time>, md5 \u003Chash>)`, recorded when the file was\n   fetched; `fetch date not recorded` marks a copy cached by an older version\n   of the script (`--refresh-binary` re-downloads it and records the fetch).\n3. Pushes files to a fresh `\u002Fdata\u002Flocal\u002Ftmp\u002Flitert_benchmark\u002Frun-\u003Ctimestamp>-\u003Cpid>\u002F`\n   (`--device-dir` changes the base). Files already on the phone are never\n   overwritten or deleted. Leftovers from an interrupted run are therefore\n   always `\u003Cbase>\u002Frun-*`: if the user asks to clean up, `adb shell ls -la \u003Cbase>`\n   first and remove only `run-*` entries (never the base or anything else in it).\n4. Reads the thermal status (`dumpsys thermalservice`), then runs the NPU\n   benchmark in strict mode:\n   `--use_npu=true --use_cpu=false --require_full_delegation=true\n   --dispatch_library_path=\u003Cdir> --num_runs=50 --warmup_runs=5\n   --report_peak_memory_footprint=true --use_profiler=true`, retrying once\n   without the profiler if that run fails.\n5.  Runs the same NPU benchmark once more, without profiler and memory monitor,\n    with `LITERT_VENDOR_HOOK_ARGS` pointing at a hook config in the run\n    directory that enables the TPU tile-time and power metrics, and divides the\n    hooks' totals (`Total TPU tile time`, `Total Energy`) by that pass's own\n    warmup + measured runs. The hooks' own counts are only checks: older\n    dispatch libraries count every inference 1 + D times (D = dispatch nodes, so\n    2x for a normal compiled model), which is why the hooks' `Average` lines are\n    never used; an old power hook is not used at all (the script checks the\n    library first). Checks and evidence: `references\u002Fbenchmark-flags.md`, \"TPU\n    tile time\" and \"TPU energy\".\n6.  Optionally runs the CPU baseline on the **uncompiled** model\n    (`--use_cpu=true`, same run counts).\n7.  Prints the Markdown header + table, writes `results.json`, `report.md` (the\n    same block as a file), `npu.log`, `tile_time.log` (the hooked pass, + the\n    hooks' `tile_time_report.txt` and `power_report.txt`), `cpu.log`,\n    `npu_profile.txt`, `cpu_profile.txt`, and deletes the per-run device\n    directory (even on failure). `--output-dir` is only created once a benchmark\n    actually ran - after exit 2\u002F3\u002F4 or `--dry-run` there are no logs to look\n    for.\n\nThe script is NPU-only by design: `--extra-arg` passes extra `benchmark_model`\nflags but cannot switch the accelerator, and an uncompiled model is rejected\n(exit 2). For a GPU or non-Tensor run use `benchmark_model` directly\n(`--use_gpu=true` etc., https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Flitert\u002Fnext\u002Fbenchmark);\nthat is outside this skill.\n\nUseful options: `--num-runs`, `--warmup-runs`, `--signature`,\n`--performance-mode\n{extreme_power_saver,power_saver,balanced,high_performance,sustained_performance,burst}`\n(benchmark default `burst`), `--cpu-num-threads`, `--extra-arg=--min_secs=5`,\n`--device-dir`, `--benchmark-bin` + `--dispatch-lib` (local copies, offline),\n`--serial`, `--allow-cpu-fallback`, `--keep-device-files`, `--refresh-binary`,\n`--no-tile-time`, `--no-power`, `--dry-run`.\n\nOffline \u002F air-gapped host: pass both `--benchmark-bin` and `--dispatch-lib`\nand run with `--dry-run` first - the plan then shows `user-provided: \u003Cpath>`\nfor both binaries without contacting the phone or the network, which is the\nproof the user wants before the real run.\n\nBinary versions: keep `benchmark_model` and the dispatch library from the\n**same** release directory of the bucket (the default does: both come from\n`binaries_release` in `versions.json`); pass\n`--benchmark-url ...\u002Fbinaries\u002F\u003Crelease>\u002Fandroid_arm64\u002Fbenchmark_model` to pin\nanother one and the dispatch URL follows. A rolling directory (`latest\u002F`,\n`nightly\u002F`) changes over time, but a cached copy stays the build that was first\ndownloaded until `--refresh-binary`; the md5 in its provenance line tells builds\napart. A numbered directory can be re-uploaded as well (bucket notes in\n`references\u002Fbenchmark-flags.md`): if a provenance md5 differs from\n`verified.binaries` in `versions.json`, rerun with `--refresh-binary`. Local\nfiles carry no version string the script could compare, so for\n`--benchmark-bin`\u002F`--dispatch-lib` the rule is \"both from the same download\"; if\nthat is uncertain, re-download both from one directory. Not every versioned\ndirectory ships the Google Tensor dispatch library (which ones do: the bucket\ntable in `references\u002Fbenchmark-flags.md`) - if the download 404s, use the\ndefault URLs or supply `--dispatch-lib` from `litert_npu_runtime_libraries.zip`\non https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002FLiteRT\u002Freleases or a build per\nhttps:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Ftensor-sdk\u002Ftensor-faqs.\n\n## Step 4 - Report\n\nNothing in the report is typed by hand. `benchmark_on_device.py` prints a\nMarkdown header + table (also stored as the `markdown` field of\n`results.json`) in which every value is read from the device or the run:\n\nReport field                                | Comes from\n------------------------------------------- | ----------\nDevice name, SoC, Android version           | `ro.product.model`, `ro.soc.model`, `ro.build.version.release` on the attached phone (a Pixel 11 reports itself as Pixel 11 \u002F Tensor G6)\nModel name, size, compiled-for SoC          | the `.tflite` file and its `LiteRtStamp`\n**Compile** (flags, ops offloaded \u002F total)  | the `\u003Cmodel>.compile.json` sidecar next to the model; `unknown` when the model was compiled elsewhere - never fill it in from memory\n**Versions**                                | the sidecar's `ai-edge-litert` version (the LiteRT that compiled the model) vs the bucket release in the binaries' URLs (`user-provided` for local files): `same release`, `DIFFERENT releases`, or why the two cannot be compared (no sidecar, a rolling `latest`\u002F`nightly` build, local files, binaries from two sources); also stored as `versions` in `results.json`\n**Delegation (runtime)**                    | the `Replacing N out of M node(s) with delegate (DispatchDelegate)` line of this run's `npu.log` - the ground truth that everything ran on the TPU\nMode, requested run counts, NPU flags       | the flags actually passed to `benchmark_model`\n**Thermal before NPU run**                  | `dumpsys thermalservice`: the throttling status (0 = none) is the signal; the sensor value is one reading, informational only\n**TPU tile time** (header line + table row) | the tile-time pass: the hook's `Total TPU tile time` ÷ that pass's warmup + measured runs. The header names the hook's call count and what it means (`1 call per run, one per dispatch invoke`, or `2 calls per run (... the known double count, corrected)` with older dispatch libraries); `unavailable`, `failed`, `no report` or `inconsistent` (count matches no known pattern: totals only) carry the reason instead\n**TPU energy** (header line + table row)    | the same hooked pass: the power hook's `Total Energy` (the TPU rail from the first inference to the model's teardown, idle gaps included) ÷ that pass's warmup + measured runs, in mJ. The header names the hook's call count (`1 call per run, one per dispatch invoke`); `unavailable` (an old power hook, which the script does not enable, or a phone that refused the energy counter), `failed`, `no report`, `unreliable` (old hook's count pattern) or `inconsistent` carry the reason instead\nBinary \u002F dispatch library provenance        | the resolver (`downloaded from \u003Curl>`, `cached download of \u003Curl>`, `user-provided: \u003Cpath>`); downloads end with `(fetched \u003CUTC time>, md5 \u003Chash>)` recorded at download time, which identifies the build of a rolling release\nAll timings, memory, actual run counts      | parsed from the `BENCHMARK RESULTS` block of this run's log\n**Inference p5 \u002F p50 \u002F p95 (ms)**           | the measured-phase `count=N ... p5= median= p95=` stats line of the log (microseconds, converted to ms by the script); these are the only percentiles `benchmark_model` produces - there is no p99\n**Log check**                               | the script's scan of `npu.log` \u002F `cpu.log` \u002F `tile_time.log`: WARNING\u002FERROR-level lines that match the benign list in `references\u002Fbenchmark-flags.md` are only counted (`N known-benign`); any other such line is listed verbatim under **Unexpected log lines** after the table (de-duplicated, with a repeat count), and the line also says whether the profiler retry happened. No grepping of the logs is needed.\n\nPaste that block verbatim (it is also saved as `report.md` in the output\ndirectory) and add only the context the benchmark script cannot know:\n\n```markdown\n**Summary:** \u003CNPU \"Inference avg\" from the table> ms on the TPU vs \u003CCPU \"Inference avg\"> ms on the CPU (\u003Cthe N.NNx value from the script's \"NPU speed-up vs CPU\" line>); peak memory \u003CNPU> vs \u003CCPU> MB; \u003Cstrict NPU | CPU fallback allowed>. TPU tile time \u003C\"TPU tile time \u002F inference\" row> ms per inference. TPU energy \u003C\"TPU energy \u002F inference\" row> mJ per inference.\n\n\u003Cheader and table exactly as printed by benchmark_on_device.py, including any \"Unexpected log lines\" section>\n\n**Notes:** \u003Cwhat the Log check line reports: \"none\" when both logs show 0 unexpected lines and no profiler retry; otherwise each unexpected line with its meaning from references\u002Fbenchmark-flags.md or troubleshooting.md, plus anything the user should validate>\n```\n\nNever edit `report.md` (it is script output). If the user wants the full\nanswer saved, write it as `summary.md` next to `report.md`, with the block\ncopied from `report.md`; do not keep hand-typed numbers anywhere else.\n\nCombined compile + benchmark job: present only this benchmark block (its\n**Compile** header line already carries the sidecar's flags and op counts)\nand start the Notes with one line of compile context - that the input was\nuncompiled and was compiled first, the output path, and that nothing existing\nwas overwritten (`compile_model.py` made sure of that). The step 2b\ncompile-only block is for jobs that end at the compile.\n\nWithout a CPU baseline the Summary line is just `**Summary:** \u003CNPU \"Inference\navg\"> ms per inference on the TPU (\u003Cstrict NPU | CPU fallback allowed>, no CPU\ncomparison requested); peak memory \u003CNPU> MB.` plus the same TPU tile time and\nTPU energy sentences. Drop each of those two sentences when its header line says\n`not measured`, `unavailable`, `failed`, `no report`, `unreliable` or\n`inconsistent`, and give the header's reason in the Notes.\n\nWhen nothing ran (check exit 2, benchmark exit 2\u002F3\u002F4\u002F5, no device), report\nthe same way - from output, not from memory:\n\n```markdown\n**Not run:** \u003Cthe script's ERROR line verbatim>\n\n- Device: \u003Cthe \"Device: ...\" line printed by benchmark_on_device.py, or the `adb devices -l` line>\n- Model: \u003Cthe \"Status: ...\" line from check_compiled.py>\n- Nothing was pushed to or removed from the phone.\n\n**Options:** \u003Cthe next actions from step 1 \u002F troubleshooting, most specific first>\n```\n\n* NEVER type, round, or \"tidy up\" numbers, and NEVER reuse values from an\n  earlier run or from any example in this skill - copy the script output.\n  Prose numbers (the Summary line) must be character-for-character the table's\n  values.\n* Report the **actual** run counts from the table: `benchmark_model` treats\n  `--num_runs`\u002F`--warmup_runs` as minimums and also runs for at least 1 s \u002F\n  0.5 s, so fast models show more runs than requested (details in\n  `references\u002Fbenchmark-flags.md`, which also shows a complete example\n  output from a real run and the list of benign log lines).\n* The **Mode** line says whether the run was strict NPU or allowed CPU\n  fallback; call that out in prose as well, because fallback numbers mix NPU\n  and CPU time.\n* The **Versions** line compares the LiteRT that compiled the model with the\n  release that ran it. Mention `DIFFERENT releases`, a\n  `but SDK package X (DIFFERENT)` or binaries from two sources in the Notes -\n  numbers from such a mix are harder to compare with other runs - unless the\n  line ends with `(the pair verified in versions.json)`.\n  `not comparable (...)` and `cannot compare` need no comment beyond the line.\n* A speed-up below 1x is a real result, not a setup error, for tiny models\n  (per-inference NPU dispatch overhead of a few ms dominates); say so instead\n  of hunting for thermal or configuration problems.\n* The per-op profile (`npu_profile.txt`) shows one `DELEGATE` node whose\n  average is lower than the table's `Inference avg` (it excludes input\u002Foutput\n  handling); report the table value, not the profile value.\n*   **TPU tile time** is pure TPU hardware time, so it is below `Inference avg`\n    (which adds host and driver work). Quote it from the table only, never from\n    the hook's own lines in `tile_time.log` \u002F `tile_time_report.txt`: older\n    dispatch libraries inflate its `Total inferences`, so their `Average` is too\n    low (half for a normal compiled model), while the table value is right with\n    every library.\n*   **TPU energy** is the TPU rail's energy (the phone's on-device power\n    monitor) for the whole hooked pass divided by its runs, so it includes the\n    TPU's idle gaps between back-to-back inferences: compare it between models,\n    builds and `--performance-mode`s on the same phone, and do not present it as\n    a lab power measurement or as the energy of one isolated inference. Quote it\n    from the table only.\n\n## Step 5 - Package for an app (Gradle)\n\nTwo cases (Step 0, \"New integration or update\"): **5a** for an app without\nGoogle Tensor builds yet, **5b** for an app that already ships them and gets\nnew builds (a new model version, other compile flags, a new LiteRT release).\n\n### 5a - New integration\n\nTurns the builds from step 2 into what an Android Gradle app needs to ship\nthem through Google Play (details: `references\u002Fapp-integration.md`): an AI\npack written by LiteRT's own exporter from the files as they are (nothing is\nrecompiled; the packed models are byte-identical), the Google Tensor NPU\nruntime as an install-time feature module (from Google Maven), the original\nmodel for the app's base assets (the CPU path), a Kotlin loader, and an\n`INTEGRATION.md` with the user's names and versions filled in.\n\n```bash\n\u003Cvenv>\u002Fbin\u002Fpython SKILL_DIR\u002Fscripts\u002Fpackage_ai_pack.py \\\n  --compiled \u002Fabs\u002Fpath\u002Fcompiled\u002Fmodel_Google_Tensor_G5.tflite \\\n  [--compiled \u002Fabs\u002Fpath\u002Fcompiled\u002Fmodel_Google_Tensor_G6.tflite] \\\n  --fallback \u002Fabs\u002Fpath\u002Fmodel.tflite \\\n  [--delivery install-time|fast-follow|on-demand] [--pack-name NAME] \\\n  [--model-name NAME] [--app-module app] [--kotlin-package com.example.litert] \\\n  [--quick-test] [--output-dir \u002Fabs\u002Fpath\u002Fapp_package\u002FNAME]\n# -> \u003Cfallback dir>\u002Fapp_package\u002F\u003Cpack name>\u002FINTEGRATION.md and the modules\n```\n\n*   Run `check_compiled.py` on every file first (step 1), and package builds\n    that passed the strict benchmark (step 3). To benchmark with the exact\n    runtime library the app will ship, pass the Maven AAR as `--dispatch-lib`\n    (`references\u002Fapp-integration.md`, \"Runtime facts\").\n*   A real run needs a Python with `ai-edge-litert` (the compile venv works; no\n    Beta SDK). `--help` and `--dry-run` work with any `python3`: run\n    `--dry-run` first to check the inputs without writing anything.\n*   Before writing, the script checks that each `--compiled` file is stamped\n    for `Tensor_G5`\u002F`Tensor_G6` with one build per SoC, that `--fallback` is\n    uncompiled, and that each build matches its `.compile.json` (SHA-256,\n    target) and was compiled from `--fallback` (only that last check can be\n    overridden, with `--allow-source-mismatch`). It writes into a staging\n    directory, verifies the result (pack folders, byte-identical models,\n    stamps, the same device groups in the XML and the runtime module, the CPU\n    model) and only then moves it to `--output-dir`. It never replaces a\n    directory it did not write; `--overwrite` replaces only its own package.\n*   Report from the script's output: paste its `**Packaged:** ...` block\n    verbatim, then one line for the user: the delivery mode in plain words and\n    \"next: follow `\u003Coutput-dir>\u002FINTEGRATION.md`\".\n*   `INTEGRATION.md` lists everything left to do in the user's project: copy\n    the modules, the Gradle and manifest edits, the loader call, and testing\n    on a Pixel with bundletool. The app needs no LiteRT or NPU code\n    beforehand: the package brings the LiteRT runtime dependency, the Google\n    Tensor dispatch library (inside the runtime feature module, from Maven),\n    the models and the loader. Then ask: \"The integration plan is ready\n    (`INTEGRATION.md`). Do you want me to apply it to your project?\" If yes,\n    go to step 6a; if not, the guide is theirs to follow. Publishing to Google\n    Play stays with the user.\n*   `--quick-test` (Step 0, optional) also writes `quick_test\u002F\u003CSoC>\u002Fassets\u002F`\n    and an INTEGRATION.md section: copied to `\u003Capp module>\u002Ftensor_quick_test\u002F`\n    with a small Gradle block, `.\u002Fgradlew installDebug\n    -PtensorQuickTest=\u003CSoC>` builds a debug APK that carries the Tensor build\n    and the NPU runtime itself, so the model runs on the TPU with no\n    bundletool. Nothing changes without the property, and release builds\n    never change. It is for trying the model, not a substitute for the\n    bundletool test before a release.\n\nExit codes: 0 ok, 2 bad input (missing file, duplicate SoC, invalid name,\noutput exists, a build that differs from its sidecar), 3 wrong model kind\n(uncompiled `--compiled`, compiled `--fallback`), 5 LiteRT not importable or\nits exporter API changed (the message has the fix), 7 post-export verification\nfailed (nothing is left at `--output-dir`).\n\n### 5b - Update builds the app already ships\n\nThe same script with `--update-project` prepares only the files to replace,\nunder the app's own paths and names, and finds out whether anything else must\nchange (details: `references\u002Fapp-integration.md`, \"Updating builds an app\nalready ships\").\n\n```bash\n\u003Cvenv>\u002Fbin\u002Fpython SKILL_DIR\u002Fscripts\u002Fpackage_ai_pack.py \\\n  --update-project \u002Fabs\u002Fpath\u002Fto\u002Fapp-project \\\n  --compiled \u002Fabs\u002Fpath\u002Fcompiled\u002Fmodel_Google_Tensor_G5.tflite \\\n  [--compiled \u002Fabs\u002Fpath\u002Fcompiled\u002Fmodel_Google_Tensor_G6.tflite] \\\n  --fallback \u002Fabs\u002Fpath\u002Fmodel.tflite \\\n  [--pack-name NAME] [--model-name NAME] [--cpu-asset PATH|none] [--dry-run]\n# -> \u003Cfallback dir>\u002Fapp_update\u002F\u003Cpack>\u002FUPDATE.md, update.json, files\u002F\u003Cpaths>\n```\n\n*   The project is only read. The script finds the AI pack with Google Tensor\n    folders, the device-targeting XML (groups are matched by SoC, so the\n    app's own group names work), the runtime (Maven artifact, version catalog\n    or a bundled `.so`), a generated `TensorModelLoader`, and the app's CPU\n    copy of the model (the pack's `#group_other` folder in LiteRT's default\n    layout, or the base assets in 5a's layout). It runs the same input checks\n    and LiteRT export as 5a and writes the new files to `files\u002F`, at the same\n    paths as in the project. `--pack-name` and `--model-name` only pick one\n    when the project has several; `--cpu-asset` names the CPU copy when the\n    script cannot find it (the error lists candidates). Quick-test copies in\n    `\u003Capp module>\u002Ftensor_quick_test\u002F\u003CSoC>\u002F` (5a's `--quick-test`) are\n    replaced too.\n*   Run `--dry-run` first and show the user the verdict and the file list:\n    `drop-in` (only model files change), `structural` (section 2 of\n    `UPDATE.md` lists the other changes: a new SoC needs a group in the XML,\n    the runtime module and the loader; a SoC without a new build keeps\n    shipping its old one until it is compiled or removed; changed model\n    inputs or outputs need app code changes; a missing runtime or mismatched\n    LiteRT versions) or `up-to-date`. The inputs\u002Foutputs comparison needs\n    LiteRT, so it runs only in the real run, which can turn `drop-in` into\n    `structural`. For `structural`, list the changes and ask how to proceed\n    before anything else.\n*   A `Runtime` check of `device` means the app's runtime is not the release\n    this skill pins, or its version is unknown: the new builds must pass the\n    strict benchmark (step 3) with the app's own library before release\n    (`--dispatch-lib` with the app bundle, the runtime AAR or the `.so`);\n    `UPDATE.md` section 4 has the exact commands.\n*   Report from the script's output: paste its `**Update plan:** ...` block\n    verbatim, then one line: the verdict in plain words and \"next:\n    `\u003Coutput-dir>\u002FUPDATE.md`\". Then ask: \"The update plan is ready\n    (`UPDATE.md`). Do you want me to apply it to your project?\" If yes, go to\n    step 6b. Never copy a 5a package over an existing pack.\n\nExit codes as in 5a; exit 2 also covers a project the script cannot update\nas is (no AI pack with Google Tensor builds - then it is a new integration -,\nseveral packs or models without `--pack-name`\u002F`--model-name`, a device group\nthat selects several SoCs, an unknown CPU copy).\n\n## Step 6 - Apply the plan to the user's project (when they say yes)\n\nYou apply the plan; the user approves it once. Before changing anything,\nshow the **whole plan** and get **one approval**. Then ask **separately** for\neach risky item (the check marks them): upgrading the Android Gradle plugin\nor Kotlin, adding Kotlin, raising `minSdk`, limiting the app to arm64-v8a\n(devices without it can no longer install the app), changing the app's\nLiteRT version, replacing project files that differ from the package,\nremoving a SoC. A declined item is skipped; say what will not work without\nit.\n\n### 6a - New integration\n\n1.  Ask for the project root (the folder with `settings.gradle.kts` or\n    `settings.gradle`). If it is a git checkout with uncommitted changes\n    (`git status --porcelain`), say so in the plan and suggest committing\n    first.\n2.  Get the plan (read-only, any `python3`, no LiteRT):\n\n    ```bash\n    python3 SKILL_DIR\u002Fscripts\u002Fpackage_ai_pack.py --check-project \u002Fabs\u002Fpath\u002Fto\u002Fapp-project \\\n      --package \u003Coutput-dir of step 5a> [--app-module app] [--json]\n    ```\n\n    Every requirement of `INTEGRATION.md` comes back as `pass`, `fail` or\n    `warn`, each `fail` with its fix: a copy command, or a snippet and the\n    file it goes in (in the project's Kotlin or Groovy DSL), and `risky` when\n    it needs a separate OK. The `fail` items are the plan; offer the `warn`\n    items (for example the optional quick test) as options.\n3.  Show the plan once: the files to copy, the files to edit with their\n    snippets, and the risky items marked. Get the approval, then the\n    separate OKs.\n4.  Apply it: run the copy commands; make the edits with your file-editing\n    tool, merged into the existing blocks (`android { }`, `defaultConfig`,\n    `dependencies`, the version catalog when the project uses one) in the\n    file's own style. Never delete or reformat unrelated code, and never\n    touch signing configs.\n5.  Re-run step 2 until nothing fails (apart from declined items).\n6.  Build once from the project root: `.\u002Fgradlew :\u003Capp module>:bundleDebug`.\n    Fix errors your edits caused; report errors that were there before\n    instead of fixing them.\n7.  Report what changed, and how the app uses the model: `INTEGRATION.md`\n    step 9 has the loader call, `model_io.json` the inputs and outputs. If\n    the user tells you where the model is used, offer to write that code\n    (the pre- and post-processing depend on their app, so show it first).\n    Then offer step 7.\n\nExit codes of `--check-project`: 0 nothing to do, 1 something fails (the\nreport lists it), 2 bad input (no project, no package, no app module).\n\n### 6b - Update\n\nThe plan is `UPDATE.md`: the file table and section 2. Get one approval for\nthe files and the non-risky edits, and separate OKs for the risky ones\n(removing a SoC, changing the runtime or LiteRT version, replacing a loader\nwith local changes; changed model inputs or outputs need app code changes,\nso show that code first). Then: copy each project file you replace to\n`\u003Coutput-dir>-backup\u002F\u003Cpath>` (not inside `\u003Coutput-dir>`, which `--overwrite`\nreplaces), copy `files\u002F\u003Cpath>` to `\u003Cproject>\u002F\u003Cpath>`, make the section 2\nchanges, and raise `versionCode`. Re-run the step 5b command with\n`--dry-run`: every file must show `unchanged`. Build once (as in 6a, step\n6), then offer step 7.\n\n## Step 7 - Install and check on a Pixel (when the user agrees)\n\nBuilds the user's app, installs it on the attached Pixel the way Google Play\nwould (bundletool local testing, for the phone's device group), and reads\nthe loader's `LITERT_TENSOR_RESULT` line:\n\n```bash\npython3 SKILL_DIR\u002Fscripts\u002Finstall_and_verify.py --project \u002Fabs\u002Fpath\u002Fto\u002Fapp-project \\\n  [--app-module app] [--variant debug|release|\u003Cflavor>Debug] [--serial \u003Cid>] \\\n  [--uninstall-first] [--dry-run]\n```\n\n*   Needs a JDK the project's Gradle supports (`JAVA_HOME`), the Android SDK\n    (adb), the project's `.\u002Fgradlew` (or `--gradle`, which may carry\n    options such as `-Pname=value`), and a Pixel with USB debugging.\n    bundletool is downloaded once (`bundletool_release`, cached) or given\n    with `--bundletool \u003Cbundletool-all jar or folder of jars>`. Run\n    `--dry-run` first: it shows the phone, its device group and whether the\n    NPU path is expected. An app with product flavors needs `--variant`\n    (for example `freeDebug`).\n*   Local testing cannot update an installed app. Exit 7 means the app is\n    installed: ask the user (uninstalling deletes the app's data on the\n    phone), then rerun with `--uninstall-first`.\n*   The app must call `TensorModelLoader.load()`. If it loads the model on a\n    particular screen, tell the user to open it while the script waits (exit\n    6 = no result line within `--timeout`).\n*   Report the script's block verbatim. `\"path\":\"npu\"` is the pass; for\n    `\"path\":\"cpu\"` on a phone the app has a build for, the `npu_error` says\n    why (`references\u002Ftroubleshooting.md`). The app stays installed.\n\nExit codes: 0 ran on the NPU (or on the CPU where no Tensor build is\nexpected; with `--no-launch`: installed), 1 CPU on a phone with a Tensor\nbuild, 2 environment (project, tools, device), 3 Gradle build failed, 4\nbundletool failed, 6 no result line, 7 already installed.\n\n## Rules\n\n* ALWAYS run `check_compiled.py` before compiling or benchmarking, on every\n  model file involved - the compiler cannot recompile a compiled model, and\n  the TPU cannot run an uncompiled one.\n* ALWAYS verify the device SoC matches the model's stamp (the benchmark\n  script does) - a `Tensor_G5` model on a `Tensor_G6` phone fails with an\n  opaque dispatch error, wasting a debugging round.\n* ALWAYS use the original uncompiled model for `--cpu-baseline` - compiled\n  models contain `DISPATCH_OP` nodes that only the NPU can execute.\n* ALWAYS clean up the per-run device directory (automatic; after an\n  interrupted run list the base first, then remove only its `run-*` entries:\n  `adb shell ls -la \u003Cbase>` and `adb shell rm -rf \u003Cbase>\u002Frun-*`, where `\u003Cbase>`\n  is `\u002Fdata\u002Flocal\u002Ftmp\u002Flitert_benchmark` or the `--device-dir` that was used).\n  NEVER `rm -rf` the base directory itself - users keep their own binaries\n  and models there.\n* NEVER combine `--use_cpu=true` with `--require_full_delegation=true` -\n  `benchmark_model` aborts on that combination.\n* NEVER install packages into the system interpreter or without telling the\n  user; use a virtualenv so pinned LiteRT versions do not break other work.\n* NEVER type LiteRT version numbers into install commands or download URLs\n  yourself - take them from `compile_model.py --print-install` and the\n  scripts' default URLs, which all read `versions.json`, so every job uses the\n  pair that was verified together. Use another release only when the user\n  asks for one (`--benchmark-url ...\u002Fbinaries\u002F\u003Crelease>\u002F...`, or the pip pair\n  at that version) and say so in the report. Changing the pin is a maintainer\n  task (`references\u002Fcontributing.md`), not part of a user job.\n*   NEVER treat macOS, Windows, or ARM hosts as a compile blocker or give manual\n    browser Colab notebook steps - run `compile_model.py --colab` directly.\n*   NEVER leave adb, benchmark, or Colab VM sessions running in the background -\n    every command here is bounded by a timeout and returns before the next step\n    (`compile_model.py --colab` stops any Colab VM it creates unless\n    `--keep-colab-session` is passed).\n*   NEVER guess compile flags or overwrite an existing output without asking\n    (`compile_model.py` exits 2; use `--output-name` or ask before\n    `--overwrite`).\n*   ALWAYS package for an app with `package_ai_pack.py` (it calls LiteRT's\n    exporter and verifies the result); NEVER create or edit AI pack folders,\n    the device-targeting XML or the runtime modules by hand.\n*   ALWAYS use `--update-project` (step 5b) when the app already ships Google\n    Tensor builds; NEVER copy a new package's `ai_packs\u002F` over an existing\n    pack - the app's group names and the place of its CPU copy can differ.\n*   ALWAYS ask before changing the user's project, show the whole plan, and\n    get one approval; ask separately for each risky item (step 6) and before\n    `install_and_verify.py --uninstall-first`. NEVER change signing configs.\n*   NEVER put a compiled build where the app runs on the CPU (base assets,\n    `--fallback`): only the original uncompiled model.\n*   NEVER publish to Google Play, sign, or upload anything for the user; the\n    skill stops at the package and the local-testing steps.\n\n## Troubleshooting (short list)\n\nSymptom                                                                                            | Fix\n-------------------------------------------------------------------------------------------------- | ---\n`compile_model.py` exit 2 `output already exists`                                                  | Reuse it (after `check_compiled.py`), or `--output-name`, or ask and `--overwrite`\n`compile_model.py` exit 3 `already compiled`                                                       | The input is a compiled artifact; compile the original export (`--force` does not help)\n`check_compiled.py` exit 4 \u002F `compiled by another toolchain` (any script)                          | The file holds an Edge TPU custom op (`edgetpu-custom-op...`) from another compiler; no flag helps. Get the original model it was made from and compile that (step 2)\n`compile_model.py` exit 5                                                                          | Local: LiteRT\u002FSDK not importable or no compiler library (use the venv's interpreter, or reinstall with `GOOGLE_TENSOR_SDK_BETA` set). `--colab`: `colab` CLI missing, auth failed (`colab sessions`), or `--sdk-tarball` not supplied - see 2a\n`compile_model.py` `WARNING: ai-edge-litert X and ai-edge-litert-sdk-google-tensor Y differ`       | The two packages must be one version: run the reinstall line the warning prints (pinned version, `--force-reinstall`) with `GOOGLE_TENSOR_SDK_BETA` set\n`INTERNAL error at compiler_plugin.cc` \u002F abort during compile                                      | `--truncation-type half --sharding-intensity maximum`\n\"does not support layers with dynamic shapes\"                                                      | Re-export the model with static shapes\n`compile_model.py` exit 4                                                                          | Host is not Linux `x86_64` - re-run with `--colab --sdk-tarball \u002Fabs\u002Fpath\u002Flitert_plugin_compiler.tar.gz`, or compile on a Linux `x86_64` machine and benchmark here\n`adb devices` lists nothing                                                                        | USB: re-plug, accept the USB-debugging prompt. Forwarded phone (serial `host:port`, e.g. `localhost:41015`): `adb connect host:port`. Prefer `adb reconnect` over `adb kill-server` (the latter drops every device on a shared host)\n\"more than one device connected\"                                                                   | `--serial \u003Cid>` from `adb devices`\nStrict NPU run fails, `--allow-cpu-fallback` works                                                 | Not fully delegated; check the compilation report\nRun dies right after `Loading shared library: ...\u002FlibLiteRtDispatch_GoogleTensor.so`               | Unusable dispatch `.so` (bad download\u002FABI, or far from the binary's release) - `--refresh-binary` with the defaults\n`download failed (...)` for the dispatch library (exit 5)                                          | HTTP 404 = that bucket directory has no Google Tensor dispatch lib; otherwise the host is offline. Use the default URLs (release from `versions.json`), or pass `--dispatch-lib`\n`benchmark_on_device.py` exit 6 `exited 0 but printed no results in the format this script parses` | The `benchmark_model` build prints a format the parser does not know (newer or older binary). Re-run with the default URLs, or pin a release with `--benchmark-url`; the fix in the skill is in `references\u002Fcontributing.md`\n`**TPU tile time:** unavailable - the dispatch library returned no telemetry hook`                 | The `--dispatch-lib` was built from the LiteRT GitHub source (hooks compiled out): use the default bucket binaries, or `--no-tile-time`\n`**TPU energy:** unavailable - this dispatch library's power hook predates the fix ...`            | The library has the old power hook (e.g. a Maven runtime AAR passed as `--dispatch-lib`, or a stale cache): use the default binaries (`--refresh-binary` for a stale cache). Latency and tile time are unaffected\n`**TPU energy:** unavailable - the power hook could not read the TPU energy counter: ...`          | The phone refused the shell access to its power monitor: report the quoted line. Latency and tile time are unaffected\n`package_ai_pack.py` exit 5                                                                        | Run it with the Python that has `ai-edge-litert` (the compile venv; the message prints the install line). \"exporter failed\": install the pinned `ai-edge-litert` (`compile_model.py --print-install`)\n`package_ai_pack.py` exit 2 `differs from the build its .compile.json describes`                   | The file changed after it was compiled: package the build that was compiled and benchmarked, or recompile\nbundletool: `refers to device groups [...] which are not in the list [...]`                        | The app's `device_targeting_configuration.xml` lacks the package's groups: copy or merge it from the package (`INTEGRATION.md` step 2)\nApp log `LITERT_TENSOR_RESULT {\"path\":\"cpu\", ...}` on a packaged Pixel                             | Check `adb shell pm path` for the `google_tensor_runtime` split, `useLegacyPackaging = true`, and one release for `litert` and `litert-npu-runtime-google-tensor`\n`--update-project` exit 2 `no AI pack with Google Tensor builds`                                   | The app has no Google Tensor setup yet: package without `--update-project` (step 5a)\n`--update-project` exit 2 `cannot tell where the app keeps the original (CPU) model`               | `--cpu-asset \u003Cpath in the project>` (the error lists the models it found), or `--cpu-asset none` if the app has no CPU copy\nQuick test (`installDebug -PtensorQuickTest=\u003CSoC>`) logs `\"path\":\"cpu\"`                            | `\u003Capp module>\u002Ftensor_quick_test\u002F\u003CSoC>\u002Fassets\u002F` must exist (Gradle ignores a missing folder), `\u003CSoC>` must be the phone's (`adb shell getprop ro.soc.model`, underscore form), and the Gradle block must be in the app module\n`install_and_verify.py` exit 7 `already installed`                                                 | Ask the user (it deletes the app's data on the phone), then rerun with `--uninstall-first`\n`install_and_verify.py` exit 6 (no result line)                                                    | The app must call `TensorModelLoader.load()`: open the screen that uses the model while it waits, or raise `--timeout`\n`install_and_verify.py` exit 4 `cannot download ...\u002Fbundletool-all-...jar`                         | Offline or GitHub blocked: download a `bundletool-all` jar elsewhere and pass `--bundletool \u003Cjar>`\n\nFull tables: `references\u002Ftroubleshooting.md`.\n\n## Public references\n\n* LiteRT NPU guide: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Flitert\u002Fnext\u002Fnpu\n* Google Tensor with LiteRT: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Flitert\u002Fnext\u002Ftensor-sdk\n* Compilation flags: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Ftensor-sdk\u002Fcompilation-flags\n* Tensor SDK FAQ: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Ftensor-sdk\u002Ftensor-faqs\n* Release notes \u002F compatibility: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Ftensor-sdk\u002Frelease-notes\n* benchmark_model tool: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Flitert\u002Fnext\u002Fbenchmark\n* Codelab: https:\u002F\u002Fcodelabs.developers.google.com\u002Fcodelabs\u002Fgoogle-tensor-ml-sdk\n*   LiteRT AOT Compilation Tutorial (Colab):\n    https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fsamples\u002Flitert\u002Fcolab\u002FLiteRT_AOT_Compilation_Tutorial.ipynb\n*   google-colab-cli: https:\u002F\u002Fgithub.com\u002Fgooglecolab\u002Fgoogle-colab-cli\n* LiteRT source and releases: https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002FLiteRT\n* Play for On-device AI (AI packs, delivery, device targeting):\n  https:\u002F\u002Fdeveloper.android.com\u002Fgoogle\u002Fplay\u002Fon-device-ai\n* Play Feature Delivery: https:\u002F\u002Fdeveloper.android.com\u002Fguide\u002Fplaycore\u002Ffeature-delivery\n* bundletool: https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fbundletool\u002Freleases\n\n## Contributions\n\nTo contribute or modify this skill or its references, follow the\n[contribution guidelines](references\u002Fcontributing.md) before making changes.\n","# LiteRT on Google Tensor: compile, benchmark, package for apps\n\nThree scriptable workflows for Pixel TPUs:\n\n1.  **Compile** a plain `.tflite` into a Google Tensor AOT-compiled `.tflite`\n    with the public LiteRT Python API (`ai_edge_litert.aot`) - either locally on\n    Linux `x86_64` or remotely on a Google Colab VM (`--colab` via\n    `google-colab-cli`) when the user is on macOS\u002FWindows or lacks a Linux box.\n2.  **Benchmark** a compiled model on a connected Pixel with LiteRT's\n    `benchmark_model` binary, NPU-only, optionally next to a CPU baseline,\n    including TPU tile time (pure TPU compute time) and TPU energy per\n    inference, both from the Tensor runtime's hooks.\n3.  **Package** compiled builds for an Android (Gradle) app: a Google Play AI\n    pack written by LiteRT's own exporter (`ai_edge_litert.aot.ai_pack`) from\n    the files as they are, the Google Tensor NPU runtime as a feature module\n    (from Google Maven), a Kotlin loader and a step-by-step `INTEGRATION.md`.\n    For an app that already ships Google Tensor builds, it prepares only the\n    files to replace, under the app's own paths, with an `UPDATE.md`. When\n    the user agrees, the agent applies the plan to the project (one approval,\n    separate OKs for risky changes) and checks the app on a Pixel.\n\nEverything runs synchronously in the user's terminal; the scripts never start\nbackground processes and always clean up what they pushed to the phone or\nallocated on Colab. All paths below are relative to this skill's directory\n(`SKILL_DIR`).\n\n```text\nSKILL_DIR\u002F\n  versions.json                    # LiteRT versions the scripts use (single source)\n  scripts\u002Fcheck_compiled.py        # is this .tflite already Tensor-compiled?\n  scripts\u002Fcompile_model.py         # AOT compile (local Linux x86_64 or --colab)\n  scripts\u002Fbenchmark_on_device.py   # adb push -> benchmark -> parse -> cleanup\n  scripts\u002Fpackage_ai_pack.py       # compiled builds -> AI pack + Gradle modules for an app, or an update of one\n  scripts\u002Finstall_and_verify.py    # build the app, install it like Google Play, read the NPU\u002FCPU result\n  templates\u002F                       # Gradle, Kotlin, INTEGRATION.md and UPDATE.md templates (package_ai_pack.py)\n  references\u002Fcompile-flags.md      # API, compiler flags, and --colab reference\n  references\u002Fbenchmark-flags.md    # benchmark_model flags, output, binaries\n  references\u002Fapp-integration.md    # packaging for an app: contents, delivery modes, checks\n  references\u002Ftroubleshooting.md    # symptom -> fix tables\n  references\u002Fcontributing.md       # maintainers: adopting a new LiteRT release\n```\n\nEvery script supports `--help` and `--dry-run` (`check_compiled.py` is\nread-only, so its `--dry-run` is a no-op) and is Python 3 standard library only.\n`compile_model.py` imports LiteRT only for a local compile: its `--help`,\n`--dry-run`, `--print-install` (`[--colab]`), and `--colab` runs work with any\nlocal `python3`, while a local compile must use the virtualenv's interpreter\n(`\u003Cvenv>\u002Fbin\u002Fpython SKILL_DIR\u002Fscripts\u002Fcompile_model.py ...`, or activate the\nvenv first). `package_ai_pack.py` likewise imports LiteRT only for a real\nrun, which needs a Python with `ai-edge-litert` (the compile venv works; the\nBeta SDK is not needed); its `--check-project` mode and the other scripts run\nwith any `python3`.\n\n## Prerequisites (tell the user up front)\n\n| Need | Compile (local) | Compile (`--colab`) | Benchmark | Package (app) |\n|---|---|---|---|---|\n| **Google Tensor SDK Beta access** - sign up at https:\u002F\u002Fservices.google.com\u002Ffb\u002Fforms\u002Ftensor_ml_sdk_experimental_access (overview: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Flitert\u002Fnext\u002Ftensor-sdk). Google emails a download link for `litert_plugin_compiler.tar.gz`; the compiler cannot be installed without it. | required | required (`--sdk-tarball` or `--sdk-url`) | - | - |\n| Host OS | Linux **x86_64**, >= 16 GB RAM | any OS (`colab` CLI on PATH; runs on a remote Colab Linux `x86_64` VM) | any OS with `adb` | any OS where `ai-edge-litert` installs (the compile venv's Linux `x86_64` host works) |\n| Python environment | Local venv with `ai-edge-litert` + `ai-edge-litert-sdk-google-tensor` at the version in `versions.json` (`compile_model.py --print-install`) | Any local `python3` + `google-colab-cli` (`compile_model.py --print-install --colab`); SDK is installed on the Colab VM automatically | any `python3` (stdlib only) | a Python with `ai-edge-litert` (the compile venv works; the SDK package is not needed) |\n| `adb` on PATH, Pixel 10\u002F11 with USB debugging enabled | - | - | required | step 7 (install and check the app) |\n| A JDK the app's Gradle supports (`JAVA_HOME`), the Android SDK, the project's `.\u002Fgradlew` | - | - | - | steps 6-7 (build, install); bundletool is downloaded once |\n| `libLiteRtDispatch_GoogleTensor.so` - downloaded automatically from the public LiteRT binaries bucket (same directory as `benchmark_model`), or supplied with `--dispatch-lib` | - | - | required | - (the app gets it from Google Maven) |\n| Internet | once for `pip install` | required (Colab VM + `pip install`) | once to fetch `benchmark_model` + dispatch lib (cached in `~\u002F.cache\u002Flitert-google-tensor\u002F`) unless supplied locally | - (the app's Gradle build downloads the LiteRT Maven artifacts) |\n\nSupported targets: `Tensor_G5` (Pixel 10 family) and `Tensor_G6` (Pixel 11\nfamily) - the SoCs listed as supported by the public Tensor SDK docs.\n\n## Step 0 - Collect parameters\n\nAsk the user only for what is missing; never invent values:\n\nParameter                           | How to get it\n----------------------------------- | -------------\nModel path                          | Absolute path to the `.tflite`. Confirm it exists.\nGoal                                | check only, compile only, benchmark only, compile + benchmark, or package for an app (step 5). Infer it from the request (\"how fast is it on the TPU vs CPU\" = compile if needed + benchmark with a CPU baseline; \"put it in my app\" \u002F \"deploy it\" = package); do not add an unrequested device run.\nTarget SoC                          | For a **compile**, the user's stated device wins (Pixel 10 family = `Tensor_G5`, Pixel 11 family = `Tensor_G6`), even if a different Pixel is attached. For a **benchmark**, the attached phone decides: `adb shell getprop ro.soc.model` -> `Tensor G5` => `Tensor_G5`, `Tensor G6` => `Tensor_G6` (the benchmark script reads and enforces this itself).\nOutput path                         | Default `\u003Coutput-dir>\u002F\u003Cstem>_Google_\u003Ctarget>.tflite`. `compile_model.py` refuses to overwrite an existing file (exit 2): run `check_compiled.py` on the existing file and reuse it, or pass `--output-name`, or `--overwrite` after asking. When a second build is needed next to an existing one, name it `\u003Cstem>_Google_\u003Ctarget>.\u003CYYYYMMDD-HHMM>.tflite` (e.g. `--output-name model_Google_Tensor_G5.20260107-0930.tflite`) or, for a flag variant, `\u003Cstem>_Google_\u003Ctarget>.\u003Cflag-summary>.tflite` (`...G5.half-maxsharding.tflite`); keep the `_Google_\u003Ctarget>` part so the file stays recognisable.\nDevice serial                       | Only if `adb devices` lists more than one device.\nDispatch library \u002F benchmark binary | Optional. Both are downloaded automatically from the same public bucket directory and cached. Only ask if the user wants a specific build, or if the host is offline: then `--benchmark-bin \u003Cbenchmark_model>` plus `--dispatch-lib \u003ClibLiteRtDispatch_GoogleTensor.so \\| dir \\| .zip \\| .aar>` (also env `LITERT_GOOGLE_TENSOR_DISPATCH_LIB`) use local copies and no network is touched.\nCPU baseline                        | Optional; needs the path of the **original uncompiled** model (ask for it if only a compiled file was given; the benchmark script rejects a compiled file here with exit 2).\nRun counts \u002F mode                   | Defaults 5 warmup \u002F 50 measured, `burst` performance mode. `benchmark_model` treats counts as **minimums** and also runs at least 0.5 s warmup \u002F 1 s measured, so fast models execute more runs than requested. If the user states exact counts, say so and ask whether to disable the floors (`--extra-arg=--min_secs=0 --extra-arg=--warmup_min_secs=0`); \"steady-state\" \u002F \"sustained\" requests map to `--performance-mode sustained_performance`.\nTPU tile time                       | On by default, nothing to ask: a second, hooked NPU pass. `--no-tile-time` turns it off; the hooked pass itself (it repeats the NPU run, so slow models take about twice as long) is skipped only with `--no-tile-time --no-power`.\nTPU energy                          | On by default, nothing to ask: the power metric of the same hooked pass (the TPU rail's energy per inference). `--no-power` skips it. The script leaves it off, and says why, for a dispatch library whose power hook predates the fix.\nCompile mode                        | Local Linux `x86_64` by default. Use `--colab` when the host is not Linux `x86_64` (macOS\u002FWindows\u002FARM) or when the user asks to compile on Google Colab; ask for the Beta tarball path (`--sdk-tarball` \u002F `GOOGLE_TENSOR_SDK_BETA`) or URL (`--sdk-url` \u002F `GOOGLE_TENSOR_ML_SDK_URL`) before running.\nCompiler flags                      | Default to the compiler defaults; only set flags the user asks for or that troubleshooting requires (see `references\u002Fcompile-flags.md`).\nApp deployment level                | Packaging jobs only; nothing to ask up front. After step 5, tell the user the plan is ready (`INTEGRATION.md`, or `UPDATE.md` for an update) and ask whether you should apply it to their project (step 6). After that, offer to install the app on their Pixel and check it uses the NPU (step 7).\nNew integration or update           | Packaging jobs only. Ask whether the app already ships Google Tensor builds (an AI pack with folders like `model#group_Google_Tensor_G5` under its `src\u002Fmain\u002Fassets`). If it does, it is an **update** (step 5b): ask for the project root (the folder with `settings.gradle.kts` or `settings.gradle`). The pack, model file, device groups, delivery mode and app module then come from the project, so do not ask for them.\nBuilds to package                   | One compiled build per target SoC (`Tensor_G5`, `Tensor_G6`) plus the **original uncompiled** model they were compiled from (the app's CPU path). Compile missing targets first (step 2); a build is per SoC, so a Pixel 11 needs a `Tensor_G6` build. For an update, build every SoC the app already ships.\nDelivery mode                       | New integrations only (an update keeps the app's). When Google Play puts the model on the phone: `install-time` (default; installed with the app, ready at first launch), `fast-follow` (downloaded right after install) or `on-demand` (downloaded when the app asks). Explain each in one sentence (`references\u002Fapp-integration.md`) and use the default when the user has no preference.\nPack \u002F model names, app module      | New integrations only. Defaults: pack name = the original's file name made valid, model name = pack name, app module `app`, Kotlin package `com.example.litert`. Ask only when the user's project differs (another app module name, their package).\nQuick test (optional)               | New integrations only. Offer it when the user wants to try the model on their own Pixel before setting up bundletool: `--quick-test` adds a copy of each build and the steps for `.\u002Fgradlew installDebug -PtensorQuickTest=\u003CSoC>`, which puts the build and the NPU runtime in the debug APK. Local testing only; release builds are unaffected.\n\n## Step 1 - Check whether the model is already compiled\n\n```bash\npython3 SKILL_DIR\u002Fscripts\u002Fcheck_compiled.py \u002Fabs\u002Fpath\u002Fmodel.tflite [--expect-target Tensor_G5]\n```\n\nRun it on every model file involved (the model to compile or benchmark, the\nCPU-baseline file, and an existing output file you are about to reuse).\nFor a benchmark job read the phone first (`adb shell getprop ro.soc.model`,\nwhich prints `Tensor G5` or `Tensor G6`) and pass the underscore form as\n`--expect-target` (`Tensor G5` -> `--expect-target Tensor_G5`), so one check\nanswers both \"compiled?\" and \"for this phone?\" (the flag only matters for\ncompiled files; a plain model exits 1 either way); the benchmark script\nenforces the match again anyway (exit 4, before anything is downloaded or\npushed).\n\n| Exit | Meaning | Next action |\n|---|---|---|\n| 0 | compiled for Google Tensor (SoC printed) | skip compile; report the SoC (and the `Compile info` line if a `.compile.json` sidecar exists). Continue to step 3 only if the goal includes a benchmark. No sidecar (compiled elsewhere, flags unknown): for a plain \"how fast is it\" job, benchmark the file as-is and say \"compile flags unknown\"; when the user needs to know how it was built (or wants a reproducible comparison), compile the original with the defaults under a new `--output-name` and compare SHA-256 (`sha256sum` of both files, or `output_sha256` in the new sidecar): compilation is deterministic, so an identical hash proves the existing file is a default build (`references\u002Fcompile-flags.md`). Never `--overwrite` to find out. |\n| 1 | plain (uncompiled) model | go to step 2 if compiling; this is the correct file for `--cpu-baseline` |\n| 2 | compiled for a different SoC than `--expect-target` | compiled models are SoC-specific (a `Tensor_G5` file never runs on a `Tensor_G6` TPU or vice versa; there is no runtime fallback, and `--allow-mismatch` only produces dispatch errors). Tell the user which SoC the phone has, then offer, in this order: an existing `*_Google_\u003Cphone target>.tflite` build of the same model in the project (verify it with this check first; do not benchmark it unless the user says so), attaching the matching phone, or recompiling the **original** uncompiled export for the phone's target. |\n| 3 | not a `.tflite` \u002F missing | fix the path |\n| 4 | compiled by another toolchain: an Edge TPU custom op (`edgetpu-custom-op...`) instead of LiteRT's `DISPATCH_OP` | not usable here: LiteRT cannot recompile it or run it on the Google Tensor TPU, and the CPU cannot run it (so it is no `--cpu-baseline` or `--fallback` either; the scripts refuse it). Ask for the original model it was compiled from and compile that (step 2) |\n\nThe check looks for LiteRT's `DISPATCH_OP` custom op and `LiteRtStamp`\nmetadata, which the AOT compiler always writes; it needs no packages. Files\nfrom the Edge TPU compiler or older Google Tensor tooling carry an\n`edgetpu-custom-op` custom op instead (exit 4). The\nstamp is authoritative, the `_Google_Tensor_G5` file suffix is only the compile\nscript's naming convention. The stamp records the SoC but **not** the compiler\nflags - those come from the `\u003Cmodel>.compile.json` sidecar that\n`compile_model.py` writes next to its output (printed by the check when\npresent); without it, say \"flags unknown\" rather than guessing.\n\n## Step 2 - Compile (Linux x86_64 or Google Colab via `--colab`)\n\n### 2a. One-time environment\n\nThe SDK pip package contains no compiler by itself: at install time it unpacks\nthe Beta tarball that `GOOGLE_TENSOR_SDK_BETA` points to (same flow as the\npublic codelab). Ask the user for the tarball path first, then print the\ninstall commands for the LiteRT version the skill pins in `versions.json`:\n\n```bash\npython3 SKILL_DIR\u002Fscripts\u002Fcompile_model.py --print-install [--colab]\n```\n\n**Local Linux `x86_64`** (`--print-install`): runs with any `python3` and prints\ncomment lines (the pinned version, when and on what it was last verified, the\nsupported Python range, and whether this host and Python qualify) followed by:\n\n```text\npython3 -m venv ~\u002F.venvs\u002Flitert-tensor && source ~\u002F.venvs\u002Flitert-tensor\u002Fbin\u002Factivate\npip install --upgrade pip\nexport GOOGLE_TENSOR_SDK_BETA=\u002Fabs\u002Fpath\u002Flitert_plugin_compiler.tar.gz   # from the Beta e-mail\npip install --no-cache-dir ai-edge-litert==\u003Cpinned> ai-edge-litert-sdk-google-tensor==\u003Cpinned>\n```\n\n**Remote Google Colab (`--print-install --colab`)** - for macOS, Windows,\nnon-x86 Linux, or when compiling on Colab without a local Linux venv: installs\n`google-colab-cli` into `~\u002F.venvs\u002Fcolab-cli` (`references\u002Fcompile-flags.md`).\nNever ask the user to run a terminal command for OAuth: when `--colab` needs\none-time OAuth, show its printed URL in chat, ask the user for the code, and\nre-run with `--colab-auth-code \u003CCODE>`. `--colab` provisions a Colab Linux\n`x86_64` VM, installs the pinned LiteRT + SDK with the user's Beta tarball,\ncompiles, downloads `\u003Coutput>.tflite` + `\u003Coutput>.compile.json`, and stops the\nVM. Run the printed commands with the real tarball path; never type a LiteRT\nversion yourself.\n\n*   Any venv location works; a local compile needs only its interpreter. On\n    Debian\u002FUbuntu `python3 -m venv` may need `sudo apt install python3-venv`. If\n    `--print-install` warns about Python version, use `python3.N -m venv ...` in\n    the supported range.\n*   Without `GOOGLE_TENSOR_SDK_BETA` pip aborts with `SDK location is not set.\n    Aborting installation.`; with a wrong path, `Google Tensor SDK beta file not\n    found`. The e-mailed URL works via `GOOGLE_TENSOR_ML_SDK_URL=\u003Curl>` (or\n    `--sdk-url \u003Curl>` with `--colab`) instead of a local tarball.\n*   `--no-cache-dir` matters when re-installing (add `--force-reinstall` then):\n    a wheel built earlier **without** the compiler - on another OS, or with\n    `SKIP_SDK_DOWNLOAD` - stays in pip's cache and fails with\n    `liblitert_plugin_compiler.so: cannot open shared object file` (exit 5).\n*   Tell the user before installing anything. Install the exact pair\n    `--print-install` prints: the runtime and SDK packages must be one version\n    (`WARNING` when they differ; `NOTE` when they match each other but not the\n    pin). The pin changes only via `references\u002Fcontributing.md` (compatibility:\n    https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Ftensor-sdk\u002Frelease-notes#compatibility).\n    Use `-nightly` packages only when the user explicitly asks for nightlies.\n\n**If the user has no Beta access yet:** say so plainly - the compiler cannot be\ninstalled until Google e-mails the tarball link (sign-up form above) - and do\nnot install anything or allocate a Colab VM meanwhile (`--colab` checks for the\ntarball before `colab new`). Still useful now: run `check_compiled.py` on their\nfiles, confirm the host and phone SoC, and run `--dry-run` \u002F `--print-install`.\n\n### 2b. Compile\n\n```bash\n# Local Linux x86_64 (using the virtualenv's interpreter):\npython3 SKILL_DIR\u002Fscripts\u002Fcompile_model.py \u002Fabs\u002Fpath\u002Fmodel.tflite \\\n  --target Tensor_G5 --output-dir \u002Fabs\u002Fpath\u002Fcompiled\n\n# Remote Google Colab VM (any OS, any local python3 + google-colab-cli):\npython3 SKILL_DIR\u002Fscripts\u002Fcompile_model.py \u002Fabs\u002Fpath\u002Fmodel.tflite \\\n  --target Tensor_G5 --colab --sdk-tarball \u002Fabs\u002Fpath\u002Flitert_plugin_compiler.tar.gz \\\n  --output-dir \u002Fabs\u002Fpath\u002Fcompiled\n# -> \u002Fabs\u002Fpath\u002Fcompiled\u002Fmodel_Google_Tensor_G5.tflite\n```\n\n*   Sets `GOOGLE_TENSOR_BACKEND_ENABLED=1` (required by the stable package),\n    checks the installed LiteRT package versions against each other and\n    `versions.json`, calls `aot_compile(..., target=Target(SocModel.TENSOR_G5),\n    keep_going=False, google_tensor_*=...)`, prints the compilation report,\n    exports to `--output-dir`, re-verifies the Tensor stamp and writes\n    `\u003Coutput>.compile.json` (flags, `ops offloaded \u002F total`, package versions,\n    SHA-256 of input\u002Foutput\u002Fcompiler lib, elapsed time, plus `compiled_via:\n    \"colab\"` with `--colab`). Never overwrites an existing output (exit 2)\n    unless `--overwrite` is given.\n*   Optional compiler flags (`references\u002Fcompile-flags.md`): `--truncation-type\n    {auto,bfloat16,half,no_truncation}`, `--sharding-intensity\n    {minimal,moderate,extensive,maximum}`, `--int64-to-int32`,\n    `--large-model-support`, `--enable-4bit`, `--extra-options-path FILE`.\n    Script options: `--output-name`, `--overwrite`, `--keep-going`,\n    `--keep-intermediates`, `--no-report-file`, `--json`, `--dry-run`. Colab\n    options: `--colab`, `--sdk-tarball`, `--sdk-url`, `--colab-session`,\n    `--keep-colab-session`, `--colab-high-mem`, `--colab-timeout`,\n    `--colab-auth`, `--colab-auth-code`, `--colab-bin`.\n* If the compiler crashes with an internal error - or the user reports that it\n  did with the defaults (no need to reproduce the crash) - use\n  `--truncation-type half --sharding-intensity maximum` (public FAQ guidance)\n  and tell the user which flags produced the artifact. `half` changes the\n  numerics (16-bit floats on the TPU), so tell the user to validate the\n  model's outputs against the original before relying on it.\n* Read the printed compilation report: ops left on the CPU mean the strict\n  NPU benchmark will fail (see step 3, `--allow-cpu-fallback`).\n* Report a compile-only job from the script's output and sidecar, nothing\n  else:\n\n  ```markdown\n  **Compiled:** \u003Coutput path> (\u003Cbytes from \"Compiled model written to\" line>)\n  **Target \u002F flags:** \u003Ccli_flags from the sidecar, or \"--target X (compiler defaults)\">\n  **Ops offloaded:** \u003CN \u002F M from the \"Ops offloaded to the TPU\" line>\n  **LiteRT:** \u003C\"packages\" from the sidecar, plus the script's WARNING\u002FNOTE line if it printed one>\n  **Verified:** check_compiled.py -> \u003Cits Status line>\n  ```\n\nExit codes: 0 ok, 2 bad input, 3 already compiled, 4 unsupported host,\n5 LiteRT packages or Beta compiler library missing (message includes the\ninstall fix), 6 compile failed (compiler log errors are printed),\n7 output verification failed.\n\n## Step 3 - Benchmark on the device\n\n```bash\npython3 SKILL_DIR\u002Fscripts\u002Fbenchmark_on_device.py \\\n  --model \u002Fabs\u002Fpath\u002Fcompiled\u002Fmodel_Google_Tensor_G5.tflite \\\n  [--cpu-baseline \u002Fabs\u002Fpath\u002Fmodel.tflite] [--serial SERIAL] \\\n  [--dispatch-lib \u002Fabs\u002Fpath\u002FlibLiteRtDispatch_GoogleTensor.so] \\\n  [--output-dir \u002Fabs\u002Fpath\u002Fresults]\n```\n\nThe script, in order:\n\n1. Selects the device, reads `ro.soc.model`, and refuses to run a model\n   compiled for another SoC (`--allow-mismatch` overrides). `--dry-run`\n   skips this - it never contacts the device - so a dry run proves nothing\n   about the SoC match.\n2. Resolves the two host-side binaries, both cached under\n   `~\u002F.cache\u002Flitert-google-tensor\u002F` (file names `\u003Crelease>_android_arm64_\u003Cfile>`,\n   `\u003Crelease>` being the bucket directory) after the first download:\n   * `benchmark_model`: `--benchmark-bin`, else a one-time download of\n     `--benchmark-url` (default\n     `https:\u002F\u002Fstorage.googleapis.com\u002Flitert\u002Fbinaries\u002F\u003Crelease>\u002Fandroid_arm64\u002Fbenchmark_model`\n     with `\u003Crelease>` = `binaries_release` from `versions.json`; `--help`\n     prints the resolved URL).\n   * `libLiteRtDispatch_GoogleTensor.so`: `--dispatch-lib` (a `.so`, or a\n     `.zip`\u002F`.aar` it is extracted from), else env\n     `LITERT_GOOGLE_TENSOR_DISPATCH_LIB`, else a one-time download of\n     `--dispatch-url` (default: the same directory as `--benchmark-url`).\n   The script prints where each binary came from; `downloaded from \u003Curl>`\n   means this was the first time - tell the user what was fetched. Downloads\n   end with `(fetched \u003CUTC time>, md5 \u003Chash>)`, recorded when the file was\n   fetched; `fetch date not recorded` marks a copy cached by an older version\n   of the script (`--refresh-binary` re-downloads it and records the fetch).\n3. Pushes files to a fresh `\u002Fdata\u002Flocal\u002Ftmp\u002Flitert_benchmark\u002Frun-\u003Ctimestamp>-\u003Cpid>\u002F`\n   (`--device-dir` changes the base). Files already on the phone are never\n   overwritten or deleted. Leftovers from an interrupted run are therefore\n   always `\u003Cbase>\u002Frun-*`: if the user asks to clean up, `adb shell ls -la \u003Cbase>`\n   first and remove only `run-*` entries (never the base or anything else in it).\n4. Reads the thermal status (`dumpsys thermalservice`), then runs the NPU\n   benchmark in strict mode:\n   `--use_npu=true --use_cpu=false --require_full_delegation=true\n   --dispatch_library_path=\u003Cdir> --num_runs=50 --warmup_runs=5\n   --report_peak_memory_footprint=true --use_profiler=true`, retrying once\n   without the profiler if that run fails.\n5.  Runs the same NPU benchmark once more, without profiler and memory monitor,\n    with `LITERT_VENDOR_HOOK_ARGS` pointing at a hook config in the run\n    directory that enables the TPU tile-time and power metrics, and divides the\n    hooks' totals (`Total TPU tile time`, `Total Energy`) by that pass's own\n    warmup + measured runs. The hooks' own counts are only checks: older\n    dispatch libraries count every inference 1 + D times (D = dispatch nodes, so\n    2x for a normal compiled model), which is why the hooks' `Average` lines are\n    never used; an old power hook is not used at all (the script checks the\n    library first). Checks and evidence: `references\u002Fbenchmark-flags.md`, \"TPU\n    tile time\" and \"TPU energy\".\n6.  Optionally runs the CPU baseline on the **uncompiled** model\n    (`--use_cpu=true`, same run counts).\n7.  Prints the Markdown header + table, writes `results.json`, `report.md` (the\n    same block as a file), `npu.log`, `tile_time.log` (the hooked pass, + the\n    hooks' `tile_time_report.txt` and `power_report.txt`), `cpu.log`,\n    `npu_profile.txt`, `cpu_profile.txt`, and deletes the per-run device\n    directory (even on failure). `--output-dir` is only created once a benchmark\n    actually ran - after exit 2\u002F3\u002F4 or `--dry-run` there are no logs to look\n    for.\n\nThe script is NPU-only by design: `--extra-arg` passes extra `benchmark_model`\nflags but cannot switch the accelerator, and an uncompiled model is rejected\n(exit 2). For a GPU or non-Tensor run use `benchmark_model` directly\n(`--use_gpu=true` etc., https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Flitert\u002Fnext\u002Fbenchmark);\nthat is outside this skill.\n\nUseful options: `--num-runs`, `--warmup-runs`, `--signature`,\n`--performance-mode\n{extreme_power_saver,power_saver,balanced,high_performance,sustained_performance,burst}`\n(benchmark default `burst`), `--cpu-num-threads`, `--extra-arg=--min_secs=5`,\n`--device-dir`, `--benchmark-bin` + `--dispatch-lib` (local copies, offline),\n`--serial`, `--allow-cpu-fallback`, `--keep-device-files`, `--refresh-binary`,\n`--no-tile-time`, `--no-power`, `--dry-run`.\n\nOffline \u002F air-gapped host: pass both `--benchmark-bin` and `--dispatch-lib`\nand run with `--dry-run` first - the plan then shows `user-provided: \u003Cpath>`\nfor both binaries without contacting the phone or the network, which is the\nproof the user wants before the real run.\n\nBinary versions: keep `benchmark_model` and the dispatch library from the\n**same** release directory of the bucket (the default does: both come from\n`binaries_release` in `versions.json`); pass\n`--benchmark-url ...\u002Fbinaries\u002F\u003Crelease>\u002Fandroid_arm64\u002Fbenchmark_model` to pin\nanother one and the dispatch URL follows. A rolling directory (`latest\u002F`,\n`nightly\u002F`) changes over time, but a cached copy stays the build that was first\ndownloaded until `--refresh-binary`; the md5 in its provenance line tells builds\napart. A numbered directory can be re-uploaded as well (bucket notes in\n`references\u002Fbenchmark-flags.md`): if a provenance md5 differs from\n`verified.binaries` in `versions.json`, rerun with `--refresh-binary`. Local\nfiles carry no version string the script could compare, so for\n`--benchmark-bin`\u002F`--dispatch-lib` the rule is \"both from the same download\"; if\nthat is uncertain, re-download both from one directory. Not every versioned\ndirectory ships the Google Tensor dispatch library (which ones do: the bucket\ntable in `references\u002Fbenchmark-flags.md`) - if the download 404s, use the\ndefault URLs or supply `--dispatch-lib` from `litert_npu_runtime_libraries.zip`\non https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002FLiteRT\u002Freleases or a build per\nhttps:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Ftensor-sdk\u002Ftensor-faqs.\n\n## Step 4 - Report\n\nNothing in the report is typed by hand. `benchmark_on_device.py` prints a\nMarkdown header + table (also stored as the `markdown` field of\n`results.json`) in which every value is read from the device or the run:\n\nReport field                                | Comes from\n------------------------------------------- | ----------\nDevice name, SoC, Android version           | `ro.product.model`, `ro.soc.model`, `ro.build.version.release` on the attached phone (a Pixel 11 reports itself as Pixel 11 \u002F Tensor G6)\nModel name, size, compiled-for SoC          | the `.tflite` file and its `LiteRtStamp`\n**Compile** (flags, ops offloaded \u002F total)  | the `\u003Cmodel>.compile.json` sidecar next to the model; `unknown` when the model was compiled elsewhere - never fill it in from memory\n**Versions**                                | the sidecar's `ai-edge-litert` version (the LiteRT that compiled the model) vs the bucket release in the binaries' URLs (`user-provided` for local files): `same release`, `DIFFERENT releases`, or why the two cannot be compared (no sidecar, a rolling `latest`\u002F`nightly` build, local files, binaries from two sources); also stored as `versions` in `results.json`\n**Delegation (runtime)**                    | the `Replacing N out of M node(s) with delegate (DispatchDelegate)` line of this run's `npu.log` - the ground truth that everything ran on the TPU\nMode, requested run counts, NPU flags       | the flags actually passed to `benchmark_model`\n**Thermal before NPU run**                  | `dumpsys thermalservice`: the throttling status (0 = none) is the signal; the sensor value is one reading, informational only\n**TPU tile time** (header line + table row) | the tile-time pass: the hook's `Total TPU tile time` ÷ that pass's warmup + measured runs. The header names the hook's call count and what it means (`1 call per run, one per dispatch invoke`, or `2 calls per run (... the known double count, corrected)` with older dispatch libraries); `unavailable`, `failed`, `no report` or `inconsistent` (count matches no known pattern: totals only) carry the reason instead\n**TPU energy** (header line + table row)    | the same hooked pass: the power hook's `Total Energy` (the TPU rail from the first inference to the model's teardown, idle gaps included) ÷ that pass's warmup + measured runs, in mJ. The header names the hook's call count (`1 call per run, one per dispatch invoke`); `unavailable` (an old power hook, which the script does not enable, or a phone that refused the energy counter), `failed`, `no report`, `unreliable` (old hook's count pattern) or `inconsistent` carry the reason instead\nBinary \u002F dispatch library provenance        | the resolver (`downloaded from \u003Curl>`, `cached download of \u003Curl>`, `user-provided: \u003Cpath>`); downloads end with `(fetched \u003CUTC time>, md5 \u003Chash>)` recorded at download time, which identifies the build of a rolling release\nAll timings, memory, actual run counts      | parsed from the `BENCHMARK RESULTS` block of this run's log\n**Inference p5 \u002F p50 \u002F p95 (ms)**           | the measured-phase `count=N ... p5= median= p95=` stats line of the log (microseconds, converted to ms by the script); these are the only percentiles `benchmark_model` produces - there is no p99\n**Log check**                               | the script's scan of `npu.log` \u002F `cpu.log` \u002F `tile_time.log`: WARNING\u002FERROR-level lines that match the benign list in `references\u002Fbenchmark-flags.md` are only counted (`N known-benign`); any other such line is listed verbatim under **Unexpected log lines** after the table (de-duplicated, with a repeat count), and the line also says whether the profiler retry happened. No grepping of the logs is needed.\n\nPaste that block verbatim (it is also saved as `report.md` in the output\ndirectory) and add only the context the benchmark script cannot know:\n\n```markdown\n**Summary:** \u003CNPU \"Inference avg\" from the table> ms on the TPU vs \u003CCPU \"Inference avg\"> ms on the CPU (\u003Cthe N.NNx value from the script's \"NPU speed-up vs CPU\" line>); peak memory \u003CNPU> vs \u003CCPU> MB; \u003Cstrict NPU | CPU fallback allowed>. TPU tile time \u003C\"TPU tile time \u002F inference\" row> ms per inference. TPU energy \u003C\"TPU energy \u002F inference\" row> mJ per inference.\n\n\u003Cheader and table exactly as printed by benchmark_on_device.py, including any \"Unexpected log lines\" section>\n\n**Notes:** \u003Cwhat the Log check line reports: \"none\" when both logs show 0 unexpected lines and no profiler retry; otherwise each unexpected line with its meaning from references\u002Fbenchmark-flags.md or troubleshooting.md, plus anything the user should validate>\n```\n\nNever edit `report.md` (it is script output). If the user wants the full\nanswer saved, write it as `summary.md` next to `report.md`, with the block\ncopied from `report.md`; do not keep hand-typed numbers anywhere else.\n\nCombined compile + benchmark job: present only this benchmark block (its\n**Compile** header line already carries the sidecar's flags and op counts)\nand start the Notes with one line of compile context - that the input was\nuncompiled and was compiled first, the output path, and that nothing existing\nwas overwritten (`compile_model.py` made sure of that). The step 2b\ncompile-only block is for jobs that end at the compile.\n\nWithout a CPU baseline the Summary line is just `**Summary:** \u003CNPU \"Inference\navg\"> ms per inference on the TPU (\u003Cstrict NPU | CPU fallback allowed>, no CPU\ncomparison requested); peak memory \u003CNPU> MB.` plus the same TPU tile time and\nTPU energy sentences. Drop each of those two sentences when its header line says\n`not measured`, `unavailable`, `failed`, `no report`, `unreliable` or\n`inconsistent`, and give the header's reason in the Notes.\n\nWhen nothing ran (check exit 2, benchmark exit 2\u002F3\u002F4\u002F5, no device), report\nthe same way - from output, not from memory:\n\n```markdown\n**Not run:** \u003Cthe script's ERROR line verbatim>\n\n- Device: \u003Cthe \"Device: ...\" line printed by benchmark_on_device.py, or the `adb devices -l` line>\n- Model: \u003Cthe \"Status: ...\" line from check_compiled.py>\n- Nothing was pushed to or removed from the phone.\n\n**Options:** \u003Cthe next actions from step 1 \u002F troubleshooting, most specific first>\n```\n\n* NEVER type, round, or \"tidy up\" numbers, and NEVER reuse values from an\n  earlier run or from any example in this skill - copy the script output.\n  Prose numbers (the Summary line) must be character-for-character the table's\n  values.\n* Report the **actual** run counts from the table: `benchmark_model` treats\n  `--num_runs`\u002F`--warmup_runs` as minimums and also runs for at least 1 s \u002F\n  0.5 s, so fast models show more runs than requested (details in\n  `references\u002Fbenchmark-flags.md`, which also shows a complete example\n  output from a real run and the list of benign log lines).\n* The **Mode** line says whether the run was strict NPU or allowed CPU\n  fallback; call that out in prose as well, because fallback numbers mix NPU\n  and CPU time.\n* The **Versions** line compares the LiteRT that compiled the model with the\n  release that ran it. Mention `DIFFERENT releases`, a\n  `but SDK package X (DIFFERENT)` or binaries from two sources in the Notes -\n  numbers from such a mix are harder to compare with other runs - unless the\n  line ends with `(the pair verified in versions.json)`.\n  `not comparable (...)` and `cannot compare` need no comment beyond the line.\n* A speed-up below 1x is a real result, not a setup error, for tiny models\n  (per-inference NPU dispatch overhead of a few ms dominates); say so instead\n  of hunting for thermal or configuration problems.\n* The per-op profile (`npu_profile.txt`) shows one `DELEGATE` node whose\n  average is lower than the table's `Inference avg` (it excludes input\u002Foutput\n  handling); report the table value, not the profile value.\n*   **TPU tile time** is pure TPU hardware time, so it is below `Inference avg`\n    (which adds host and driver work). Quote it from the table only, never from\n    the hook's own lines in `tile_time.log` \u002F `tile_time_report.txt`: older\n    dispatch libraries inflate its `Total inferences`, so their `Average` is too\n    low (half for a normal compiled model), while the table value is right with\n    every library.\n*   **TPU energy** is the TPU rail's energy (the phone's on-device power\n    monitor) for the whole hooked pass divided by its runs, so it includes the\n    TPU's idle gaps between back-to-back inferences: compare it between models,\n    builds and `--performance-mode`s on the same phone, and do not present it as\n    a lab power measurement or as the energy of one isolated inference. Quote it\n    from the table only.\n\n## Step 5 - Package for an app (Gradle)\n\nTwo cases (Step 0, \"New integration or update\"): **5a** for an app without\nGoogle Tensor builds yet, **5b** for an app that already ships them and gets\nnew builds (a new model version, other compile flags, a new LiteRT release).\n\n### 5a - New integration\n\nTurns the builds from step 2 into what an Android Gradle app needs to ship\nthem through Google Play (details: `references\u002Fapp-integration.md`): an AI\npack written by LiteRT's own exporter from the files as they are (nothing is\nrecompiled; the packed models are byte-identical), the Google Tensor NPU\nruntime as an install-time feature module (from Google Maven), the original\nmodel for the app's base assets (the CPU path), a Kotlin loader, and an\n`INTEGRATION.md` with the user's names and versions filled in.\n\n```bash\n\u003Cvenv>\u002Fbin\u002Fpython SKILL_DIR\u002Fscripts\u002Fpackage_ai_pack.py \\\n  --compiled \u002Fabs\u002Fpath\u002Fcompiled\u002Fmodel_Google_Tensor_G5.tflite \\\n  [--compiled \u002Fabs\u002Fpath\u002Fcompiled\u002Fmodel_Google_Tensor_G6.tflite] \\\n  --fallback \u002Fabs\u002Fpath\u002Fmodel.tflite \\\n  [--delivery install-time|fast-follow|on-demand] [--pack-name NAME] \\\n  [--model-name NAME] [--app-module app] [--kotlin-package com.example.litert] \\\n  [--quick-test] [--output-dir \u002Fabs\u002Fpath\u002Fapp_package\u002FNAME]\n# -> \u003Cfallback dir>\u002Fapp_package\u002F\u003Cpack name>\u002FINTEGRATION.md and the modules\n```\n\n*   Run `check_compiled.py` on every file first (step 1), and package builds\n    that passed the strict benchmark (step 3). To benchmark with the exact\n    runtime library the app will ship, pass the Maven AAR as `--dispatch-lib`\n    (`references\u002Fapp-integration.md`, \"Runtime facts\").\n*   A real run needs a Python with `ai-edge-litert` (the compile venv works; no\n    Beta SDK). `--help` and `--dry-run` work with any `python3`: run\n    `--dry-run` first to check the inputs without writing anything.\n*   Before writing, the script checks that each `--compiled` file is stamped\n    for `Tensor_G5`\u002F`Tensor_G6` with one build per SoC, that `--fallback` is\n    uncompiled, and that each build matches its `.compile.json` (SHA-256,\n    target) and was compiled from `--fallback` (only that last check can be\n    overridden, with `--allow-source-mismatch`). It writes into a staging\n    directory, verifies the result (pack folders, byte-identical models,\n    stamps, the same device groups in the XML and the runtime module, the CPU\n    model) and only then moves it to `--output-dir`. It never replaces a\n    directory it did not write; `--overwrite` replaces only its own package.\n*   Report from the script's output: paste its `**Packaged:** ...` block\n    verbatim, then one line for the user: the delivery mode in plain words and\n    \"next: follow `\u003Coutput-dir>\u002FINTEGRATION.md`\".\n*   `INTEGRATION.md` lists everything left to do in the user's project: copy\n    the modules, the Gradle and manifest edits, the loader call, and testing\n    on a Pixel with bundletool. The app needs no LiteRT or NPU code\n    beforehand: the package brings the LiteRT runtime dependency, the Google\n    Tensor dispatch library (inside the runtime feature module, from Maven),\n    the models and the loader. Then ask: \"The integration plan is ready\n    (`INTEGRATION.md`). Do you want me to apply it to your project?\" If yes,\n    go to step 6a; if not, the guide is theirs to follow. Publishing to Google\n    Play stays with the user.\n*   `--quick-test` (Step 0, optional) also writes `quick_test\u002F\u003CSoC>\u002Fassets\u002F`\n    and an INTEGRATION.md section: copied to `\u003Capp module>\u002Ftensor_quick_test\u002F`\n    with a small Gradle block, `.\u002Fgradlew installDebug\n    -PtensorQuickTest=\u003CSoC>` builds a debug APK that carries the Tensor build\n    and the NPU runtime itself, so the model runs on the TPU with no\n    bundletool. Nothing changes without the property, and release builds\n    never change. It is for trying the model, not a substitute for the\n    bundletool test before a release.\n\nExit codes: 0 ok, 2 bad input (missing file, duplicate SoC, invalid name,\noutput exists, a build that differs from its sidecar), 3 wrong model kind\n(uncompiled `--compiled`, compiled `--fallback`), 5 LiteRT not importable or\nits exporter API changed (the message has the fix), 7 post-export verification\nfailed (nothing is left at `--output-dir`).\n\n### 5b - Update builds the app already ships\n\nThe same script with `--update-project` prepares only the files to replace,\nunder the app's own paths and names, and finds out whether anything else must\nchange (details: `references\u002Fapp-integration.md`, \"Updating builds an app\nalready ships\").\n\n```bash\n\u003Cvenv>\u002Fbin\u002Fpython SKILL_DIR\u002Fscripts\u002Fpackage_ai_pack.py \\\n  --update-project \u002Fabs\u002Fpath\u002Fto\u002Fapp-project \\\n  --compiled \u002Fabs\u002Fpath\u002Fcompiled\u002Fmodel_Google_Tensor_G5.tflite \\\n  [--compiled \u002Fabs\u002Fpath\u002Fcompiled\u002Fmodel_Google_Tensor_G6.tflite] \\\n  --fallback \u002Fabs\u002Fpath\u002Fmodel.tflite \\\n  [--pack-name NAME] [--model-name NAME] [--cpu-asset PATH|none] [--dry-run]\n# -> \u003Cfallback dir>\u002Fapp_update\u002F\u003Cpack>\u002FUPDATE.md, update.json, files\u002F\u003Cpaths>\n```\n\n*   The project is only read. The script finds the AI pack with Google Tensor\n    folders, the device-targeting XML (groups are matched by SoC, so the\n    app's own group names work), the runtime (Maven artifact, version catalog\n    or a bundled `.so`), a generated `TensorModelLoader`, and the app's CPU\n    copy of the model (the pack's `#group_other` folder in LiteRT's default\n    layout, or the base assets in 5a's layout). It runs the same input checks\n    and LiteRT export as 5a and writes the new files to `files\u002F`, at the same\n    paths as in the project. `--pack-name` and `--model-name` only pick one\n    when the project has several; `--cpu-asset` names the CPU copy when the\n    script cannot find it (the error lists candidates). Quick-test copies in\n    `\u003Capp module>\u002Ftensor_quick_test\u002F\u003CSoC>\u002F` (5a's `--quick-test`) are\n    replaced too.\n*   Run `--dry-run` first and show the user the verdict and the file list:\n    `drop-in` (only model files change), `structural` (section 2 of\n    `UPDATE.md` lists the other changes: a new SoC needs a group in the XML,\n    the runtime module and the loader; a SoC without a new build keeps\n    shipping its old one until it is compiled or removed; changed model\n    inputs or outputs need app code changes; a missing runtime or mismatched\n    LiteRT versions) or `up-to-date`. The inputs\u002Foutputs comparison needs\n    LiteRT, so it runs only in the real run, which can turn `drop-in` into\n    `structural`. For `structural`, list the changes and ask how to proceed\n    before anything else.\n*   A `Runtime` check of `device` means the app's runtime is not the release\n    this skill pins, or its version is unknown: the new builds must pass the\n    strict benchmark (step 3) with the app's own library before release\n    (`--dispatch-lib` with the app bundle, the runtime AAR or the `.so`);\n    `UPDATE.md` section 4 has the exact commands.\n*   Report from the script's output: paste its `**Update plan:** ...` block\n    verbatim, then one line: the verdict in plain words and \"next:\n    `\u003Coutput-dir>\u002FUPDATE.md`\". Then ask: \"The update plan is ready\n    (`UPDATE.md`). Do you want me to apply it to your project?\" If yes, go to\n    step 6b. Never copy a 5a package over an existing pack.\n\nExit codes as in 5a; exit 2 also covers a project the script cannot update\nas is (no AI pack with Google Tensor builds - then it is a new integration -,\nseveral packs or models without `--pack-name`\u002F`--model-name`, a device group\nthat selects several SoCs, an unknown CPU copy).\n\n## Step 6 - Apply the plan to the user's project (when they say yes)\n\nYou apply the plan; the user approves it once. Before changing anything,\nshow the **whole plan** and get **one approval**. Then ask **separately** for\neach risky item (the check marks them): upgrading the Android Gradle plugin\nor Kotlin, adding Kotlin, raising `minSdk`, limiting the app to arm64-v8a\n(devices without it can no longer install the app), changing the app's\nLiteRT version, replacing project files that differ from the package,\nremoving a SoC. A declined item is skipped; say what will not work without\nit.\n\n### 6a - New integration\n\n1.  Ask for the project root (the folder with `settings.gradle.kts` or\n    `settings.gradle`). If it is a git checkout with uncommitted changes\n    (`git status --porcelain`), say so in the plan and suggest committing\n    first.\n2.  Get the plan (read-only, any `python3`, no LiteRT):\n\n    ```bash\n    python3 SKILL_DIR\u002Fscripts\u002Fpackage_ai_pack.py --check-project \u002Fabs\u002Fpath\u002Fto\u002Fapp-project \\\n      --package \u003Coutput-dir of step 5a> [--app-module app] [--json]\n    ```\n\n    Every requirement of `INTEGRATION.md` comes back as `pass`, `fail` or\n    `warn`, each `fail` with its fix: a copy command, or a snippet and the\n    file it goes in (in the project's Kotlin or Groovy DSL), and `risky` when\n    it needs a separate OK. The `fail` items are the plan; offer the `warn`\n    items (for example the optional quick test) as options.\n3.  Show the plan once: the files to copy, the files to edit with their\n    snippets, and the risky items marked. Get the approval, then the\n    separate OKs.\n4.  Apply it: run the copy commands; make the edits with your file-editing\n    tool, merged into the existing blocks (`android { }`, `defaultConfig`,\n    `dependencies`, the version catalog when the project uses one) in the\n    file's own style. Never delete or reformat unrelated code, and never\n    touch signing configs.\n5.  Re-run step 2 until nothing fails (apart from declined items).\n6.  Build once from the project root: `.\u002Fgradlew :\u003Capp module>:bundleDebug`.\n    Fix errors your edits caused; report errors that were there before\n    instead of fixing them.\n7.  Report what changed, and how the app uses the model: `INTEGRATION.md`\n    step 9 has the loader call, `model_io.json` the inputs and outputs. If\n    the user tells you where the model is used, offer to write that code\n    (the pre- and post-processing depend on their app, so show it first).\n    Then offer step 7.\n\nExit codes of `--check-project`: 0 nothing to do, 1 something fails (the\nreport lists it), 2 bad input (no project, no package, no app module).\n\n### 6b - Update\n\nThe plan is `UPDATE.md`: the file table and section 2. Get one approval for\nthe files and the non-risky edits, and separate OKs for the risky ones\n(removing a SoC, changing the runtime or LiteRT version, replacing a loader\nwith local changes; changed model inputs or outputs need app code changes,\nso show that code first). Then: copy each project file you replace to\n`\u003Coutput-dir>-backup\u002F\u003Cpath>` (not inside `\u003Coutput-dir>`, which `--overwrite`\nreplaces), copy `files\u002F\u003Cpath>` to `\u003Cproject>\u002F\u003Cpath>`, make the section 2\nchanges, and raise `versionCode`. Re-run the step 5b command with\n`--dry-run`: every file must show `unchanged`. Build once (as in 6a, step\n6), then offer step 7.\n\n## Step 7 - Install and check on a Pixel (when the user agrees)\n\nBuilds the user's app, installs it on the attached Pixel the way Google Play\nwould (bundletool local testing, for the phone's device group), and reads\nthe loader's `LITERT_TENSOR_RESULT` line:\n\n```bash\npython3 SKILL_DIR\u002Fscripts\u002Finstall_and_verify.py --project \u002Fabs\u002Fpath\u002Fto\u002Fapp-project \\\n  [--app-module app] [--variant debug|release|\u003Cflavor>Debug] [--serial \u003Cid>] \\\n  [--uninstall-first] [--dry-run]\n```\n\n*   Needs a JDK the project's Gradle supports (`JAVA_HOME`), the Android SDK\n    (adb), the project's `.\u002Fgradlew` (or `--gradle`, which may carry\n    options such as `-Pname=value`), and a Pixel with USB debugging.\n    bundletool is downloaded once (`bundletool_release`, cached) or given\n    with `--bundletool \u003Cbundletool-all jar or folder of jars>`. Run\n    `--dry-run` first: it shows the phone, its device group and whether the\n    NPU path is expected. An app with product flavors needs `--variant`\n    (for example `freeDebug`).\n*   Local testing cannot update an installed app. Exit 7 means the app is\n    installed: ask the user (uninstalling deletes the app's data on the\n    phone), then rerun with `--uninstall-first`.\n*   The app must call `TensorModelLoader.load()`. If it loads the model on a\n    particular screen, tell the user to open it while the script waits (exit\n    6 = no result line within `--timeout`).\n*   Report the script's block verbatim. `\"path\":\"npu\"` is the pass; for\n    `\"path\":\"cpu\"` on a phone the app has a build for, the `npu_error` says\n    why (`references\u002Ftroubleshooting.md`). The app stays installed.\n\nExit codes: 0 ran on the NPU (or on the CPU where no Tensor build is\nexpected; with `--no-launch`: installed), 1 CPU on a phone with a Tensor\nbuild, 2 environment (project, tools, device), 3 Gradle build failed, 4\nbundletool failed, 6 no result line, 7 already installed.\n\n## Rules\n\n* ALWAYS run `check_compiled.py` before compiling or benchmarking, on every\n  model file involved - the compiler cannot recompile a compiled model, and\n  the TPU cannot run an uncompiled one.\n* ALWAYS verify the device SoC matches the model's stamp (the benchmark\n  script does) - a `Tensor_G5` model on a `Tensor_G6` phone fails with an\n  opaque dispatch error, wasting a debugging round.\n* ALWAYS use the original uncompiled model for `--cpu-baseline` - compiled\n  models contain `DISPATCH_OP` nodes that only the NPU can execute.\n* ALWAYS clean up the per-run device directory (automatic; after an\n  interrupted run list the base first, then remove only its `run-*` entries:\n  `adb shell ls -la \u003Cbase>` and `adb shell rm -rf \u003Cbase>\u002Frun-*`, where `\u003Cbase>`\n  is `\u002Fdata\u002Flocal\u002Ftmp\u002Flitert_benchmark` or the `--device-dir` that was used).\n  NEVER `rm -rf` the base directory itself - users keep their own binaries\n  and models there.\n* NEVER combine `--use_cpu=true` with `--require_full_delegation=true` -\n  `benchmark_model` aborts on that combination.\n* NEVER install packages into the system interpreter or without telling the\n  user; use a virtualenv so pinned LiteRT versions do not break other work.\n* NEVER type LiteRT version numbers into install commands or download URLs\n  yourself - take them from `compile_model.py --print-install` and the\n  scripts' default URLs, which all read `versions.json`, so every job uses the\n  pair that was verified together. Use another release only when the user\n  asks for one (`--benchmark-url ...\u002Fbinaries\u002F\u003Crelease>\u002F...`, or the pip pair\n  at that version) and say so in the report. Changing the pin is a maintainer\n  task (`references\u002Fcontributing.md`), not part of a user job.\n*   NEVER treat macOS, Windows, or ARM hosts as a compile blocker or give manual\n    browser Colab notebook steps - run `compile_model.py --colab` directly.\n*   NEVER leave adb, benchmark, or Colab VM sessions running in the background -\n    every command here is bounded by a timeout and returns before the next step\n    (`compile_model.py --colab` stops any Colab VM it creates unless\n    `--keep-colab-session` is passed).\n*   NEVER guess compile flags or overwrite an existing output without asking\n    (`compile_model.py` exits 2; use `--output-name` or ask before\n    `--overwrite`).\n*   ALWAYS package for an app with `package_ai_pack.py` (it calls LiteRT's\n    exporter and verifies the result); NEVER create or edit AI pack folders,\n    the device-targeting XML or the runtime modules by hand.\n*   ALWAYS use `--update-project` (step 5b) when the app already ships Google\n    Tensor builds; NEVER copy a new package's `ai_packs\u002F` over an existing\n    pack - the app's group names and the place of its CPU copy can differ.\n*   ALWAYS ask before changing the user's project, show the whole plan, and\n    get one approval; ask separately for each risky item (step 6) and before\n    `install_and_verify.py --uninstall-first`. NEVER change signing configs.\n*   NEVER put a compiled build where the app runs on the CPU (base assets,\n    `--fallback`): only the original uncompiled model.\n*   NEVER publish to Google Play, sign, or upload anything for the user; the\n    skill stops at the package and the local-testing steps.\n\n## Troubleshooting (short list)\n\nSymptom                                                                                            | Fix\n-------------------------------------------------------------------------------------------------- | ---\n`compile_model.py` exit 2 `output already exists`                                                  | Reuse it (after `check_compiled.py`), or `--output-name`, or ask and `--overwrite`\n`compile_model.py` exit 3 `already compiled`                                                       | The input is a compiled artifact; compile the original export (`--force` does not help)\n`check_compiled.py` exit 4 \u002F `compiled by another toolchain` (any script)                          | The file holds an Edge TPU custom op (`edgetpu-custom-op...`) from another compiler; no flag helps. Get the original model it was made from and compile that (step 2)\n`compile_model.py` exit 5                                                                          | Local: LiteRT\u002FSDK not importable or no compiler library (use the venv's interpreter, or reinstall with `GOOGLE_TENSOR_SDK_BETA` set). `--colab`: `colab` CLI missing, auth failed (`colab sessions`), or `--sdk-tarball` not supplied - see 2a\n`compile_model.py` `WARNING: ai-edge-litert X and ai-edge-litert-sdk-google-tensor Y differ`       | The two packages must be one version: run the reinstall line the warning prints (pinned version, `--force-reinstall`) with `GOOGLE_TENSOR_SDK_BETA` set\n`INTERNAL error at compiler_plugin.cc` \u002F abort during compile                                      | `--truncation-type half --sharding-intensity maximum`\n\"does not support layers with dynamic shapes\"                                                      | Re-export the model with static shapes\n`compile_model.py` exit 4                                                                          | Host is not Linux `x86_64` - re-run with `--colab --sdk-tarball \u002Fabs\u002Fpath\u002Flitert_plugin_compiler.tar.gz`, or compile on a Linux `x86_64` machine and benchmark here\n`adb devices` lists nothing                                                                        | USB: re-plug, accept the USB-debugging prompt. Forwarded phone (serial `host:port`, e.g. `localhost:41015`): `adb connect host:port`. Prefer `adb reconnect` over `adb kill-server` (the latter drops every device on a shared host)\n\"more than one device connected\"                                                                   | `--serial \u003Cid>` from `adb devices`\nStrict NPU run fails, `--allow-cpu-fallback` works                                                 | Not fully delegated; check the compilation report\nRun dies right after `Loading shared library: ...\u002FlibLiteRtDispatch_GoogleTensor.so`               | Unusable dispatch `.so` (bad download\u002FABI, or far from the binary's release) - `--refresh-binary` with the defaults\n`download failed (...)` for the dispatch library (exit 5)                                          | HTTP 404 = that bucket directory has no Google Tensor dispatch lib; otherwise the host is offline. Use the default URLs (release from `versions.json`), or pass `--dispatch-lib`\n`benchmark_on_device.py` exit 6 `exited 0 but printed no results in the format this script parses` | The `benchmark_model` build prints a format the parser does not know (newer or older binary). Re-run with the default URLs, or pin a release with `--benchmark-url`; the fix in the skill is in `references\u002Fcontributing.md`\n`**TPU tile time:** unavailable - the dispatch library returned no telemetry hook`                 | The `--dispatch-lib` was built from the LiteRT GitHub source (hooks compiled out): use the default bucket binaries, or `--no-tile-time`\n`**TPU energy:** unavailable - this dispatch library's power hook predates the fix ...`            | The library has the old power hook (e.g. a Maven runtime AAR passed as `--dispatch-lib`, or a stale cache): use the default binaries (`--refresh-binary` for a stale cache). Latency and tile time are unaffected\n`**TPU energy:** unavailable - the power hook could not read the TPU energy counter: ...`          | The phone refused the shell access to its power monitor: report the quoted line. Latency and tile time are unaffected\n`package_ai_pack.py` exit 5                                                                        | Run it with the Python that has `ai-edge-litert` (the compile venv; the message prints the install line). \"exporter failed\": install the pinned `ai-edge-litert` (`compile_model.py --print-install`)\n`package_ai_pack.py` exit 2 `differs from the build its .compile.json describes`                   | The file changed after it was compiled: package the build that was compiled and benchmarked, or recompile\nbundletool: `refers to device groups [...] which are not in the list [...]`                        | The app's `device_targeting_configuration.xml` lacks the package's groups: copy or merge it from the package (`INTEGRATION.md` step 2)\nApp log `LITERT_TENSOR_RESULT {\"path\":\"cpu\", ...}` on a packaged Pixel                             | Check `adb shell pm path` for the `google_tensor_runtime` split, `useLegacyPackaging = true`, and one release for `litert` and `litert-npu-runtime-google-tensor`\n`--update-project` exit 2 `no AI pack with Google Tensor builds`                                   | The app has no Google Tensor setup yet: package without `--update-project` (step 5a)\n`--update-project` exit 2 `cannot tell where the app keeps the original (CPU) model`               | `--cpu-asset \u003Cpath in the project>` (the error lists the models it found), or `--cpu-asset none` if the app has no CPU copy\nQuick test (`installDebug -PtensorQuickTest=\u003CSoC>`) logs `\"path\":\"cpu\"`                            | `\u003Capp module>\u002Ftensor_quick_test\u002F\u003CSoC>\u002Fassets\u002F` must exist (Gradle ignores a missing folder), `\u003CSoC>` must be the phone's (`adb shell getprop ro.soc.model`, underscore form), and the Gradle block must be in the app module\n`install_and_verify.py` exit 7 `already installed`                                                 | Ask the user (it deletes the app's data on the phone), then rerun with `--uninstall-first`\n`install_and_verify.py` exit 6 (no result line)                                                    | The app must call `TensorModelLoader.load()`: open the screen that uses the model while it waits, or raise `--timeout`\n`install_and_verify.py` exit 4 `cannot download ...\u002Fbundletool-all-...jar`                         | Offline or GitHub blocked: download a `bundletool-all` jar elsewhere and pass `--bundletool \u003Cjar>`\n\nFull tables: `references\u002Ftroubleshooting.md`.\n\n## Public references\n\n* LiteRT NPU guide: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Flitert\u002Fnext\u002Fnpu\n* Google Tensor with LiteRT: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Flitert\u002Fnext\u002Ftensor-sdk\n* Compilation flags: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Ftensor-sdk\u002Fcompilation-flags\n* Tensor SDK FAQ: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Ftensor-sdk\u002Ftensor-faqs\n* Release notes \u002F compatibility: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Ftensor-sdk\u002Frelease-notes\n* benchmark_model tool: https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Flitert\u002Fnext\u002Fbenchmark\n* Codelab: https:\u002F\u002Fcodelabs.developers.google.com\u002Fcodelabs\u002Fgoogle-tensor-ml-sdk\n*   LiteRT AOT Compilation Tutorial (Colab):\n    https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fsamples\u002Flitert\u002Fcolab\u002FLiteRT_AOT_Compilation_Tutorial.ipynb\n*   google-colab-cli: https:\u002F\u002Fgithub.com\u002Fgooglecolab\u002Fgoogle-colab-cli\n* LiteRT source and releases: https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002FLiteRT\n* Play for On-device AI (AI packs, delivery, device targeting):\n  https:\u002F\u002Fdeveloper.android.com\u002Fgoogle\u002Fplay\u002Fon-device-ai\n* Play Feature Delivery: https:\u002F\u002Fdeveloper.android.com\u002Fguide\u002Fplaycore\u002Ffeature-delivery\n* bundletool: https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fbundletool\u002Freleases\n\n## Contributions\n\nTo contribute or modify this skill or its references, follow the\n[contribution guidelines](references\u002Fcontributing.md) before making changes.\n",{"name":5,"description":7},"main","9467001474dc4e072ebc588e5eb9b5fdcf63abbc","skills\u002Flitert-google-tensor",134,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples","https:\u002F\u002Fdevelopers.google.com\u002Fedge\u002Flitert","https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge","2026-11-13 14:36:51",{"source":8,"skillName":5,"commands":52,"prompt":62,"zipAvailable":63},[53,56,59],{"tool":54,"command":55},"npx","npx skills add google-ai-edge\u002Flitert-samples --skill litert-google-tensor",{"tool":57,"command":58},"bunx","bunx skills add google-ai-edge\u002Flitert-samples --skill litert-google-tensor",{"tool":60,"command":61},"pnpm","pnpm dlx skills add google-ai-edge\u002Flitert-samples --skill litert-google-tensor","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002FSKILL.md",true,[65,71,75,79,83,87,91,97,101,105,109,113,119,123,127,131,135,139,143,147,151],{"path":66,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":69,"sourceHtmlUrl":70},"README.md","REFERENCE","参考文档",21674,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002FREADME.md",{"path":72,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":73,"sourceHtmlUrl":74},"references\u002Fapp-integration.md",28313,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Freferences\u002Fapp-integration.md",{"path":76,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":77,"sourceHtmlUrl":78},"references\u002Fbenchmark-flags.md",37358,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Freferences\u002Fbenchmark-flags.md",{"path":80,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":81,"sourceHtmlUrl":82},"references\u002Fcompile-flags.md",13220,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Freferences\u002Fcompile-flags.md",{"path":84,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":85,"sourceHtmlUrl":86},"references\u002Fcontributing.md",17986,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Freferences\u002Fcontributing.md",{"path":88,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":89,"sourceHtmlUrl":90},"references\u002Ftroubleshooting.md",47737,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Freferences\u002Ftroubleshooting.md",{"path":92,"resourceType":93,"resourceTypeDesc":94,"sizeBytes":95,"sourceHtmlUrl":96},"scripts\u002Fbenchmark_on_device.py","SCRIPT","脚本",79952,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Fscripts\u002Fbenchmark_on_device.py",{"path":98,"resourceType":93,"resourceTypeDesc":94,"sizeBytes":99,"sourceHtmlUrl":100},"scripts\u002Fcheck_compiled.py",7918,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Fscripts\u002Fcheck_compiled.py",{"path":102,"resourceType":93,"resourceTypeDesc":94,"sizeBytes":103,"sourceHtmlUrl":104},"scripts\u002Fcompile_model.py",64768,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Fscripts\u002Fcompile_model.py",{"path":106,"resourceType":93,"resourceTypeDesc":94,"sizeBytes":107,"sourceHtmlUrl":108},"scripts\u002Finstall_and_verify.py",26816,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Fscripts\u002Finstall_and_verify.py",{"path":110,"resourceType":93,"resourceTypeDesc":94,"sizeBytes":111,"sourceHtmlUrl":112},"scripts\u002Fpackage_ai_pack.py",144427,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Fscripts\u002Fpackage_ai_pack.py",{"path":114,"resourceType":115,"resourceTypeDesc":116,"sizeBytes":117,"sourceHtmlUrl":118},"templates\u002Fai_pack\u002Fbuild.gradle.kts","OTHER","其他",1194,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Ftemplates\u002Fai_pack\u002Fbuild.gradle.kts",{"path":120,"resourceType":115,"resourceTypeDesc":116,"sizeBytes":121,"sourceHtmlUrl":122},"templates\u002Fgoogle_tensor_runtime\u002Fbuild.gradle.kts",1504,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Ftemplates\u002Fgoogle_tensor_runtime\u002Fbuild.gradle.kts",{"path":124,"resourceType":115,"resourceTypeDesc":116,"sizeBytes":125,"sourceHtmlUrl":126},"templates\u002Fgoogle_tensor_runtime\u002Fsrc\u002Fmain\u002FAndroidManifest.xml",1587,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Ftemplates\u002Fgoogle_tensor_runtime\u002Fsrc\u002Fmain\u002FAndroidManifest.xml",{"path":128,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":129,"sourceHtmlUrl":130},"templates\u002FINTEGRATION_quick_test.md",1959,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Ftemplates\u002FINTEGRATION_quick_test.md",{"path":132,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":133,"sourceHtmlUrl":134},"templates\u002FINTEGRATION.md",7653,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Ftemplates\u002FINTEGRATION.md",{"path":136,"resourceType":115,"resourceTypeDesc":116,"sizeBytes":137,"sourceHtmlUrl":138},"templates\u002Fruntime_strings\u002Fbuild.gradle.kts",1082,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Ftemplates\u002Fruntime_strings\u002Fbuild.gradle.kts",{"path":140,"resourceType":115,"resourceTypeDesc":116,"sizeBytes":141,"sourceHtmlUrl":142},"templates\u002Fruntime_strings\u002Fsrc\u002Fmain\u002Fres\u002Fvalues\u002Fstrings.xml",869,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Ftemplates\u002Fruntime_strings\u002Fsrc\u002Fmain\u002Fres\u002Fvalues\u002Fstrings.xml",{"path":144,"resourceType":115,"resourceTypeDesc":116,"sizeBytes":145,"sourceHtmlUrl":146},"templates\u002FTensorModelLoader.kt",7290,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Ftemplates\u002FTensorModelLoader.kt",{"path":148,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":149,"sourceHtmlUrl":150},"templates\u002FUPDATE.md",1970,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Ftemplates\u002FUPDATE.md",{"path":152,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":153,"sourceHtmlUrl":154},"versions.json",4543,"https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-google-tensor\u002Fversions.json",[156,169,199,216,235,248],{"skillNo":157,"name":158,"nameZh":6,"description":159,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":160,"categories":161,"keywords":162,"publishedAt":38},"01M6BY5T6RQENC1DMWZ8NZ8JFF","on-device-verification","Prove a converted or quantized LiteRT model on the actual device via the CompiledModel API - confirm GPU residency, compare device output against the source model, and diagnose device-only failures such as silent CPU fallback, whole-graph compile ceilings, and fp16 range breaks. Use after conversion or quantization, when device output is wrong or NaN, when a clean graph fails to compile only on device, or when GPU and CPU outputs are suspiciously identical.","https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Fon-device-verification\u002FSKILL.md",[],[163,164,165,28,166,19,167,168,24,26],"on device verification","verification","diagnose","FP16","api","graph",{"skillNo":170,"name":171,"nameZh":6,"description":172,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":173,"categories":174,"keywords":180,"publishedAt":38},"01M6BY5BY9H6SDG9GRRTT04EWA","litert-conversion-workflow","Convert a Hugging Face LLM or vision-language model checkpoint into a .litertlm bundle that runs on the LiteRT-LM runtime with verified quality - classify the architecture against known runtime walls, pick the recipe family (dense, reasoning, hybrid SSM, VLM), export, quantize, gate the result against the source model, and publish. Use when converting a new LLM or VLM to LiteRT-LM, when a converted bundle crashes on the first message or dies at engine creation, when a quantized model answers worse than its source, or when deciding whether a model is convertible at all.","https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-conversion-workflow\u002FSKILL.md",[175],{"categoryNo":176,"categoryName":177,"taxonomyType":178,"taxonomyTypeDesc":179},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[181,19,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,24,197,198],"litert conversion workflow","workflow","conversion","message","face","hugging face","verified","export","quantize","at","result","classify","recipe","creation","architecture","pick","publish","source",{"skillNo":200,"name":201,"nameZh":6,"description":202,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":203,"categories":204,"keywords":205,"publishedAt":38},"01M6BY5835F3RMMW9KCCZ006JM","litert-compiled-model-migration","Rapidly migrate an Android application from legacy TensorFlow Lite (TFLite) to the modern LiteRT CompiledModel API (2.x) in Open Source GitHub repositories, keeping the TensorFlow Lite Support Library as LiteRT Support (litert-support 2.3.0). Covers buffer reuse, NPU JIT compilation, a reference implementation, and a two-stage verification gate (compile check, then an on-device parity test against a golden output).","https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Flitert-compiled-model-migration\u002FSKILL.md",[],[206,24,19,207,208,209,210,211,164,37,167,212,213,214,215,28],"litert compiled model migration","test","library","jit","support","github","migrate","open source","open","tensorflow",{"skillNo":217,"name":218,"nameZh":6,"description":219,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":220,"categories":221,"keywords":222,"publishedAt":38},"01M6BY55P8GVX66H0FG8JHXHWF","gpu-clean-conversion","Convert a PyTorch or Hugging Face model into a LiteRT model that runs fully on the GPU via the CompiledModel API with verified-correct output, and lay it out as a model recipe. Use when converting a new model, or when a converted model is rejected by the GPU, falls back to CPU, or returns wrong numbers on device.","https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Fgpu-clean-conversion\u002FSKILL.md",[],[183,223,26,224,225,186,187,226,227,228,229,230,193,231,167,232,233,234,24,19],"clean","gpu clean conversion","as","CPU","via","with","output","use","back","convert","pytorch","correct",{"skillNo":236,"name":237,"nameZh":6,"description":238,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":239,"categories":240,"keywords":241,"publishedAt":38},"01M6BY52X9KX3VRXQJS622DZH3","compiled-model-app-scaffolding","Build a new Android app (Kotlin, Compose) around a verified LiteRT model using the CompiledModel API - the app architecture, the inference-layer lifecycle rules, model delivery, and the UI traps that masquerade as model bugs. Use when turning a converted and device-verified model into a demo or product app, when an app's inference layer leaks memory or blocks the UI, or when a model that verified clean looks wrong inside an app.","https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Fcompiled-model-app-scaffolding\u002FSKILL.md",[],[24,242,195,243,23,244,35,245,19,37,167,246,247],"compiled model app scaffolding","ui","lifecycle","product","inference","demo",{"skillNo":249,"name":250,"nameZh":6,"description":251,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":252,"categories":253,"keywords":254,"publishedAt":38},"01M6BY501NESZTA4NSEKX9BSDE","benchmark-on-ddp","Measure a LiteRT .tflite model with `benchmark_model` on Developer Device Platform (DDP) lab phones through `litert benchmark --ddp`, or on the Mac you run it from, and turn the session into rows of the performance leaderboard under benchmark\u002Fleaderboard - one matrix entry per model, one session per accelerator, collect results.pb and runtime_info.pb, rebuild board.json, check the page, commit the data. Use when a model needs latency and memory numbers per platform, device and accelerator, or when a new benchmark_model release means re-measuring the board.","https:\u002F\u002Fgithub.com\u002Fgoogle-ai-edge\u002Flitert-samples\u002Fblob\u002Fmain\u002Fskills\u002Fbenchmark-on-ddp\u002FSKILL.md",[],[255,22,256,257,258,259,260,34,261,262,263,35,24,19],"benchmark on ddp","data","developer","commit","release","collect","json","run","page"]