A minimal on-device LLM agent app for Android, built as a simple reference
for consuming localllmhost — the
ArkLlm host that runs a LiteRT-LM model on your phone.
This app does not run the model itself. It binds to the ArkLlm host over AIDL, sends your conversation, and streams back answers — exactly like any other app would use the host's client SDK. The "agent" part (system prompt, tool calling, local tool execution) lives here in the client, so this is a working example of the full flow: chat UI → agent loop → host inference → tools.
- Jetpack Compose chat UI with streaming replies and a "thinking…" indicator
- Two tools exposed to the on-device model:
get_system_time— returns the device's current time/dateweb_search— real web search via the Tavily API
- Agentic tool-calling loop: the model requests a tool, this app executes it locally and feeds the result back, repeating until a plain answer arrives
- Multi-turn context (full conversation history is replayed per session)
- Fully offline inference — the model runs on-device in the host app
┌────────────────────────┐ bind (signature-level) ┌─────────────────────────┐
│ Simple Agent (this) │ ─────────────────────────▶ │ ArkLlm Host app │
│ AgentRunner loop │ AIDL: ILlmService │ LiteRT-LM engine │
│ tools (time/search) │ ◀───────────────────────── │ session replay + │
└────────────────────────┘ streaming callbacks │ on-device generation │
└─────────────────────────┘
- The host app (
com.arkj.llmserver) loads and owns a single warm LiteRT-LM engine. Binding is gated by theBIND_LLM_SERVICEsignature-level permission, so both apps must be signed with the same key. - On each send, this app opens a host session (
llm-host-client) and sends the whole conversation history; the host replays and streams tokens back. - When the model emits a tool call, the stream stops, this app executes the
tool (no network needed for
get_system_time, Tavily API forweb_search) and sends the result back for the next turn — up to 8 turns.
| Repo | Role |
|---|---|
Ark946/localllmhost |
The host app + llm-contract + llm-host-client SDK (LiteRT-LM backend) |
| this repo | A client app demonstrating how to use the host client |
- Android Studio (or a
gradlew-capable environment), AGP 9.x, JDK 17. - Build & install the ArkLlm host app from
localllmhost, then in its UI select and download a model (the default CPU backend works out of the box). - Sign both apps with the same signing key — binding is signature-scoped.
- (Optional) A Tavily API key to use
web_search.
# 1. (optional) add your Tavily key — never commit local.properties
# app/local.properties:
# tavilyApiKey=tvly-xxxxxxxxxxxxxxxxxxxx
./gradlew :app:assembleDebugor open the project in Android Studio and press Run. Install on a device (or emulator) that already has the host app installed and a model selected.
Note: the Tavily key is compiled into the APK via
BuildConfig. Fine for a personal app; if you distribute it, route search through a server instead.
| Dependency | Version | Purpose |
|---|---|---|
com.github.Ark946.localllmhost:llm-host-client |
v0.1.0 |
Host client SDK (via JitPack) |
dev.langchain4j:langchain4j-core |
1.12.2 |
Tool-call types / tool spec schema |
com.squareup.okhttp3:okhttp |
4.12.0 |
Web search HTTP |
com.google.code.gson:gson |
2.13.2 |
JSON (tools, Tavily) |
| Jetpack Compose (BOM) | 2026.02.01 |
UI |
Minimum SDK 28, target SDK 36. Kotlin 2.2.10, AGP 9.1.1.
app/src/main/java/com/arkj/simpleagent/
├── MainActivity.kt # Compose chat UI
├── AgentTools.kt # Tool specs, tool execution, and the agent loop
└── ui/theme/ # Material 3 theme
The interesting part for your own agent is AgentTools.kt: it defines the two
tools, executes them, and runs the streaming tool-calling loop over
RemoteLlmClient from llm-host-client.
Apache-2.0 — the same license as
localllmhost.