| <!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>
|
|
|