Упрощение проверки связи: удалён временный диалог, оставлен запрос информации об агенте

This commit is contained in:
2026-09-28 11:40:03 +03:00
parent 7ad3f55b57
commit 9aaf0c3a46
2 changed files with 31 additions and 32 deletions
@@ -6,6 +6,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import pw.binom.agentik.client.AgentikAgent import pw.binom.agentik.client.AgentikAgent
import pw.binom.agentik.desktop.settings.AgentConfig import pw.binom.agentik.desktop.settings.AgentConfig
import pw.binom.agentik.proto.Agent
/** Один шаг проверки связи. */ /** Один шаг проверки связи. */
data class CheckStep( data class CheckStep(
@@ -28,58 +29,56 @@ data class ConnectionReport(
/** /**
* Проверка связи с агентом без открытия полноценного [AgentConnection]: * Проверка связи с агентом без открытия полноценного [AgentConnection]:
* отдельное «одноразовое» подключение, которое закрывается сразу после * отдельное «одноразовое» подключение, которое закрывается сразу после
* прогона шагов. * прогона шага.
* *
* Шаги: * Единственный шаг — **запрос информации об агенте**: конструктор
* 1. **Ответ сервера** — `GET {baseUrl}` за `AgentInfo` (имя/описание). * [AgentikAgent] синхронно делает `GET {baseUrl}` за `AgentInfo`
* 2. **Диалог** — создать временный диалог и удалить его (проверяет * (имя/описание). Успешный ответ и есть «сервер жив и отвечает на своём
* conversation-роуты, не тратит токены LLM). * API» — без единого побочного эффекта на стороне сервера.
* *
* Сознательно **не** отправляем реальный ход: это дёргает модель (время, * Сознательно **не** создаём временный диалог и **не** отправляем реальный
* стоимость, побочные эффекты в истории). Проверки маршрутов достаточно, * ход: временный диалог — это мусор в истории сервера (и лишний визит к БД),
* чтобы отличить «связи нет» от «связь есть». * а ход ещё и дёргает модель (время, стоимость, след в журнале). Для ответа
* «связь есть / связи нет» достаточно ответа на запрос информации.
*/ */
class ConnectionChecker( class ConnectionChecker(
private val engineFactory: HttpClientEngineFactory<*> = CIO, private val engineFactory: HttpClientEngineFactory<*> = CIO,
private val timeoutMillis: Long = 15_000,
) { ) {
suspend fun check(config: AgentConfig, clientId: String): ConnectionReport = withContext(Dispatchers.IO) { suspend fun check(config: AgentConfig, clientId: String): ConnectionReport = withContext(Dispatchers.IO) {
val steps = mutableListOf<CheckStep>()
var agent: pw.binom.agentik.proto.Agent? = null
var name: String? = null
val t0 = System.nanoTime() val t0 = System.nanoTime()
try { val agent: Agent = try {
agent = AgentikAgent( AgentikAgent(
id = clientId, id = clientId,
baseUrl = config.baseUrl, baseUrl = config.baseUrl,
engineFactory = engineFactory, engineFactory = engineFactory,
token = config.token, token = config.token,
) )
name = agent.info.name
steps += CheckStep("Ответ сервера", true, elapsed(t0), runCatching { agent.info.description }.getOrNull())
} catch (t: Throwable) { } catch (t: Throwable) {
steps += CheckStep("Ответ сервера", false, elapsed(t0), t.message ?: t::class.simpleName) return@withContext ConnectionReport(
return@withContext ConnectionReport(config.baseUrl, steps, name) 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 { try {
val conv = a.createConversation(temp = true) val info = agent.info
val id = conv.id ConnectionReport(
conv.close() baseUrl = config.baseUrl,
a.deleteConversation(id) steps = listOf(
steps += CheckStep("Создание диалога", true, elapsed(t1)) CheckStep(STEP_AGENT_INFO, true, elapsed(t0), runCatching { info.description }.getOrNull()),
} catch (t: Throwable) { ),
steps += CheckStep("Создание диалога", false, elapsed(t1), t.message ?: t::class.simpleName) agentName = info.name,
)
} finally { } finally {
runCatching { a.close() } runCatching { agent.close() }
} }
ConnectionReport(config.baseUrl, steps, name)
} }
private fun elapsed(fromNanos: Long): Long = (System.nanoTime() - fromNanos) / 1_000_000 private fun elapsed(fromNanos: Long): Long = (System.nanoTime() - fromNanos) / 1_000_000
companion object {
/** Заголовок единственного шага проверки в отчёте/UI. */
const val STEP_AGENT_INFO = "Информация об агенте"
}
} }
@@ -240,7 +240,7 @@ class AppState(
/** Проверка связи для формы агента — вызывается UI из своей корутины. */ /** Проверка связи для формы агента — вызывается UI из своей корутины. */
suspend fun checkConnection(config: AgentConfig): ConnectionReport = suspend fun checkConnection(config: AgentConfig): ConnectionReport =
runCatching { checker.check(config, settingsState.clientId) } 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 ───── // ───── conversations ─────