feat: local providers refactor & enhancement (#1968)
* 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>
This commit is contained in:
165
app/lib/hooks/useLocalModelHealth.ts
Normal file
165
app/lib/hooks/useLocalModelHealth.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
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),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user