On-Device AI Framework for iOS and Android
Locanara is an on-device AI framework inspired by LangChain, purpose-built for mobile. Build, compose, and extend AI features using platform-native models — all processing happens locally on the device.
No cloud. No data leaves. Privacy by design.
Documentation: locanara.hyo.dev | Blog: LangChain for Mobile, Entirely On-Device — Meet Locanara
| Engine | Description | Requirements |
|---|---|---|
| Apple Intelligence | OS-level Foundation Models | iOS 18.1+ / macOS 15.1+ (iOS 26+ / macOS 26+ recommended), Apple Silicon, 7GB+ free space |
| llama.cpp | GGUF models with Metal GPU acceleration | iOS 17+ / macOS 14+, Apple Silicon |
| CoreML | Neural Engine accelerated inference | iOS 17+ / macOS 14+, Apple Silicon |
| MLX | Apple Silicon optimized inference | macOS 14+, Apple Silicon |
| Engine | Description | Requirements |
|---|---|---|
| Gemini Nano | ML Kit GenAI (Prompt API) | Android 14+ (API 34+) |
| ExecuTorch | Meta's on-device inference runtime | Android 12+ (API 31+) |
Locanara automatically detects device capabilities and routes inference to the best available engine.
Most on-device AI SDKs give you raw model access. Locanara gives you a framework — composable chains, memory management, guardrails, and a pipeline DSL — so you can build production AI features, not just call a model.
- Simple — Call a model extension for a common task.
- Chain — Configure and run a built-in chain directly.
- Custom — Implement
Chainfor app-specific behavior.
Swift
import Foundation
import Locanara
let model = LocanaraDefaults.model
let article = "On-device AI keeps private data on the device."
// 1. Simple
let summary = try await model.summarize(article, bulletCount: 3)
// 2. Chain
let chain = SummarizeChain(model: model, bulletCount: 3)
let configured = try await chain.run(article)
// 3. Custom
struct TrimChain: Chain {
let name = "TrimChain"
func invoke(_ input: ChainInput) async throws -> ChainOutput {
let text = input.text.trimmingCharacters(in: .whitespacesAndNewlines)
return ChainOutput(value: text, text: text, metadata: input.metadata)
}
}Kotlin
import com.locanara.builtin.SummarizeChain
import com.locanara.composable.Chain
import com.locanara.core.ChainInput
import com.locanara.core.ChainOutput
import com.locanara.core.LocanaraDefaults
import com.locanara.dsl.summarize
suspend fun threeLevelsExample() {
val model = LocanaraDefaults.model
val article = "On-device AI keeps private data on the device."
// 1. Simple
val summary = model.summarize(article, bulletCount = 3)
// 2. Chain
val chain = SummarizeChain(model = model, bulletCount = 3)
val configured = chain.run(article)
// 3. Custom
class TrimChain : Chain {
override val name = "TrimChain"
override suspend fun invoke(input: ChainInput): ChainOutput {
val text = input.text.trim()
return ChainOutput(value = text, text = text, metadata = input.metadata)
}
}
}┌─────────────────────────────────────────────┐
│ Runtime Layer │
│ Agent · Session · ChainExecutor │
├─────────────────────────────────────────────┤
│ Built-in Chains │
│ Summarize · Classify · Chat · Translate · │
│ Extract · Rewrite · Proofread │
├─────────────────────────────────────────────┤
│ Composable Layer │
│ Chain · Tool · Memory · Guardrail │
├─────────────────────────────────────────────┤
│ Core Layer │
│ LocanaraModel · PromptTemplate · │
│ OutputParser · Schema │
├─────────────────────────────────────────────┤
│ DSL Layer │
│ Pipeline · PipelineStep · ModelExtensions │
├─────────────────────────────────────────────┤
│ Platform Layer │
│ FoundationLanguageModel · PromptApiModel │
├─────────────────────────────────────────────┤
│ Engine Layer │
│ InferenceRouter · LlamaCppEngine · │
│ ExecuTorchEngine · DeviceCapabilityDetector│
├─────────────────────────────────────────────┤
│ ModelManager Layer │
│ ModelManager · ModelDownloader · │
│ ModelRegistry · ModelStorage │
├─────────────────────────────────────────────┤
│ RAG Layer │
│ VectorStore · DocumentChunker · │
│ EmbeddingEngine · RAGQueryEngine │
├─────────────────────────────────────────────┤
│ Personalization Layer │
│ PersonalizationManager · FeedbackCollector │
│ PreferenceAnalyzer · PromptOptimizer │
└─────────────────────────────────────────────┘
iOS (Swift Package Manager)
https://github.com/hyodotdev/locanara
Android (Gradle)
implementation("com.locanara:locanara:1.1.2")- Chain — Composable unit of AI logic with typed input/output
- Pipeline DSL — Compose chains while tracking the last step's result type
- Memory — BufferMemory (last N turns) and SummaryMemory (compressed history)
- Guardrail — Input/output validation and content filtering
- Tool — External capability integration for agents
7 ready-to-use chains, each returning typed results:
| Chain | Result Type | Description |
|---|---|---|
| SummarizeChain | SummarizeResult | Text summarization |
| ClassifyChain | ClassifyResult | Text classification |
| ExtractChain | ExtractResult | Entity extraction |
| ChatChain | ChatResult | Conversational AI with memory |
| TranslateChain | TranslateResult | Language translation |
| RewriteChain | RewriteResult | Text rewriting by style |
| ProofreadChain | ProofreadResult | Grammar correction |
- InferenceEngine — Unified protocol for all inference backends
- InferenceRouter — Automatic engine selection based on device capabilities
- DeviceCapabilityDetector — Hardware detection (NPU, memory, chipset)
- MemoryManager — Intelligent memory allocation for model loading
- ModelManager — Download, load, and manage on-device models
- ModelRegistry — Available model catalog with metadata
- ModelDownloader — Background download with progress tracking
- ModelStorage — Local storage and cache management
- VectorStore — Local vector storage for embeddings
- DocumentChunker — Text splitting with configurable strategies
- EmbeddingEngine — On-device embedding generation
- RAGQueryEngine — Similarity search and context retrieval
- FeedbackCollector — Collect user feedback on AI outputs
- PreferenceAnalyzer — Learn user preferences over time
- PromptOptimizer — Adapt prompts based on user behavior
- ChainExecutor — Instrumented execution with retry and history
- Session — Stateful conversation management
- Agent — ReAct-lite autonomous agent with tools
Compose multiple AI steps into one workflow. Each step passes its text and metadata to the next step, and the last step determines the pipeline's compile-time return type. The builders do not prove that every adjacent step is semantically compatible.
Swift
import Locanara
let model = FoundationLanguageModel()
// Step 1: fix typos
let proofread = try await model.proofread(
"Ths is a tset of on-devce AI."
)
// Step 2: translate the corrected text
let translated = try await model.translate(
proofread.correctedText, to: "ko"
)
print(translated.translatedText)Kotlin
import com.locanara.dsl.*
import com.locanara.platform.PromptApiModel
suspend fun example(context: Context) {
val model = PromptApiModel(context)
// Step 1: fix typos
val proofread = model.proofread(
"Ths is a tset of on-devce AI."
)
// Step 2: translate the corrected text
val translated = model.translate(
proofread.correctedText, to = "ko"
)
println(translated.translatedText)
}Swift's @PipelineBuilder tracks the last step's result type. Assigning the result to a different type is a compile error, while adjacent steps still exchange ChainOutput.text and metadata at runtime.
import Locanara
let model = FoundationLanguageModel()
// Two-step: proofread → translate
// Return type is TranslateResult — tracked from the final step
let result = try await model.pipeline {
Proofread()
Translate(to: "ko")
}.run("Ths is a tset sentece about on-devce AI.")
print(result.translatedText) // "이것은 온디바이스 AI에 관한 테스트 문장입니다."
print(result.targetLanguage) // "ko"
// Three-step: summarize → proofread → translate
let threeStep = try await model.pipeline {
Summarize(bulletCount: 3)
Proofread()
Translate(to: "ja")
}.run(longArticle)
// Returns TranslateResult (last step determines the type)import com.locanara.dsl.*
import com.locanara.platform.PromptApiModel
suspend fun pipelineExample(context: Context) {
val model = PromptApiModel(context)
// Fluent pipeline API
val result = model.pipeline()
.proofread()
.translate(to = "ko")
.run("Ths is a tset sentece about on-devce AI.")
// result is TranslateResult (the final step determines the type)
println(result.translatedText)
// Three-step pipeline
val threeStep = model.pipeline()
.summarize(bulletCount = 3)
.proofread()
.translate(to = "ja")
.run(longArticle)
}| Step | Swift | Kotlin | Output |
|---|---|---|---|
| Summarize | Summarize(bulletCount:) |
.summarize(bulletCount:) |
SummarizeResult |
| Classify | Classify(categories:) |
.classify(categories:) |
ClassifyResult |
| Translate | Translate(to:) |
.translate(to:) |
TranslateResult |
| Proofread | Proofread() |
.proofread() |
ProofreadResult |
| Rewrite | Rewrite(style:) |
.rewrite(style:) |
RewriteResult |
| Extract | Extract(entityTypes:) |
.extract(entityTypes:) |
ExtractResult |
Web and wrappers: The Web SDK, Expo, React Native, and Flutter APIs do not expose the native Pipeline builder. Compose their feature calls explicitly and use streaming methods such as
summarizeStreaming(),translateStreaming(), andrewriteStreaming()where supported.Full tutorial: locanara.hyo.dev/docs/tutorials/pipeline
-
apple — iOS/macOS SDK
-
android — Android SDK
-
site — Website + Documentation → locanara.hyo.dev
-
expo-ondevice-ai — Expo module
-
flutter_ondevice_ai — Flutter plugin
iOS / macOS
- Minimum: iOS 17+ / macOS 14+ (llama.cpp, CoreML engines)
- Apple Intelligence: iOS 18.1+ / macOS 15.1+ (iOS 26+ / macOS 26+ recommended)
- Requires Apple Silicon (A17 Pro+ for iPhone, M1+ for Mac)
- Requires 7GB+ free storage space
- Apple Silicon device required for all engines
Android
- Minimum: Android 12+ (API 31+) for ExecuTorch engine
- Full: Android 14+ (API 34+) for Gemini Nano engine
- Device with NPU support recommended
AGPL-3.0 License — see LICENSE for details.
Built with conviction that AI should run where your data lives — on your device.
