# `:vector-index-ksqlite` — ANN-индекс поверх SQLite (brute-force) ## Что это KMP-реализация `MutableVectorIndexStore` поверх [ksqlite](https://github.com/caffeine-mgn/ksqlite). Persistence "бесплатно" — обычная SQLite БД. Стратегия поиска: **brute-force cosine similarity**. Один `SELECT` всех записей → декодирование BLOB → cosine sim в Kotlin → sort desc → топ-K. Просто, KMP-совместимо, **без** sqlite-vec extension. Используется как persistent ANN-индекс для small-to-medium датасетов (≤10K записей на embedding ~512d). Для больших — переезжать на `:vector-index-jvector` или sqlite-vec. ## Где используется Standalone-агентом, когда нужна persistence embeddings и масштаб не превышает ~10K записей. Прямо сейчас модуль **не подключён** в `:standalone` — это API-готовый компонент, ждущий интеграции. ## Что внутри ```kotlin class KsqliteVectorIndexStore private constructor( override val dimension: Int, connection: SQLiteConnection, ownsConnection: Boolean, ) : MutableVectorIndexStore { constructor(dimension: Int, path: String) constructor(dimension: Int, connection: SQLiteConnection) override suspend fun getSize(): Long override suspend fun add(id: String?, embedding: FloatArray, payload: String?): VectorIndex override suspend fun delete(id: String): Boolean override suspend fun clear(): Long override suspend fun search(embedding: FloatArray, limit: Int): List override fun close() companion object { fun memory(dimension: Int, name: String? = null): KsqliteVectorIndexStore } } ``` **Lifecycle:** - `(dimension, path)` — открывает файловое соединение, мигрирует, закрывает в `close()`. - `(dimension, connection)` — внешнее соединение (shared bundle); store НЕ закрывает его. - `memory(dimension, name)` — in-memory, мигрирует, закрывает. **Schema:** единственная таблица `vector_index (id TEXT PRIMARY KEY, dimension INTEGER, embedding BLOB, payload TEXT)`. Embedding хранится как raw float32 little-endian через ручной `FloatArray.toLittleEndianBytes()` / `ByteArray.toFloatArray()` (hand-rolled в commonMain — без `java.nio.ByteBuffer`). **Миграции:** `PRAGMA user_version` + `BEGIN`/`COMMIT`. Идемпотентен. **UUID:** `kotlin.uuid.Uuid.random()` (commonMain-safe, не JVM-only `java.util.UUID`). ## Цели сборки **Только:** `jvm()`, `linuxX64()`, `linuxArm64()`, `mingwX64()` — ksqlite 0.1.2 публикует нативные бинари только для этих 4 целей (плюс 4 Android NDK). Нет macOS/iOS — для Apple использовать `:vector-index-jvector`. ## Как подключить ```kotlin dependencies { implementation("pw.binom.agentik:vector-index-ksqlite:0.1.0") implementation("pw.binom.agentik:vector-index-api:0.1.0") } val store = KsqliteVectorIndexStore(dimension = 768, path = "~/.agentik/vectors.db") store.add(id = "doc-1", embedding = embeddingOf("hello"), payload = """{"src":"chat"}""") val top = store.search(embedding = queryEmbedding, limit = 10) store.close() ``` ## Тесты ``` ./gradlew :vector-index-ksqlite:jvmTest ``` Покрывают: explicit/auto id, duplicate throws, delete+idempotency, clear, top-K sort, payload round-trip, BLOB bit-exact round-trip (FloatArray<->BLOB encoding), schema migration idempotency. ## Зависимости - `pw.binom.db:ksqlite:0.1.2` - `pw.binom.agentik:vector-index-api` - `kotlinx-coroutines-core` ## Чего здесь НЕТ - ANN на стороне SQL — нет sqlite-vec. Brute-force в Kotlin. - Фильтрации в `search()` — чистый ANN. - Apple targets (см. выше). ## Текущий статус Реализация готова, тесты зелёные. Интеграция в `:standalone` отложена до момента, когда понадобится.