OnDeviceModel gains a chat() method accepting the full message history and
a list of ToolDef entries. Returns ChatResult which is either a text reply or
a list of ToolCallData the model wants to invoke. Default impl flattens
messages to a prompt so Nano and MediaPipe backends work unchanged.
GeminiCloudModel overrides chat() with native Gemini function calling:
- Converts OpenAI tools to Gemini functionDeclarations
- Handles system messages via Gemini's systemInstruction field
- Converts multi-turn history including assistant tool_calls and tool results
(role=tool → Gemini functionResponse with name resolved from prior turns)
- Parses functionCall parts in the response and returns ToolCallData list
- Falls back to text content when no function call is present
AIApiServer routes to chat() when tools are provided or the conversation
has more than one turn. Returns finish_reason=tool_calls and the tool_calls
array in the assistant message so OpenClaw / any OpenAI-compatible agent
client can execute tools and feed results back.
ApiModels updated: Message.content nullable, tool_calls and tool_call_id
added to Message, Tool/ToolFunction/ToolCall/FunctionCallDetail added,
tools and tool_choice added to ChatRequest.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Expose two model IDs matching the Gemini app's modes:
- pixel10-fast: Gemini 2.0 Flash, low latency, no reasoning trace
- pixel10-thinking: Gemini 2.5 Flash, step-by-step reasoning before answering
OnDeviceModel gains generateWithThinking() returning ThinkingResult
(thinking: String, response: String). Default impl delegates to generate()
so Nano and MediaPipe backends work unchanged.
GeminiCloudModel overrides generateWithThinking() to call
gemini-2.5-flash-preview-04-17 with thinkingConfig.thinkingBudget. Parts
with thought=true are collected as the reasoning trace; remaining parts form
the final answer.
ChatRequest gains thinking_budget (0 = fast, >0 = thinking) and model fields.
AIApiServer routes to thinking mode when model name contains "think" or
thinking_budget > 0. Thinking responses include a non-standard thinking field
in Choice alongside the normal content. /v1/models lists both model IDs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add GeminiCloudModel: proxies inference to Gemini 2.0 Flash via HTTPS
using HttpURLConnection (no new deps). Supports both blocking and SSE
streaming. Works from any context — background, emulator, CI.
- Fix background inference: OnDeviceModel.create() now accepts an apiKey;
when set, GeminiCloudModel is selected immediately, bypassing Gemini
Nano's foreground-only restriction. ApiServerService reads the key from
SharedPreferences at startup.
- Add API key UI: password field in MainActivity saved to SharedPreferences
before the service starts; disabled while server is running.
- Add GitHub Actions CI (.github/workflows/ci.yml): build job produces a
debug APK artifact; test job spins up a KVM-accelerated Android 31
emulator, installs the APK, writes the GEMINI_API_KEY secret into
SharedPreferences via adb run-as, starts the service, and runs curl
assertions against /health, /v1/models, /v1/chat/completions, and the
SSE streaming endpoint.
- Add release workflow (.github/workflows/release.yml): triggered on v*
tags, builds the APK and creates a GitHub Release with it attached.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add adaptive app icon with AI chip + signal wave vector design
- Add Apache 2.0 LICENSE file
- Rewrite README with badges, full API docs, Python examples,
device compatibility table, dependency licenses, and ToS disclaimer
- Extract all hardcoded layout strings to strings.xml
- Add roundIcon support in AndroidManifest
- Add GitHub community files: issue templates, PR template,
CONTRIBUTING.md, SECURITY.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Bump Kotlin from 2.0.21 to 2.1.20 for ML Kit genai metadata 2.2.0 compat
- Fix GeminiNanoModel imports: DownloadStatus/FeatureStatus moved to genai.common,
TextPart/generateContentRequest moved out of .type subpackage
- Fix MediaPipeModel: replace removed setTopK/setTemperature/setRandomSeed
with setMaxTopK (MediaPipe 0.10.24 API)
- Fix gradlew: remove broken lines that passed GradleWrapperMain as task arg
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- GeminiNanoModel: Rewrite to use actual ML Kit Prompt API
(Generation.getClient(), checkStatus(), download(), warmup())
- MediaPipeModel: Fix streaming to use sync fallback since
MediaPipe requires result listener set at build time
- ApiModels: Add @SerializedName("object") for OpenAI compat,
add "created" timestamp to ChatResponse/StreamChunk
- settings.gradle.kts: Fix dependencyResolutionManagement typo
- MainActivity: Use GradientDrawable.setColor() to preserve
oval shape on status dot
- Add Gradle wrapper scripts (gradlew, gradlew.bat, jar)
https://claude.ai/code/session_01GvqMLSMmfMR8uz66BFVXX2
Android app that turns a Pixel 10 into a free AI API server by leveraging
the Tensor G5's on-device AI capabilities through an embedded HTTP server.
Key components:
- Dual AI backend: Gemini Nano (ML Kit Prompt API) + MediaPipe LLM fallback
- OpenAI-compatible REST API (chat/completions, completions, models, health)
- NanoHTTPD embedded server with CORS support and SSE streaming
- Foreground service with wake lock for persistent background operation
- Material Design 3 dashboard with live request logging
All inference runs entirely on-device — zero cloud costs, full offline capability.
https://claude.ai/code/session_01GvqMLSMmfMR8uz66BFVXX2