docs: NATIVE-COMPATIBILITY.md checklist for linuxX64 bring-up
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
# Совместимость с native (linuxX64) — чеклист
|
||||
|
||||
Цель: `:standalone` собирается и запускается как `linuxX64` executable (и потенциально
|
||||
остальные нативные таргеты KMP). Текущее состояние — JVM-only executable на
|
||||
`runJvm`-таске.
|
||||
|
||||
Формат: `- N. [ ]` — задача; `- N. [x]` — выполнена; `- N. [-]` — отменена/не нужна.
|
||||
Решения (что выбрали и почему) — курсивом внизу пункта.
|
||||
|
||||
---
|
||||
|
||||
## Блок 1. Подготовка (уже сделана или тривиальная)
|
||||
|
||||
- 1. [x] **Замена `java.util.UUID` на KMP-stdlib (`kotlin.uuid.Uuid`).** Helper `Ids.new(prefix)` в `standalone/src/commonMain/.../persistence/Ids.kt`, три call-site (`ChatAgent`, `ChatConversation`, `SqliteWorkingMemoryStore`). Коммит `e816d8d`.
|
||||
- 2. [x] **HTTP-движок: `ktor-server-netty` → `ktor-server-cio`.** Netty — JVM-only; CIO — KMP (jvm + linuxX64 + ios/...). Каталог-эntry Netty оставлен в `libs.versions.toml` на случай отката. Коммит `e816d8d`.
|
||||
|
||||
## Блок 2. Библиотеки litert-kmp (наши)
|
||||
|
||||
- 3. [ ] **`litert-api`: добавить `linuxX64()` в `kotlin{}`.**
|
||||
Сейчас только `androidTarget() + jvm()`. commonMain уже KMP-ready (только
|
||||
`kotlinx-coroutines-core`). Объём: одна строка в `litert-api/build.gradle.kts`,
|
||||
один `litert-api-linuxx64`-артефакт публикуется в Nexus.
|
||||
- 4. [ ] **`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`).
|
||||
- 5. [-] **`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` модуль
|
||||
|
||||
- 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-нейтральность уже заложена.*
|
||||
|
||||
## Блок 4. Наш `:proto` модуль
|
||||
|
||||
- 7. [ ] **`: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 — нативный драйвер
|
||||
|
||||
- 8. [ ] **`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.*
|
||||
- 9. [ ] **Тесты 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`) — наши
|
||||
|
||||
- 10. [ ] **`agui-server`: проверить KMP-готовность.** Per Memory #3561 — KMP
|
||||
jvm+native. *Если по факту уже KMP — только подключить `linuxX64`-артефакт в `:standalone`; если JVM-only — пункт 11.*
|
||||
- 11. [ ] **`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
|
||||
|
||||
- 12. [ ] **`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.
|
||||
- 13. [ ] **`Main.kt`: разделить на `commonMain` (бизнес-логика: создание
|
||||
ChatAgent, MCP, LlmConfig) + `jvmMain`/`linuxX64Main` (выбор engine,
|
||||
embeddedServer).** embeddedServer(CIO, …) — один и тот же код на обеих
|
||||
платформах, KMP-нейтрален. Главное отличие — `SqliteStores.open()` и
|
||||
обработка GOOGLE-бэкенда на linuxX64 (fail-fast).
|
||||
|
||||
## Блок 8. Тесты и CI
|
||||
|
||||
- 14. [ ] **`linuxX64Test` source-set.** Покрыть те же 44 теста на
|
||||
native: должен зелёный прогон через `./gradlew :standalone:linuxX64Test`.
|
||||
SQLDelight-тесты на native требуют `libsqlite3`/`SQLiteBundle`
|
||||
на машине (CI-linux предоставляет).
|
||||
- 15. [ ] **Smoke e2e на linuxX64.** Запустить
|
||||
`./gradlew :standalone:runLinuxX64` локально, проверить
|
||||
`POST /agentik/conversations` → 201, цикл с реальным LLM через OpenAI
|
||||
backend; убедиться что LiteRT-LM GOOGLE не подключается (отказ с
|
||||
понятной ошибкой).
|
||||
|
||||
## Блок 9. Дистрибуция native-бинаря
|
||||
|
||||
- 16. [ ] **`runLinuxX64`-таска через KMP binaries DSL.** После добавления
|
||||
`linuxX64()` KMP генерирует её автоматически — нужно только убедиться
|
||||
что `binaries { executable { mainClass.set("pw.binom.agentik.standalone.MainKt") } }`
|
||||
работает для linuxX64 (по аналогии с уже настроенным `runJvm`).
|
||||
- 17. [ ] **Дистрибутив `.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)
|
||||
Reference in New Issue
Block a user