tsjetpiti icon

tsjetpiti

Tiny app. Big model. Absolutely no idea what it's talking about.

ANDROID 11+  •  ARM64  •  FULLY ON-DEVICE  •  CONFIDENTLY WRONG


Meet tsjet

tsjetpiti is a dead-simple Android chat app running an uncensored Qwen3.5-2B model entirely on your phone through embedded llama.cpp. No cloud inference, no account, and no conversation leaving the device.

tsjet has one special talent: it always answers, sounds completely confident, and is hilariously, deliberately wrong. Hit new tsjet whenever you want to erase the evidence 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 CMake FetchContent — no submodule to init.
  • Model: Qwen3.5-2B-Uncensored-HauhauCS-Aggressive in Q6_K format (~1.5 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 in ModelDownloader.kt.)
  • Persona: set via SYSTEM_PROMPT in MainActivity.kt; sampling is a little hot (temp 0.9) for playful answers. The model is a Qwen3 "thinking" model, so the prompt disables reasoning (empty <think></think> prefill + /no_think) to keep replies fast and punchy instead of burning the token budget on hidden thoughts.
  • 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.12479018 and CMake 3.22.1).
  • A phone: 64-bit ARM (arm64-v8a), Android 11+ (minSdk 30), and enough free RAM to hold a ~1.5 GB Q6 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).

Install (sideload)

A prebuilt app-debug.apk is attached to every Release.

The debug APK is signed with the debug key, so you can sideload it directly: copy app-debug.apk to the phone, enable "install unknown apps" for your file manager, and tap it. First launch downloads the ~1.5 GB model over Wi-Fi.

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

Credits

tsjetpiti is small because it stands on the shoulders of some decidedly not-small projects:

TODO / notes

  • Branding: launcher icon (adaptive + legacy densities) is generated from piti-icon.png via python3 tools/gen_launcher_icons.py; the header wordmark is piti-logo.png (copied to assets/web/logo.png). Source art lives at the repo root.
  • The model may emit <think>…</think> blocks; the UI strips them from the display and from history (stripThink in app.js).
  • Bumping the llama.cpp tag? Re-check the C API calls in llama-jni.cpp against that tag's include/llama.h — it uses the raw C API directly.
  • Native is always built optimized. AGP compiles the debug variant's C/C++ at -O0 by default, which makes llama.cpp ~10x too slow; CMakeLists.txt forces Release/-O3 regardless of variant. Native libs are also 16 KB page-aligned.
  • Verified on-device (Pixel 7, Android, 4 KB pages): downloads the model, loads it (n_ctx=4096, 6 threads), streams a reply, and "new tsjet" clears the chat. ./gradlew assembleDebug → ~13 MB arm64-v8a APK (model downloads on first launch).
Description
Barebones chat app for a local Ollama model (Qwen3.5-2B) — the tsjet.
Readme 514 KiB
2026-08-09 20:44:13 +00:00
Languages
Kotlin 39.9%
C++ 20.9%
CSS 12.1%
JavaScript 11.4%
Python 7.8%
Other 7.9%