prepare linuxX64: switch to kotlin.uuid.Uuid and ktor-server-cio

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:
   '<prefix>-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.
This commit is contained in:
2026-09-13 18:16:30 +03:00
parent 1e4c4d679f
commit e816d8d9d1
7 changed files with 25 additions and 8 deletions
+2 -1
View File
@@ -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" }
+3 -1
View File
@@ -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)
@@ -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-сборке.
*
* Сохраняет формат `<prefix>-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` —
* `:server` его парсит как opaque string, без знания внутренней структуры.
*/
object Ids {
fun new(prefix: String): String = "$prefix-${Uuid.random()}"
}
@@ -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")
@@ -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,
@@ -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, Any?>): String {
val el = kotlinx.serialization.json.JsonElement.serializer()
@@ -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")