@hivetechs/hive-ai
Version:
Real-time streaming AI consensus platform with HTTP+SSE MCP integration for Claude Code, VS Code, Cursor, and Windsurf - powered by OpenRouter's unified API
238 lines ⢠10.6 kB
JavaScript
/**
* Health Status Tool
*
* Provides comprehensive system health monitoring for hive-ai.
* Displays OpenRouter connectivity, database health, model availability, and error statistics.
*/
import { z } from "zod";
import { globalHealthMonitor } from '../health-monitor.js';
import { globalErrorHandler } from '../error-handling.js';
export const HealthStatusToolSchema = z.object({
detailed: z.boolean().default(false).describe('Show detailed health report with error statistics')
});
export async function runHealthStatusTool(args) {
try {
// Force fresh health checks
await globalHealthMonitor.runHealthChecks();
if (args.detailed) {
return await getDetailedHealthReport();
}
else {
return await getBasicHealthReport();
}
}
catch (error) {
return {
result: `ā Error checking system health: ${error instanceof Error ? error.message : 'Unknown error'}`
};
}
}
async function getBasicHealthReport() {
const health = globalHealthMonitor.getSystemHealth();
let report = '\nš„ System Health Status\n';
report += '='.repeat(50) + '\n\n';
// Overall status
const overallEmoji = health.overall === 'healthy' ? 'ā
' :
health.overall === 'degraded' ? 'ā ļø' : 'ā';
report += `${overallEmoji} Overall Status: ${health.overall.toUpperCase()}\n\n`;
// Service details
report += 'š Services:\n';
report += ` š OpenRouter API: ${getStatusEmoji(health.openrouter.status)} ${health.openrouter.status}`;
if (health.openrouter.responseTime > 0) {
report += ` (${health.openrouter.responseTime}ms)`;
}
if (health.openrouter.lastError) {
report += `\n Error: ${health.openrouter.lastError}`;
}
report += '\n';
report += ` š¾ Database: ${getStatusEmoji(health.database.status)} ${health.database.status}`;
if (health.database.responseTime > 0) {
report += ` (${health.database.responseTime}ms)`;
}
if (health.database.lastError) {
report += `\n Error: ${health.database.lastError}`;
}
report += '\n\n';
// Model health summary
if (health.models.length > 0) {
const availableModels = health.models.filter(m => m.status === 'available').length;
const degradedModels = health.models.filter(m => m.status === 'degraded').length;
const unavailableModels = health.models.filter(m => m.status === 'unavailable').length;
report += 'š¤ Model Health Summary:\n';
if (availableModels > 0)
report += ` ā
Available: ${availableModels} models\n`;
if (degradedModels > 0)
report += ` ā ļø Degraded: ${degradedModels} models\n`;
if (unavailableModels > 0)
report += ` ā Unavailable: ${unavailableModels} models\n`;
report += '\n';
}
report += `ā° Last updated: ${health.timestamp.toLocaleString()}\n`;
// Show recent errors if any
const errorStats = globalErrorHandler.getErrorStats(3600000); // Last hour
if (errorStats.totalErrors > 0) {
report += `\nā ļø Recent Issues:\n`;
report += ` ⢠${errorStats.totalErrors} errors in the last hour\n`;
const topErrors = Object.entries(errorStats.errorsByType)
.sort(([, a], [, b]) => b - a)
.slice(0, 3);
if (topErrors.length > 0) {
report += ` ⢠Top error types: ${topErrors.map(([type, count]) => `${type} (${count})`).join(', ')}\n`;
}
}
report += '\nš” Use --detailed for comprehensive health report\n';
return { result: report };
}
async function getDetailedHealthReport() {
const health = globalHealthMonitor.getSystemHealth();
const errorStats = globalErrorHandler.getErrorStats(3600000); // Last hour
const circuitBreakers = globalErrorHandler.getCircuitBreakerStatus();
let report = '\nš„ Detailed System Health Report\n';
report += '='.repeat(60) + '\n\n';
// Overall status
const overallEmoji = health.overall === 'healthy' ? 'ā
' :
health.overall === 'degraded' ? 'ā ļø' : 'ā';
report += `${overallEmoji} Overall Status: ${health.overall.toUpperCase()}\n`;
report += `ā° Report generated: ${new Date().toLocaleString()}\n`;
report += `š System uptime: ${formatUptime(process.uptime())}\n\n`;
// Detailed service status
report += 'š Service Details:\n';
report += '-'.repeat(40) + '\n';
// OpenRouter API
report += `š OpenRouter API:\n`;
report += ` Status: ${getStatusEmoji(health.openrouter.status)} ${health.openrouter.status}\n`;
report += ` Response Time: ${health.openrouter.responseTime}ms\n`;
report += ` Last Checked: ${health.openrouter.lastChecked.toLocaleString()}\n`;
report += ` Consecutive Failures: ${health.openrouter.consecutiveFailures}\n`;
if (health.openrouter.lastError) {
report += ` Last Error: ${health.openrouter.lastError}\n`;
}
if (health.openrouter.metadata) {
report += ` Models Available: ${health.openrouter.metadata.modelsCount || 'Unknown'}\n`;
}
report += '\n';
// Database
report += `š¾ Database:\n`;
report += ` Status: ${getStatusEmoji(health.database.status)} ${health.database.status}\n`;
report += ` Response Time: ${health.database.responseTime}ms\n`;
report += ` Last Checked: ${health.database.lastChecked.toLocaleString()}\n`;
report += ` Consecutive Failures: ${health.database.consecutiveFailures}\n`;
if (health.database.lastError) {
report += ` Last Error: ${health.database.lastError}\n`;
}
if (health.database.metadata) {
report += ` Profile Count: ${health.database.metadata.profileCount || 'Unknown'}\n`;
}
report += '\n';
// Individual model health
if (health.models.length > 0) {
report += 'š¤ Model Health Details:\n';
report += '-'.repeat(40) + '\n';
health.models.forEach(model => {
const emoji = model.status === 'available' ? 'ā
' :
model.status === 'degraded' ? 'ā ļø' : 'ā';
report += `${emoji} ${model.provider}/${model.model}:\n`;
report += ` Status: ${model.status}\n`;
report += ` Success Rate: ${model.successRate}%\n`;
report += ` Avg Response Time: ${model.avgResponseTime}ms\n`;
report += ` Error Count: ${model.errorCount}\n`;
report += ` Last Tested: ${model.lastTested.toLocaleString()}\n\n`;
});
}
// Error statistics
if (errorStats.totalErrors > 0) {
report += 'š Error Statistics (Last Hour):\n';
report += '-'.repeat(40) + '\n';
report += `Total Errors: ${errorStats.totalErrors}\n\n`;
if (Object.keys(errorStats.errorsByType).length > 0) {
report += 'By Type:\n';
Object.entries(errorStats.errorsByType)
.sort(([, a], [, b]) => b - a)
.forEach(([type, count]) => {
report += ` ⢠${type}: ${count}\n`;
});
report += '\n';
}
if (Object.keys(errorStats.errorsByProvider).length > 0) {
report += 'By Provider:\n';
Object.entries(errorStats.errorsByProvider)
.sort(([, a], [, b]) => b - a)
.forEach(([provider, count]) => {
report += ` ⢠${provider}: ${count}\n`;
});
report += '\n';
}
if (errorStats.recentErrors.length > 0) {
report += 'Recent Errors:\n';
errorStats.recentErrors.slice(-5).forEach((error, index) => {
report += ` ${index + 1}. [${error.type}] ${error.stage} - ${error.message}\n`;
report += ` Provider: ${error.provider}/${error.model}\n`;
report += ` Time: ${error.timestamp.toLocaleString()}\n\n`;
});
}
}
// Circuit breaker status
if (Object.keys(circuitBreakers).length > 0) {
report += 'š Circuit Breaker Status:\n';
report += '-'.repeat(40) + '\n';
Object.entries(circuitBreakers).forEach(([key, status]) => {
const [provider, model] = key.split(':');
const emoji = status.state === 'CLOSED' ? 'ā
' :
status.state === 'HALF_OPEN' ? 'ā ļø' : 'ā';
report += `${emoji} ${provider}/${model}:\n`;
report += ` State: ${status.state}\n`;
report += ` Failures: ${status.failures}\n`;
if (status.nextAttemptTime) {
report += ` Next Attempt: ${status.nextAttemptTime.toLocaleString()}\n`;
}
report += '\n';
});
}
// Recommendations
report += 'š” Recommendations:\n';
report += '-'.repeat(40) + '\n';
if (health.overall === 'unhealthy') {
report += '⢠System is experiencing significant issues\n';
report += '⢠Check OpenRouter API key and connectivity\n';
report += '⢠Verify database is accessible\n';
}
else if (health.overall === 'degraded') {
report += '⢠Some services are experiencing issues\n';
report += '⢠Monitor error rates and response times\n';
}
else {
report += '⢠All systems operational\n';
report += '⢠Continue monitoring for optimal performance\n';
}
if (errorStats.totalErrors > 10) {
report += '⢠High error rate detected - investigate recent failures\n';
}
report += '⢠Run health checks regularly with: hive-ai health\n';
report += '⢠Use circuit breaker resets if needed: hive-ai reset-circuit <provider>/<model>\n';
return { result: report };
}
function getStatusEmoji(status) {
switch (status) {
case 'healthy': return 'ā
';
case 'degraded': return 'ā ļø';
case 'unhealthy': return 'ā';
default: return 'ā';
}
}
function formatUptime(seconds) {
const days = Math.floor(seconds / 86400);
const hours = Math.floor((seconds % 86400) / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
if (days > 0) {
return `${days}d ${hours}h ${minutes}m`;
}
else if (hours > 0) {
return `${hours}h ${minutes}m`;
}
else {
return `${minutes}m`;
}
}
export const healthStatusToolName = 'health_status';
export const healthStatusToolDescription = 'Check system health including OpenRouter connectivity, database status, and error statistics';
//# sourceMappingURL=health-status.js.map