Complete example
Application.kt
Step by step
1. Initialize
The Android overload ofinitialize() takes a Context and installs the platform context
(secure storage, model path resolution) before phase 1 starts.
AndroidPlatformContext.initialize() or CppBridgeModelPaths.setBaseDirectory().
Both are SDK-internal.
initialize() is synchronous and returns in single-digit milliseconds. Phase 2 (authentication,
model assignments, device registration) runs in the background and is awaited automatically on
first feature use.
For production, pass credentials:
Environment options
SDKEnvironment is the Wire-generated proto enum ai.runanywhere.proto.v1.SDKEnvironment. Values
are fully qualified: SDKEnvironment.SDK_ENVIRONMENT_DEVELOPMENT, not SDKEnvironment.DEVELOPMENT.
The only other value is
SDK_ENVIRONMENT_UNSPECIFIED. There is no staging value.
2. Register backends
Order matters. Register llama.cpp and ONNX beforeinitialize(): once initialization returns,
a concurrent caller can reach loadModel() while only the platform backend is registered, and the
request fails with “no provider could handle the request”.
Register QHexRT after initialize(), because its DSP skel installer needs the application
context the SDK holds.
register() is a suspend function on all three modules and takes no arguments. There is no
priority parameter.
Wrap the CPU backends in runCatching: a build that ships only some native libraries throws on the
missing one, and an unguarded call aborts SDK setup. QHexRT.register() rejects itself on
unsupported parts and logs instead of throwing.
3. Register models
registerModel() is a suspend function with three overloads.
Single file:
.tar.gz:
mmproj projector:
registerMultiFileModel; the multi-file form is an overload of registerModel.
Registration is idempotent. The registry merges on re-save and preserves runtime fields such as
is_downloaded and per-file local paths, so a catalog can be re-seeded on every launch. Call
RunAnywhere.refreshModelRegistry() afterwards so a local rescan picks up what is already on disk.
4. Download and load
downloadModelStream(model: RAModelInfo) returns Flow<DownloadProgress>. The suspend form is
downloadModel(model, onProgress), which returns the terminal progress. Both take a ModelInfo,
not a model id string. total_bytes is 0 until the planner has sized the artifact.
loadModel() is one entry point for every modality. Category is inferred from the registry entry
and is only needed to disambiguate a model registered under more than one:
loadModel() returns a ModelLoadResult with a success flag rather than throwing:
5. Run inference
generate() returns an LLMGenerationResult and transcribe() an STTOutput, so both need
.text. Proto field names are snake_case throughout.
6. Stream
generateStream() emits LLMStreamEvent, not String, and is not a suspend function. See
Streaming.
What’s next?
LLM chat
Multi-turn conversations
Streaming
Token streaming with Flows
Speech-to-text
Transcribe audio to text
Text-to-speech
Convert text to spoken audio