storage-sqlite: выделить SQLDelight + SQLite-импл в отдельный модуль

Перенесён SQLDelight (4 .sq файла, конфигурация databases { AgentikDatabase })
и 5 SQLite-импл классов (SqliteStores, SqliteConversationStore, SqliteMessageStore,
SqliteWorkingMemoryStore, SqliteReflectionStore) из :standalone в новый JVM-only
модуль :storage-sqlite под пакетом pw.binom.agentik.storage.sqlite.

Изменения:
- Новый :storage-sqlite модуль с sqldelight-плагином + sqlite JDBC driver
- Все .sq файлы и Kotlin-классы переехали с переименованием пакета
- ReflectionStore.kt в :standalone (только SQLite-импл) удалён — функционал
  живёт в :storage-sqlite/SqliteReflectionStore.kt
- :standalone/build.gradle.kts: убран sqldelight-плагин и конфигурация,
  добавлена зависимость :storage-sqlite
- Все импорты в :standalone (8 main + 7 test) перенаправлены на новый пакет
- ReflectionStoreTest.kt переехал в :storage-sqlite/jvmTest (тестирует
  internal fun encode/decodeStringArray в :storage-sqlite)

Совместимость:
- SqliteStores доступен по новому пути pw.binom.agentik.storage.sqlite.SqliteStores
- Старые импорты в тестах обновлены (минимум diff — 1 строка на файл)
- В commit 6 ChatAgent переключится на StorageBundle API; SqliteStores
  станет деталью реализации :standalone

Тесты: 299/299 green. Fatjar standalone-all.jar 240 MB.

Преимущества:
- :standalone больше не зависит от SQLDelight плагина (легче поддерживать)
- :storage-sqlite может быть заменён/расширен (например, :storage-android)
- Тесты storage-слоя сгруппированы по модулю реализации
This commit is contained in:
2026-09-15 14:51:58 +03:00
parent 61f8205f40
commit a7d8cbe713
23 changed files with 76 additions and 42 deletions
+6
View File
@@ -52,3 +52,9 @@ include(":storage-core")
// IO-зависимостей. Используется в тестах (быстрый setup, без JDBC) и будет // IO-зависимостей. Используется в тестах (быстрый setup, без JDBC) и будет
// использоваться в Android-сборке (JVector/SQLite не подходят для ART out-of-box). // использоваться в Android-сборке (JVector/SQLite не подходят для ART out-of-box).
include(":storage-inmemory") include(":storage-inmemory")
// SQLDelight-реализация всех 4 store'ов из :storage-core. JVM-only (SQLDelight
// native драйверов для KMP вне JVM пока не публикует). Содержит 4 .sq-файла +
// 5 классов: SqliteStores, SqliteConversationStore, SqliteMessageStore,
// SqliteWorkingMemoryStore, SqliteReflectionStore. Бэкенд для прод-запуска
// :standalone (путь к .db файлу в AGENTIK_DB).
include(":storage-sqlite")
+4 -19
View File
@@ -9,7 +9,6 @@ import org.gradle.api.artifacts.ConfigurationContainer
plugins { plugins {
alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.kotlin.serialization) alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.sqldelight)
alias(libs.plugins.shadow) alias(libs.plugins.shadow)
} }
@@ -36,29 +35,24 @@ kotlin {
// litert-kmp: контракт (commonMain) // litert-kmp: контракт (commonMain)
api(libs.litert.api) api(libs.litert.api)
// SQLDelight runtime (commonMain) // litert-openai: JVM-реализация
api(libs.sqldelight.runtime) implementation(libs.litert.openai)
api(libs.sqldelight.coroutines)
} }
jvmMain.dependencies { jvmMain.dependencies {
// Парсер и загрузчик скилов (YAML frontmatter + markdown body). // Парсер и загрузчик скилов (YAML frontmatter + markdown body).
implementation(project(":skills")) implementation(project(":skills"))
// Долговременная память (Hermes-style MD-бэкенд). // Долговременная память (Hermes-style MD-бэкенд) + хранилище истории.
implementation(project(":memory-api")) implementation(project(":memory-api"))
implementation(project(":memory-md")) implementation(project(":memory-md"))
implementation(project(":memory-vector")) implementation(project(":memory-vector"))
implementation(project(":storage-core")) implementation(project(":storage-core"))
implementation(project(":storage-sqlite"))
// litert-openai: JVM-реализация
implementation(libs.litert.openai)
// litert-google: встроенный LiteRT-LM движок, нужен только на runtime // litert-google: встроенный LiteRT-LM движок, нужен только на runtime
runtimeOnly(libs.litert.google) runtimeOnly(libs.litert.google)
// SQLDelight JDBC driver (JVM)
implementation(libs.sqldelight.sqlite.driver)
// Ktor server (для :server facade + a2aServer) // Ktor server (для :server facade + a2aServer)
// Используем CIO вместо Netty — он KMP (jvm + linuxX64/ios/...), нам нужен // Используем CIO вместо Netty — он KMP (jvm + linuxX64/ios/...), нам нужен
// для будущей native-сборки; на JVM работает идентично для нашего сценария. // для будущей native-сборки; на JVM работает идентично для нашего сценария.
@@ -100,15 +94,6 @@ kotlin {
} }
} }
sqldelight {
databases {
create("AgentikDatabase") {
packageName.set("pw.binom.agentik.standalone.persistence.sqlite")
srcDirs.setFrom("src/jvmMain/sqldelight")
}
}
}
// --- Fatjar (uberjar) --- // --- Fatjar (uberjar) ---
// //
// :standalone — KMP jvm-only бэкенд. Стандартный KMP `jvmJar` содержит только // :standalone — KMP jvm-only бэкенд. Стандартный KMP `jvmJar` содержит только
@@ -14,7 +14,7 @@ import pw.binom.agentik.standalone.agent.ChatConversation
import pw.binom.agentik.standalone.agent.LlmReflector import pw.binom.agentik.standalone.agent.LlmReflector
import pw.binom.agentik.standalone.agent.SkillMiner import pw.binom.agentik.standalone.agent.SkillMiner
import pw.binom.agentik.standalone.agent.memory.Curator import pw.binom.agentik.standalone.agent.memory.Curator
import pw.binom.agentik.standalone.persistence.sqlite.SqliteStores import pw.binom.agentik.storage.sqlite.SqliteStores
import pw.binom.agentik.skills.SkillStore import pw.binom.agentik.skills.SkillStore
/** /**
@@ -27,7 +27,7 @@ import pw.binom.agentik.standalone.config.AgentikConfig
import pw.binom.agentik.standalone.config.AgentikConfig.MemoryBackend import pw.binom.agentik.standalone.config.AgentikConfig.MemoryBackend
import pw.binom.agentik.standalone.llm.LlmBackend import pw.binom.agentik.standalone.llm.LlmBackend
import pw.binom.agentik.standalone.mcp.McpRegistry import pw.binom.agentik.standalone.mcp.McpRegistry
import pw.binom.agentik.standalone.persistence.sqlite.SqliteStores import pw.binom.agentik.storage.sqlite.SqliteStores
import java.io.File import java.io.File
/** /**
* standalone-контейнер agentik: * standalone-контейнер agentik:
@@ -19,7 +19,7 @@ import pw.binom.agentik.standalone.llm.LlmConfig
import pw.binom.agentik.storage.ConversationRecord import pw.binom.agentik.storage.ConversationRecord
import pw.binom.agentik.storage.Reflection import pw.binom.agentik.storage.Reflection
import pw.binom.agentik.storage.WorkingMemoryEntry import pw.binom.agentik.storage.WorkingMemoryEntry
import pw.binom.agentik.standalone.persistence.sqlite.SqliteStores import pw.binom.agentik.storage.sqlite.SqliteStores
import pw.binom.litert.LiteLlm import pw.binom.litert.LiteLlm
import kotlin.time.Instant import kotlin.time.Instant
@@ -38,7 +38,7 @@ import pw.binom.agentik.storage.MessageStore
import pw.binom.agentik.storage.WorkingMemoryEntry import pw.binom.agentik.storage.WorkingMemoryEntry
import pw.binom.agentik.storage.WorkingMemoryRow import pw.binom.agentik.storage.WorkingMemoryRow
import pw.binom.agentik.storage.WorkingMemoryStore import pw.binom.agentik.storage.WorkingMemoryStore
import pw.binom.agentik.standalone.persistence.sqlite.SqliteStores import pw.binom.agentik.storage.sqlite.SqliteStores
import pw.binom.litert.LiteContentPart import pw.binom.litert.LiteContentPart
import pw.binom.litert.LiteConversation import pw.binom.litert.LiteConversation
import pw.binom.litert.LiteConversationConfig import pw.binom.litert.LiteConversationConfig
@@ -14,7 +14,7 @@ import pw.binom.agentik.skills.SkillCatalog
import pw.binom.agentik.skills.SkillFile import pw.binom.agentik.skills.SkillFile
import pw.binom.agentik.standalone.llm.LlmBackend import pw.binom.agentik.standalone.llm.LlmBackend
import pw.binom.agentik.standalone.llm.LlmConfig import pw.binom.agentik.standalone.llm.LlmConfig
import pw.binom.agentik.standalone.persistence.sqlite.SqliteStores import pw.binom.agentik.storage.sqlite.SqliteStores
import pw.binom.litert.LiteContentPart import pw.binom.litert.LiteContentPart
import pw.binom.litert.LiteConversation import pw.binom.litert.LiteConversation
import pw.binom.litert.LiteConversationConfig import pw.binom.litert.LiteConversationConfig
@@ -12,7 +12,7 @@ import pw.binom.agentik.standalone.config.AgentikConfig
import pw.binom.agentik.standalone.llm.LlmBackend import pw.binom.agentik.standalone.llm.LlmBackend
import pw.binom.agentik.standalone.llm.LlmConfig import pw.binom.agentik.standalone.llm.LlmConfig
import pw.binom.agentik.standalone.llm.OpenAiConfig import pw.binom.agentik.standalone.llm.OpenAiConfig
import pw.binom.agentik.standalone.persistence.sqlite.SqliteStores import pw.binom.agentik.storage.sqlite.SqliteStores
import pw.binom.agentik.proto.Content as ProtoContent import pw.binom.agentik.proto.Content as ProtoContent
import pw.binom.litert.LiteConversation import pw.binom.litert.LiteConversation
import pw.binom.litert.LiteConversationConfig import pw.binom.litert.LiteConversationConfig
@@ -30,7 +30,7 @@ import pw.binom.agentik.standalone.llm.LlmBackend
import pw.binom.agentik.standalone.llm.LlmConfig import pw.binom.agentik.standalone.llm.LlmConfig
import pw.binom.agentik.standalone.llm.OpenAiConfig import pw.binom.agentik.standalone.llm.OpenAiConfig
import pw.binom.agentik.storage.WorkingMemoryEntry import pw.binom.agentik.storage.WorkingMemoryEntry
import pw.binom.agentik.standalone.persistence.sqlite.SqliteStores import pw.binom.agentik.storage.sqlite.SqliteStores
import kotlin.test.AfterTest import kotlin.test.AfterTest
import kotlin.test.BeforeTest import kotlin.test.BeforeTest
import kotlin.test.Test import kotlin.test.Test
@@ -4,7 +4,7 @@ import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNull import kotlin.test.assertNull
import kotlin.time.Instant import kotlin.time.Instant
import pw.binom.agentik.standalone.persistence.sqlite.SqliteStores import pw.binom.agentik.storage.sqlite.SqliteStores
class TokenStatsTest { class TokenStatsTest {
@@ -1,7 +1,7 @@
package pw.binom.agentik.storage package pw.binom.agentik.storage
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import pw.binom.agentik.standalone.persistence.sqlite.SqliteStores import pw.binom.agentik.storage.sqlite.SqliteStores
import kotlin.test.AfterTest import kotlin.test.AfterTest
import kotlin.test.BeforeTest import kotlin.test.BeforeTest
import kotlin.test.Test import kotlin.test.Test
@@ -4,7 +4,7 @@ import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
import pw.binom.agentik.standalone.persistence.sqlite.SqliteStores import pw.binom.agentik.storage.sqlite.SqliteStores
class SqliteStoresMigrationTest { class SqliteStoresMigrationTest {
+44
View File
@@ -0,0 +1,44 @@
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.sqldelight)
}
kotlin {
jvmToolchain(21)
// JVM-only: SQLDelight сейчас не имеет KMP-таргетов за пределами JVM/Android.
// Если в будущем понадобится Native (iOS/macOS), придётся либо подключать
// platform-native SQLDelight driver (https://github.com/cashapp/sqldelight
// multiplatform module), либо использовать :storage-inmemory там.
jvm()
sourceSets {
commonMain.dependencies {
api(project(":storage-core"))
api(libs.sqldelight.runtime)
api(libs.sqldelight.coroutines)
}
jvmMain.dependencies {
implementation(libs.sqldelight.sqlite.driver)
implementation(libs.kotlin.logging)
}
commonTest.dependencies {
implementation(kotlin("test"))
implementation(libs.kotlinx.coroutines.test)
}
jvmTest.dependencies {
// SQLDelight JDBC driver — для тестов, которые поднимают in-memory DB
implementation(libs.sqldelight.sqlite.driver)
}
}
}
sqldelight {
databases {
create("AgentikDatabase") {
packageName.set("pw.binom.agentik.storage.sqlite")
srcDirs.setFrom("src/jvmMain/sqldelight")
}
}
}
@@ -1,4 +1,4 @@
package pw.binom.agentik.standalone.persistence.sqlite package pw.binom.agentik.storage.sqlite
import kotlin.time.Instant import kotlin.time.Instant
import pw.binom.agentik.storage.ConversationRecord import pw.binom.agentik.storage.ConversationRecord
@@ -1,4 +1,4 @@
package pw.binom.agentik.standalone.persistence.sqlite package pw.binom.agentik.storage.sqlite
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import pw.binom.agentik.storage.MessageRecord import pw.binom.agentik.storage.MessageRecord
@@ -1,4 +1,4 @@
package pw.binom.agentik.standalone.persistence package pw.binom.agentik.storage.sqlite
import kotlin.time.Clock import kotlin.time.Clock
import kotlin.time.Instant import kotlin.time.Instant
@@ -9,9 +9,9 @@ import mu.KotlinLogging
import pw.binom.agentik.storage.Reflection import pw.binom.agentik.storage.Reflection
import pw.binom.agentik.storage.ReflectionEvent import pw.binom.agentik.storage.ReflectionEvent
import pw.binom.agentik.storage.ReflectionStore import pw.binom.agentik.storage.ReflectionStore
import pw.binom.agentik.standalone.persistence.sqlite.AgentikDatabase import pw.binom.agentik.storage.sqlite.AgentikDatabase
import pw.binom.agentik.standalone.persistence.sqlite.Reflection as DbReflection import pw.binom.agentik.storage.sqlite.Reflection as DbReflection
import pw.binom.agentik.standalone.persistence.sqlite.ReflectionQueries import pw.binom.agentik.storage.sqlite.ReflectionQueries
private val log = KotlinLogging.logger {} private val log = KotlinLogging.logger {}
@@ -1,4 +1,4 @@
package pw.binom.agentik.standalone.persistence.sqlite package pw.binom.agentik.storage.sqlite
import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.QueryResult
import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlDriver
@@ -6,7 +6,7 @@ import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
import pw.binom.agentik.storage.ConversationStore import pw.binom.agentik.storage.ConversationStore
import pw.binom.agentik.storage.MessageStore import pw.binom.agentik.storage.MessageStore
import pw.binom.agentik.storage.ReflectionStore import pw.binom.agentik.storage.ReflectionStore
import pw.binom.agentik.standalone.persistence.SqliteReflectionStore import pw.binom.agentik.storage.sqlite.SqliteReflectionStore
import pw.binom.agentik.storage.WorkingMemoryStore import pw.binom.agentik.storage.WorkingMemoryStore
/** /**
@@ -1,4 +1,4 @@
package pw.binom.agentik.standalone.persistence.sqlite package pw.binom.agentik.storage.sqlite
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import pw.binom.agentik.storage.WorkingMemoryEntry import pw.binom.agentik.storage.WorkingMemoryEntry
@@ -1,13 +1,12 @@
package pw.binom.agentik.storage package pw.binom.agentik.storage.sqlite
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNull import kotlin.test.assertNull
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.time.Instant import kotlin.time.Instant
import pw.binom.agentik.standalone.persistence.decodeStringArray import pw.binom.agentik.storage.Reflection
import pw.binom.agentik.standalone.persistence.encodeStringArray import pw.binom.agentik.storage.ReflectionEvent
import pw.binom.agentik.standalone.persistence.sqlite.SqliteStores
class ReflectionStoreTest { class ReflectionStoreTest {