From e816d8d9d15801272e98d90e75425a3bb86d56ef Mon Sep 17 00:00:00 2001 From: subochev Date: Sun, 13 Sep 2026 18:16:30 +0300 Subject: [PATCH] prepare linuxX64: switch to kotlin.uuid.Uuid and ktor-server-cio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. UUID: java.util.UUID.randomUUID() → commonMain helper Ids.new(prefix) wrapping kotlin.uuid.Uuid.random(). kotlin.uuid is stdlib (KMP: jvm + all native targets), so the call sites (ChatAgent, ChatConversation, SqliteWorkingMemoryStore) are now ready for native builds. Format kept: '-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'. 2. HTTP engine: ktor-server-netty → ktor-server-cio. Netty is JVM-only; CIO is KMP (jvm, linuxX64, ios, etc.) and feature-complete for our needs (POST + SSE). standalone/build.gradle.kts drops netty and adds cio; Main.kt imports io.ktor.server.cio.CIO and calls embeddedServer(CIO, port = port). Netty is left in libs.versions.toml (catalog entry kept) in case anyone needs to fall back; only :standalone stopped depending on it. E2E verified end-to-end against litellm local/codding: standalone-1.0.0 tarball starts on AGENTIK_PORT, GET /health -> 'ok', POST /agentik/conversations -> 201 with new UUID-format id, POST .../messages returns real LLM answer ('Ассистент'). 44/44 unit tests green. --- gradle/libs.versions.toml | 3 ++- standalone/build.gradle.kts | 4 +++- .../pw/binom/agentik/standalone/persistence/Ids.kt | 14 ++++++++++++++ .../kotlin/pw/binom/agentik/standalone/Main.kt | 6 +++--- .../pw/binom/agentik/standalone/agent/ChatAgent.kt | 2 +- .../agentik/standalone/agent/ChatConversation.kt | 2 +- .../persistence/sqlite/SqliteWorkingMemoryStore.kt | 2 +- 7 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 standalone/src/commonMain/kotlin/pw/binom/agentik/standalone/persistence/Ids.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2754d5c..8c095a9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,9 +36,10 @@ sqldelight-runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sq sqldelight-sqlite-driver = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" } -# --- Ktor (сервер, JVM) --- +# --- Ktor (сервер) --- ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" } ktor-server-sse = { module = "io.ktor:ktor-server-sse", version.ref = "ktor" } +ktor-server-cio = { module = "io.ktor:ktor-server-cio", version.ref = "ktor" } ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" } ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" } ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } diff --git a/standalone/build.gradle.kts b/standalone/build.gradle.kts index 0d22f7e..105bd7e 100644 --- a/standalone/build.gradle.kts +++ b/standalone/build.gradle.kts @@ -48,9 +48,11 @@ kotlin { implementation(libs.sqldelight.sqlite.driver) // Ktor server (для :server facade + a2aServer) + // Используем CIO вместо Netty — он KMP (jvm + linuxX64/ios/...), нам нужен + // для будущей native-сборки; на JVM работает идентично для нашего сценария. implementation(libs.ktor.server.core) implementation(libs.ktor.server.sse) - implementation(libs.ktor.server.netty) + implementation(libs.ktor.server.cio) implementation(libs.ktor.server.content.negotiation) implementation(libs.ktor.serialization.kotlinx.json) diff --git a/standalone/src/commonMain/kotlin/pw/binom/agentik/standalone/persistence/Ids.kt b/standalone/src/commonMain/kotlin/pw/binom/agentik/standalone/persistence/Ids.kt new file mode 100644 index 0000000..d67fdb4 --- /dev/null +++ b/standalone/src/commonMain/kotlin/pw/binom/agentik/standalone/persistence/Ids.kt @@ -0,0 +1,14 @@ +package pw.binom.agentik.standalone.persistence + +import kotlin.uuid.Uuid + +/** + * Генератор id. Использует `kotlin.uuid.Uuid` из stdlib (KMP: jvm + native), + * чтобы не зависеть от `java.util.UUID` и подготовить код к linuxX64-сборке. + * + * Сохраняет формат `-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` — + * `:server` его парсит как opaque string, без знания внутренней структуры. + */ +object Ids { + fun new(prefix: String): String = "$prefix-${Uuid.random()}" +} diff --git a/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/Main.kt b/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/Main.kt index fcbc25a..97cc623 100644 --- a/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/Main.kt +++ b/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/Main.kt @@ -1,7 +1,7 @@ package pw.binom.agentik.standalone +import io.ktor.server.cio.CIO import io.ktor.server.engine.embeddedServer -import io.ktor.server.netty.Netty import io.ktor.server.response.respondText import io.ktor.server.routing.get import io.ktor.server.routing.routing @@ -13,7 +13,7 @@ import pw.binom.agentik.standalone.mcp.McpRegistry import pw.binom.agentik.standalone.persistence.sqlite.SqliteStores /** * standalone-контейнер agentik: - * - :server (proto): встраиваемый Ktor (Netty), порт AGENTIK_PORT (default 8080) + * - :server (proto): встраиваемый Ktor (CIO), порт AGENTIK_PORT (default 8080) * POST /agentik/conversations -> ConversationSnapshot (201) * GET /agentik/conversations -> [ConversationSnapshot] * GET /agentik/conversations/{id} -> ConversationSnapshot @@ -49,7 +49,7 @@ fun main() { tools = mcpRegistry.namedTools, ) - val server = embeddedServer(Netty, port = port) { + val server = embeddedServer(CIO, port = port) { routing { get("/health") { call.respondText("ok") } agentikAgent(agent, path = "/agentik") diff --git a/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/agent/ChatAgent.kt b/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/agent/ChatAgent.kt index fb4940a..9e6db78 100644 --- a/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/agent/ChatAgent.kt +++ b/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/agent/ChatAgent.kt @@ -52,7 +52,7 @@ class ChatAgent( override fun createConversation(temp: Boolean): ProtoConversation { val now = now() - val id = "conv-${java.util.UUID.randomUUID()}" + val id = pw.binom.agentik.standalone.persistence.Ids.new("conv") val rec = ConversationRecord( id = id, title = null, diff --git a/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/agent/ChatConversation.kt b/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/agent/ChatConversation.kt index da2d5ab..c7c100f 100644 --- a/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/agent/ChatConversation.kt +++ b/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/agent/ChatConversation.kt @@ -357,7 +357,7 @@ class ChatConversation( private fun now(): Instant = Instant.fromEpochMilliseconds(System.currentTimeMillis()) - private fun newId(prefix: String): String = "$prefix-${java.util.UUID.randomUUID()}" + private fun newId(prefix: String): String = pw.binom.agentik.standalone.persistence.Ids.new(prefix) private fun encodeArgsJson(arguments: Map): String { val el = kotlinx.serialization.json.JsonElement.serializer() diff --git a/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/persistence/sqlite/SqliteWorkingMemoryStore.kt b/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/persistence/sqlite/SqliteWorkingMemoryStore.kt index d27465c..29b9468 100644 --- a/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/persistence/sqlite/SqliteWorkingMemoryStore.kt +++ b/standalone/src/jvmMain/kotlin/pw/binom/agentik/standalone/persistence/sqlite/SqliteWorkingMemoryStore.kt @@ -67,4 +67,4 @@ private fun Working_memory.toRow(): WorkingMemoryRow { ) } -private fun newId(): String = "wm-${java.util.UUID.randomUUID()}" +private fun newId(): String = pw.binom.agentik.standalone.persistence.Ids.new("wm")