docs: per-module READMEs (run vs library) + root navigation hub
ci / JVM build + tests (pull_request) Failing after 54s

Every subproject now has README.md:
- 3 runnable modules (:standalone, :agentik-cli, :agentik-tui):
  quickstart, env table, parameters, known limits
- 11 library modules: what it is, which problem solves, how to
  wire it in, where versions live

Root README.md is the navigation hub (Quickstart, Modules table,
publish + CI/CD notes).

Also: ci.yml prunes the :memory-vector -x excludes now that
text-embedding-kmp artifacts are published to caffeine.

518 tests green.

Verified publish pipeline: :proto:publish to caffeine produces
pom.module + per-target klibs + sources for all 9 KMP targets.

🤖 Generated with [opencode]
This commit is contained in:
SubochevAV
2026-09-16 20:44:16 +03:00
parent 5ad972767d
commit 8f616f359f
22 changed files with 1125 additions and 890 deletions
+75 -41
View File
@@ -1,67 +1,101 @@
# :client — `pw.binom.agentik.client`
# `:client` — Ktor-клиент к `:server`/`:proto` (KMP, jvm + native)
**Ktor-клиент, превращающий HTTP-фасад `:server` обратно в `Agent`/`Conversation` из `:proto`.**
Подходит для JVM-приложений (CLI, desktop, integration-тесты).
## Что это
## Какую проблему решает
Ktor client (`io.ktor.client.HttpClient` + `ContentNegotiation(json) +
Sse`), превращающий HTTP/SSE-фасад `:server` в `Agent`/`Conversation`
интерфейсы `:proto`:
После того, как `:server` выставляет агента по HTTP, встаёт задача: дать
вызывающей стороне **тот же интерфейс**, что был на сервере — а не отдельный
REST-клиент с хендшейкингом SSE, парсингом полей, ручной постраничной подгрузкой.
`:client` — обёртка: `AgentikAgent(baseUrl).createConversation()` возвращает
`Conversation`, идентичный серверному, а вызовы `send/getMessages/events`
прозрачно ездят по HTTP.
- `AgentikAgent(id, baseUrl)` — entry-point фабрики.
- `AgentClient` — список и lifecycle диалогов.
- `ConversationClient` — `send()`, `events()`, `interrupt()`,
`getMessages()`, `rename()`, `close()`.
- Внутренний парсер SSE → `Flow<Event>`.
## Использование
Решает: пишем нативный Kotlin-клиент, без curl/JS/Python boilerplate,
с теми же типами, что и сервер. Один и тот же клиент работает на
JVM, iOS, macOS, Linux, Windows.
## Где используется
- `:agentik-cli` — REPL.
- `:agentik-tui` — Compose-for-Mosaic клиент.
- Любой внешний KMP-проект, который хочет встроить агента в свой UI.
## Как подключить
```kotlin
import pw.binom.agentik.client.AgentikAgent
// build.gradle.kts
kotlin {
sourceSets.commonMain.dependencies {
api("pw.binom.agentik:client:0.1.0")
}
}
val agent = AgentikAgent(id = "ops-bot", baseUrl = "http://localhost:8080/agentik")
val conv = agent.createConversation(temp = false)
conv.events(after = Clock.System.now()).collect { ev ->
when (ev) {
is Event.AppendText -> print(ev.body)
is Event.End -> println()
// ваш код:
val agent = AgentikAgent(id = "agentik", baseUrl = "http://192.168.76.166:8080/agentik")
val conv = agent.createConversation(title = "test")
conv.send(listOf(Content.Text("hello"))).collect { event ->
when (event) {
is Event.AppendText -> print(event.body)
is Event.End -> println("\n--- end ---")
is Event.Error -> error("agent error: ${event.message}")
else -> Unit
}
}
conv.send(listOf(Content.Text("Привет. Сколько будет 2+2?")))
// ... события стримятся в collect выше
conv.close()
```
Для фоновой подписки (reconnect-safe):
## Версии
`gradle/libs.versions.toml` → `[versions] agentik-client`.
Поддерживает все KMP-таргеты, что и `:proto`.
## Примеры API
```kotlin
// Долгая живая подписка на события диалога.
conv.events(after = lastSeen).collect { ev ->
if (ev is Event.End) lastSeen = ev.date
// список диалогов
agent.getConversations().collect { println(it.id to it.title) }
// live-подписка на события отдельного диалога
val sub = conversation.events(after = Instant.parse("2026-09-01T00:00:00Z")).collect { }
// прерывание текущего хода
conversation.interrupt()
// история
conversation.getMessages(offset = 0).collect { msg ->
when (msg) {
is Message.UserMessage -> println("user: ${msg.content}")
is Message.AssistantMessage -> println("assistant: ${msg.content}")
else -> Unit
}
}
```
## Подключение
## Тесты
```kotlin
implementation("pw.binom.agentik:client:$version")
// Транзитивно тянет :proto + ktor-client-core/cio/... + kotlinx-serialization.
```
./gradlew :client:jvmTest
```
## SSE-парсер
Покрывают: JSON-парсинг Event'ов, SSE-стрим, recovery после разрыва,
401/404.
Внутри — самописный парсер SSE (режет поток на `data:` строки, буферизует
частичные, переживает keep-alive-комментарии). Зависимости — `ktor-client-cio`
по умолчанию; если нужен другой engine — подмените через `AgentikAgent(engineFactory = …)`.
## Чего здесь НЕТ
## Где смотреть версии
- Никакого LLM-кода. Это просто клиент.
- Никакого persistent state. История хранится у сервера, клиент её
запрашивает через `getMessages` или подписывается через `events`.
- `:client` синхронизирован с `:proto`/`server` — `version` из `gradle.properties`
- релизы: `https://git.binom.pw/subochev/agentik/releases`
## Текущий статус
## Сборка
Используется продакшеном. Бэкендом служит `:server` поверх `:standalone`,
но клиент совместим с любым сервером, который держит wire-контракт
`:server`.
```bash
./gradlew :client:build
```
## Известное ограничение
JVM-only (ktor-client-engine-cio — JVM).
SSE event-stream в не-TTY ssh-сессии (без `-tt`) закрывается на
default-таймауте Ktor. Используйте либо ssh -tt, либо нативный
terminal (TTY). Это upstream-особенность Ktor SSE.