One Interface.
Every AI Model.
Zero Data Lock-In.
Run local GGUF models on-device with zero lock-in, auto-sync Markdown to Obsidian, and orchestrate 8+ cloud providers seamlessly.
Interactive Prompt Lab
Single-turn precision tools implemented in PromptLabScreen.kt. Test real on-device transformations right here in your browser.
Select a tool and click "Run Transformation" to view the generated output.
Built-In HuggingFace GGUF Hub
Search, inspect and stream-download quantized GGUF weights directly into Documents/Oorty/models/ with live download speed telemetry.
Qwen 2.5 (0.5B-Instruct)
Smallest high-efficiency model. Ideal for 4GB RAM phones. Highest generation speed (35+ tok/s).
Q4_K_M
Context: 2,048
Llama 3.2 (1B-Instruct)
The sweet spot for mobile instruction following, coding snippets, and concise explanations.
Q4_K_M
Context: 4,096
Gemma 2 (2B-IT)
Google's powerhouse compact architecture with superior knowledge synthesis and mathematical reasoning.
Q4_K_M
Context: 4,096
Phi-3-Mini (3.8B-4k)
Full agentic tool calling support with high JSON parsing reliability and deep multi-turn context retention.
Q4_K_M
Context: 4,096
HuggingFaceApi.kt)
Dynamic RAM Watchdog & Fit Matrix
Implemented with ActivityManager.MemoryInfo in HardwareChecker.kt. Never worry about Out-Of-Memory system crashes.
Llama 3.2 (1B-Instruct)
Quant: Q4_K_M • ~850 MB VRAMOptimal choice for 6GB devices. Delivers 25–35 tok/s decode speed with fast responses and low battery consumption. Leaves ample RAM for Android OS.
Built Without Compromise
Designed from the ground up in native Kotlin with Jetpack Compose for uncompromising speed and privacy.
Native llama.cpp JNI Engine
Zero Python, zero Node.js daemon overhead. High-performance C++ bindings via llamacpp-kotlin executing direct ARM NEON & Vulkan instructions.
Obsidian Markdown Dual-Write
Every chat turn is automatically formatted into clean Markdown with YAML frontmatter tags and saved directly to your local documents folder.
Model Context Protocol (MCP)
Run multi-server tool loops with strict JSON schema validation, human approval prompts, and local SQLite query execution.
Zero Telemetry & 100% Offline
Your chats, memory embeddings, and API keys never leave your phone. Works seamlessly in airplane mode without internet connectivity.
Real-Time Reasoning Parser
Streaming <think> token parser dynamically hides/reveals model reasoning accordions without blocking generation flow.
Multi-Cloud Router (BYOK)
Bring your own API keys for Google Gemini 2.5, Claude 3.7, OpenAI, Groq, Mistral, and NVIDIA NIM with zero subscription fees.
Your Chats Belong In Your Obsidian Vault
Unlike proprietary apps that trap your conversations inside locked SQLite databases, Oorty dual-writes every session as plain text Markdown.
YAML Metadata Frontmatter
Includes session ID, model name, token timestamps, and auto-generated topic tags.
Direct File System Access
Open Documents/Oorty/chats/ directly in Obsidian, Logseq, VS Code, or Git.
Bi-directional Wikilinks
Link ideas across conversations with standard [[note-title]] syntax.
Autonomous MCP Agent Workbench
Connect local tools and remote MCP servers through standard JSON-RPC contracts. Enable tool-calling on both local GGUF and frontier cloud models.
Registered MCP Servers
Configure authorized tools and permission boundaries.
Under The Hood
Explore the exact Kotlin and JNI abstractions driving Oorty's on-device performance.
// Native Coroutine Flow Stream from llama.cpp JNI
fun generateStream(prompt: String, params: InferenceParams): Flow<StreamChunk> = callbackFlow {
val startTime = System.nanoTime()
var tokenCount = 0
llamaEngine.streamTokens(prompt) { token ->
tokenCount++
val elapsedSec = (System.nanoTime() - startTime) / 1_000_000_000.0
val tokPerSec = if (elapsedSec > 0) tokenCount / elapsedSec else 0.0
trySend(StreamChunk(text = token, tokPerSec = tokPerSec))
}
awaitClose { llamaEngine.cancel() }
}.flowOn(Dispatchers.Default)
// Sub-10ms Title Extraction without loading LLM
fun extractTopicTitle(firstMessage: String): String {
val clean = firstMessage.replace(PUNCTUATION_REGEX, "")
val keywords = clean.split(" ")
.filter { it.length > 3 && !STOP_WORDS.contains(it.lowercase()) }
.take(4)
.joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }
return keywords.ifEmpty { "New Conversation" }
}
// Real-time parse of DeepSeek <think> reasoning tags
class StreamingThoughtParser {
fun parseChunk(chunk: String): ParsedThoughtState {
val thinkStart = chunk.indexOf("<think>")
val thinkEnd = chunk.indexOf("</think>")
return when {
thinkStart != -1 && thinkEnd != -1 ->
ParsedThoughtState.Complete(thought = chunk.substring(thinkStart + 7, thinkEnd))
thinkStart != -1 ->
ParsedThoughtState.Thinking(partial = chunk.substring(thinkStart + 7))
else -> ParsedThoughtState.Output(response = chunk)
}
}
}
// Dual-write Markdown with YAML Frontmatter
suspend fun writeChatToObsidian(session: ChatSession, messages: List<ChatMessage>) = withContext(Dispatchers.IO) {
val frontmatter = buildFrontmatter(session)
val body = messages.joinToString("
") { "### ${it.sender}
${it.content}" }
val file = File(vaultDir, "${session.id}.md")
file.writeText("$frontmatter
$body")
}
Everything You Need To Know
Clear answers to your technical and architectural questions.
What is Oorty and why is it unique?
llamacpp-kotlin, with zero internet requirement, while automatically syncing every turn as an open Markdown document into an Obsidian vault.
Which GGUF models are supported on my phone?
How does the Obsidian vault dual-write work?
.md file with structured YAML frontmatter (session ID, model name, tokens/sec, and timestamp) directly into Documents/Oorty/chats/.