Files
agentik/NATIVE-COMPATIBILITY.md
T
subochev d5e3f2dcef proto: drop dead kotlinx-datetime dep, keep only kotlin.time.Instant
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.
2026-09-13 19:04:32 +03:00

154 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Совместимость с 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)