From 75e7b628cab5a97eea027f0607405498b7913d1f Mon Sep 17 00:00:00 2001 From: LordMathis Date: Sun, 26 Oct 2025 19:12:35 +0100 Subject: [PATCH] Remove 'loading' and 'error' states --- webui/src/components/HealthBadge.tsx | 14 +-- webui/src/lib/healthService.ts | 132 +++++++-------------------- webui/src/types/instance.ts | 6 +- 3 files changed, 37 insertions(+), 115 deletions(-) diff --git a/webui/src/components/HealthBadge.tsx b/webui/src/components/HealthBadge.tsx index e2b04e7..13eda9f 100644 --- a/webui/src/components/HealthBadge.tsx +++ b/webui/src/components/HealthBadge.tsx @@ -2,7 +2,7 @@ import React from "react"; import { Badge } from "@/components/ui/badge"; import type { HealthStatus } from "@/types/instance"; -import { CheckCircle, Loader2, XCircle, Clock, AlertCircle } from "lucide-react"; +import { CheckCircle, Loader2, XCircle, Clock } from "lucide-react"; interface HealthBadgeProps { health?: HealthStatus; @@ -17,16 +17,12 @@ const HealthBadge: React.FC = ({ health }) => { switch (health.state) { case "ready": return ; - case "loading": - return ; case "starting": return ; case "restarting": return ; case "stopped": return ; - case "error": - return ; case "failed": return ; } @@ -36,16 +32,12 @@ const HealthBadge: React.FC = ({ health }) => { switch (health.state) { case "ready": return "default"; - case "loading": - return "outline"; case "starting": return "outline"; case "restarting": return "outline"; case "stopped": return "secondary"; - case "error": - return "destructive"; case "failed": return "destructive"; } @@ -55,16 +47,12 @@ const HealthBadge: React.FC = ({ health }) => { switch (health.state) { case "ready": return "Ready"; - case "loading": - return "Loading"; case "starting": return "Starting"; case "restarting": return "Restarting"; case "stopped": return "Stopped"; - case "error": - return "Error"; case "failed": return "Failed"; } diff --git a/webui/src/lib/healthService.ts b/webui/src/lib/healthService.ts index 5830d53..aabea0c 100644 --- a/webui/src/lib/healthService.ts +++ b/webui/src/lib/healthService.ts @@ -6,12 +6,10 @@ type HealthCallback = (health: HealthStatus) => void // Polling intervals based on health state (in milliseconds) const POLLING_INTERVALS: Record = { 'starting': 5000, // 5 seconds - frequent during startup - 'loading': 5000, // 5 seconds - model loading 'restarting': 5000, // 5 seconds - restart in progress 'ready': 60000, // 60 seconds - stable state 'stopped': 0, // No polling 'failed': 0, // No polling - 'error': 10000, // 10 seconds - retry on error } class HealthService { @@ -42,7 +40,7 @@ class HealthService { try { await instancesApi.getHealth(instanceName) - // HTTP health check succeeded + // HTTP health check succeeded - instance is ready const health: HealthStatus = { state: 'ready', instanceStatus: 'running', @@ -54,45 +52,21 @@ class HealthService { return health } catch (httpError) { - // HTTP health check failed while instance is running - // Re-verify instance is still running - try { - const verifyInstance = await instancesApi.get(instanceName) - - if (verifyInstance.status !== 'running') { - // Instance stopped/failed since our first check - const health: HealthStatus = { - state: this.mapStatusToHealthState(verifyInstance.status), - instanceStatus: verifyInstance.status, - lastChecked: new Date(), - source: 'backend' - } - - this.updateCache(instanceName, health) - return health - } - - // Instance still running but HTTP failed - classify error - const health = this.classifyHttpError(httpError as Error, 'running') - this.updateCache(instanceName, health) - return health - - } catch (verifyError) { - // Failed to verify - return error state - const health: HealthStatus = { - state: 'error', - instanceStatus: 'running', - lastChecked: new Date(), - error: 'Failed to verify instance status', - source: 'error' - } - - this.updateCache(instanceName, health) - return health + // HTTP health check failed - instance is still starting + // Any error (503, connection refused, timeout, etc.) means "starting" + const health: HealthStatus = { + state: 'starting', + instanceStatus: 'running', + lastChecked: new Date(), + error: httpError instanceof Error ? httpError.message : 'Health check failed', + source: 'http' } + + this.updateCache(instanceName, health) + return health } } else { - // Instance not running - return backend status + // Instance not running - map backend status directly const health: HealthStatus = { state: this.mapStatusToHealthState(instance.status), instanceStatus: instance.status, @@ -105,56 +79,11 @@ class HealthService { } } catch (error) { - // Failed to get instance - const health: HealthStatus = { - state: 'error', - instanceStatus: 'unknown', - lastChecked: new Date(), - error: error instanceof Error ? error.message : 'Unknown error', - source: 'error' - } - - this.updateCache(instanceName, health) - return health - } - } - - /** - * Classifies HTTP errors into appropriate health states - */ - private classifyHttpError(error: Error, instanceStatus: InstanceStatus): HealthStatus { - const errorMessage = error.message.toLowerCase() - - // Parse HTTP status code from error message if available - if (errorMessage.includes('503')) { - return { - state: 'loading', - instanceStatus, - lastChecked: new Date(), - error: 'Service loading', - source: 'http' - } - } - - if (errorMessage.includes('connection refused') || - errorMessage.includes('econnrefused') || - errorMessage.includes('network error')) { - return { - state: 'starting', - instanceStatus, - lastChecked: new Date(), - error: 'Connection refused', - source: 'http' - } - } - - // Other HTTP errors - return { - state: 'error', - instanceStatus, - lastChecked: new Date(), - error: error.message, - source: 'http' + // Failed to get instance status from backend + // This is a backend communication error, not an instance health error + // Let the error propagate so polling can retry + console.error(`Failed to get instance status for ${instanceName}:`, error) + throw error } } @@ -164,10 +93,9 @@ class HealthService { private mapStatusToHealthState(status: InstanceStatus): HealthState { switch (status) { case 'stopped': return 'stopped' - case 'running': return 'starting' // Unknown without HTTP check + case 'running': return 'starting' // Should not happen as we check HTTP for running case 'failed': return 'failed' case 'restarting': return 'restarting' - default: return 'error' } } @@ -188,15 +116,20 @@ class HealthService { // Invalidate cache this.healthCache.delete(instanceName) - const health = await this.performHealthCheck(instanceName) - this.notifyCallbacks(instanceName, health) + try { + const health = await this.performHealthCheck(instanceName) + this.notifyCallbacks(instanceName, health) - // Update last state and adjust polling interval if needed - const previousState = this.lastHealthState.get(instanceName) - this.lastHealthState.set(instanceName, health.state) + // Update last state and adjust polling interval if needed + const previousState = this.lastHealthState.get(instanceName) + this.lastHealthState.set(instanceName, health.state) - if (previousState !== health.state) { - this.adjustPollingInterval(instanceName, health.state) + if (previousState !== health.state) { + this.adjustPollingInterval(instanceName, health.state) + } + } catch (error) { + // Error getting health - keep polling if active + console.error(`Failed to refresh health for ${instanceName}:`, error) } } @@ -273,7 +206,7 @@ class HealthService { const pollInterval = POLLING_INTERVALS[state] - // Don't poll for stable states (stopped, failed, ready has long interval) + // Don't poll for stable states (stopped, failed) if (pollInterval === 0) { return } @@ -293,6 +226,7 @@ class HealthService { } } catch (error) { console.error(`Health check failed for ${instanceName}:`, error) + // Continue polling even on error } }, pollInterval) diff --git a/webui/src/types/instance.ts b/webui/src/types/instance.ts index 97d3cac..1c42547 100644 --- a/webui/src/types/instance.ts +++ b/webui/src/types/instance.ts @@ -13,14 +13,14 @@ export type BackendTypeValue = typeof BackendType[keyof typeof BackendType] export type InstanceStatus = 'running' | 'stopped' | 'failed' | 'restarting' -export type HealthState = 'stopped' | 'starting' | 'loading' | 'ready' | 'error' | 'failed' | 'restarting' +export type HealthState = 'stopped' | 'starting' | 'ready' | 'failed' | 'restarting' export interface HealthStatus { state: HealthState - instanceStatus: InstanceStatus | 'unknown' + instanceStatus: InstanceStatus lastChecked: Date error?: string - source: 'backend' | 'http' | 'error' + source: 'backend' | 'http' } export interface Instance {