dd69cb6d65ff4d4c545f19a38f94c5fd8df60d9b
- Launcher icon generated from piti-icon.png: adaptive icon (black bg + brain foreground) for API 26+, plus legacy square/round PNGs at all densities - Web header now uses the tsjetpiti wordmark (piti-logo.png -> assets/web/logo.png) - App label -> "tsjetpiti"; keep source art (piti-icon.png, piti-logo.png) in repo Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tsjetpiti
A dead-simple Android chat app that runs an uncensored Qwen3.5-2B model fully on-device via an embedded llama.cpp, wrapped in an extremely barebones WebView UI.
tsjet has a deliberate personality: it always answers, sounds confident and plausible, and is hilariously, purposely wrong. Hit new tsjet to wipe the conversation and start fresh.
How it works
┌─────────────────────────────────────────┐
│ MainActivity (Kotlin) │
│ • full-screen WebView ── UI ──────────┼──> assets/web/{index.html,app.js,styles.css}
│ • JS bridge "TsjetNative" │ (the whole chat UI, ~200 lines)
│ • model download + prompt formatting │
└──────────────┬──────────────────────────┘
│ JNI (LlamaBridge)
▼
┌─────────────────────────────────────────┐
│ cpp/llama-jni.cpp → libtsjet.so │
│ talks to llama.cpp C API (pinned) │
└─────────────────────────────────────────┘
- Inference: llama.cpp compiled from source (pinned tag
b10333) through the NDK. Pulled automatically at build time via CMakeFetchContent— no submodule to init. - Model:
Qwen3.5-2B-Uncensored-HauhauCS-Aggressive-Q8_0.gguf(~1.9 GB) is downloaded on first launch from Hugging Face into the app's private storage. It is not bundled in the APK. (Swap the quant inModelDownloader.kt.) - Persona: set via
SYSTEM_PROMPTinMainActivity.kt; sampling is a little hot (temp 0.9) for playful answers. - Conversation: the web layer holds the full history and sends it each turn; native rebuilds the ChatML prompt and clears the KV cache before every reply, so "new tsjet" is just: clear JS state + reset cache.
Requirements
- A build machine with the Android SDK + NDK. Easiest path: open the project
in Android Studio (it will offer to install the matching NDK
27.2.12479018and CMake3.22.1). - A phone: 64-bit ARM (
arm64-v8a), Android 8.0+ (minSdk 26), and enough free RAM to hold a ~1.9 GB Q8 model (~4+ GB RAM device recommended). - Network on first launch to download the model.
Build
./gradlew assembleDebug
The APK lands in app/build/outputs/apk/debug/. Or just Run ▶ from Android Studio.
First build compiles llama.cpp from source, so it takes a while and needs network (CMake fetches the pinned llama.cpp).
Where things live
| What | Where |
|---|---|
| Chat UI (HTML/CSS/JS) | app/src/main/assets/web/ |
| Android glue + model download | app/src/main/java/monster/autisme/tsjetpiti/ |
| Native llama.cpp bridge | app/src/main/cpp/llama-jni.cpp |
| Pinned llama.cpp version | app/src/main/cpp/CMakeLists.txt (GIT_TAG b10333) |
| Model URL / filename | ModelDownloader.kt |
| Generation params (ctx, temp, tokens) | MainActivity.kt + llama-jni.cpp |
TODO / notes
- Logo: juli is on it. The header logo is a placeholder
🐟inassets/web/index.html(#logo), and there's no launcher icon yet — add anandroid:iconinAndroidManifest.xml+ amipmapwhen the artwork is ready. - The model may emit
<think>…</think>blocks; the UI strips them from the display and from history (stripThinkinapp.js). - Bumping the llama.cpp tag? Re-check the C API calls in
llama-jni.cppagainst that tag'sinclude/llama.h— it uses the raw C API directly. - Not yet compiled end-to-end in CI; first real build is the acid test for the native layer.
Releases
2
tsjetpiti 0.1.1
Latest
Languages
Kotlin
39.9%
C++
20.9%
CSS
12.1%
JavaScript
11.4%
Python
7.8%
Other
7.9%