diff --git a/src/jvmMain/kotlin/pw/binom/agentik/desktop/net/ConnectionChecker.kt b/src/jvmMain/kotlin/pw/binom/agentik/desktop/net/ConnectionChecker.kt index 8e730dc..3eeb8e2 100644 --- a/src/jvmMain/kotlin/pw/binom/agentik/desktop/net/ConnectionChecker.kt +++ b/src/jvmMain/kotlin/pw/binom/agentik/desktop/net/ConnectionChecker.kt @@ -6,6 +6,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import pw.binom.agentik.client.AgentikAgent import pw.binom.agentik.desktop.settings.AgentConfig +import pw.binom.agentik.proto.Agent /** Один шаг проверки связи. */ data class CheckStep( @@ -28,58 +29,56 @@ data class ConnectionReport( /** * Проверка связи с агентом без открытия полноценного [AgentConnection]: * отдельное «одноразовое» подключение, которое закрывается сразу после - * прогона шагов. + * прогона шага. * - * Шаги: - * 1. **Ответ сервера** — `GET {baseUrl}` за `AgentInfo` (имя/описание). - * 2. **Диалог** — создать временный диалог и удалить его (проверяет - * conversation-роуты, не тратит токены LLM). + * Единственный шаг — **запрос информации об агенте**: конструктор + * [AgentikAgent] синхронно делает `GET {baseUrl}` за `AgentInfo` + * (имя/описание). Успешный ответ и есть «сервер жив и отвечает на своём + * API» — без единого побочного эффекта на стороне сервера. * - * Сознательно **не** отправляем реальный ход: это дёргает модель (время, - * стоимость, побочные эффекты в истории). Проверки маршрутов достаточно, - * чтобы отличить «связи нет» от «связь есть». + * Сознательно **не** создаём временный диалог и **не** отправляем реальный + * ход: временный диалог — это мусор в истории сервера (и лишний визит к БД), + * а ход ещё и дёргает модель (время, стоимость, след в журнале). Для ответа + * «связь есть / связи нет» достаточно ответа на запрос информации. */ class ConnectionChecker( private val engineFactory: HttpClientEngineFactory<*> = CIO, - private val timeoutMillis: Long = 15_000, ) { suspend fun check(config: AgentConfig, clientId: String): ConnectionReport = withContext(Dispatchers.IO) { - val steps = mutableListOf() - var agent: pw.binom.agentik.proto.Agent? = null - var name: String? = null - val t0 = System.nanoTime() - try { - agent = AgentikAgent( + val agent: Agent = try { + AgentikAgent( id = clientId, baseUrl = config.baseUrl, engineFactory = engineFactory, token = config.token, ) - name = agent.info.name - steps += CheckStep("Ответ сервера", true, elapsed(t0), runCatching { agent.info.description }.getOrNull()) } catch (t: Throwable) { - steps += CheckStep("Ответ сервера", false, elapsed(t0), t.message ?: t::class.simpleName) - return@withContext ConnectionReport(config.baseUrl, steps, name) + return@withContext ConnectionReport( + baseUrl = config.baseUrl, + steps = listOf(CheckStep(STEP_AGENT_INFO, false, elapsed(t0), t.message ?: t::class.simpleName)), + ) } - val a = agent - val t1 = System.nanoTime() try { - val conv = a.createConversation(temp = true) - val id = conv.id - conv.close() - a.deleteConversation(id) - steps += CheckStep("Создание диалога", true, elapsed(t1)) - } catch (t: Throwable) { - steps += CheckStep("Создание диалога", false, elapsed(t1), t.message ?: t::class.simpleName) + val info = agent.info + ConnectionReport( + baseUrl = config.baseUrl, + steps = listOf( + CheckStep(STEP_AGENT_INFO, true, elapsed(t0), runCatching { info.description }.getOrNull()), + ), + agentName = info.name, + ) } finally { - runCatching { a.close() } + runCatching { agent.close() } } - - ConnectionReport(config.baseUrl, steps, name) } private fun elapsed(fromNanos: Long): Long = (System.nanoTime() - fromNanos) / 1_000_000 + + companion object { + /** Заголовок единственного шага проверки в отчёте/UI. */ + const val STEP_AGENT_INFO = "Информация об агенте" + } } diff --git a/src/jvmMain/kotlin/pw/binom/agentik/desktop/ui/AppState.kt b/src/jvmMain/kotlin/pw/binom/agentik/desktop/ui/AppState.kt index 1351a98..427cf60 100644 --- a/src/jvmMain/kotlin/pw/binom/agentik/desktop/ui/AppState.kt +++ b/src/jvmMain/kotlin/pw/binom/agentik/desktop/ui/AppState.kt @@ -240,7 +240,7 @@ class AppState( /** Проверка связи для формы агента — вызывается UI из своей корутины. */ suspend fun checkConnection(config: AgentConfig): ConnectionReport = runCatching { checker.check(config, settingsState.clientId) } - .getOrElse { ConnectionReport(config.baseUrl, listOf(CheckStep("Ответ сервера", false, 0, it.message))) } + .getOrElse { ConnectionReport(config.baseUrl, listOf(CheckStep(ConnectionChecker.STEP_AGENT_INFO, false, 0, it.message))) } // ───── conversations ─────