// Crypto Intelligence Hub - Main JavaScript // Global state const AppState = { currentTab: 'dashboard', data: {}, charts: {} }; // Initialize app document.addEventListener('DOMContentLoaded', () => { initTabs(); checkAPIStatus(); loadDashboard(); // Auto-refresh every 30 seconds setInterval(() => { if (AppState.currentTab === 'dashboard') { loadDashboard(); } }, 30000); // Listen for trading pairs loaded event document.addEventListener('tradingPairsLoaded', function(e) { console.log('Trading pairs loaded:', e.detail.pairs.length); initTradingPairSelectors(); }); }); // Initialize trading pair selectors after pairs are loaded function initTradingPairSelectors() { // Initialize asset symbol selector const assetSymbolContainer = document.getElementById('asset-symbol-container'); if (assetSymbolContainer && window.TradingPairsLoader) { const pairs = window.TradingPairsLoader.getTradingPairs(); if (pairs && pairs.length > 0) { assetSymbolContainer.innerHTML = window.TradingPairsLoader.createTradingPairCombobox( 'asset-symbol', 'Select or type trading pair', 'BTCUSDT' ); } } } // Tab Navigation function initTabs() { const tabButtons = document.querySelectorAll('.tab-btn'); const tabContents = document.querySelectorAll('.tab-content'); tabButtons.forEach(btn => { btn.addEventListener('click', () => { const tabId = btn.dataset.tab; // Update buttons tabButtons.forEach(b => b.classList.remove('active')); btn.classList.add('active'); // Update content tabContents.forEach(c => c.classList.remove('active')); document.getElementById(`tab-${tabId}`).classList.add('active'); AppState.currentTab = tabId; // Load tab data loadTabData(tabId); }); }); } // Load tab-specific data - synchronized with HTML tabs function loadTabData(tabId) { switch(tabId) { case 'dashboard': loadDashboard(); break; case 'market': loadMarketData(); break; case 'models': loadModels(); break; case 'sentiment': loadSentimentModels(); // Populate model dropdown loadSentimentHistory(); // Load history from localStorage break; case 'ai-analyst': // AI analyst tab is interactive, no auto-load needed break; case 'trading-assistant': // Trading assistant tab is interactive, no auto-load needed break; case 'news': loadNews(); break; case 'providers': loadProviders(); break; case 'diagnostics': loadDiagnostics(); break; case 'api-explorer': loadAPIEndpoints(); break; default: console.log('No specific loader for tab:', tabId); } } // Load available API endpoints function loadAPIEndpoints() { const endpointSelect = document.getElementById('api-endpoint'); if (!endpointSelect) return; // Add more endpoints const endpoints = [ { value: '/api/health', text: 'GET /api/health - Health Check' }, { value: '/api/status', text: 'GET /api/status - System Status' }, { value: '/api/stats', text: 'GET /api/stats - Statistics' }, { value: '/api/market', text: 'GET /api/market - Market Data' }, { value: '/api/trending', text: 'GET /api/trending - Trending Coins' }, { value: '/api/sentiment', text: 'GET /api/sentiment - Fear & Greed Index' }, { value: '/api/news', text: 'GET /api/news - Latest News' }, { value: '/api/news/latest', text: 'GET /api/news/latest - Latest News (Alt)' }, { value: '/api/resources', text: 'GET /api/resources - Resources Summary' }, { value: '/api/providers', text: 'GET /api/providers - List Providers' }, { value: '/api/models/list', text: 'GET /api/models/list - List Models' }, { value: '/api/models/status', text: 'GET /api/models/status - Models Status' }, { value: '/api/models/data/stats', text: 'GET /api/models/data/stats - Models Statistics' }, { value: '/api/analyze/text', text: 'POST /api/analyze/text - AI Text Analysis' }, { value: '/api/trading/decision', text: 'POST /api/trading/decision - Trading Signal' }, { value: '/api/sentiment/analyze', text: 'POST /api/sentiment/analyze - Analyze Sentiment' }, { value: '/api/logs/recent', text: 'GET /api/logs/recent - Recent Logs' }, { value: '/api/logs/errors', text: 'GET /api/logs/errors - Error Logs' }, { value: '/api/diagnostics/last', text: 'GET /api/diagnostics/last - Last Diagnostics' }, { value: '/api/hf/models', text: 'GET /api/hf/models - HF Models' }, { value: '/api/hf/health', text: 'GET /api/hf/health - HF Health' } ]; // Clear existing options except first one endpointSelect.innerHTML = ''; endpoints.forEach(ep => { const option = document.createElement('option'); option.value = ep.value; option.textContent = ep.text; endpointSelect.appendChild(option); }); } // Check API Status async function checkAPIStatus() { try { const response = await fetch('/health'); const data = await response.json(); const statusBadge = document.getElementById('api-status'); if (data.status === 'healthy') { statusBadge.className = 'status-badge'; statusBadge.innerHTML = '✅ System Active'; } else { statusBadge.className = 'status-badge error'; statusBadge.innerHTML = '❌ Error'; } } catch (error) { const statusBadge = document.getElementById('api-status'); statusBadge.className = 'status-badge error'; statusBadge.innerHTML = '❌ Connection Failed'; } } // Load Dashboard async function loadDashboard() { // Show loading state const statsElements = [ 'stat-total-resources', 'stat-free-resources', 'stat-models', 'stat-providers' ]; statsElements.forEach(id => { const el = document.getElementById(id); if (el) el.textContent = '...'; }); const systemStatusDiv = document.getElementById('system-status'); if (systemStatusDiv) { systemStatusDiv.innerHTML = '
Loading system status...
'; } try { // Load resources - use enhanced API client with caching const resourcesData = await window.apiClient.get('/api/resources', { cacheDuration: 30000 }); if (resourcesData.success && resourcesData.summary) { document.getElementById('stat-total-resources').textContent = resourcesData.summary.total_resources || 0; document.getElementById('stat-free-resources').textContent = resourcesData.summary.free_resources || 0; document.getElementById('stat-models').textContent = resourcesData.summary.models_available || 0; } // Load system status - use enhanced API client try { const statusData = await window.apiClient.get('/api/status', { cacheDuration: 15000 }); document.getElementById('stat-providers').textContent = statusData.total_apis || statusData.total_providers || 0; // Display system status const systemStatusDiv = document.getElementById('system-status'); const healthStatus = statusData.system_health || 'unknown'; const healthClass = healthStatus === 'healthy' ? 'alert-success' : healthStatus === 'degraded' ? 'alert-warning' : 'alert-error'; systemStatusDiv.innerHTML = `
System Status: ${healthStatus}
Online APIs: ${statusData.online || 0}
Degraded APIs: ${statusData.degraded || 0}
Offline APIs: ${statusData.offline || 0}
Avg Response Time: ${statusData.avg_response_time_ms || 0}ms
Last Update: ${new Date(statusData.last_update || Date.now()).toLocaleString('en-US')}
`; } catch (statusError) { console.warn('Status endpoint not available:', statusError); document.getElementById('stat-providers').textContent = '-'; } // Load categories chart if (resourcesData.success && resourcesData.summary.categories) { createCategoriesChart(resourcesData.summary.categories); } } catch (error) { console.error('Error loading dashboard:', error); showError('Failed to load dashboard. Please check the backend is running.'); // Show error state const systemStatusDiv = document.getElementById('system-status'); if (systemStatusDiv) { systemStatusDiv.innerHTML = '
Failed to load dashboard data. Please refresh or check backend status.
'; } } } // Create Categories Chart - Enhanced with better visuals function createCategoriesChart(categories) { const ctx = document.getElementById('categories-chart'); if (!ctx) return; // Check if Chart.js is loaded if (typeof Chart === 'undefined') { console.error('Chart.js is not loaded'); ctx.parentElement.innerHTML = '

Chart library not loaded

'; return; } if (AppState.charts.categories) { AppState.charts.categories.destroy(); } // Enhanced gradient colors const colors = [ 'rgba(102, 126, 234, 0.8)', 'rgba(16, 185, 129, 0.8)', 'rgba(245, 158, 11, 0.8)', 'rgba(59, 130, 246, 0.8)', 'rgba(240, 147, 251, 0.8)', 'rgba(255, 107, 157, 0.8)' ]; const borderColors = [ 'rgba(102, 126, 234, 1)', 'rgba(16, 185, 129, 1)', 'rgba(245, 158, 11, 1)', 'rgba(59, 130, 246, 1)', 'rgba(240, 147, 251, 1)', 'rgba(255, 107, 157, 1)' ]; AppState.charts.categories = new Chart(ctx, { type: 'bar', data: { labels: Object.keys(categories), datasets: [{ label: 'Total Resources', data: Object.values(categories), backgroundColor: colors, borderColor: borderColors, borderWidth: 2, borderRadius: 8, hoverBackgroundColor: borderColors }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(17, 24, 39, 0.95)', backdropFilter: 'blur(10px)', padding: 12, titleColor: '#f9fafb', bodyColor: '#f9fafb', borderColor: 'rgba(102, 126, 234, 0.5)', borderWidth: 1, cornerRadius: 8, displayColors: true, callbacks: { title: function(context) { return context[0].label; }, label: function(context) { return 'Resources: ' + context.parsed.y; } } } }, scales: { y: { beginAtZero: true, grid: { color: 'rgba(255, 255, 255, 0.05)', drawBorder: false }, ticks: { color: '#9ca3af', font: { size: 12 } } }, x: { grid: { display: false }, ticks: { color: '#9ca3af', font: { size: 12 } } } }, animation: { duration: 1000, easing: 'easeInOutQuart' } } }); } // Load Market Data async function loadMarketData() { // Show loading states const marketDiv = document.getElementById('market-data'); const trendingDiv = document.getElementById('trending-coins'); const fgDiv = document.getElementById('fear-greed'); if (marketDiv) marketDiv.innerHTML = '
Loading market data...
'; if (trendingDiv) trendingDiv.innerHTML = '
Loading trending coins...
'; if (fgDiv) fgDiv.innerHTML = '
Loading Fear & Greed Index...
'; try { // Use enhanced API client with caching const data = await window.apiClient.get('/api/market', { cacheDuration: 60000 // Cache for 1 minute }); if (data.cryptocurrencies && data.cryptocurrencies.length > 0) { const marketDiv = document.getElementById('market-data'); marketDiv.innerHTML = `
${data.cryptocurrencies.map(coin => ` `).join('')}
# Name Price (USD) 24h Change 24h Volume Market Cap
${coin.rank || '-'} ${coin.image ? `` : ''} ${coin.symbol} ${coin.name} $${formatNumber(coin.price)} ${coin.change_24h >= 0 ? '↑' : '↓'} ${Math.abs(coin.change_24h || 0).toFixed(2)}% $${formatNumber(coin.volume_24h)} $${formatNumber(coin.market_cap)}
${data.total_market_cap ? `
Total Market Cap: $${formatNumber(data.total_market_cap)} | BTC Dominance: ${(data.btc_dominance || 0).toFixed(2)}%
` : ''} `; } else { document.getElementById('market-data').innerHTML = '
No data found
'; } // Load trending - use enhanced API client try { const trendingData = await window.apiClient.get('/api/trending', { cacheDuration: 60000 }); if (trendingData.trending && trendingData.trending.length > 0) { const trendingDiv = document.getElementById('trending-coins'); trendingDiv.innerHTML = `
${trendingData.trending.map((coin, index) => `
#${index + 1}
${coin.symbol || coin.id} - ${coin.name || 'Unknown'} ${coin.market_cap_rank ? `
Market Cap Rank: ${coin.market_cap_rank}
` : ''}
${coin.score ? coin.score.toFixed(2) : 'N/A'}
`).join('')}
`; } else { document.getElementById('trending-coins').innerHTML = '
No data found
'; } } catch (trendingError) { console.warn('Trending endpoint error:', trendingError); document.getElementById('trending-coins').innerHTML = '
Error loading trending coins
'; } // Load Fear & Greed - use enhanced API client try { const sentimentData = await window.apiClient.get('/api/sentiment', { cacheDuration: 60000 }); if (sentimentData.fear_greed_index !== undefined) { const fgDiv = document.getElementById('fear-greed'); const fgValue = sentimentData.fear_greed_index; const fgLabel = sentimentData.fear_greed_label || 'Unknown'; // Determine color based on value let fgColor = 'var(--warning)'; if (fgValue >= 75) fgColor = 'var(--success)'; else if (fgValue >= 50) fgColor = 'var(--info)'; else if (fgValue >= 25) fgColor = 'var(--warning)'; else fgColor = 'var(--danger)'; fgDiv.innerHTML = `
${fgValue}
${fgLabel}
Market Fear & Greed Index
${sentimentData.timestamp ? `
Last Update: ${new Date(sentimentData.timestamp).toLocaleString('en-US')}
` : ''}
`; } else { document.getElementById('fear-greed').innerHTML = '
No data found
'; } } catch (sentimentError) { console.warn('Sentiment endpoint error:', sentimentError); document.getElementById('fear-greed').innerHTML = '
Error loading Fear & Greed Index
'; } } catch (error) { console.error('Error loading market data:', error); showError('Failed to load market data. Please check the backend connection.'); const marketDiv = document.getElementById('market-data'); if (marketDiv) { marketDiv.innerHTML = '
Failed to load market data. The backend may be offline or the CoinGecko API may be unavailable.
'; } } } // Format large numbers function formatNumber(num) { if (!num) return '0'; if (num >= 1e12) return (num / 1e12).toFixed(2) + 'T'; if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B'; if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M'; if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K'; return num.toLocaleString('en-US', { maximumFractionDigits: 2 }); } // Load Models async function loadModels() { // Show loading state const modelsListDiv = document.getElementById('models-list'); const statusDiv = document.getElementById('models-status'); if (modelsListDiv) modelsListDiv.innerHTML = '
Loading models...
'; if (statusDiv) statusDiv.innerHTML = '
Loading status...
'; try { const response = await fetch('/api/models/list'); const data = await response.json(); const models = data.models || data || []; if (models.length > 0) { const modelsListDiv = document.getElementById('models-list'); modelsListDiv.innerHTML = `
${models.map(model => { const status = model.status || 'unknown'; const isAvailable = status === 'available' || status === 'loaded'; const statusColor = isAvailable ? 'var(--success)' : 'var(--danger)'; const statusBg = isAvailable ? 'rgba(16, 185, 129, 0.2)' : 'rgba(239, 68, 68, 0.2)'; return `

${model.model_id || model.name || 'Unknown'}

${model.task || model.category || 'N/A'}
${model.category ? `
Category: ${model.category}
` : ''} ${model.requires_auth !== undefined ? `
${model.requires_auth ? '🔐 Requires Authentication' : '🔓 No Auth Required'}
` : ''}
${isAvailable ? '✅ Available' : '❌ Unavailable'}
${model.key ? `
Key: ${model.key}
` : ''}
`; }).join('')}
`; } else { document.getElementById('models-list').innerHTML = '
No models found
'; } // Load models status try { const statusRes = await fetch('/api/models/status'); const statusData = await statusRes.json(); const statusDiv = document.getElementById('models-status'); if (statusDiv) { // Use honest status from backend const status = statusData.status || 'unknown'; const statusMessage = statusData.status_message || 'Unknown status'; const hfMode = statusData.hf_mode || 'unknown'; const modelsLoaded = statusData.models_loaded || statusData.pipelines_loaded || 0; const modelsFailed = statusData.models_failed || 0; // Determine status class based on honest status let statusClass = 'alert-warning'; if (status === 'ok') statusClass = 'alert-success'; else if (status === 'disabled' || status === 'transformers_unavailable') statusClass = 'alert-error'; else if (status === 'partial') statusClass = 'alert-warning'; statusDiv.innerHTML = `
Status: ${statusMessage}
HF Mode: ${hfMode}
Models Loaded: ${modelsLoaded}
Models Failed: ${modelsFailed}
${statusData.transformers_available !== undefined ? `Transformers Available: ${statusData.transformers_available ? '✅ Yes' : '❌ No'}
` : ''} ${statusData.initialized !== undefined ? `Initialized: ${statusData.initialized ? '✅ Yes' : '❌ No'}
` : ''} ${hfMode === 'off' ? `
Note: HF models are disabled (HF_MODE=off). To enable them, set HF_MODE=public or HF_MODE=auth in the environment.
` : ''} ${hfMode !== 'off' && modelsLoaded === 0 && modelsFailed > 0 ? `
Warning: No models could be loaded. ${modelsFailed} model(s) failed. Check model IDs or HF access.
` : ''}
`; } } catch (statusError) { console.warn('Models status endpoint error:', statusError); } // Load models stats try { const statsRes = await fetch('/api/models/data/stats'); const statsData = await statsRes.json(); if (statsData.success && statsData.statistics) { const statsDiv = document.getElementById('models-stats'); statsDiv.innerHTML = `
${statsData.statistics.total_analyses || 0}
Total Analyses
${statsData.statistics.unique_symbols || 0}
Unique Symbols
${statsData.statistics.most_used_model ? `
${statsData.statistics.most_used_model}
Most Used Model
` : ''}
`; } } catch (statsError) { console.warn('Models stats endpoint error:', statsError); } } catch (error) { console.error('Error loading models:', error); showError('Failed to load models. Please check the backend connection.'); const modelsListDiv = document.getElementById('models-list'); if (modelsListDiv) { modelsListDiv.innerHTML = '
Failed to load models. Check backend status.
'; } } } // Initialize Models async function initializeModels() { try { const response = await fetch('/api/models/initialize', { method: 'POST' }); const data = await response.json(); if (data.success) { showSuccess('Models loaded successfully'); loadModels(); } else { showError(data.error || 'Error loading models'); } } catch (error) { showError('Error loading models: ' + error.message); } } // Load Sentiment Models - updated to populate dropdown for sentiment analysis async function loadSentimentModels() { try { const response = await fetch('/api/models/list'); const data = await response.json(); const models = data.models || data || []; const select = document.getElementById('sentiment-model'); if (!select) return; select.innerHTML = ''; // Filter and add models - only sentiment and generation models models.filter(m => { const category = m.category || ''; const task = m.task || ''; // Include sentiment models and generation/trading models return category.includes('sentiment') || category.includes('generation') || category.includes('trading') || task.includes('classification') || task.includes('generation'); }).forEach(model => { const option = document.createElement('option'); const modelKey = model.key || model.id; const modelName = model.model_id || model.name || modelKey; const desc = model.description || model.category || ''; option.value = modelKey; // Show model name with short description const displayName = modelName.length > 40 ? modelName.substring(0, 37) + '...' : modelName; option.textContent = displayName; option.title = desc; // Full description on hover select.appendChild(option); }); // If no models available, show message if (select.options.length === 1) { const option = document.createElement('option'); option.value = ''; option.textContent = 'No models available - will use fallback'; option.disabled = true; select.appendChild(option); } console.log(`Loaded ${select.options.length - 1} sentiment models into dropdown`); } catch (error) { console.error('Error loading sentiment models:', error); const select = document.getElementById('sentiment-model'); if (select) { select.innerHTML = ''; } } } // Analyze Global Market Sentiment async function analyzeGlobalSentiment() { const resultDiv = document.getElementById('global-sentiment-result'); resultDiv.innerHTML = '
Analyzing market sentiment...
'; try { // Use market text analysis with sample market-related text const marketText = "Cryptocurrency market analysis: Bitcoin, Ethereum, and major altcoins showing mixed signals. Market sentiment analysis required."; const response = await fetch('/api/sentiment/analyze', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: marketText, mode: 'crypto' }) }); const data = await response.json(); if (!data.available) { resultDiv.innerHTML = `
⚠️ Models Not Available: ${data.error || 'AI models are currently unavailable'}
`; return; } const sentiment = data.sentiment || 'neutral'; const confidence = data.confidence || 0; const sentimentEmoji = sentiment === 'bullish' ? '📈' : sentiment === 'bearish' ? '📉' : '➡️'; const sentimentColor = sentiment === 'bullish' ? 'var(--success)' : sentiment === 'bearish' ? 'var(--danger)' : 'var(--text-secondary)'; resultDiv.innerHTML = `

Global Market Sentiment

${sentimentEmoji}
${sentiment === 'bullish' ? 'Bullish' : sentiment === 'bearish' ? 'Bearish' : 'Neutral'}
Confidence: ${(confidence * 100).toFixed(1)}%
Details:
This analysis is based on AI models.
`; } catch (error) { console.error('Global sentiment analysis error:', error); resultDiv.innerHTML = `
Analysis Error: ${error.message}
`; showError('Error analyzing market sentiment'); } } // Analyze Asset Sentiment async function analyzeAssetSentiment() { const symbol = document.getElementById('asset-symbol').value.trim().toUpperCase(); const text = document.getElementById('asset-sentiment-text').value.trim(); if (!symbol) { showError('Please enter a cryptocurrency symbol'); return; } const resultDiv = document.getElementById('asset-sentiment-result'); resultDiv.innerHTML = '
Analyzing...
'; try { // Use provided text or default text with symbol const analysisText = text || `${symbol} market analysis and sentiment`; const response = await fetch('/api/sentiment/analyze', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: analysisText, mode: 'crypto', symbol: symbol }) }); const data = await response.json(); if (!data.available) { resultDiv.innerHTML = `
⚠️ Models Not Available: ${data.error || 'AI models are currently unavailable'}
`; return; } const sentiment = data.sentiment || 'neutral'; const confidence = data.confidence || 0; const sentimentEmoji = sentiment === 'bullish' ? '📈' : sentiment === 'bearish' ? '📉' : '➡️'; const sentimentColor = sentiment === 'bullish' ? 'var(--success)' : sentiment === 'bearish' ? 'var(--danger)' : 'var(--text-secondary)'; resultDiv.innerHTML = `

Sentiment Analysis Result for ${symbol}

Sentiment: ${sentimentEmoji} ${sentiment === 'bullish' ? 'Bullish' : sentiment === 'bearish' ? 'Bearish' : 'Neutral'}
Confidence: ${(confidence * 100).toFixed(2)}%
${text ? `
Analyzed Text:
"${text.substring(0, 200)}${text.length > 200 ? '...' : ''}"
` : ''}
`; } catch (error) { console.error('Asset sentiment analysis error:', error); resultDiv.innerHTML = `
Analysis Error: ${error.message}
`; showError('Error analyzing asset sentiment'); } } // Analyze News Sentiment async function analyzeNewsSentiment() { const title = document.getElementById('news-title').value.trim(); const content = document.getElementById('news-content').value.trim(); if (!title && !content) { showError('Please enter news title or content'); return; } const resultDiv = document.getElementById('news-sentiment-result'); resultDiv.innerHTML = '
Analyzing...
'; try { const response = await fetch('/api/news/analyze', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: title, content: content, description: content }) }); const data = await response.json(); if (!data.available) { resultDiv.innerHTML = `
⚠️ Models Not Available: ${data.news?.error || data.error || 'AI models are currently unavailable'}
`; return; } const newsData = data.news || {}; const sentiment = newsData.sentiment || 'neutral'; const confidence = newsData.confidence || 0; const sentimentEmoji = sentiment === 'bullish' || sentiment === 'positive' ? '📈' : sentiment === 'bearish' || sentiment === 'negative' ? '📉' : '➡️'; const sentimentColor = sentiment === 'bullish' || sentiment === 'positive' ? 'var(--success)' : sentiment === 'bearish' || sentiment === 'negative' ? 'var(--danger)' : 'var(--text-secondary)'; resultDiv.innerHTML = `

News Sentiment Analysis Result

Title: ${title || 'No title'}
Sentiment: ${sentimentEmoji} ${sentiment === 'bullish' || sentiment === 'positive' ? 'Positive' : sentiment === 'bearish' || sentiment === 'negative' ? 'Negative' : 'Neutral'}
Confidence: ${(confidence * 100).toFixed(2)}%
`; } catch (error) { console.error('News sentiment analysis error:', error); resultDiv.innerHTML = `
Analysis Error: ${error.message}
`; showError('Error analyzing news sentiment'); } } // Summarize News async function summarizeNews() { const title = document.getElementById('summary-news-title').value.trim(); const content = document.getElementById('summary-news-content').value.trim(); if (!title && !content) { showError('Please enter news title or content'); return; } const resultDiv = document.getElementById('news-summary-result'); resultDiv.innerHTML = '
Generating summary...
'; try { const response = await fetch('/api/news/summarize', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: title, content: content }) }); const data = await response.json(); if (!data.success) { resultDiv.innerHTML = `
❌ Summarization Failed: ${data.error || 'Failed to generate summary'}
`; return; } const summary = data.summary || ''; const model = data.model || 'Unknown'; const isHFModel = data.available !== false && model !== 'fallback_extractive'; const modelDisplay = isHFModel ? model : `${model} (Fallback)`; // Create collapsible card with summary resultDiv.innerHTML = `

📝 News Summary

${title ? `
Title: ${title}
` : ''}
Summary:

${summary}

`; // Store summary for clipboard window.lastSummary = summary; } catch (error) { console.error('News summarization error:', error); resultDiv.innerHTML = `
Summarization Error: ${error.message}
`; showError('Error summarizing news'); } } // Toggle summary details function toggleSummaryDetails() { const details = document.getElementById('summary-details'); const icon = document.getElementById('toggle-summary-icon'); if (details.style.display === 'none') { details.style.display = 'block'; icon.textContent = '▲'; } else { details.style.display = 'none'; icon.textContent = '▼'; } } // Copy summary to clipboard async function copySummaryToClipboard() { if (!window.lastSummary) { showError('No summary to copy'); return; } try { await navigator.clipboard.writeText(window.lastSummary); showSuccess('Summary copied to clipboard!'); } catch (error) { console.error('Failed to copy:', error); showError('Failed to copy summary'); } } // Clear summary form function clearSummaryForm() { document.getElementById('summary-news-title').value = ''; document.getElementById('summary-news-content').value = ''; document.getElementById('news-summary-result').innerHTML = ''; window.lastSummary = null; } // Analyze Sentiment (updated with model_key support) async function analyzeSentiment() { const text = document.getElementById('sentiment-text').value; const mode = document.getElementById('sentiment-mode').value; const modelKey = document.getElementById('sentiment-model').value; if (!text.trim()) { showError('Please enter text to analyze'); return; } const resultDiv = document.getElementById('sentiment-result'); resultDiv.innerHTML = '
Analyzing...
'; try { let response; // Build request body const requestBody = { text: text, mode: mode }; // Add model_key if specific model selected if (modelKey && modelKey !== '') { requestBody.model_key = modelKey; } // Use the sentiment endpoint with mode and optional model_key response = await fetch('/api/sentiment', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(requestBody) }); const data = await response.json(); if (!data.available) { resultDiv.innerHTML = `
⚠️ Models Not Available: ${data.error || 'AI models are currently unavailable'}
`; return; } const label = data.sentiment || 'neutral'; const confidence = data.confidence || 0; const result = data.result || {}; // Determine sentiment emoji and color const sentimentEmoji = label === 'bullish' || label === 'positive' ? '📈' : label === 'bearish' || label === 'negative' ? '📉' : '➡️'; const sentimentColor = label === 'bullish' || label === 'positive' ? 'var(--success)' : label === 'bearish' || label === 'negative' ? 'var(--danger)' : 'var(--text-secondary)'; resultDiv.innerHTML = `

Sentiment Analysis Result

Sentiment: ${sentimentEmoji} ${label === 'bullish' || label === 'positive' ? 'Bullish/Positive' : label === 'bearish' || label === 'negative' ? 'Bearish/Negative' : 'Neutral'}
Confidence: ${(confidence * 100).toFixed(2)}%
Analysis Type: ${mode}
Analyzed Text:
"${text.substring(0, 200)}${text.length > 200 ? '...' : ''}"
`; // Save to history (localStorage) saveSentimentToHistory({ text: text.substring(0, 100), label: label, confidence: confidence, model: mode, timestamp: new Date().toISOString() }); // Reload history loadSentimentHistory(); } catch (error) { console.error('Sentiment analysis error:', error); resultDiv.innerHTML = `
Analysis Error: ${error.message}
`; showError('Error analyzing sentiment'); } } // Save sentiment to history function saveSentimentToHistory(analysis) { try { const history = JSON.parse(localStorage.getItem('sentiment_history') || '[]'); history.unshift(analysis); // Keep only last 50 if (history.length > 50) history = history.slice(0, 50); localStorage.setItem('sentiment_history', JSON.stringify(history)); } catch (e) { console.warn('Could not save to history:', e); } } // Load sentiment history function loadSentimentHistory() { try { const history = JSON.parse(localStorage.getItem('sentiment_history') || '[]'); const historyDiv = document.getElementById('sentiment-history'); if (history.length === 0) { historyDiv.innerHTML = '
No history available
'; return; } historyDiv.innerHTML = `
${history.slice(0, 20).map(item => { const sentimentEmoji = item.label.toUpperCase().includes('POSITIVE') || item.label.toUpperCase().includes('BULLISH') ? '📈' : item.label.toUpperCase().includes('NEGATIVE') || item.label.toUpperCase().includes('BEARISH') ? '📉' : '➡️'; return `
${sentimentEmoji} ${item.label} ${new Date(item.timestamp).toLocaleString('en-US')}
${item.text}
Confidence: ${(item.confidence * 100).toFixed(0)}% | Model: ${item.model}
`; }).join('')}
`; } catch (e) { console.warn('Could not load history:', e); } } // Load News async function loadNews() { // Show loading state const newsDiv = document.getElementById('news-list'); if (newsDiv) { newsDiv.innerHTML = '
Loading news...
'; } try { // Try /api/news/latest first, fallback to /api/news let response; try { response = await fetch('/api/news/latest?limit=20'); } catch { response = await fetch('/api/news?limit=20'); } const data = await response.json(); const newsItems = data.news || data.data || []; if (newsItems.length > 0) { const newsDiv = document.getElementById('news-list'); newsDiv.innerHTML = `
${newsItems.map((item, index) => { const sentiment = item.sentiment_label || item.sentiment || 'neutral'; const sentimentLower = sentiment.toLowerCase(); const sentimentConfidence = item.sentiment_confidence || 0; // Determine sentiment styling let sentimentColor, sentimentBg, sentimentEmoji, sentimentLabel; if (sentimentLower.includes('positive') || sentimentLower.includes('bullish')) { sentimentColor = '#10b981'; sentimentBg = 'rgba(16, 185, 129, 0.15)'; sentimentEmoji = '📈'; sentimentLabel = 'Bullish'; } else if (sentimentLower.includes('negative') || sentimentLower.includes('bearish')) { sentimentColor = '#ef4444'; sentimentBg = 'rgba(239, 68, 68, 0.15)'; sentimentEmoji = '📉'; sentimentLabel = 'Bearish'; } else { sentimentColor = '#6b7280'; sentimentBg = 'rgba(107, 114, 128, 0.15)'; sentimentEmoji = '➡️'; sentimentLabel = 'Neutral'; } const publishedDate = item.published_date || item.published_at || item.analyzed_at; const publishedTime = publishedDate ? new Date(publishedDate).toLocaleString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : 'Unknown date'; const content = item.content || item.description || ''; const contentPreview = content.length > 250 ? content.substring(0, 250) + '...' : content; return `

${item.title || 'No title'}

${sentimentEmoji} ${sentimentLabel}
${contentPreview ? `

${contentPreview}

` : ''}
📰 ${item.source || 'Unknown Source'}
${sentimentConfidence > 0 ? `
🎯 ${(sentimentConfidence * 100).toFixed(0)}% confidence
` : ''}
🕒 ${publishedTime}
${item.related_symbols && Array.isArray(item.related_symbols) && item.related_symbols.length > 0 ? `
💰
${item.related_symbols.slice(0, 3).map(symbol => ` ${symbol} `).join('')} ${item.related_symbols.length > 3 ? `+${item.related_symbols.length - 3}` : ''}
` : ''}
${item.url ? ` Read More → ` : ''}
`; }).join('')}
Showing ${newsItems.length} article${newsItems.length !== 1 ? 's' : ''} • Last updated: ${new Date().toLocaleTimeString('en-US')}
`; } else { document.getElementById('news-list').innerHTML = `
📰
No news articles found
News articles will appear here once they are analyzed and stored in the database.
`; } } catch (error) { console.error('Error loading news:', error); showError('Error loading news'); document.getElementById('news-list').innerHTML = `
Error loading news
${error.message || 'Failed to fetch news articles. Please try again later.'}
`; } } // Load Providers async function loadProviders() { // Show loading state const providersDiv = document.getElementById('providers-list'); if (providersDiv) { providersDiv.innerHTML = '
Loading providers...
'; } try { // Load providers and auto-discovery health summary in parallel const [providersRes, healthRes] = await Promise.all([ fetch('/api/providers'), fetch('/api/providers/health-summary').catch(() => null) // Optional ]); const providersData = await providersRes.json(); const providers = providersData.providers || providersData || []; // Update providers list const providersDiv = document.getElementById('providers-list'); if (providersDiv) { if (providers.length > 0) { providersDiv.innerHTML = `
${providers.map(provider => { const status = provider.status || 'unknown'; const statusConfig = { 'VALID': { color: 'var(--success)', bg: 'rgba(16, 185, 129, 0.2)', text: '✅ Valid' }, 'validated': { color: 'var(--success)', bg: 'rgba(16, 185, 129, 0.2)', text: '✅ Valid' }, 'available': { color: 'var(--success)', bg: 'rgba(16, 185, 129, 0.2)', text: '✅ Available' }, 'online': { color: 'var(--success)', bg: 'rgba(16, 185, 129, 0.2)', text: '✅ Online' }, 'CONDITIONALLY_AVAILABLE': { color: 'var(--warning)', bg: 'rgba(245, 158, 11, 0.2)', text: '⚠️ Conditional' }, 'INVALID': { color: 'var(--danger)', bg: 'rgba(239, 68, 68, 0.2)', text: '❌ Invalid' }, 'unvalidated': { color: 'var(--warning)', bg: 'rgba(245, 158, 11, 0.2)', text: '⚠️ Unvalidated' }, 'not_loaded': { color: 'var(--warning)', bg: 'rgba(245, 158, 11, 0.2)', text: '⚠️ Not Loaded' }, 'offline': { color: 'var(--danger)', bg: 'rgba(239, 68, 68, 0.2)', text: '❌ Offline' }, 'degraded': { color: 'var(--warning)', bg: 'rgba(245, 158, 11, 0.2)', text: '⚠️ Degraded' } }; const statusInfo = statusConfig[status] || { color: 'var(--text-secondary)', bg: 'rgba(156, 163, 175, 0.2)', text: '❓ Unknown' }; return ` `; }).join('')}
ID Name Category Type Status Details
${provider.provider_id || provider.id || '-'} ${provider.name || 'Unknown'} ${provider.category || '-'} ${provider.type || '-'} ${statusInfo.text} ${provider.response_time_ms ? `${provider.response_time_ms}ms` : ''} ${provider.endpoint ? `🔗` : ''} ${provider.error_reason ? `⚠️` : ''}
Total Providers: ${providersData.total || providers.length}
`; } else { providersDiv.innerHTML = '
No providers found
'; } } // Update health summary if available if (healthRes) { try { const healthData = await healthRes.json(); const healthSummaryDiv = document.getElementById('providers-health-summary'); if (healthSummaryDiv && healthData.ok && healthData.summary) { const summary = healthData.summary; healthSummaryDiv.innerHTML = `

Provider Health Summary

${summary.total_active_providers || 0}
Total Active
${summary.http_valid || 0}
HTTP Valid
${summary.http_invalid || 0}
HTTP Invalid
${summary.http_conditional || 0}
Conditional
`; } } catch (e) { console.warn('Could not load health summary:', e); } } } catch (error) { console.error('Error loading providers:', error); showError('Error loading providers'); const providersDiv = document.getElementById('providers-list'); if (providersDiv) { providersDiv.innerHTML = '
Error loading providers
'; } } } // Search Resources async function searchResources() { const query = document.getElementById('search-resources').value; if (!query.trim()) { showError('Please enter a search query'); return; } const resultsDiv = document.getElementById('search-results'); resultsDiv.innerHTML = '
Searching...
'; try { const response = await fetch(`/api/resources/search?q=${encodeURIComponent(query)}`); const data = await response.json(); if (data.success && data.resources && data.resources.length > 0) { resultsDiv.innerHTML = `
${data.count || data.resources.length} result(s) found
${data.resources.map(resource => `
${resource.name || 'Unknown'}
Category: ${resource.category || 'N/A'}
${resource.base_url ? `
${resource.base_url}
` : ''}
${resource.free !== undefined ? ` ${resource.free ? '🆓 Free' : '💰 Paid'} ` : ''}
`).join('')}
`; } else { resultsDiv.innerHTML = '
No results found
'; } } catch (error) { console.error('Search error:', error); resultsDiv.innerHTML = '
Search error
'; showError('Search error'); } } // Load Diagnostics async function loadDiagnostics() { try { // Load system status try { const statusRes = await fetch('/api/status'); const statusData = await statusRes.json(); const statusDiv = document.getElementById('diagnostics-status'); const health = statusData.system_health || 'unknown'; const healthClass = health === 'healthy' ? 'alert-success' : health === 'degraded' ? 'alert-warning' : 'alert-error'; statusDiv.innerHTML = `

System Status

Overall Status: ${health}
Total APIs: ${statusData.total_apis || 0}
Online: ${statusData.online || 0}
Degraded: ${statusData.degraded || 0}
Offline: ${statusData.offline || 0}
Avg Response Time: ${statusData.avg_response_time_ms || 0}ms
${statusData.last_update ? `
Last Update: ${new Date(statusData.last_update).toLocaleString('en-US')}
` : ''}
`; } catch (statusError) { document.getElementById('diagnostics-status').innerHTML = '
Error loading system status
'; } // Load error logs try { const errorsRes = await fetch('/api/logs/errors'); const errorsData = await errorsRes.json(); const errors = errorsData.errors || errorsData.error_logs || []; const errorsDiv = document.getElementById('error-logs'); if (errors.length > 0) { errorsDiv.innerHTML = `
${errors.slice(0, 10).map(error => `
${error.message || error.error_message || error.type || 'Error'}
${error.error_type ? `
Type: ${error.error_type}
` : ''} ${error.provider ? `
Provider: ${error.provider}
` : ''}
${error.timestamp ? new Date(error.timestamp).toLocaleString('en-US') : ''}
`).join('')}
${errors.length > 10 ? `
Showing ${Math.min(10, errors.length)} of ${errors.length} errors
` : ''} `; } else { errorsDiv.innerHTML = '
No errors found ✅
'; } } catch (errorsError) { document.getElementById('error-logs').innerHTML = '
Error loading error logs
'; } // Load recent logs try { const logsRes = await fetch('/api/logs/recent'); const logsData = await logsRes.json(); const logs = logsData.logs || logsData.recent || []; const logsDiv = document.getElementById('recent-logs'); if (logs.length > 0) { logsDiv.innerHTML = `
${logs.slice(0, 20).map(log => { const level = log.level || log.status || 'info'; const levelColor = level === 'ERROR' ? 'var(--danger)' : level === 'WARNING' ? 'var(--warning)' : 'var(--text-secondary)'; return `
${level}
${log.timestamp ? new Date(log.timestamp).toLocaleString('en-US') : ''}
${log.message || log.content || JSON.stringify(log)}
${log.provider ? `
Provider: ${log.provider}
` : ''}
`; }).join('')}
`; } else { logsDiv.innerHTML = '
No logs found
'; } } catch (logsError) { document.getElementById('recent-logs').innerHTML = '
Error loading logs
'; } } catch (error) { console.error('Error loading diagnostics:', error); showError('Error loading diagnostics'); } } // Run Diagnostics async function runDiagnostics() { try { const response = await fetch('/api/diagnostics/run', { method: 'POST' }); const data = await response.json(); if (data.success) { showSuccess('Diagnostics completed successfully'); setTimeout(loadDiagnostics, 1000); } else { showError(data.error || 'Error running diagnostics'); } } catch (error) { showError('Error running diagnostics: ' + error.message); } } // Load Health Diagnostics async function loadHealthDiagnostics() { const resultDiv = document.getElementById('health-diagnostics-result'); resultDiv.innerHTML = '
Loading health data...
'; try { const response = await fetch('/api/diagnostics/health'); const data = await response.json(); if (data.status !== 'success') { resultDiv.innerHTML = `
Error: ${data.error || 'Failed to load health diagnostics'}
`; return; } const providerSummary = data.providers.summary; const modelSummary = data.models.summary; const providerEntries = data.providers.entries || []; const modelEntries = data.models.entries || []; // Helper function to get status color const getStatusColor = (status) => { switch (status) { case 'healthy': return 'var(--success)'; case 'degraded': return 'var(--warning)'; case 'unavailable': return 'var(--danger)'; default: return 'var(--text-secondary)'; } }; // Helper function to get status badge const getStatusBadge = (status, inCooldown) => { const color = getStatusColor(status); const icon = status === 'healthy' ? '✅' : status === 'degraded' ? '⚠️' : status === 'unavailable' ? '❌' : '❓'; const cooldownText = inCooldown ? ' (cooldown)' : ''; return `${icon} ${status}${cooldownText}`; }; resultDiv.innerHTML = `
${providerSummary.total}
Total Providers
✅ ${providerSummary.healthy} ⚠️ ${providerSummary.degraded} ❌ ${providerSummary.unavailable}
${modelSummary.total}
Total Models
✅ ${modelSummary.healthy} ⚠️ ${modelSummary.degraded} ❌ ${modelSummary.unavailable}
${data.overall_health.providers_ok && data.overall_health.models_ok ? '💚' : '⚠️'}
Overall Health
${data.overall_health.providers_ok && data.overall_health.models_ok ? 'HEALTHY' : 'DEGRADED'}
${providerEntries.length > 0 ? `

🔌 Provider Health (${providerEntries.length})

${providerEntries.map(provider => `
${provider.name}
${getStatusBadge(provider.status, provider.in_cooldown)}
Errors: ${provider.error_count} | Successes: ${provider.success_count}
${provider.last_success ? `
Last Success: ${new Date(provider.last_success * 1000).toLocaleString()}
` : ''} ${provider.last_error ? `
Last Error: ${new Date(provider.last_error * 1000).toLocaleString()}
` : ''} ${provider.last_error_message ? `
Error: ${provider.last_error_message.substring(0, 100)}${provider.last_error_message.length > 100 ? '...' : ''}
` : ''}
`).join('')}
` : '
No provider health data available yet
'} ${modelEntries.length > 0 ? `

🤖 Model Health (${modelEntries.length})

${modelEntries.filter(m => m.loaded || m.status !== 'unknown').slice(0, 20).map(model => `
${model.model_id}
${model.key} • ${model.category}
${getStatusBadge(model.status, model.in_cooldown)} ${model.status === 'unavailable' && !model.in_cooldown ? `` : ''}
Errors: ${model.error_count} | Successes: ${model.success_count} | Loaded: ${model.loaded ? 'Yes' : 'No'}
${model.last_success ? `
Last Success: ${new Date(model.last_success * 1000).toLocaleString()}
` : ''} ${model.last_error ? `
Last Error: ${new Date(model.last_error * 1000).toLocaleString()}
` : ''} ${model.last_error_message ? `
Error: ${model.last_error_message.substring(0, 150)}${model.last_error_message.length > 150 ? '...' : ''}
` : ''}
`).join('')}
` : '
No model health data available yet
'}
Last updated: ${new Date(data.timestamp).toLocaleString()}
`; } catch (error) { console.error('Error loading health diagnostics:', error); resultDiv.innerHTML = `
Error: ${error.message || 'Failed to load health diagnostics'}
`; } } // Trigger self-heal for all failed models async function triggerSelfHeal() { try { const response = await fetch('/api/diagnostics/self-heal', { method: 'POST' }); const data = await response.json(); if (data.status === 'completed') { const summary = data.summary; showSuccess(`Self-heal completed: ${summary.successful}/${summary.total_attempts} successful`); // Reload health after a short delay setTimeout(loadHealthDiagnostics, 2000); } else { showError(data.error || 'Self-heal failed'); } } catch (error) { showError('Error triggering self-heal: ' + error.message); } } // Reinitialize specific model async function reinitModel(modelKey) { try { const response = await fetch(`/api/diagnostics/self-heal?model_key=${encodeURIComponent(modelKey)}`, { method: 'POST' }); const data = await response.json(); if (data.status === 'completed' && data.results && data.results.length > 0) { const result = data.results[0]; if (result.status === 'success') { showSuccess(`Model ${modelKey} reinitialized successfully`); } else { showError(`Failed to reinit ${modelKey}: ${result.message || result.error || 'Unknown error'}`); } // Reload health after a short delay setTimeout(loadHealthDiagnostics, 1500); } else { showError(data.error || 'Reinitialization failed'); } } catch (error) { showError('Error reinitializing model: ' + error.message); } } // Test API async function testAPI() { const endpoint = document.getElementById('api-endpoint').value; const method = document.getElementById('api-method').value; const bodyText = document.getElementById('api-body').value; if (!endpoint) { showError('Please select an endpoint'); return; } const resultDiv = document.getElementById('api-result'); resultDiv.innerHTML = '
Sending request...
'; try { const options = { method }; // Parse body if provided let body = null; if (method === 'POST' && bodyText) { try { body = JSON.parse(bodyText); options.headers = { 'Content-Type': 'application/json' }; } catch (e) { showError('Invalid JSON in body'); resultDiv.innerHTML = '
JSON parsing error
'; return; } } if (body) { options.body = JSON.stringify(body); } const startTime = Date.now(); const response = await fetch(endpoint, options); const responseTime = Date.now() - startTime; let data; const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { data = await response.json(); } else { data = { text: await response.text() }; } const statusClass = response.ok ? 'alert-success' : 'alert-error'; const statusEmoji = response.ok ? '✅' : '❌'; resultDiv.innerHTML = `
${statusEmoji} Status: ${response.status} ${response.statusText}
Response Time: ${responseTime}ms

Response:

${JSON.stringify(data, null, 2)}
Endpoint: ${method} ${endpoint}
`; } catch (error) { resultDiv.innerHTML = `

Error:

${error.message}

`; showError('API test error: ' + error.message); } } // Utility Functions function showError(message) { const alert = document.createElement('div'); alert.className = 'alert alert-error'; alert.textContent = message; document.body.appendChild(alert); setTimeout(() => alert.remove(), 5000); } function showSuccess(message) { const alert = document.createElement('div'); alert.className = 'alert alert-success'; alert.textContent = message; document.body.appendChild(alert); setTimeout(() => alert.remove(), 5000); } // Additional tab loaders for HTML tabs async function loadMonitorData() { // Load API monitor data try { const response = await fetch('/api/status'); const data = await response.json(); const monitorContainer = document.getElementById('monitor-content'); if (monitorContainer) { monitorContainer.innerHTML = `

API Status

${JSON.stringify(data, null, 2)}
`; } } catch (error) { console.error('Error loading monitor data:', error); } } async function loadAdvancedData() { // Load advanced/API explorer data loadAPIEndpoints(); loadDiagnostics(); } async function loadAdminData() { // Load admin panel data try { const [providersRes, modelsRes] = await Promise.all([ fetch('/api/providers'), fetch('/api/models/status') ]); const providers = await providersRes.json(); const models = await modelsRes.json(); const adminContainer = document.getElementById('admin-content'); if (adminContainer) { adminContainer.innerHTML = `

System Status

Providers: ${providers.total || 0}

Models: ${models.models_loaded || 0} loaded

`; } } catch (error) { console.error('Error loading admin data:', error); } } async function loadHFHealth() { // Load HF models health status try { const response = await fetch('/api/models/status'); const data = await response.json(); const hfContainer = document.getElementById('hf-status'); if (hfContainer) { hfContainer.innerHTML = `

HF Models Status

Mode: ${data.hf_mode || 'unknown'}

Loaded: ${data.models_loaded || 0}

Failed: ${data.failed_count || 0}

Status: ${data.status || 'unknown'}

`; } } catch (error) { console.error('Error loading HF health:', error); } } async function loadPools() { // Load provider pools try { const response = await fetch('/api/pools'); const data = await response.json(); const poolsContainer = document.getElementById('pools-content'); if (poolsContainer) { poolsContainer.innerHTML = `

Provider Pools

${data.message || 'No pools available'}

${JSON.stringify(data, null, 2)}
`; } } catch (error) { console.error('Error loading pools:', error); } } async function loadLogs() { // Load recent logs try { const response = await fetch('/api/logs/recent'); const data = await response.json(); const logsContainer = document.getElementById('logs-content'); if (logsContainer) { const logsHtml = data.logs && data.logs.length > 0 ? data.logs.map(log => `
${JSON.stringify(log)}
`).join('') : '

No logs available

'; logsContainer.innerHTML = `

Recent Logs

${logsHtml}
`; } } catch (error) { console.error('Error loading logs:', error); } } async function loadReports() { // Load reports/analytics try { const response = await fetch('/api/providers/health-summary'); const data = await response.json(); const reportsContainer = document.getElementById('reports-content'); if (reportsContainer) { reportsContainer.innerHTML = `

Provider Health Report

${JSON.stringify(data, null, 2)}
`; } } catch (error) { console.error('Error loading reports:', error); } } async function loadResources() { // Load resources summary try { const response = await fetch('/api/resources'); const data = await response.json(); const resourcesContainer = document.getElementById('resources-summary'); if (resourcesContainer) { const summary = data.summary || {}; resourcesContainer.innerHTML = `

Resources Summary

Total: ${summary.total_resources || 0}

Free: ${summary.free_resources || 0}

Models: ${summary.models_available || 0}

`; } } catch (error) { console.error('Error loading resources:', error); } } async function loadAPIRegistry() { // Load API registry from all_apis_merged_2025.json try { const response = await fetch('/api/resources/apis'); const data = await response.json(); if (!data.ok) { console.warn('API registry not available:', data.error); const registryContainer = document.getElementById('api-registry-section'); if (registryContainer) { registryContainer.innerHTML = `
📚
API Registry Not Available
${data.error || 'API registry file not found'}
`; } return; } const registryContainer = document.getElementById('api-registry-section'); if (registryContainer) { const metadata = data.metadata || {}; const categories = data.categories || []; const rawFiles = data.raw_files_preview || []; registryContainer.innerHTML = `

📚 ${metadata.name || 'API Registry'}

${metadata.description || 'Comprehensive API registry for cryptocurrency data sources'}

Version
${metadata.version || 'N/A'}
${categories.length}
Categories
${data.total_raw_files || 0}
Total Files
${metadata.created_at ? `
Created
${new Date(metadata.created_at).toLocaleDateString('en-US')}
` : ''}
${categories.length > 0 ? `

📂 Categories

${categories.map(cat => ` ${cat.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())} `).join('')}
` : ''} ${rawFiles.length > 0 ? `

📄 Sample Files (${rawFiles.length} of ${data.total_raw_files || 0})

${rawFiles.map(file => `
${file.filename || 'Unknown file'}
Size: ${file.size ? (file.size / 1024).toFixed(1) + ' KB' : file.full_size ? (file.full_size / 1024).toFixed(1) + ' KB' : 'N/A'}
${file.preview ? `
${file.preview}
` : ''}
`).join('')}
` : ''}
`; } // Also update metadata container if it exists const metadataContainer = document.getElementById('api-registry-metadata'); if (metadataContainer) { metadataContainer.innerHTML = `

Metadata

${JSON.stringify(metadata, null, 2)}
`; } } catch (error) { console.error('Error loading API registry:', error); const registryContainer = document.getElementById('api-registry-section'); if (registryContainer) { registryContainer.innerHTML = `
Error Loading API Registry
${error.message || 'Failed to load API registry data'}
`; } } } // Theme Toggle function toggleTheme() { const body = document.body; const themeToggle = document.querySelector('.theme-toggle'); if (body.classList.contains('light-theme')) { body.classList.remove('light-theme'); localStorage.setItem('theme', 'dark'); // Update icon to moon (dark mode) if (themeToggle) { themeToggle.innerHTML = ''; } } else { body.classList.add('light-theme'); localStorage.setItem('theme', 'light'); // Update icon to sun (light mode) if (themeToggle) { themeToggle.innerHTML = ''; } } } // Load theme preference document.addEventListener('DOMContentLoaded', () => { const savedTheme = localStorage.getItem('theme'); const themeToggle = document.querySelector('.theme-toggle'); if (savedTheme === 'light') { document.body.classList.add('light-theme'); if (themeToggle) { themeToggle.innerHTML = ''; } } }); // Update header stats function updateHeaderStats() { const totalResources = document.getElementById('stat-total-resources')?.textContent || '-'; const totalModels = document.getElementById('stat-models')?.textContent || '-'; const headerResources = document.getElementById('header-resources'); const headerModels = document.getElementById('header-models'); if (headerResources) headerResources.textContent = totalResources; if (headerModels) headerModels.textContent = totalModels; } // Call updateHeaderStats after loading dashboard const originalLoadDashboard = loadDashboard; loadDashboard = async function() { await originalLoadDashboard(); updateHeaderStats(); }; // ===== AI Analyst Functions ===== async function runAIAnalyst() { const prompt = document.getElementById('ai-analyst-prompt').value.trim(); const mode = document.getElementById('ai-analyst-mode').value; const maxLength = parseInt(document.getElementById('ai-analyst-max-length').value); if (!prompt) { showError('Please enter a prompt or question'); return; } const resultDiv = document.getElementById('ai-analyst-result'); resultDiv.innerHTML = '
Generating analysis...
'; try { const response = await fetch('/api/analyze/text', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: prompt, mode: mode, max_length: maxLength }) }); const data = await response.json(); if (!data.available) { resultDiv.innerHTML = `
⚠️ Model Not Available: ${data.error || 'AI generation model is currently unavailable'} ${data.note ? `
${data.note}` : ''}
`; return; } if (!data.success) { resultDiv.innerHTML = `
❌ Generation Failed: ${data.error || 'Failed to generate analysis'}
`; return; } const generatedText = data.text || ''; const model = data.model || 'Unknown'; resultDiv.innerHTML = `

✨ AI Generated Analysis

${generatedText}
Model: ${model}
Mode: ${mode}
Prompt: "${prompt.substring(0, 100)}${prompt.length > 100 ? '...' : ''}"
Timestamp: ${new Date(data.timestamp).toLocaleString()}
`; // Store for clipboard window.lastAIAnalysis = generatedText; } catch (error) { console.error('AI analyst error:', error); resultDiv.innerHTML = `
Generation Error: ${error.message}
`; showError('Error generating analysis'); } } function setAIAnalystPrompt(text) { document.getElementById('ai-analyst-prompt').value = text; } async function copyAIAnalystResult() { if (!window.lastAIAnalysis) { showError('No analysis to copy'); return; } try { await navigator.clipboard.writeText(window.lastAIAnalysis); showSuccess('Analysis copied to clipboard!'); } catch (error) { console.error('Failed to copy:', error); showError('Failed to copy analysis'); } } function clearAIAnalystForm() { document.getElementById('ai-analyst-prompt').value = ''; document.getElementById('ai-analyst-result').innerHTML = ''; window.lastAIAnalysis = null; } // ===== Trading Assistant Functions ===== async function runTradingAssistant() { const symbol = document.getElementById('trading-symbol').value.trim().toUpperCase(); const context = document.getElementById('trading-context').value.trim(); if (!symbol) { showError('Please enter a trading symbol'); return; } const resultDiv = document.getElementById('trading-assistant-result'); resultDiv.innerHTML = '
Analyzing and generating trading signal...
'; try { const response = await fetch('/api/trading/decision', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ symbol: symbol, context: context }) }); const data = await response.json(); if (!data.available) { resultDiv.innerHTML = `
⚠️ Model Not Available: ${data.error || 'Trading signal model is currently unavailable'} ${data.note ? `
${data.note}` : ''}
`; return; } if (!data.success) { resultDiv.innerHTML = `
❌ Analysis Failed: ${data.error || 'Failed to generate trading signal'}
`; return; } const decision = data.decision || 'HOLD'; const confidence = data.confidence || 0; const rationale = data.rationale || ''; const model = data.model || 'Unknown'; // Determine colors and icons based on decision let decisionColor, decisionBg, decisionIcon; if (decision === 'BUY') { decisionColor = 'var(--success)'; decisionBg = 'rgba(16, 185, 129, 0.2)'; decisionIcon = '📈'; } else if (decision === 'SELL') { decisionColor = 'var(--danger)'; decisionBg = 'rgba(239, 68, 68, 0.2)'; decisionIcon = '📉'; } else { decisionColor = 'var(--text-secondary)'; decisionBg = 'rgba(156, 163, 175, 0.2)'; decisionIcon = '➡️'; } resultDiv.innerHTML = `

🎯 Trading Signal for ${symbol}

${decisionIcon}
${decision}
Decision
${(confidence * 100).toFixed(0)}%
Confidence
AI Rationale:

${rationale}

${context ? `
Your Context:
"${context.substring(0, 200)}${context.length > 200 ? '...' : ''}"
` : ''}
Model: ${model}
Timestamp: ${new Date(data.timestamp).toLocaleString()}
⚠️ Reminder:

This is an AI-generated signal for informational purposes only. Always do your own research and consider multiple factors before trading.

`; } catch (error) { console.error('Trading assistant error:', error); resultDiv.innerHTML = `
Analysis Error: ${error.message}
`; showError('Error generating trading signal'); } } // Initialize trading pair selector for trading assistant tab function initTradingSymbolSelector() { const tradingSymbolContainer = document.getElementById('trading-symbol-container'); if (tradingSymbolContainer && window.TradingPairsLoader) { const pairs = window.TradingPairsLoader.getTradingPairs(); if (pairs && pairs.length > 0) { tradingSymbolContainer.innerHTML = window.TradingPairsLoader.createTradingPairCombobox( 'trading-symbol', 'Select or type trading pair', 'BTCUSDT' ); } } } // Update loadTabData to handle new tabs const originalLoadTabData = loadTabData; loadTabData = function(tabId) { originalLoadTabData(tabId); // Additional handlers for new tabs if (tabId === 'ai-analyst') { // No initialization needed for AI Analyst yet } else if (tabId === 'trading-assistant') { initTradingSymbolSelector(); } }; // Listen for trading pairs loaded event to initialize trading symbol selector document.addEventListener('tradingPairsLoaded', function(e) { initTradingSymbolSelector(); });