feat(health): register LlamaServerHealthProbe (A§4)

Wire the built-but-unregistered LlamaServerHealthProbe (266bbf0) into the health monitor:
when a `llamacpp` provider URL is configured, add an HttpLlamaLivenessClient-backed probe
(dedicated CIO client, 3s per-ping timeout, shutdown-hook close) to the probe list. Gated
on a configured provider URL so a model-less box never reports a spurious LLAMA_SERVER
DEGRADED. Replaces the Main.kt TODO(wiring). main() is not unit-tested; the probe + client
are already covered by 266bbf0 — compile + :apps:server:test + detekt green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 08:25:25 +00:00
parent 3302930b00
commit 64a90e74a9
@@ -96,6 +96,9 @@ import java.nio.file.Paths
private val log = LoggerFactory.getLogger("com.correx.apps.server.Main") private val log = LoggerFactory.getLogger("com.correx.apps.server.Main")
/** Per-ping timeout (ms) for the llama-server health probe — short so a hung server never stalls it. */
private const val LLAMA_HEALTH_TIMEOUT_MS = 3_000L
fun main() { fun main() {
log.info("=== correx server starting ===") log.info("=== correx server starting ===")
log.info(" port : 8080") log.info(" port : 8080")
@@ -438,9 +441,21 @@ fun main() {
val seeded = DefaultEventReplayer(eventStore, com.correx.apps.server.health.HealthProjection()) val seeded = DefaultEventReplayer(eventStore, com.correx.apps.server.health.HealthProjection())
.rebuild(com.correx.apps.server.health.SYSTEM_SESSION) .rebuild(com.correx.apps.server.health.SYSTEM_SESSION)
.subjects.mapValues { it.value.status } .subjects.mapValues { it.value.status }
// Monitor the llama server only when a llamacpp provider URL is configured — otherwise the
// LLAMA_SERVER subject would report a spurious DEGRADED on a box that runs no local model.
// A dedicated short-timeout client keeps a hung server from stalling the probe loop.
val llamaProbe = correxConfig.providers.firstOrNull { it.type == "llamacpp" }?.url?.let { url ->
val healthHttpClient = io.ktor.client.HttpClient(io.ktor.client.engine.cio.CIO) {
engine { requestTimeout = LLAMA_HEALTH_TIMEOUT_MS }
}
Runtime.getRuntime().addShutdownHook(Thread { healthHttpClient.close() })
com.correx.apps.server.health.LlamaServerHealthProbe(
com.correx.apps.server.health.HttpLlamaLivenessClient(healthHttpClient, url),
)
}
com.correx.apps.server.health.HealthMonitor( com.correx.apps.server.health.HealthMonitor(
eventStore = eventStore, eventStore = eventStore,
probes = listOf( probes = listOfNotNull(
com.correx.apps.server.health.DiskWatermarkProbe( com.correx.apps.server.health.DiskWatermarkProbe(
monitoredPaths = listOf(dataDir), monitoredPaths = listOf(dataDir),
warnBytes = hc.diskWarnBytes, warnBytes = hc.diskWarnBytes,
@@ -450,9 +465,7 @@ fun main() {
eventStore, eventStore,
hc.eventStoreWarnLatencyMs, hc.eventStoreWarnLatencyMs,
), ),
// TODO(wiring): register LlamaServerHealthProbe once an HttpClient + tokens/sec telemetry are llamaProbe,
// exposed to the health-monitor scope (currently only built inside
// InfrastructureModule.createModelManager).
), ),
intervalMs = hc.intervalMs, intervalMs = hc.intervalMs,
initialStatuses = seeded, initialStatuses = seeded,