d5e3f2dcef
Punkt 7 of NATIVE-COMPATIBILITY.md. Proto sources were already on
kotlin.time.Instant (migrated earlier); the kotlinx-datetime api dep
in :proto/build.gradle.kts and the implementation dep in
:standalone/build.gradle.kts were dead weight.
Dropped:
- api(libs.kotlinx.datetime) from proto/build.gradle.kts
- implementation(libs.kotlinx.datetime) from standalone/build.gradle.kts
- [versions] kotlinx-datetime and [libraries] kotlinx-datetime from
gradle/libs.versions.toml
Verification: ./gradlew :server:assemble (all 9 native targets) +
./gradlew :standalone:jvmTest (44/44 green, 0 failures) — both pass.
The kotlinx-datetime typealias was deprecated in 0.8.0 (memory #3708);
now nothing in agentik pulls the library.
154 lines
11 KiB
Markdown
154 lines
11 KiB
Markdown
# Совместимость с 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. [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` модуль
|
||
|
||
- 7. [x] **`:proto`: убрать `kotlinx-datetime` api-dep, оставить `kotlin.time.Instant`.**
|
||
Источник уже был на `kotlin.time.Instant` (мигрирован ранее), но
|
||
`kotlinx-datetime` оставался в gradle как мёртвая `api`-зависимость.
|
||
Удалено:
|
||
- `api(libs.kotlinx.datetime)` из `proto/build.gradle.kts`
|
||
- `implementation(libs.kotlinx.datetime)` из `standalone/build.gradle.kts`
|
||
- `[versions] kotlinx-datetime` и `[libraries] kotlinx-datetime` из
|
||
`gradle/libs.versions.toml`
|
||
`kotlin.time.Instant` доступен во всех KMP-целях без opt-in.
|
||
`./gradlew :server:assemble` + `:standalone:jvmTest` 44/44 — зелёные.
|
||
|
||
## Блок 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)
|