| const hfFeedback = () => window.UIFeedback || {}; | |
| const $ = (id) => document.getElementById(id); | |
| async function loadRegistry() { | |
| try { | |
| const [health, registry] = await Promise.all([ | |
| hfFeedback().fetchJSON?.('/api/hf/health', {}, 'HF health'), | |
| hfFeedback().fetchJSON?.('/api/hf/registry?kind=models', {}, 'HF registry'), | |
| ]); | |
| hfFeedback().setBadge?.( | |
| $('hf-console-health'), | |
| `HF ${health.status}`, | |
| health.status === 'healthy' ? 'success' : health.status === 'degraded' ? 'warning' : 'danger', | |
| ); | |
| $('hf-console-summary').textContent = `Models available: ${registry.items?.length || 0}`; | |
| $('hf-console-models').innerHTML = | |
| registry.items | |
| ?.map((model) => `<li><span>${model}</span><span class="badge info">Model</span></li>`) | |
| .join('') || '<li class="empty-state">No registry entries yet.</li>'; | |
| } catch { | |
| $('hf-console-models').innerHTML = '<li class="empty-state">Unable to load registry.</li>'; | |
| hfFeedback().setBadge?.($('hf-console-health'), 'HF unavailable', 'warning'); | |
| } | |
| } | |
| async function runSentiment() { | |
| const button = $('run-sentiment'); | |
| button.disabled = true; | |
| const modelName = $('sentiment-model').value; | |
| const texts = $('sentiment-texts').value | |
| .split('\n') | |
| .map((line) => line.trim()) | |
| .filter(Boolean); | |
| hfFeedback().showLoading?.($('sentiment-results'), 'Running sentiment…'); | |
| try { | |
| const payload = { model: modelName, texts }; | |
| const response = await hfFeedback().fetchJSON?.('/api/hf/models/sentiment', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(payload), | |
| }); | |
| $('sentiment-results').innerHTML = | |
| response.results | |
| ?.map((entry) => `<div class="stream-item"><strong>${entry.text}</strong><pre>${JSON.stringify(entry.result, null, 2)}</pre></div>`) | |
| .join('') || '<div class="stream-item empty-state">No sentiment data.</div>'; | |
| hfFeedback().toast?.('success', 'Sentiment complete', `${response.results?.length || 0} text(s)`); | |
| } catch (err) { | |
| $('sentiment-results').innerHTML = `<div class="stream-item empty-state">${err.message}</div>`; | |
| } finally { | |
| button.disabled = false; | |
| } | |
| } | |
| async function runForecast() { | |
| const button = $('run-forecast'); | |
| button.disabled = true; | |
| const series = $('forecast-series').value | |
| .split(',') | |
| .map((val) => val.trim()) | |
| .filter(Boolean); | |
| const model = $('forecast-model').value; | |
| const steps = parseInt($('forecast-steps').value, 10) || 3; | |
| hfFeedback().showLoading?.($('forecast-results'), 'Requesting forecast…'); | |
| try { | |
| const payload = { model, series, steps }; | |
| const response = await hfFeedback().fetchJSON?.('/api/hf/models/forecast', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(payload), | |
| }); | |
| $('forecast-results').innerHTML = `<div class="stream-item"><strong>${response.model}</strong><div>Predictions: ${response.predictions.join(', ')}</div><small style="color:var(--ui-text-muted);">Volatility ${response.volatility}</small></div>`; | |
| hfFeedback().toast?.('success', 'Forecast ready', `${response.predictions.length} points`); | |
| } catch (err) { | |
| $('forecast-results').innerHTML = `<div class="stream-item empty-state">${err.message}</div>`; | |
| } finally { | |
| button.disabled = false; | |
| } | |
| } | |
| const datasetRoutes = { | |
| 'market-ohlcv': '/api/hf/datasets/market/ohlcv?symbol=BTC&interval=1h&limit=50', | |
| 'market-btc': '/api/hf/datasets/market/btc_technical?limit=60', | |
| 'news-semantic': '/api/hf/datasets/news/semantic?limit=10', | |
| }; | |
| async function loadDataset(key) { | |
| const route = datasetRoutes[key]; | |
| if (!route) return; | |
| hfFeedback().showLoading?.($('dataset-output'), 'Loading dataset…'); | |
| try { | |
| const data = await hfFeedback().fetchJSON?.(route, {}, 'HF dataset'); | |
| const items = data.items || data.data || []; | |
| $('dataset-output').innerHTML = | |
| items | |
| .slice(0, 6) | |
| .map((item) => `<div class="stream-item"><pre>${JSON.stringify(item, null, 2)}</pre></div>`) | |
| .join('') || '<div class="stream-item empty-state">Dataset returned no rows.</div>'; | |
| } catch (err) { | |
| $('dataset-output').innerHTML = `<div class="stream-item empty-state">${err.message}</div>`; | |
| } | |
| } | |
| function wireDatasetButtons() { | |
| document.querySelectorAll('[data-dataset]').forEach((button) => { | |
| button.addEventListener('click', () => loadDataset(button.dataset.dataset)); | |
| }); | |
| } | |
| function initHFConsole() { | |
| loadRegistry(); | |
| $('run-sentiment').addEventListener('click', runSentiment); | |
| $('run-forecast').addEventListener('click', runForecast); | |
| wireDatasetButtons(); | |
| } | |
| document.addEventListener('DOMContentLoaded', initHFConsole); | |