From 5b4c8ceae8ac3ddecfdd701c5bd40a37cfafd3c1 Mon Sep 17 00:00:00 2001 From: subochev Date: Sun, 13 Sep 2026 18:28:12 +0300 Subject: [PATCH] server: convert :server from kotlin-jvm to KMP with all 9 targets Punkt 6 of NATIVE-COMPATIBILITY.md. Mirrors :proto's target set: jvm + macosX64 + macosArm64 + iosX64 + iosArm64 + iosSimulatorArm64 + linuxX64 + linuxArm64 + mingwX64. All 9 targets compile via './gradlew :server:assemble'. No blockers: the only deps (ktor-server-core, ktor-server-content- negotiation, ktor-serialization-kotlinx-json, kotlinx-coroutines-core, kotlinx-serialization-json) ship native variants for every target. kotlin.time.Instant was already in use (not the deprecated kotlinx.datetime.Instant), so no proto-side work needed. Only API change vs the JVM version: respondTextWriter (JVM-only) -> respondBytesWriter + writeStringUtf8 (KMP, ByteWriteChannel API). SSE behaviour is identical ('data: \n\n' chunks flushed as they arrive). Verification: :standalone:jvmTest 44/44 green after the migration (ChatAgent 15, LlmConfig 6, McpConfig 8, McpRegistry 4, Persistence 11). No call-site changes needed; :standalone picks up the new :server jvm artifact automatically. --- NATIVE-COMPATIBILITY.md | 22 ++++---- server/build.gradle.kts | 50 +++++++++++++------ .../kotlin/pw/binom/agentik/server/Dto.kt | 0 .../kotlin/pw/binom/agentik/server/Module.kt | 0 .../kotlin/pw/binom/agentik/server/Routes.kt | 11 ++-- .../pw/binom/agentik/server/Serialization.kt | 0 6 files changed, 52 insertions(+), 31 deletions(-) rename server/src/{main => commonMain}/kotlin/pw/binom/agentik/server/Dto.kt (100%) rename server/src/{main => commonMain}/kotlin/pw/binom/agentik/server/Module.kt (100%) rename server/src/{main => commonMain}/kotlin/pw/binom/agentik/server/Routes.kt (94%) rename server/src/{main => commonMain}/kotlin/pw/binom/agentik/server/Serialization.kt (100%) diff --git a/NATIVE-COMPATIBILITY.md b/NATIVE-COMPATIBILITY.md index a3e661b..4a8bec6 100644 --- a/NATIVE-COMPATIBILITY.md +++ b/NATIVE-COMPATIBILITY.md @@ -40,16 +40,18 @@ ## Блок 3. Наш `:server` модуль -- 6. [ ] **Переписать `:server` с `kotlin("jvm")` на `kotlin("multiplatform")` с - `jvm() + linuxX64()`.** Текущий контракт `Route.agentikAgent(agent, path)` — - чистый Ktor Route, не зависит от engine. commonMain: Ktor core + SSE + - ContentNegotiation + kotlinx-serialization-json. jvmMain: текущее - содержимое. linuxX64Main: тот же commonMain + ktor-server-cio engine для - embedded-сервера, если `Main.kt` на linuxX64 будет поднимать сервер сам - (иначе — оставить сервер только в jvmMain/:standalone, а `:server`-маршруты - вызывать из native-обёртки). Объём: ~30 строк gradle + перенос imports. - *Решение: на linuxX64 `:server`-роуты вызываются тем же `Main.kt`-ом через - `embeddedServer(CIO, ...)` — engine-нейтральность уже заложена.* +- 6. [x] **Переписать `:server` с `kotlin("jvm")` на `kotlin("multiplatform")`.** + Все 9 целей сборки (jvm + macosX64 + macosArm64 + iosX64 + iosArm64 + + iosSimulatorArm64 + linuxX64 + linuxArm64 + mingwX64) собираются + `./gradlew :server:assemble` без ошибок. Блокеров нет: Ktor + `ktor-server-{core,sse,cio,content-negotiation}` и `ktor-io` (для + `ByteWriteChannel.writeStringUtf8`) есть в KMP-вариантах под все + цели; `kotlin.time.Instant` уже использовался. Единственное + изменение по сравнению с JVM-версией: `respondTextWriter` (JVM-only) + → `respondBytesWriter` + `writeStringUtf8` (KMP, тот же + `ByteWriteChannel` API). + Источники переехали `src/main/kotlin/...` → `src/commonMain/kotlin/...`. + `:standalone:jvmTest` 44/44 зелёные после миграции. ## Блок 4. Наш `:proto` модуль diff --git a/server/build.gradle.kts b/server/build.gradle.kts index e99a707..c3365d9 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -1,23 +1,41 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { - alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.kotlin.serialization) } kotlin { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_21) + jvmToolchain(21) + + // "Все возможные цели сборки": jvm + весь натив. Зеркалит набор :proto. + // Никаких блокеров нет: ktor-server-{core,sse,cio,content-negotiation} имеет + // опубликованные linuxX64/macosX64/macosArm64/iosX64/iosArm64/iosSimulatorArm64/ + // linuxArm64/mingwX64 варианты; :proto уже собирается под все эти таргеты. + jvm() + macosX64() + macosArm64() + iosX64() + iosArm64() + iosSimulatorArm64() + linuxX64() + linuxArm64() + mingwX64() + + sourceSets { + commonMain.dependencies { + implementation(project(":proto")) + + // Ktor (без engine — engine подключает потребитель, см. :standalone). + implementation(libs.ktor.server.core) + implementation(libs.ktor.server.content.negotiation) + implementation(libs.ktor.serialization.kotlinx.json) + + // Commons + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.serialization.json) + } + commonTest.dependencies { + implementation(libs.kotlinx.coroutines.core) + implementation(kotlin("test")) + } } } - -dependencies { - implementation(project(":proto")) - - implementation(libs.ktor.server.core) - implementation(libs.ktor.server.content.negotiation) - implementation(libs.ktor.serialization.kotlinx.json) - - implementation(libs.kotlinx.coroutines.core) - implementation(libs.kotlinx.serialization.json) -} diff --git a/server/src/main/kotlin/pw/binom/agentik/server/Dto.kt b/server/src/commonMain/kotlin/pw/binom/agentik/server/Dto.kt similarity index 100% rename from server/src/main/kotlin/pw/binom/agentik/server/Dto.kt rename to server/src/commonMain/kotlin/pw/binom/agentik/server/Dto.kt diff --git a/server/src/main/kotlin/pw/binom/agentik/server/Module.kt b/server/src/commonMain/kotlin/pw/binom/agentik/server/Module.kt similarity index 100% rename from server/src/main/kotlin/pw/binom/agentik/server/Module.kt rename to server/src/commonMain/kotlin/pw/binom/agentik/server/Module.kt diff --git a/server/src/main/kotlin/pw/binom/agentik/server/Routes.kt b/server/src/commonMain/kotlin/pw/binom/agentik/server/Routes.kt similarity index 94% rename from server/src/main/kotlin/pw/binom/agentik/server/Routes.kt rename to server/src/commonMain/kotlin/pw/binom/agentik/server/Routes.kt index df3696d..0d929dc 100644 --- a/server/src/main/kotlin/pw/binom/agentik/server/Routes.kt +++ b/server/src/commonMain/kotlin/pw/binom/agentik/server/Routes.kt @@ -6,13 +6,14 @@ import io.ktor.server.application.ApplicationCall import io.ktor.server.application.call import io.ktor.server.request.receive import io.ktor.server.response.respond +import io.ktor.server.response.respondBytesWriter import io.ktor.server.response.respondText -import io.ktor.server.response.respondTextWriter import io.ktor.server.routing.Route import io.ktor.server.routing.delete import io.ktor.server.routing.get import io.ktor.server.routing.patch import io.ktor.server.routing.post +import io.ktor.utils.io.writeStringUtf8 import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.serialization.KSerializer @@ -147,13 +148,13 @@ private suspend fun ApplicationCall.streamJsonSse( serializer: KSerializer, json: Json = agentikJson, ) { - respondTextWriter(contentType = ContentType.Text.EventStream) { + respondBytesWriter(contentType = ContentType.Text.EventStream) { flow.catch { /* клиент отвалился — глушим */ } .collect { value -> val s = json.encodeToString(serializer, value) - write("data: ") - write(s) - write("\n\n") + writeStringUtf8("data: ") + writeStringUtf8(s) + writeStringUtf8("\n\n") flush() } } diff --git a/server/src/main/kotlin/pw/binom/agentik/server/Serialization.kt b/server/src/commonMain/kotlin/pw/binom/agentik/server/Serialization.kt similarity index 100% rename from server/src/main/kotlin/pw/binom/agentik/server/Serialization.kt rename to server/src/commonMain/kotlin/pw/binom/agentik/server/Serialization.kt