* feat: improve local providers health monitoring and model management - Add automatic health monitoring initialization for enabled providers - Add LM Studio model management and display functionality - Fix endpoint status detection by setting default base URLs - Replace mixed icon libraries with consistent Lucide icons only - Fix button styling with transparent backgrounds - Add comprehensive setup guides with web-researched content - Add proper navigation with back buttons between views - Fix all TypeScript and linting issues in LocalProvidersTab 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Remove Service Status tab and related code The Service Status tab and all associated files, components, and provider checkers have been deleted. References to 'service-status' have been removed from tab constants, types, and the control panel. This simplifies the settings UI and codebase by eliminating the service status monitoring feature. * Update LocalProvidersTab.tsx * Fix all linter and TypeScript errors in local providers components - Remove unused imports and fix import formatting - Fix type-only imports for OllamaModel and LMStudioModel - Fix Icon component usage in ProviderCard.tsx - Clean up unused imports across all local provider files - Ensure all TypeScript and ESLint checks pass --------- Co-authored-by: Claude <noreply@anthropic.com>
166 lines
5.6 KiB
TypeScript
166 lines
5.6 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react';
|
|
import { localModelHealthMonitor, type ModelHealthStatus } from '~/lib/services/localModelHealthMonitor';
|
|
|
|
export interface UseLocalModelHealthOptions {
|
|
autoStart?: boolean;
|
|
checkInterval?: number;
|
|
}
|
|
|
|
export interface UseLocalModelHealthReturn {
|
|
healthStatuses: ModelHealthStatus[];
|
|
getHealthStatus: (provider: 'Ollama' | 'LMStudio' | 'OpenAILike', baseUrl: string) => ModelHealthStatus | undefined;
|
|
startMonitoring: (provider: 'Ollama' | 'LMStudio' | 'OpenAILike', baseUrl: string, checkInterval?: number) => void;
|
|
stopMonitoring: (provider: 'Ollama' | 'LMStudio' | 'OpenAILike', baseUrl: string) => void;
|
|
performHealthCheck: (provider: 'Ollama' | 'LMStudio' | 'OpenAILike', baseUrl: string) => Promise<void>;
|
|
isHealthy: (provider: 'Ollama' | 'LMStudio' | 'OpenAILike', baseUrl: string) => boolean;
|
|
getOverallHealth: () => { healthy: number; unhealthy: number; checking: number; unknown: number };
|
|
}
|
|
|
|
/**
|
|
* React hook for monitoring local model health
|
|
*/
|
|
export function useLocalModelHealth(options: UseLocalModelHealthOptions = {}): UseLocalModelHealthReturn {
|
|
const { checkInterval } = options;
|
|
const [healthStatuses, setHealthStatuses] = useState<ModelHealthStatus[]>([]);
|
|
|
|
// Update health statuses when they change
|
|
useEffect(() => {
|
|
const handleStatusChanged = (status: ModelHealthStatus) => {
|
|
setHealthStatuses((current) => {
|
|
const index = current.findIndex((s) => s.provider === status.provider && s.baseUrl === status.baseUrl);
|
|
|
|
if (index >= 0) {
|
|
const updated = [...current];
|
|
updated[index] = status;
|
|
|
|
return updated;
|
|
} else {
|
|
return [...current, status];
|
|
}
|
|
});
|
|
};
|
|
|
|
localModelHealthMonitor.on('statusChanged', handleStatusChanged);
|
|
|
|
// Initialize with current statuses
|
|
setHealthStatuses(localModelHealthMonitor.getAllHealthStatuses());
|
|
|
|
return () => {
|
|
localModelHealthMonitor.off('statusChanged', handleStatusChanged);
|
|
};
|
|
}, []);
|
|
|
|
// Get health status for a specific provider
|
|
const getHealthStatus = useCallback((provider: 'Ollama' | 'LMStudio' | 'OpenAILike', baseUrl: string) => {
|
|
return localModelHealthMonitor.getHealthStatus(provider, baseUrl);
|
|
}, []);
|
|
|
|
// Start monitoring a provider
|
|
const startMonitoring = useCallback(
|
|
(provider: 'Ollama' | 'LMStudio' | 'OpenAILike', baseUrl: string, interval?: number) => {
|
|
console.log(`[Health Monitor] Starting monitoring for ${provider} at ${baseUrl}`);
|
|
localModelHealthMonitor.startMonitoring(provider, baseUrl, interval || checkInterval);
|
|
},
|
|
[checkInterval],
|
|
);
|
|
|
|
// Stop monitoring a provider
|
|
const stopMonitoring = useCallback((provider: 'Ollama' | 'LMStudio' | 'OpenAILike', baseUrl: string) => {
|
|
console.log(`[Health Monitor] Stopping monitoring for ${provider} at ${baseUrl}`);
|
|
localModelHealthMonitor.stopMonitoring(provider, baseUrl);
|
|
|
|
// Remove from local state
|
|
setHealthStatuses((current) => current.filter((s) => !(s.provider === provider && s.baseUrl === baseUrl)));
|
|
}, []);
|
|
|
|
// Perform manual health check
|
|
const performHealthCheck = useCallback(async (provider: 'Ollama' | 'LMStudio' | 'OpenAILike', baseUrl: string) => {
|
|
await localModelHealthMonitor.performHealthCheck(provider, baseUrl);
|
|
}, []);
|
|
|
|
// Check if a provider is healthy
|
|
const isHealthy = useCallback(
|
|
(provider: 'Ollama' | 'LMStudio' | 'OpenAILike', baseUrl: string) => {
|
|
const status = getHealthStatus(provider, baseUrl);
|
|
return status?.status === 'healthy';
|
|
},
|
|
[getHealthStatus],
|
|
);
|
|
|
|
// Get overall health statistics
|
|
const getOverallHealth = useCallback(() => {
|
|
const stats = { healthy: 0, unhealthy: 0, checking: 0, unknown: 0 };
|
|
|
|
healthStatuses.forEach((status) => {
|
|
stats[status.status]++;
|
|
});
|
|
|
|
return stats;
|
|
}, [healthStatuses]);
|
|
|
|
return {
|
|
healthStatuses,
|
|
getHealthStatus,
|
|
startMonitoring,
|
|
stopMonitoring,
|
|
performHealthCheck,
|
|
isHealthy,
|
|
getOverallHealth,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hook for monitoring a specific provider
|
|
*/
|
|
export function useProviderHealth(
|
|
provider: 'Ollama' | 'LMStudio' | 'OpenAILike',
|
|
baseUrl: string,
|
|
options: UseLocalModelHealthOptions = {},
|
|
) {
|
|
const { autoStart = true, checkInterval } = options;
|
|
const { getHealthStatus, startMonitoring, stopMonitoring, performHealthCheck, isHealthy } = useLocalModelHealth();
|
|
|
|
const [status, setStatus] = useState<ModelHealthStatus | undefined>();
|
|
|
|
// Update status when it changes
|
|
useEffect(() => {
|
|
const updateStatus = () => {
|
|
setStatus(getHealthStatus(provider, baseUrl));
|
|
};
|
|
|
|
const handleStatusChanged = (changedStatus: ModelHealthStatus) => {
|
|
if (changedStatus.provider === provider && changedStatus.baseUrl === baseUrl) {
|
|
setStatus(changedStatus);
|
|
}
|
|
};
|
|
|
|
localModelHealthMonitor.on('statusChanged', handleStatusChanged);
|
|
updateStatus();
|
|
|
|
return () => {
|
|
localModelHealthMonitor.off('statusChanged', handleStatusChanged);
|
|
};
|
|
}, [provider, baseUrl, getHealthStatus]);
|
|
|
|
// Auto-start monitoring if enabled
|
|
useEffect(() => {
|
|
if (autoStart && baseUrl) {
|
|
startMonitoring(provider, baseUrl, checkInterval);
|
|
|
|
return () => {
|
|
stopMonitoring(provider, baseUrl);
|
|
};
|
|
}
|
|
|
|
return undefined;
|
|
}, [autoStart, provider, baseUrl, checkInterval, startMonitoring, stopMonitoring]);
|
|
|
|
return {
|
|
status,
|
|
isHealthy: isHealthy(provider, baseUrl),
|
|
performHealthCheck: () => performHealthCheck(provider, baseUrl),
|
|
startMonitoring: (interval?: number) => startMonitoring(provider, baseUrl, interval),
|
|
stopMonitoring: () => stopMonitoring(provider, baseUrl),
|
|
};
|
|
}
|