|
|
<!DOCTYPE html> |
|
|
<html lang="en" dir="rtl"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>HF Console - Crypto API Monitor</title> |
|
|
<style> |
|
|
* { margin: 0; padding: 0; box-sizing: border-box; } |
|
|
body { |
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
padding: 20px; |
|
|
direction: rtl; |
|
|
} |
|
|
.container { |
|
|
max-width: 1400px; |
|
|
margin: 0 auto; |
|
|
background: white; |
|
|
border-radius: 16px; |
|
|
padding: 30px; |
|
|
box-shadow: 0 10px 40px rgba(0,0,0,0.2); |
|
|
} |
|
|
h1 { |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
-webkit-background-clip: text; |
|
|
-webkit-text-fill-color: transparent; |
|
|
margin-bottom: 10px; |
|
|
font-size: 32px; |
|
|
} |
|
|
.subtitle { color: #666; margin-bottom: 30px; } |
|
|
section { |
|
|
margin-bottom: 30px; |
|
|
padding: 20px; |
|
|
background: #f8f9fa; |
|
|
border-radius: 12px; |
|
|
border: 2px solid #e9ecef; |
|
|
} |
|
|
h3 { |
|
|
color: #333; |
|
|
margin-bottom: 15px; |
|
|
font-size: 20px; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 10px; |
|
|
} |
|
|
.badge { |
|
|
display: inline-block; |
|
|
padding: 4px 12px; |
|
|
border-radius: 12px; |
|
|
font-size: 14px; |
|
|
font-weight: 600; |
|
|
} |
|
|
.badge-success { background: #d1fae5; color: #10b981; } |
|
|
.badge-warning { background: #fef3c7; color: #f59e0b; } |
|
|
.badge-info { background: #dbeafe; color: #3b82f6; } |
|
|
pre { |
|
|
background: #1e293b; |
|
|
color: #e2e8f0; |
|
|
padding: 15px; |
|
|
border-radius: 8px; |
|
|
overflow-x: auto; |
|
|
font-size: 13px; |
|
|
line-height: 1.6; |
|
|
max-height: 300px; |
|
|
overflow-y: auto; |
|
|
} |
|
|
button { |
|
|
padding: 10px 20px; |
|
|
border: none; |
|
|
border-radius: 8px; |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
color: white; |
|
|
font-weight: 600; |
|
|
cursor: pointer; |
|
|
transition: all 0.3s; |
|
|
margin: 5px; |
|
|
} |
|
|
button:hover { |
|
|
transform: translateY(-2px); |
|
|
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); |
|
|
} |
|
|
button:active { transform: translateY(0); } |
|
|
input, textarea { |
|
|
width: 100%; |
|
|
padding: 10px; |
|
|
border: 2px solid #e9ecef; |
|
|
border-radius: 8px; |
|
|
font-family: inherit; |
|
|
margin: 10px 0; |
|
|
} |
|
|
input:focus, textarea:focus { |
|
|
outline: none; |
|
|
border-color: #667eea; |
|
|
} |
|
|
.list-box { |
|
|
max-height: 250px; |
|
|
overflow-y: auto; |
|
|
border: 1px solid #e9ecef; |
|
|
padding: 10px; |
|
|
background: white; |
|
|
border-radius: 8px; |
|
|
margin: 10px 0; |
|
|
} |
|
|
.list-box ul { list-style: none; } |
|
|
.list-box li { |
|
|
padding: 8px; |
|
|
border-bottom: 1px solid #f1f5f9; |
|
|
font-size: 14px; |
|
|
color: #475569; |
|
|
} |
|
|
.list-box li:last-child { border-bottom: none; } |
|
|
.vote-display { |
|
|
font-size: 24px; |
|
|
font-weight: 700; |
|
|
padding: 15px; |
|
|
background: white; |
|
|
border-radius: 8px; |
|
|
text-align: center; |
|
|
margin: 10px 0; |
|
|
} |
|
|
.vote-positive { color: #10b981; } |
|
|
.vote-negative { color: #ef4444; } |
|
|
.vote-neutral { color: #6b7280; } |
|
|
.loading { |
|
|
display: inline-block; |
|
|
width: 16px; |
|
|
height: 16px; |
|
|
border: 3px solid #f3f4f6; |
|
|
border-top-color: #667eea; |
|
|
border-radius: 50%; |
|
|
animation: spin 0.8s linear infinite; |
|
|
} |
|
|
@keyframes spin { |
|
|
to { transform: rotate(360deg); } |
|
|
} |
|
|
.grid { |
|
|
display: grid; |
|
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); |
|
|
gap: 20px; |
|
|
} |
|
|
</style> |
|
|
|
|
|
<script src="config.js"></script> |
|
|
</head> |
|
|
<body> |
|
|
<div class="container"> |
|
|
<h1>π€ HuggingFace Console</h1> |
|
|
<p class="subtitle">Test HF connectivity, registry, search, and sentiment analysis</p> |
|
|
<div style="background: #f0f9ff; padding: 10px; border-radius: 8px; margin-bottom: 20px; font-size: 13px; color: #0369a1;"> |
|
|
<strong>π Environment:</strong> <span id="envInfo">Loading...</span> | |
|
|
<strong>π‘ API:</strong> <span id="apiInfo">Loading...</span> |
|
|
</div> |
|
|
|
|
|
<section> |
|
|
<h3> |
|
|
<span>π Health Status</span> |
|
|
<span class="badge badge-info" id="healthBadge">Loading...</span> |
|
|
</h3> |
|
|
<button onclick="loadHealth()">π Refresh Health</button> |
|
|
<button onclick="doRefresh()">π Force Registry Refresh</button> |
|
|
<pre id="healthOutput">Loading...</pre> |
|
|
</section> |
|
|
|
|
|
<div class="grid"> |
|
|
<section> |
|
|
<h3> |
|
|
<span>π€ Models Registry</span> |
|
|
<span class="badge badge-success" id="modelsCount">0</span> |
|
|
</h3> |
|
|
<button onclick="loadModels()">Load Models</button> |
|
|
<div class="list-box" id="modelsList"> |
|
|
<p style="color: #94a3b8;">Click "Load Models" to fetch...</p> |
|
|
</div> |
|
|
</section> |
|
|
|
|
|
<section> |
|
|
<h3> |
|
|
<span>π Datasets Registry</span> |
|
|
<span class="badge badge-success" id="datasetsCount">0</span> |
|
|
</h3> |
|
|
<button onclick="loadDatasets()">Load Datasets</button> |
|
|
<div class="list-box" id="datasetsList"> |
|
|
<p style="color: #94a3b8;">Click "Load Datasets" to fetch...</p> |
|
|
</div> |
|
|
</section> |
|
|
</div> |
|
|
|
|
|
<section> |
|
|
<h3>π Search Registry (Local Snapshot)</h3> |
|
|
<input type="text" id="searchQuery" placeholder="Search query (e.g., crypto, bitcoin, sentiment)" value="crypto"> |
|
|
<button onclick="doSearch()">Search Models</button> |
|
|
<button onclick="doSearchDatasets()">Search Datasets</button> |
|
|
<div class="list-box" id="searchResults"> |
|
|
<p style="color: #94a3b8;">Enter a query and click search...</p> |
|
|
</div> |
|
|
</section> |
|
|
|
|
|
<section> |
|
|
<h3>π Sentiment Analysis (Local Pipeline)</h3> |
|
|
<p style="color: #666; font-size: 14px; margin-bottom: 10px;"> |
|
|
Enter text samples (one per line) to analyze crypto sentiment using local transformers |
|
|
</p> |
|
|
<textarea id="sentimentTexts" rows="5" placeholder="BTC looks strong ETH is weak today Market sentiment is bullish">BTC strong breakout |
|
|
ETH looks weak |
|
|
Crypto market is bullish today |
|
|
Bears are taking control |
|
|
Neutral market conditions</textarea> |
|
|
<button onclick="doSentiment()">π§ Run Sentiment Analysis</button> |
|
|
<div class="vote-display" id="voteDisplay"> |
|
|
<span style="color: #94a3b8;">β</span> |
|
|
</div> |
|
|
<pre id="sentimentOutput">Results will appear here...</pre> |
|
|
</section> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
const API_BASE = CONFIG.API_BASE; |
|
|
const fetchJSON = CONFIG.fetchJSON; |
|
|
const postJSON = CONFIG.postJSON; |
|
|
|
|
|
|
|
|
function updateEnvironmentInfo() { |
|
|
const envType = CONFIG.IS_HUGGINGFACE_SPACES ? 'π€ HuggingFace Spaces' : |
|
|
CONFIG.IS_LOCALHOST ? 'π» Localhost' : 'π Custom Deployment'; |
|
|
document.getElementById('envInfo').textContent = envType; |
|
|
document.getElementById('apiInfo').textContent = CONFIG.API_BASE; |
|
|
} |
|
|
|
|
|
async function loadHealth() { |
|
|
try { |
|
|
const data = await fetchJSON(CONFIG.ENDPOINTS.HF_HEALTH); |
|
|
document.getElementById('healthOutput').textContent = JSON.stringify(data, null, 2); |
|
|
document.getElementById('healthBadge').textContent = data.ok ? 'β Healthy' : 'β Unhealthy'; |
|
|
document.getElementById('healthBadge').className = data.ok ? 'badge badge-success' : 'badge badge-warning'; |
|
|
} catch (err) { |
|
|
document.getElementById('healthOutput').textContent = `Error: ${err.message}`; |
|
|
document.getElementById('healthBadge').textContent = 'β Error'; |
|
|
document.getElementById('healthBadge').className = 'badge badge-warning'; |
|
|
} |
|
|
} |
|
|
|
|
|
async function doRefresh() { |
|
|
try { |
|
|
document.getElementById('healthOutput').textContent = 'Refreshing registry...'; |
|
|
const data = await postJSON(CONFIG.ENDPOINTS.HF_REFRESH, {}); |
|
|
document.getElementById('healthOutput').textContent = JSON.stringify(data, null, 2); |
|
|
await loadHealth(); |
|
|
} catch (err) { |
|
|
document.getElementById('healthOutput').textContent = `Error: ${err.message}`; |
|
|
} |
|
|
} |
|
|
|
|
|
async function loadModels() { |
|
|
try { |
|
|
const data = await fetchJSON(`${CONFIG.ENDPOINTS.HF_REGISTRY}?kind=models`); |
|
|
const items = data.items || []; |
|
|
document.getElementById('modelsCount').textContent = items.length; |
|
|
const html = items.length > 0 |
|
|
? '<ul>' + items.slice(0, 50).map(i => `<li>π€ ${i.id} β’ ${i.pipeline_tag || 'N/A'} β’ <small>${i.source}</small></li>`).join('') + '</ul>' |
|
|
: '<p style="color: #94a3b8;">No models found</p>'; |
|
|
document.getElementById('modelsList').innerHTML = html; |
|
|
} catch (err) { |
|
|
document.getElementById('modelsList').innerHTML = `<p style="color: #ef4444;">Error: ${err.message}</p>`; |
|
|
} |
|
|
} |
|
|
|
|
|
async function loadDatasets() { |
|
|
try { |
|
|
const data = await fetchJSON(`${CONFIG.ENDPOINTS.HF_REGISTRY}?kind=datasets`); |
|
|
const items = data.items || []; |
|
|
document.getElementById('datasetsCount').textContent = items.length; |
|
|
const html = items.length > 0 |
|
|
? '<ul>' + items.slice(0, 50).map(i => `<li>π ${i.id} β’ <small>${i.source}</small></li>`).join('') + '</ul>' |
|
|
: '<p style="color: #94a3b8;">No datasets found</p>'; |
|
|
document.getElementById('datasetsList').innerHTML = html; |
|
|
} catch (err) { |
|
|
document.getElementById('datasetsList').innerHTML = `<p style="color: #ef4444;">Error: ${err.message}</p>`; |
|
|
} |
|
|
} |
|
|
|
|
|
async function doSearch() { |
|
|
const q = document.getElementById('searchQuery').value; |
|
|
try { |
|
|
const data = await fetchJSON(`${CONFIG.ENDPOINTS.HF_SEARCH}?q=${encodeURIComponent(q)}&kind=models`); |
|
|
const items = data.items || []; |
|
|
const html = items.length > 0 |
|
|
? `<p style="color: #10b981; font-weight: 600;">Found ${items.length} models</p><ul>` + items.map(i => `<li>π€ ${i.id}</li>`).join('') + '</ul>' |
|
|
: '<p style="color: #94a3b8;">No results found</p>'; |
|
|
document.getElementById('searchResults').innerHTML = html; |
|
|
} catch (err) { |
|
|
document.getElementById('searchResults').innerHTML = `<p style="color: #ef4444;">Error: ${err.message}</p>`; |
|
|
} |
|
|
} |
|
|
|
|
|
async function doSearchDatasets() { |
|
|
const q = document.getElementById('searchQuery').value; |
|
|
try { |
|
|
const data = await fetchJSON(`${CONFIG.ENDPOINTS.HF_SEARCH}?q=${encodeURIComponent(q)}&kind=datasets`); |
|
|
const items = data.items || []; |
|
|
const html = items.length > 0 |
|
|
? `<p style="color: #10b981; font-weight: 600;">Found ${items.length} datasets</p><ul>` + items.map(i => `<li>π ${i.id}</li>`).join('') + '</ul>' |
|
|
: '<p style="color: #94a3b8;">No results found</p>'; |
|
|
document.getElementById('searchResults').innerHTML = html; |
|
|
} catch (err) { |
|
|
document.getElementById('searchResults').innerHTML = `<p style="color: #ef4444;">Error: ${err.message}</p>`; |
|
|
} |
|
|
} |
|
|
|
|
|
async function doSentiment() { |
|
|
const texts = document.getElementById('sentimentTexts').value.split('\n').filter(t => t.trim()); |
|
|
if (texts.length === 0) { |
|
|
alert('Please enter at least one text sample'); |
|
|
return; |
|
|
} |
|
|
try { |
|
|
document.getElementById('voteDisplay').innerHTML = '<span class="loading"></span>'; |
|
|
document.getElementById('sentimentOutput').textContent = 'Running sentiment analysis...'; |
|
|
|
|
|
const data = await postJSON(CONFIG.ENDPOINTS.HF_RUN_SENTIMENT, { texts }); |
|
|
|
|
|
const vote = data.vote || 0; |
|
|
let voteClass = 'vote-neutral'; |
|
|
let voteEmoji = 'π'; |
|
|
if (vote > 0.2) { voteClass = 'vote-positive'; voteEmoji = 'π'; } |
|
|
else if (vote < -0.2) { voteClass = 'vote-negative'; voteEmoji = 'π'; } |
|
|
|
|
|
document.getElementById('voteDisplay').innerHTML = `<span class="${voteClass}">${voteEmoji} ${vote.toFixed(3)}</span>`; |
|
|
document.getElementById('sentimentOutput').textContent = JSON.stringify(data, null, 2); |
|
|
} catch (err) { |
|
|
document.getElementById('voteDisplay').innerHTML = '<span style="color: #ef4444;">Error</span>'; |
|
|
document.getElementById('sentimentOutput').textContent = `Error: ${err.message}`; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
window.addEventListener('load', () => { |
|
|
updateEnvironmentInfo(); |
|
|
loadHealth(); |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
|