model-hub / app.js
AEUPH's picture
Fix search form - add Enter key handler and form submit
a2f1999 verified
Raw
History Blame Contribute Delete
17.8 kB
// AEUPH Model Hub - Static Space JavaScript
const HF_MODELS = [
{ id: "meta-llama/Meta-Llama-3.1-8B-Instruct", name: "Llama 3.1 8B Instruct", org: "meta-llama", type: "llm", tags: ["chat", "instruction", "tool-calling"], vram: 16, downloads: 1200000, likes: 8500, featured: true, description: "Latest Llama 3.1 with 128K context, multilingual, tool calling. Best open LLM for general use." },
{ id: "mistralai/Mistral-7B-Instruct-v0.3", name: "Mistral 7B Instruct v0.3", org: "mistralai", type: "llm", tags: ["chat", "instruction", "fast"], vram: 8, downloads: 2100000, likes: 12000, featured: true, description: "Fast, efficient 7B model. Excellent for chat, coding, and reasoning. Low VRAM requirement." },
{ id: "Qwen/Qwen2.5-7B-Instruct", name: "Qwen 2.5 7B Instruct", org: "Qwen", type: "llm", tags: ["chat", "multilingual", "coding", "reasoning"], vram: 8, downloads: 900000, likes: 6200, featured: true, description: "Strong multilingual (29 langs), coding, and reasoning. 128K context. Apache 2.0." },
{ id: "microsoft/Phi-3.5-mini-instruct", name: "Phi-3.5 Mini Instruct", org: "microsoft", type: "llm", tags: ["small", "fast", "reasoning", "128k"], vram: 4, downloads: 600000, likes: 4800, featured: true, description: "3.8B params, 128K context. Remarkable reasoning for size. Runs on 4GB VRAM." },
{ id: "codellama/CodeLlama-13b-Instruct-hf", name: "CodeLlama 13B Instruct", org: "codellama", type: "code", tags: ["coding", "python", "completion", "infilling"], vram: 24, downloads: 400000, likes: 3200, featured: true, description: "Specialized for code generation, completion, infilling. Python, JS, TS, C++, Rust, Go." },
{ id: "deepseek-ai/DeepSeek-Coder-V2-Lite-Instruct", name: "DeepSeek Coder V2 Lite", org: "deepseek-ai", type: "code", tags: ["coding", "reasoning", "128k", "math"], vram: 16, downloads: 300000, likes: 2800, featured: false, description: "16B MoE (2.4B active). Top-tier coding + math reasoning. 128K context." },
{ id: "openai/whisper-large-v3", name: "Whisper Large v3", org: "openai", type: "audio", tags: ["asr", "transcription", "multilingual", "translation"], vram: 8, downloads: 1500000, likes: 9500, featured: true, description: "State-of-the-art speech recognition. 99+ languages. Translation to English." },
{ id: "openai/whisper-large-v3-turbo", name: "Whisper Large v3 Turbo", org: "openai", type: "audio", tags: ["asr", "fast", "transcription"], vram: 6, downloads: 800000, likes: 5200, featured: false, description: "8x faster than large-v3 with minimal quality loss. Best for real-time." },
{ id: "suno/bark", name: "Bark TTS", org: "suno", type: "audio", tags: ["tts", "voice", "multilingual", "music"], vram: 12, downloads: 200000, likes: 3500, featured: false, description: "Generative audio model. Text-to-speech, music, sound effects. Multiple speakers." },
{ id: "coqui-ai/TTS", name: "Coqui TTS (XTTS v2)", org: "coqui-ai", type: "audio", tags: ["tts", "voice-cloning", "multilingual"], vram: 6, downloads: 150000, likes: 2800, featured: false, description: "Zero-shot voice cloning in 17 languages. 6s reference audio needed." },
{ id: "google/siglip-so400m-patch14-384", name: "SigLIP SO400M", org: "google", type: "vision", tags: ["clip", "embeddings", "zero-shot", "classification"], vram: 4, downloads: 100000, likes: 1200, featured: false, description: "Sigmoid loss CLIP. Better zero-shot classification. 384x384 resolution." },
{ id: "openai/clip-vit-large-patch14", name: "CLIP ViT-L/14", org: "openai", type: "vision", tags: ["clip", "embeddings", "retrieval", "classification"], vram: 4, downloads: 500000, likes: 4500, featured: false, description: "Classic CLIP. Image-text embeddings for search, classification, retrieval." },
{ id: "BAAI/bge-large-en-v1.5", name: "BGE Large EN v1.5", org: "BAAI", type: "embed", tags: ["embeddings", "retrieval", "rag", "english"], vram: 2, downloads: 800000, likes: 5800, featured: true, description: "Top MTEB retrieval model. 1024-dim. Best for English RAG." },
{ id: "intfloat/multilingual-e5-large", name: "Multilingual E5 Large", org: "intfloat", type: "embed", tags: ["embeddings", "multilingual", "retrieval", "100-langs"], vram: 2, downloads: 400000, likes: 3200, featured: false, description: "100 languages. Strong cross-lingual retrieval. 1024-dim." },
{ id: "microsoft/Phi-3-vision-128k-instruct", name: "Phi-3 Vision 128K", org: "microsoft", type: "vision", tags: ["vlm", "ocr", "charts", "documents", "128k"], vram: 8, downloads: 150000, likes: 2100, featured: false, description: "4.2B VLM. OCR, chart reading, document understanding. 128K context." },
{ id: "llava-hf/llava-v1.6-mistral-7b-hf", name: "LLaVA 1.6 Mistral 7B", org: "llava-hf", type: "vision", tags: ["vlm", "chat", "reasoning", "images"], vram: 16, downloads: 200000, likes: 2500, featured: false, description: "Visual chat assistant. Strong reasoning on images. Mistral 7B backbone." },
{ id: "Salesforce/blip2-opt-2.7b", name: "BLIP-2 OPT 2.7B", org: "Salesforce", type: "vision", tags: ["captioning", "vqa", "lightweight"], vram: 6, downloads: 100000, likes: 1800, featured: false, description: "Lightweight image captioning and VQA. Good for edge deployment." },
{ id: "google/gemma-2-9b-it", name: "Gemma 2 9B Instruct", org: "google", type: "llm", tags: ["chat", "instruction", "safety", "multilingual"], vram: 12, downloads: 400000, likes: 3800, featured: false, description: "Google's open model. Strong safety alignment. 8K context." },
{ id: "allenai/OLMo-7B-Instruct", name: "OLMo 7B Instruct", org: "allenai", type: "llm", tags: ["open", "transparent", "chat", "research"], vram: 8, downloads: 50000, likes: 800, featured: false, description: "Fully open (data, code, weights). Truly transparent LLM for research." },
{ id: "NVIDIA/Nemotron-3-8B-Instruct-4k", name: "Nemotron-3 8B Instruct", org: "NVIDIA", type: "llm", tags: ["chat", "reasoning", "alignment", "helpful"], vram: 16, downloads: 80000, likes: 1200, featured: false, description: "NVIDIA's aligned model. Strong helpfulness and reasoning. 4K context." },
{ id: "HuggingFaceH4/zephyr-7b-beta", name: "Zephyr 7B Beta", org: "HuggingFaceH4", type: "llm", tags: ["chat", "dpo", "alignment", "helpful"], vram: 8, downloads: 600000, likes: 4200, featured: false, description: "DPO-aligned Mistral. Very helpful assistant. No system prompt needed." },
];
const GPU_OPTIONS = [
{ id: "rtx-3080", name: "RTX 3080", vram: "10 GB", price: 0.17, spot: 0.085, featured: false, minVram: 0, maxVram: 10 },
{ id: "rtx-3090", name: "RTX 3090", vram: "24 GB", price: 0.22, spot: 0.11, featured: true, minVram: 0, maxVram: 24 },
{ id: "rtx-4090", name: "RTX 4090", vram: "24 GB", price: 0.44, spot: 0.22, featured: false, minVram: 0, maxVram: 24 },
{ id: "a10g", name: "A10G", vram: "24 GB", price: 0.55, spot: 0.275, featured: false, minVram: 0, maxVram: 24 },
{ id: "a100-40", name: "A100 40GB", vram: "40 GB", price: 1.10, spot: 0.55, featured: false, minVram: 24, maxVram: 40 },
{ id: "a100-80", name: "A100 80GB", vram: "80 GB", price: 2.20, spot: 1.10, featured: false, minVram: 40, maxVram: 80 },
{ id: "h100", name: "H100 80GB", vram: "80 GB", price: 4.50, spot: 2.25, featured: false, minVram: 40, maxVram: 999 },
];
let selectedModel = null;
let selectedGpu = null;
let currentFilter = 'all';
let searchQuery = '';
function formatNumber(n) {
if (n >= 1000000) return (n/1000000).toFixed(1) + 'M';
if (n >= 1000) return (n/1000).toFixed(1) + 'K';
return n.toString();
}
function getTypeIcon(type) {
const icons = { llm: '🤖', code: '💻', audio: '🔊', vision: '👁️', embed: '🔍' };
return icons[type] || '📦';
}
function getTypeLabel(type) {
const labels = { llm: 'LLM', code: 'Code', audio: 'Audio', vision: 'Vision', embed: 'Embeddings' };
return labels[type] || type;
}
function renderStats() {
const stats = [
{ num: HF_MODELS.length, label: 'Models Indexed' },
{ num: HF_MODELS.filter(m => m.featured).length, label: 'Featured' },
{ num: GPU_OPTIONS.length, label: 'GPU Types' },
{ num: HF_MODELS.reduce((a,b) => a + b.downloads, 0), label: 'Total Downloads' },
];
document.getElementById('stats-bar').innerHTML = stats.map(s => `
<div class="stat-card">
<div class="stat-number">${typeof s.num === 'number' && s.num > 10000 ? formatNumber(s.num) : s.num}</div>
<div class="stat-label">${s.label}</div>
</div>
`).join('');
}
function renderFeatured() {
const featured = HF_MODELS.filter(m => m.featured);
document.getElementById('featured-grid').innerHTML = featured.map(m => createModelCard(m)).join('');
}
function createFilterTabs() {
const types = ['all', 'llm', 'code', 'audio', 'vision', 'embed'];
const labels = { all: 'All', llm: 'LLM/Chat', code: 'Code', audio: 'Audio', vision: 'Vision', embed: 'Embeddings' };
document.getElementById('filter-tabs').innerHTML = types.map(t => `
<button class="filter-tab ${t === currentFilter ? 'active' : ''}" data-filter="${t}">${labels[t]}</button>
`).join('');
document.querySelectorAll('.filter-tab').forEach(btn => {
btn.addEventListener('click', () => {
currentFilter = btn.dataset.filter;
document.querySelectorAll('.filter-tab').forEach(b => b.classList.toggle('active', b.dataset.filter === currentFilter));
renderModels();
});
});
}
function renderModels() {
let models = HF_MODELS;
if (currentFilter !== 'all') models = models.filter(m => m.type === currentFilter);
if (searchQuery) {
const q = searchQuery.toLowerCase();
models = models.filter(m =>
m.name.toLowerCase().includes(q) ||
m.id.toLowerCase().includes(q) ||
m.org.toLowerCase().includes(q) ||
m.tags.some(t => t.toLowerCase().includes(q))
);
}
document.getElementById('models-grid').innerHTML = models.map(m => createModelCard(m)).join('');
document.querySelectorAll('.model-card').forEach(card => {
card.addEventListener('click', (e) => {
if (!e.target.closest('.deploy-btn')) {
const modelId = card.dataset.modelId;
const model = HF_MODELS.find(m => m.id === modelId);
if (model) openDeployModal(model);
}
});
});
}
function createModelCard(model) {
const recommendedGpus = GPU_OPTIONS.filter(g => g.maxVram >= model.vram && g.minVram <= model.vram).slice(0, 2);
return `
<article class="model-card ${model.featured ? 'featured' : ''}" data-model-id="${model.id}">
<div class="model-header">
<div class="model-avatar">${getTypeIcon(model.type)}</div>
<div class="model-info">
<div class="model-name">${model.name}</div>
<div class="model-org"><span class="hf-logo">🤗</span> ${model.org}</div>
</div>
</div>
<div class="model-meta">
<span class="model-tag ${model.type}">${getTypeLabel(model.type)}</span>
<span class="gpu-badge">≥${model.vram}GB VRAM</span>
</div>
<p class="model-desc">${model.description}</p>
<div class="model-footer">
<div class="model-stats">
<span class="model-stat">⬇ ${formatNumber(model.downloads)}</span>
<span class="model-stat">♥ ${formatNumber(model.likes)}</span>
</div>
<button class="deploy-btn" onclick="event.stopPropagation(); openDeployModal(HF_MODELS.find(m => m.id === '${model.id}'))">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
Deploy
</button>
</div>
</article>
`;
}
function renderGpus() {
document.getElementById('gpu-grid').innerHTML = GPU_OPTIONS.map(gpu => `
<div class="dimension-card ${gpu.featured ? 'dim-prism' : 'dim-nexus'}">
<span class="dim-icon">${gpu.featured ? '⭐' : '🎮'}</span>
<h3>${gpu.name}</h3>
<p>${gpu.vram} VRAM · ${gpu.minVram > 0 ? 'Best for ' + gpu.minVram + 'GB+' : 'Entry level'}</p>
<div style="margin: 16px 0; text-align: left;">
<div style="display:flex; justify-content:space-between; margin-bottom:4px;">
<span style="color:var(--text2); font-size:0.8rem;">On-Demand</span>
<span style="font-weight:700; color:var(--hf-yellow);">$${gpu.price}/hr</span>
</div>
<div style="display:flex; justify-content:space-between;">
<span style="color:var(--text2); font-size:0.8rem;">Spot (save ${Math.round((1-gpu.spot/gpu.price)*100)}%)</span>
<span style="font-weight:700; color:var(--model-green);">$${gpu.spot}/hr</span>
</div>
</div>
<a href="#models" class="dim-cta">Select for Deployment →</a>
</div>
`).join('');
}
let deployModalModel = null;
let deployModalGpu = null;
function openDeployModal(model, gpuHint = null) {
deployModalModel = model;
deployModalGpu = null;
const modal = document.getElementById('deploy-modal');
const title = document.getElementById('modal-title');
const info = document.getElementById('modal-model-info');
const gpuOptions = document.getElementById('gpu-options');
if (model) {
title.textContent = `Deploy ${model.name}`;
info.innerHTML = `
<div class="glass" style="padding: 16px; border-radius: var(--radius-sm);">
<div class="model-header" style="margin-bottom: 12px;">
<div class="model-avatar">${getTypeIcon(model.type)}</div>
<div class="model-info">
<div class="model-name">${model.name}</div>
<div class="model-org"><span class="hf-logo">🤗</span> ${model.org} · ${model.id}</div>
</div>
</div>
<div class="model-meta">
<span class="model-tag ${model.type}">${getTypeLabel(model.type)}</span>
<span class="gpu-badge">Min VRAM: ${model.vram}GB</span>
</div>
</div>
`;
const suitable = GPU_OPTIONS.filter(g => g.maxVram >= model.vram);
gpuOptions.innerHTML = suitable.map(g => `
<div class="gpu-option ${g.featured ? 'featured' : ''}" data-gpu="${g.id}" onclick="selectGpu('${g.id}')">
<div class="gpu-name">${g.name} ${g.featured ? '⭐' : ''}</div>
<div class="gpu-vram">${g.vram} VRAM</div>
<div class="gpu-price">$${g.price}/hr <span style="color:var(--model-green); font-size:0.8rem;">(spot $${g.spot})</span></div>
</div>
`).join('');
if (gpuHint === 'featured') {
const rec = suitable.find(g => g.featured) || suitable[0];
if (rec) selectGpu(rec.id);
}
} else {
title.textContent = 'Quick Deploy';
info.innerHTML = '<p style="color:var(--text2);">Select a model from the catalog first, or enter a Hugging Face model ID.</ gpuOptions.innerHTML = GPU_OPTIONS.map(g => `
<div class="gpu-option ${g.featured ? 'featured' : ''}" data-gpu="${g.id}" onclick="selectGpu('${g.id}')">
<div class="gpu-name">${g.name} ${g.featured ? '' : ''}</div>
<div class="gpu-vram">${g.vram} VRAM</div>
<div class="gpu-price">$${g.price}/hr</div>
</div>
`).join('');
}
modal.classList.add('open');
document.body.style.overflow = 'hidden';
}
function closeDeployModal() {
document.getElementById('deploy-modal').classList.remove('open');
document.body.style.overflow = '';
deployModalModel = null;
deployModalGpu = null;
}
function selectGpu(gpuId) {
deployModalGpu = gpuId;
document.querySelectorAll('.gpu-option').forEach(el => {
el.classList.toggle('selected', el.dataset.gpu === gpuId);
});
}
function submitDeployment() {
if (!deployModalModel) {
alert('Please select a model first');
return;
}
if (!deployModalGpu) {
alert('Please select a GPU');
return;
}
const gpu = GPU_OPTIONS.find(g => g.id === deployModalGpu);
const model = deployModalModel;
const endpoint = `https://${model.id.replace('/', '-')}-${deployModalGpu}.runpod.io`;
alert(`🚀 Deploying ${model.name} on ${gpu.name}...\n\nEndpoint: ${endpoint}\n\nThis would trigger RunPod API to create a serverless endpoint.\nEstimated cost: $${gpu.price}/hr (spot: $${gpu.spot}/hr)\n\n💡 Pro tip: Sign up at https://runpod.io?ref=lc0jiiz3 for $10 free credits!`);
closeDeployModal();
}
// Handle form submission
const searchForm = document.getElementById('search-form');
if (searchForm) {
searchForm.addEventListener('submit', (e) => {
e.preventDefault();
document.getElementById('models').scrollIntoView({ behavior: 'smooth' });
});
}
document.getElementById('search-input').addEventListener('input', (e) => {
searchQuery = e.target.value.trim();
renderModels();
});
document.getElementById('search-input').addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
document.getElementById('models').scrollIntoView({ behavior: 'smooth' });
}
});
document.getElementById('deploy-modal').addEventListener('click', (e) => {
if (e.target === e.currentTarget) closeDeployModal();
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeDeployModal();
});
function scrollToModels() {
document.getElementById('models').scrollIntoView({ behavior: 'smooth' });
}
document.addEventListener('DOMContentLoaded', () => {
renderStats();
renderFeatured();
createFilterTabs();
renderModels();
renderGpus();
// Signal bar animation
let signal = 0;
setInterval(() => {
signal = Math.min(100, signal + Math.random() * 15);
if (signal > 95) signal = Math.random() * 20;
document.getElementById('signal-fill').style.width = signal + '%';
document.getElementById('signal-pct').textContent = Math.round(signal) + '%';
}, 2000);
});
function scrollToModels() {
document.getElementById('models').scrollIntoView({ behavior: 'smooth' });
}