Упрощение проверки связи: удалён временный диалог, оставлен запрос информации об агенте
This commit is contained in:
@@ -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 ─────
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user