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

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 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<CheckStep>()
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 = "Информация об агенте"
}
}
@@ -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 ─────