> ## Documentation Index
> Fetch the complete documentation index at: https://docs.synheart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Kotlin

> syni-kotlin — Android SDK for Syni's adaptive on-device LLM inference with hybrid local/cloud chat.

## Overview

`syni-kotlin` is the **Android consumer SDK** for Syni. Same agent surface as the [Flutter sibling](/syni/flutter) and the [Swift sibling](/syni/swift), idiomatic for Kotlin (`suspend` calls, `Flow<SyniChatEvent>` streaming, `StateFlow<SyniInstallState>` for the install lifecycle).

## Platform support

* Android 8.0+ (`minSdk 26`)
* Kotlin 2.0+ · Java 17 toolchain · AGP 9.0+
* Native runtime delivered by the `synheart` CLI for `arm64-v8a`, `armeabi-v7a`, `x86_64`, `x86` (see install steps below)

## Installation

```kotlin theme={null}
// build.gradle.kts
dependencies {
    implementation("ai.synheart:syni:0.0.3")
}
```

### Native runtime

The AAR ships Kotlin sources only. Install the native runtime once into
your project with the `synheart` CLI:

```bash theme={null}
curl -fsSL https://synheart.sh/install | sh
synheart install runtime syni
```

This writes `libsyni_ffi.so` for every Android ABI under
`<your-project>/synheart/vendor/syni/android/jniLibs/`. Wire the vendor
path into your **app** module's `build.gradle.kts` so JNA can find the
library at runtime:

```kotlin theme={null}
// app/build.gradle.kts
android {
    sourceSets {
        getByName("main").jniLibs.srcDirs(
            file("${rootProject.projectDir}/synheart/vendor/syni/android/jniLibs"),
        )
    }
}
```

The SDK uses JNA (`Native.load`) to resolve `libsyni_ffi` at runtime, so
the AAR compiles standalone — calls into the runtime fail with a clear
error if the install step hasn't been run.

## Basic usage

```kotlin theme={null}
import ai.synheart.syni.SyniAgent
import ai.synheart.syni.SyniModels
import ai.synheart.syni.SyniSpecPersona

val agent = SyniAgent(context = application)

// Load a persona by id from bundled spec assets.
val persona = SyniSpecPersona.load(context, "focus.coach.v1")

// First-run install: download + verify the model, load the engine,
// bind the persona. Emits lifecycle events on agent.installState.
agent.install(
    persona = persona,
    model = SyniModels.qwen25_15bInstructQ4,
)

// Single-turn chat.
val response = agent.chat("How can I focus right now?")
println(response.displayText)
```

## Streaming

`chatStream` returns a `Flow<SyniChatEvent>` — token-level `Delta` events followed by exactly one `Final` carrying the structured response.

```kotlin theme={null}
agent.chatStream("hello").collect { event ->
    when (event) {
        is SyniChatEvent.Delta -> print(event.delta)
        is SyniChatEvent.Final -> println("\n[${event.response.displayText.length} chars]")
    }
}
```

## Hybrid local / cloud

```kotlin theme={null}
val agent = SyniAgent(
    context = application,
    cloudConfig = SyniCloudConfig(
        baseUrl = "https://api.synheart.ai",
        authToken = { "<bearer-token>" },
        tenantId = "<tenant>",
        userId = "<user>",
    ),
)

agent.chat(
    message = "how was my recent session?",
    mode = SyniExecutionMode.CLOUD_FIRST, // try cloud, fall back to local
)
```

`SyniExecutionMode` values: `LOCAL_FIRST` (default), `CLOUD_FIRST`, `LOCAL_ONLY`, `CLOUD_ONLY`.

## Install lifecycle

Wire `installState` into your UI to surface progress:

```kotlin theme={null}
agent.installState.collect { state ->
    when (state) {
        SyniInstallState.NotInstalled -> showInstallPrompt()
        is SyniInstallState.Installing -> showProgress(state.stage, state.progress)
        is SyniInstallState.Installed -> showReady()
        is SyniInstallState.Failed -> showError(state.reason)
    }
}
```

Stages: `DOWNLOADING_MODEL` → `VERIFYING_MODEL` → `LOADING_ENGINE` → `BINDING_PERSONA`.

## Where this fits

`syni-kotlin` is the **agent layer** — inference, install lifecycle, persona binding, chat orchestration. It does NOT own:

* HSI signal collection (the [synheart-core](/synheart-core/kotlin) SDK does), or
* the four-authority gate (consent + capability + activation + session; also a host concern).

Synheart-ecosystem apps typically depend on `synheart-core` and use `SyniModule`, which wraps this SDK with those layers. Standalone use of `syni-kotlin` is fully supported when you don't need the wider Synheart contract.

## Models

Out-of-the-box GGUF models exposed under `SyniModels`:

* `qwen25_15bInstructQ4` (default, \~1.5 GB)
* `qwen2_05bInstructQ4` (\~500 MB)
* `gemma3_1bInstructQ4`

Pinned SHA-256 per model; verified at install time. Bring your own GGUF via a custom `SyniModelSpec`.

## API reference

| Surface                                                           | Notes                                                                                                                                    |
| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `SyniAgent`                                                       | Constructed with `context` + optional `installer` / `cloudConfig`. Exposes `installState` / `currentState` / `isInstalled` / `hasCloud`. |
| `SyniAgent.install / restoreInstallIfReady / uninstall / dispose` | `suspend`; emit install-state transitions.                                                                                               |
| `SyniAgent.chat / chatStream`                                     | `suspend` / `Flow<SyniChatEvent>`.                                                                                                       |
| `SyniInstallState`                                                | Sealed class: `NotInstalled`, `Installing(stage, progress)`, `Installed(...)`, `Failed(reason, cause?)`.                                 |
| `SyniChatResponse`                                                | `displayText`, `message`, `suggestions`, `kind` (`chat` / `coach` / `suggestions` / `unknown`).                                          |
| `SyniSpecPersona`                                                 | `load(context, personaId)` — bundled persona JSON.                                                                                       |
| `SyniModelCatalog`                                                | List local + cloud model options; bundled defaults under `SyniModelCatalog.bundled`.                                                     |
| `SyniCloudConfig`                                                 | `baseUrl`, `authToken: () -> String?`, `tenantId`, `userId`.                                                                             |
| `SyniExecutionMode`                                               | `LOCAL_FIRST`, `CLOUD_FIRST`, `LOCAL_ONLY`, `CLOUD_ONLY`.                                                                                |

## Related

* [Syni overview](/syni/overview)
* [Syni spec](/syni-spec/overview)
* [synheart-core/kotlin](/synheart-core/kotlin) — host SDK with `SyniModule` consent gate
