Files
agentik/NATIVE-COMPATIBILITY.md
T

10 KiB
Raw Blame History

Совместимость с native (linuxX64) — чеклист

Цель: :standalone собирается и запускается как linuxX64 executable (и потенциально остальные нативные таргеты KMP). Текущее состояние — JVM-only executable на runJvm-таске.

Формат: - N. [ ] — задача; - N. [x] — выполнена; - N. [-] — отменена/не нужна. Решения (что выбрали и почему) — курсивом внизу пункта.


Блок 1. Подготовка (уже сделана или тривиальная)

    1. Замена java.util.UUID на KMP-stdlib (kotlin.uuid.Uuid). Helper Ids.new(prefix) в standalone/src/commonMain/.../persistence/Ids.kt, три call-site (ChatAgent, ChatConversation, SqliteWorkingMemoryStore). Коммит e816d8d.
    1. HTTP-движок: ktor-server-netty → ktor-server-cio. Netty — JVM-only; CIO — KMP (jvm + linuxX64 + ios/...). Каталог-эntry Netty оставлен в libs.versions.toml на случай отката. Коммит e816d8d.

Блок 2. Библиотеки litert-kmp (наши)

    1. litert-api: добавить linuxX64() в kotlin{}. Сейчас только androidTarget() + jvm(). commonMain уже KMP-ready (только kotlinx-coroutines-core). Объём: одна строка в litert-api/build.gradle.kts, один litert-api-linuxx64-артефакт публикуется в Nexus.
    1. litert-openai: добавить linuxX64() в kotlin{}. Та же история. commonMain deps (ktor-client-core, ktor-client-cio, kotlinx-serialization-json) — все KMP с готовыми linuxX64-артефактами. Объём: одна строка + проверить, что Android-only фичи (если есть) изолированы в androidMain (сейчас модуль не имеет androidMain-специфичного кода). Каталог-запись litert-openai-jvm → переименовать в litert-openai (чтобы Linux-таргет подтягивал -linuxx64-вариант автоматически, не нужно ручного when target).
    1. [-] litert-google: linuxX64 — невозможно. Upstream-блокер: Google публикует litertlm-android (AAR с .so) и litertlm-jvm (fat-jar с .so для linux/mac/win JVM), но НЕ нативного litertlm-linuxx64. Без правки upstream LiteRT-LM KMP-враппер для linuxX64 не напишется. Варианты: (a) выкинуть litert-google на linuxX64 (backend=google → отказ); (b) альтернативный движок (llama.cpp через JNI/Kotlin/Native); (c) upstream PR в litertlm. Решение: (a) для v1 linuxX64 — упасть с понятной ошибкой LlmBackend.GOOGLE is not supported on this platform вместо reflection-fallback на JVM .so, который на linuxX64 не сработает бы.

Блок 3. Наш :server модуль

    1. Переписать :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-нейтральность уже заложена.

Блок 4. Наш :proto модуль

    1. :proto: заменить kotlinx.datetime.Instant на kotlin.time.Instant. Per Memory #3708 — typealias kotlinx.datetime.Instant deprecated в kotlinx-datetime 0.8.0, нужно перейти на kotlin.time.Instant (он же в commonMain). После: дропнуть api(libs.kotlinx.datetime) из commonMain, и из libs.versions.toml. Объём: замена импорта + типов + возможно InstantSerializer в :server/src/main/.../Serialization.kt.

Блок 5. SQLDelight — нативный драйвер

    1. sqlite-driver (JVM/JDBC) → native-driver для linuxX64. В jvmMain: оставить sqlite-driver (JDBC over java.sql.DriverManager). В linuxX64Main: добавить native-driver — KMP driver поверх cinterop с libsqlite3 (нужен -lsqlite3 linker-флаг или SQLiteBundle из co.touchlab:sqliter для бандлинга). Создать SqliteStores- нейтральный интерфейс хранилищ (он уже есть в commonMain), разделить jvmMain/SqliteStores.kt и linuxX64Main/SqliteStoresNative.kt — оба реализуют один commonMain-интерфейс. Объём: ~150 строк (новый SqliteStoresNative.kt + gradle wiring). Решение по бандлу: использовать SQLiteBundle из co.touchlab:sqliter (KMP-обёртка, тянет свой libsqlite) — без зависимости от системного libsqlite3, бинарь работает на любом linux.
    1. Тесты SQLite переписать на commonTest + expect/actual обёртку. inMemory()-открытие в native-driver использует другой API (нет JDBC :memory:). Объём: один expect fun openInMemory() в commonMain + два actual (JDBC jdbc:sqlite::memory: / native-driver нативный API).

Блок 6. Внешние либы (a2a-server, agui-server) — наши

    1. agui-server: проверить KMP-готовность. Per Memory #3561 — KMP jvm+native. Если по факту уже KMP — только подключить linuxX64-артефакт в :standalone; если JVM-only — пункт 11.
    1. a2a-server: JVM-only → KMP. Per Memory #3561 — JVM-only (client/server; shared уже KMP). Без native-таргета весь стек A2A недоступен на linuxX64. Объём: переписать на multiplatform, выделить engine-нейтральную маршрутизацию. Альтернатива: на linuxX64 не подключать a2a-server вообще (только :server-фасад). Решение отложено — зависит от того, нужен ли нативный MCP-юзер-кейс вообще подключаться к A2A.

Блок 7. :standalone — добавить native target

    1. standalone/build.gradle.kts: добавить linuxX64() и linuxX64Main/linuxX64Test source-sets. В linuxX64Main: :server (после п.6), litert-openai (после п.4), litert-api (после п.3), ktor-server-cio (engine), ktor-client-cio, kotlin-sdk-client (MCP — KMP, готов), kotlinx-io (KMP, готов). Исключить litert-google (п.5) и a2a-server (п.11) из linuxX64Main.
    1. Main.kt: разделить на commonMain (бизнес-логика: создание ChatAgent, MCP, LlmConfig) + jvmMain/linuxX64Main (выбор engine, embeddedServer). embeddedServer(CIO, …) — один и тот же код на обеих платформах, KMP-нейтрален. Главное отличие — SqliteStores.open() и обработка GOOGLE-бэкенда на linuxX64 (fail-fast).

Блок 8. Тесты и CI

    1. linuxX64Test source-set. Покрыть те же 44 теста на native: должен зелёный прогон через ./gradlew :standalone:linuxX64Test. SQLDelight-тесты на native требуют libsqlite3/SQLiteBundle на машине (CI-linux предоставляет).
    1. Smoke e2e на linuxX64. Запустить ./gradlew :standalone:runLinuxX64 локально, проверить POST /agentik/conversations → 201, цикл с реальным LLM через OpenAI backend; убедиться что LiteRT-LM GOOGLE не подключается (отказ с понятной ошибкой).

Блок 9. Дистрибуция native-бинаря

    1. runLinuxX64-таска через KMP binaries DSL. После добавления linuxX64() KMP генерирует её автоматически — нужно только убедиться что binaries { executable { mainClass.set("pw.binom.agentik.standalone.MainKt") } } работает для linuxX64 (по аналогии с уже настроенным runJvm).
    1. Дистрибутив .tar/.zip/AppImage/Docker. KMP assembleDist для linuxX64 делает tar.gz/zip. Для прод-дистрибуции — Docker-образ (FROM gcr.io/distroless/cc) или AppImage. Объём: ~50 строк Dockerfile + GitHub Action.

Что НЕ блокирует linuxX64 (закрыто или тривиально)

  • kotlinx-coroutines-core — KMP linuxX64 ✓
  • kotlinx-serialization-json — KMP linuxX64 ✓
  • kotlinx-io — KMP linuxX64 (используется в MCP stdio-transport) ✓
  • kotlin-sdk-client (MCP) — KMP full targets ✓
  • ktor-server-core, ktor-server-sse, ktor-server-cio — KMP linuxX64 ✓
  • ktor-client-core, ktor-client-cio — KMP linuxX64 ✓
  • pw.binom.uuid — отсутствует в проекте (используем kotlin.uuid) ✓

Жёсткие блокеры (не обойти без upstream-работы)

  • litertlm-jvm → нужен litertlm-native от Google (см. п.5)
  • a2a-server JVM-only (см. п.11) — нужно переписать или явно не подключать
  • agui-server KMP-готовность под вопросом (см. п.10)