fix(health): surface ollama endpoint diagnosis
Some checks failed
CD Pipeline / tests (push) Successful in 1m30s
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / build-and-deploy (push) Successful in 3m44s
CD Pipeline / post-deploy-checks (push) Has been cancelled

This commit is contained in:
Your Name
2026-05-31 18:38:23 +08:00
parent e6a433da22
commit 7987da7f3f
7 changed files with 163 additions and 3 deletions

View File

@@ -16,11 +16,18 @@ interface ModelInfo {
role: 'primary' | 'backup' | 'local' | 'agent' | 'provider'
status: 'up' | 'down' | 'degraded' | 'unknown'
latencyMs?: number | null
diagnosisCode?: string | null
retryAfterSeconds?: number | null
isCooldown?: boolean
}
interface HealthComponent {
status?: 'up' | 'down' | 'degraded'
latency_ms?: number | null
diagnosis_code?: string | null
retry_after_seconds?: number | null
is_cooldown?: boolean
error?: string | null
}
interface HealthResponse {
@@ -49,6 +56,31 @@ function statusColor(status: ModelInfo['status']) {
return '#87867f'
}
function modelDetail(model: ModelInfo, t: ReturnType<typeof useTranslations>) {
if (typeof model.latencyMs === 'number' && model.status === 'up') {
return `${Math.round(model.latencyMs)}ms`
}
if (model.isCooldown) {
const seconds = Math.max(0, Math.round(model.retryAfterSeconds ?? 0))
return seconds > 0
? t('aiModelHealth.cooldownSeconds', { seconds })
: t('aiModelHealth.cooldown')
}
if (model.diagnosisCode === 'local_proxy_upstream_unreachable') {
return t('aiModelHealth.localProxy')
}
if (model.diagnosisCode === 'endpoint_timeout') {
return t('aiModelHealth.timeout')
}
if (model.diagnosisCode === 'endpoint_network_unreachable') {
return t('aiModelHealth.network')
}
if (model.diagnosisCode === 'endpoint_connection_refused') {
return t('aiModelHealth.refused')
}
return t(`aiModelRoles.${model.role}` as never)
}
export function AIModelStatus() {
const t = useTranslations('dashboard')
const [models, setModels] = useState<ModelInfo[]>([
@@ -72,6 +104,9 @@ export function AIModelStatus() {
role: PROVIDER_ROLES[key] ?? 'provider',
status: d.components?.[key]?.status ?? 'unknown',
latencyMs: d.components?.[key]?.latency_ms,
diagnosisCode: d.components?.[key]?.diagnosis_code,
retryAfterSeconds: d.components?.[key]?.retry_after_seconds,
isCooldown: d.components?.[key]?.is_cooldown,
})))
})
.catch(() => {})
@@ -98,9 +133,7 @@ export function AIModelStatus() {
<span style={{ width: 5, height: 5, borderRadius: '50%', background: statusColor(m.status), flexShrink: 0 }} />
<span style={{ fontSize: 12, fontWeight: 500, color: '#141413' }}>{m.name}</span>
<span style={{ fontSize: 10, color: '#87867f', marginLeft: 'auto' }}>
{typeof m.latencyMs === 'number'
? `${Math.round(m.latencyMs)}ms`
: t(`aiModelRoles.${m.role}` as never)}
{modelDetail(m, t)}
</span>
</div>
))}