Compiled Model App Scaffolding logo

Compiled Model App Scaffolding

Organization
google-ai-edge
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.

Overview

Publishergoogle-ai-edge
Repositorylitert-samples
Skill namecompiled-model-app-scaffolding
Stars
470
Forks
124
Bundled files
Instructions only
LicenseApache-2.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by google-ai-edge on GitHub. Read the source before you install it.

Installation

Install the Compiled Model App Scaffolding AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/google-ai-edge/litert-samples/tree/main/skills/compiled-model-app-scaffolding
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/google-ai-edge/litert-samples.git /tmp/litert-samples
mkdir -p .claude/skills
cp -r /tmp/litert-samples/skills/compiled-model-app-scaffolding .claude/skills/compiled-model-app-scaffolding
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Compiled Model App Scaffolding in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Compiled Model App Scaffolding on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Compiled Model App Scaffolding is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

CompiledModel app scaffolding

An app around a verified model is done when three things hold, in this order:

  1. the app reproduces the model recipe's verification numbers on the accelerator the recipe verified — before any UI exists,
  2. inference is confined and leak-free: one dispatcher owns the model, every buffer is closed, benchmarks include the readback,
  3. the inference code is liftable — another app could take the helper file unchanged.

Scope: this scaffolds a new app from a model recipe. Migrating an existing TFLite Interpreter app to CompiledModel is a different task with its own skill, litert-compiled-model-migration. LLMs are served through the LiteRT-LM Engine rather than raw CompiledModel — samples/litert/text_to_speech_lm is the reference for that case; everything below is the non-LLM CompiledModel app.

Step 0: prove parity before building UI

The first milestone has no UI: a bare harness that loads the .tflite, runs the recipe's verification input on the target accelerator, and reproduces the numbers recorded in the recipe README (correlation, residency). If they do not reproduce, stop — that is an on-device-verification problem, and no amount of app code fixes it. Only then build the ViewModel and the screen.

The shape

One sample = one standalone Gradle project, four layers:

app/src/main/java/<pkg>/
  <Task>Helper.kt        inference infra: owns the CompiledModel and its
                         buffers; pre/post-processing; NO Android UI types
  MainViewModel.kt       state machine: drives the helper on a confined
                         dispatcher, exposes UiState
  UiState.kt             one immutable data class
  MainActivity.kt        ComponentActivity + setContent, nothing else
  view/                  Screen.kt, Theme.kt, Color.kt — Compose only
app/src/main/res/values/ strings.xml etc. — no UI strings in Kotlin

The worked example of the full shape is samples/litert/image_segmentation/kotlin_cpu_gpu/android (litert 2.3.0: the GPU accelerator ships in litert-gpu, so declare litert:2.3.0 and litert-gpu:2.3.0; without the second the runtime logs GPU accelerator could not be loaded and registered and a GPU + CPU set runs on the CPU — Galaxy S26. The sample keeps Kotlin 2.2.21 with -Xskip-metadata-version-check; the 2.3.0 classes carry Kotlin 2.4 metadata). The layer boundary that matters most is the helper's: pre/post-processing is part of the model contract (it must match what the recipe exported against), so it lives with the model, not in the screen. Reusable inference code is worth more than UI polish — a reader will lift <Task>Helper.kt and delete the rest.

Inference-layer rules

  1. One serial dispatcher owns the model. Dispatchers.IO.limitedParallelism(1, "ModelDispatcher") in the helper (the named form needs kotlinx-coroutines 1.9+; litert-api 2.3.0 pulls in 1.8.0); create, run, and close the model only inside withContext(modelDispatcher) — it serializes calls without pinning a thread. Not a bare executor in the Activity, and never the main thread.
  2. Buffers are created once, reused every frame, and closed. TensorBuffer is AutoCloseable; forgetting the buffers leaks native memory even when the model itself is closed. Close in onCleared() on the dispatcher of rule 1 (viewModelScope is already cancelled there, so a launch never runs). The vendored CompiledModelRunner (below) gets the buffer lifecycle right.
  3. run() enqueues; the readback waits. run() may return before the GPU finishes — the output read is the synchronization point. Benchmark run + readback together, never run() alone.
  4. Warm up once at init. The first GPU inference includes shader compilation. Run one inference on dummy input right after create, so the first user action is not the compile, and no first-run number ever gets quoted as latency.
  5. Ask for the strict accelerator the recipe verified. Accelerator.GPU alone makes create throw when the GPU cannot take the graph (Kotlin LiteRtException Failed to compile model, Galaxy S26, litert 2.2.0 and 2.3.0). Surface it as a visible error and route it back to the model recipe; do not paper over it with a CPU fallback that makes a slow app look like a working one. The exception: a node the delegate declines while building (TopK requires src tensor C dimension to be divisible by 4, 2.2.0 and 2.3.0 wheels on a Mac) runs on the CPU with only a log line — also read the runtime's operations will run on the GPU line.
  6. Stateful and multi-graph models: move references, not data. Feed step N's output buffers as step N+1's inputs (buffer ping-pong) instead of copying state through the host. When one app creates many CompiledModels (pipelines, chunked models), create one Environment and pass it to every CompiledModel.create call, and close per-run buffers. An Environment created and closed per model reloads the GPU accelerator library each time (one reload aborted the process, once in 50 loads, Galaxy S26, litert 2.2.0). Treat the GPU program-cache option (GpuOptions key SERIALIZE_PROGRAM_CACHE) as untested per device, and note that the compiler-cache environment option targets NPU JIT, not GPU shader caching.

Vendor the helpers, don't rewrite them

utilities/common/kotlin/ holds the canonical copies of the code every app needs and every app gets subtly wrong when written from scratch:

FileWhat it provides
CompiledModelRunner.ktthe buffer lifecycle of rules 2–3 (its factories take no Environment; create the model yourself when rule 6 applies)
ImageTensor.ktBitmap → float tensor; mean/std, NCHW/NHWC, RGB/BGR, letterbox with coordinate mapping back
AudioCapture.kt16 kHz mono AudioRecord loop delivering float chunks
RealtimeCameraPipeline.ktCameraX capture → pooled Bitmap incl. rotation
MathOps.ktsoftmax, argmax, IoU, NMS

Vendor a copy with utilities/tools/sync_common.py --apply (it writes the provenance line), keep model-specific values in constructor arguments, and fix bugs in the canonical first — --check is the drift gate (exit 1 on a diverged copy). The preprocessing arguments are the model contract: a wrong mean/std or RGB/BGR looks exactly like a broken model, and it is the first thing to diff against the recipe's export script when app output is subtly wrong.

Model delivery

Weights are never committed.

  • Bundleable models: fetch at build with a download_model.gradle (Hugging Face URL → assets/), and set androidResources { noCompress += "tflite" } (Kotlin DSL) so the asset stays mmappable. Worked example: samples/litert/image_segmentation/kotlin_cpu_gpu/android.
  • Models too big to bundle: stage into the app's private filesDir with an install_to_device.sh (adb push to /data/local/tmp, then run-as <pkg> cp, debug builds only), and load with the from-file path. Worked example: samples/litert/text_to_speech/kotlin_cpu_gpu/android.

Link the model recipe (models/<family>/<model>/) from the app README; conversion and verification scripts belong to the recipe, not the app.

UI traps that masquerade as model bugs

  • The tiny-output trap. Image(contentScale = Fit) with fillMaxWidth() inside a verticalScroll renders the bitmap at native pixel size: with no height constraint the row is exactly as tall as the bitmap and Fit never upscales, so a 256-px model output looks broken on a 1080-px screen. Drop the scroll and give each image weight(1f) in the Column.
  • Audio I/O belongs to the ViewModel. Mic capture and AudioTrack playback run on their own serial dispatcher in the ViewModel, not the model's (a blocking AudioRecord.read loop would hold inference); the screen only requests permission (rememberLauncherForActivityResult) and renders state. For file input use OpenDocument with audio MIME types — the photo picker cannot see audio.
  • A "slow model" is often the UI thread. If the app stutters, check that every helper call site goes through the ViewModel's dispatcher before profiling the model — a verified model that benchmarked fine in step 0 did not get slower by being in an app.

Output layout

samples/litert/<task>/kotlin_cpu_gpu/android/
  app/src/main/java/...   the four layers above
  app/download_model.gradle   or install_to_device.sh at the app root
  README.md               what it demos, which model recipe it consumes,
                          device + accelerator it was verified on

State the device the app was verified on, the same way the model recipe does. An app README that names no device inherits none of the recipe's verification.

Frequently asked questions

What does the Compiled Model App Scaffolding AI skill do?

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.

Why use Compiled Model App Scaffolding on TypingMind?

Because you install it once and use it with any model. Compiled Model App Scaffolding is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Compiled Model App Scaffolding in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/google-ai-edge/litert-samples/tree/main/skills/compiled-model-app-scaffolding. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Compiled Model App Scaffolding?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Compiled Model App Scaffolding?

As many as you like. As long as a model supports skills, you can use Compiled Model App Scaffolding with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Compiled Model App Scaffolding AI skill free?

Yes. It is published on GitHub by google-ai-edge under the Apache-2.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇