Каркас сборки десктопа: KMP (jvm), Compose Multiplatform, зависимости из :agentik

- только jvm-таргет; composite includeBuild("../agentik")
- mavenLocal() первым в репозиториях: локальные публикации перекрывают Nexus
- Compose 1.7.3 + Kotlin 2.4.20, compose.desktop.currentOs
- вендоренный markdown (ru.otpbank.ai:markdown), mic-api/asr-vosk/vad
- gradle wrapper, gradle.properties, .gitignore (локальный тулинг)
This commit is contained in:
2026-09-28 10:07:52 +03:00
parent 093115c415
commit 960cd60120
9 changed files with 661 additions and 0 deletions
+128
View File
@@ -0,0 +1,128 @@
// `binaries {}` — экспериментальный API KGP, `compose.uiTest` — экспериментальная
// библиотека Compose (компилятор требует явного opt-in в скрипте).
@file:OptIn(
org.jetbrains.compose.ExperimentalComposeLibrary::class,
org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class,
)
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.kotlin.serialization)
// UI — Compose Multiplatform. `kotlin-compose` — компиляторный плагин
// (@Composable), `compose` — рантайм/DSL (`compose.material3`, ...).
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.compose)
// `application` plugin (он тянет `java`) **несовместим** с
// `kotlin("multiplatform")` — проверка `checkKotlinGradlePluginConfigurationErrors`
// падает на старте. В KMP executable настраивается через
// `kotlin { jvm { binaries { executable { ... } } } }` ниже — он создаёт
// `run` / `installDist` / jar с mainClass автоматически.
}
group = "pw.binom.agentik.desktop"
version = "0.1.0-SNAPSHOT"
kotlin {
// KMP-проект с **одной** целью — JVM. Структура каталогов
// `src/jvmMain/kotlin/...` (а не `src/main/kotlin/...`), чтобы при
// расширении целями (iOS / Native) не пришлось переименовывать.
// Toolchain 21 = как у agentik (`:client` / `:journal-ksqlite` тоже
// jvmToolchain(21)) — иначе Gradle тащит второй JDK через foojay.
jvmToolchain(21)
jvm {
binaries {
executable {
mainClass.set("pw.binom.agentik.desktop.MainKt")
// `jvmJar` создаётся автоматически; `run` запускает main.
}
}
}
sourceSets {
jvmMain.dependencies {
// --- Agentik (композитная сборка, см. settings.gradle.kts) ---
// `AgentikAgent` / `wrapWithLocalConversationCache` /
// `ReconnectingOutbox` — всё что нужно для клиента уже здесь.
implementation(libs.agentik.client)
// Контракты `MessageRecord` / `ConversationRecord` /
// `MutableJournalStore` / `MutableConversationStore`.
implementation(libs.agentik.journal.api)
// SQLite-реализации обоих store'ов из `:journal-ksqlite`.
implementation(libs.agentik.journal.ksqlite)
// `Event` / `AgentEvent` / `CommonEvent` / `OutboxStore` (live-поток).
implementation(libs.agentik.outbox.api)
// `Agent` / `Conversation` / `AgentInfo` / `Content` — proto-контракты.
implementation(libs.agentik.proto)
// --- Ktor client engine (передаётся в AgentikAgent как engineFactory) ---
implementation(libs.ktor.client.cio)
// --- Низкоуровневая БД (`SQLiteConnection` / prepared statements) ---
implementation(libs.ksqlite)
// --- Kotlinx ---
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.serialization.json)
// --- UI: Compose Multiplatform (markdown-рендер + экраны) ---
implementation(compose.foundation)
implementation(compose.ui)
implementation(compose.material3)
implementation(compose.materialIconsExtended)
// Оконный рантайм Compose Desktop (Window/application) + skiko
// для текущей ОС. Без него `compose.ui.window.application` не резолвится.
implementation(compose.desktop.currentOs)
// --- Markdown: парсер (ru.otpbank.ai:markdown, обёртка над
// org.intellij.markdown) + вендоренный рендер сегментов. ---
implementation(libs.markdown)
// --- Голос: микрофон → VAD → распознавание (см. MIC-ASR-SEARCH.md).
// JVM-реализация микрофона — `javax.sound.sampled`. ---
implementation(libs.mic.api)
implementation(libs.asr.api)
implementation(libs.asr.vosk)
implementation(libs.vad)
// --- Логирование ---
implementation(libs.kotlin.logging)
implementation(libs.logback.classic)
}
jvmTest.dependencies {
implementation(libs.kotlin.test)
implementation(libs.kotlinx.coroutines.test)
implementation(libs.junit.jupiter)
// Оффскрин-рендер Compose: прогоняем реальную тему/компоненты
// в ImageComposeScene и проверяем палитру по пикселям.
implementation(compose.uiTest)
// E2E: поднимаем настоящий `:server` (HTTP/SSE) поверх
// in-memory-агента и гоняем десктопный стек по живому протоколу.
implementation(libs.agentik.server)
implementation(libs.agentik.journal.inmemory)
implementation(libs.agentik.outbox.inmemory)
implementation(libs.ktor.server.cio)
}
}
}
// `application` плагин в KMP конфликтует с `kotlin("multiplatform")`,
// executable объявлен выше в `kotlin { jvm { binaries { executable { ... } } } }`
// — он автоматически создаёт `run` / `installDist` и jvmJar.
tasks.withType<Test>().configureEach {
useJUnitPlatform()
// Один JVM на тест-класс. Причина не в изоляции данных, а в нативных
// библиотеках: skiko (Compose) и C++-стек распознавания (Vosk/Kaldi, ONNX)
// конфликтуют по порядку загрузки — тот, кто грузится ПОСЛЕ skiko, падает
// с SIGSEGV. Классы, которым нужен нативный ASR, обязаны прогревать его
// первым делом (VoiceNative / RealSpeechIT), а skiko-классы — не мешать им.
// forkEvery = 1 гарантирует, что порядок классов не важен.
forkEvery = 1
// Изолируем данные приложения от реального $HOME разработчика:
// AppPaths читает AGENTIK_DESKTOP_HOME, тесты пишут в build/.
environment(
"AGENTIK_DESKTOP_HOME",
layout.buildDirectory.dir("test-agentik-home").get().asFile.absolutePath,
)
}