/** * Model Status Widget * Displays AI model status with health indicators */ import { modelsClient } from '../core/models-client.js'; /** * Get models page path (works from any location) */ function getModelsPagePath() { const basePath = window.location.pathname.includes('/static/') ? window.location.pathname.split('/static/')[0] + '/static' : '/static'; return `${basePath}/pages/models/index.html`; } /** * Render model status widget */ export async function renderModelStatusWidget(containerId) { const container = document.getElementById(containerId); if (!container) { console.error(`Container ${containerId} not found`); return; } // Show loading state container.innerHTML = `

Loading AI models status...

`; try { // Fetch models summary const summary = await modelsClient.getModelsSummary(); if (!summary.ok) { container.innerHTML = `

⚠️ Models Status

${summary.error || 'Failed to load models'}

Using fallback sentiment analysis

`; return; } const stats = summary.summary; // Render widget container.innerHTML = `

🤖 AI Models

${stats.hf_mode}
${stats.total_models}
Total
${stats.loaded_models}
Loaded
${stats.failed_models}
Failed

Models by Category

`; // Render categories renderCategories(`${containerId}-categories`, summary.categories); } catch (error) { console.error('Error rendering model status widget:', error); container.innerHTML = `

⚠️ Models Status

Failed to load: ${error.message}

`; } } /** * Render categories */ function renderCategories(containerId, categories) { const container = document.getElementById(containerId); if (!container || !categories) return; let html = ''; for (const [category, models] of Object.entries(categories)) { const loaded = models.filter(m => m.loaded).length; const healthy = models.filter(m => m.status === 'healthy').length; html += `
${formatCategoryName(category)} ${loaded}/${models.length}
`; } container.innerHTML = html; } /** * Format category name */ function formatCategoryName(category) { const names = { 'sentiment_crypto': 'Crypto Sentiment', 'sentiment_social': 'Social Sentiment', 'sentiment_financial': 'Financial Sentiment', 'sentiment_news': 'News Sentiment', 'analysis_generation': 'AI Analysis', 'trading_signal': 'Trading Signals', 'summarization': 'Summarization', 'legacy': 'Legacy' }; return names[category] || category; } /** * CSS for model status widget (to be injected) */ export const modelStatusWidgetCSS = ` .model-status-widget { background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 16px; padding: 1.5rem; } .model-status-widget.loading { text-align: center; padding: 2rem; } .model-status-widget.error { border-color: rgba(239, 68, 68, 0.3); background: rgba(239, 68, 68, 0.1); } .widget-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; } .widget-header h3 { margin: 0; font-size: 1.25rem; font-weight: 600; } .hf-mode-badge { padding: 0.25rem 0.75rem; background: rgba(45, 212, 191, 0.2); border: 1px solid rgba(45, 212, 191, 0.3); border-radius: 999px; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; } .stats-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1.5rem; } .stat-card { text-align: center; padding: 1rem; background: rgba(0, 0, 0, 0.3); border-radius: 12px; } .stat-card.loaded { background: rgba(34, 197, 94, 0.1); border: 1px solid rgba(34, 197, 94, 0.2); } .stat-card.warning { background: rgba(239, 68, 68, 0.1); border: 1px solid rgba(239, 68, 68, 0.2); } .stat-value { font-size: 2rem; font-weight: 700; color: #2dd4bf; } .stat-label { font-size: 0.875rem; color: rgba(255, 255, 255, 0.6); margin-top: 0.25rem; } .categories-section h4 { font-size: 1rem; font-weight: 600; margin-bottom: 1rem; color: rgba(255, 255, 255, 0.8); } .categories-list { display: flex; flex-direction: column; gap: 0.75rem; } .category-item { padding: 0.75rem; background: rgba(0, 0, 0, 0.2); border-radius: 8px; } .category-header { display: flex; justify-content: space-between; margin-bottom: 0.5rem; } .category-name { font-weight: 500; } .category-count { color: rgba(255, 255, 255, 0.6); font-size: 0.875rem; } .category-progress { height: 4px; background: rgba(255, 255, 255, 0.1); border-radius: 999px; overflow: hidden; } .progress-fill { height: 100%; background: linear-gradient(90deg, #2dd4bf, #818cf8); transition: width 0.3s; } .widget-footer { margin-top: 1.5rem; text-align: center; } .btn-view-all { padding: 0.75rem 1.5rem; background: linear-gradient(135deg, #2dd4bf, #818cf8); border: none; border-radius: 8px; color: white; font-weight: 600; cursor: pointer; transition: transform 0.2s; } .btn-view-all:hover { transform: translateY(-2px); } .error-message { color: #fca5a5; margin: 0.5rem 0; } .fallback-note { color: rgba(255, 255, 255, 0.6); font-size: 0.875rem; margin-top: 0.5rem; } `;