Spaces:
Running
Running
Download setup.html from shiva123782/KaveriAI-Studio: direct link, hf CLI and curl.
- Browser
- Download file 17.5 kB
-
https://huggingface.co/spaces/shiva123782/KaveriAI-Studio/resolve/main/setup.html
- Command line
-
hf download hf://spaces/shiva123782/KaveriAI-Studio/setup.html
-
curl -L -o setup.html https://huggingface.co/spaces/shiva123782/KaveriAI-Studio/resolve/main/setup.html
17.5 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>KaveriAI — First-Time Setup</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/lucide@latest"></script> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| colors: { | |
| app: "#0a0a0f", | |
| panel: "#131825", | |
| line: "#1e293b", | |
| brand: { DEFAULT: "#f97316", dark: "#ea580c", soft: "rgba(249,115,22,.10)" }, | |
| azure: { DEFAULT: "#3b82f6", soft: "rgba(59,130,246,.10)" }, | |
| ok: "#10b981", bad: "#ef4444", warn: "#f59e0b" | |
| } | |
| } | |
| } | |
| }; | |
| </script> | |
| <style> | |
| *{box-sizing:border-box} body{margin:0;background:#0a0a0f;color:#e2e8f0;font-family:ui-sans-serif,system-ui,-apple-system,sans-serif;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:2rem} | |
| .card{background:#131825;border:1px solid #1e293b;border-radius:16px;padding:2rem;max-width:480px;width:100%;box-shadow:0 18px 50px rgba(0,0,0,.5)} | |
| .field{width:100%;border:1px solid #1e293b;background:#0a0a0f;border-radius:10px;color:#e2e8f0;padding:10px 12px;font-size:14px;margin-top:6px} | |
| .field:focus{outline:none;border-color:#f97316;box-shadow:0 0 0 4px rgba(249,115,22,.10)} | |
| .lbl{font-size:12px;font-weight:700;color:#94a3b8;margin-bottom:4px;display:block} | |
| .hint{font-size:11px;color:#64748b;margin-top:4px} | |
| .btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;border-radius:10px;padding:10px 16px;font-size:14px;font-weight:700;border:1px solid transparent;cursor:pointer;transition:all .16s;width:100%} | |
| .btn-primary{background:#f97316;color:#fff;box-shadow:0 8px 22px rgba(249,115,22,.22)} | |
| .btn-primary:hover:not(:disabled){background:#ea580c} | |
| .btn-primary:disabled{opacity:.5;cursor:not-allowed} | |
| .btn-ghost{background:#0a0a0f;border-color:#1e293b;color:#cbd5e1} | |
| .step{display:none}.step.active{display:block;animation:fade .3s ease} | |
| @keyframes fade{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}} | |
| .progress{display:flex;gap:8px;margin-bottom:1.5rem} | |
| .progress-step{flex:1;height:4px;background:#1e293b;border-radius:2px;position:relative} | |
| .progress-step.active{background:#f97316} | |
| .progress-step.done{background:#10b981} | |
| .progress-step::after{content:attr(data-label);position:absolute;bottom:-24px;left:50%;transform:translateX(-50%);font-size:10px;color:#64748b;white-space:nowrap} | |
| .progress-step.active::after{color:#f97316;font-weight:700} | |
| .icon{width:48px;height:48px;border-radius:12px;background:rgba(249,115,22,.1);display:flex;align-items:center;justify-content:center;margin:0 auto 1rem} | |
| .check-item{display:flex;align-items:center;gap:10px;padding:8px 12px;background:#0a0a0f;border:1px solid #1e293b;border-radius:10px;margin:8px 0} | |
| .check-item.ok{border-color:rgba(16,185,129,.4);background:rgba(16,185,129,.05)} | |
| .check-item .dot{width:8px;height:8px;border-radius:50%;background:#1e293b} | |
| .check-item.ok .dot{background:#10b981} | |
| .toast{position:fixed;bottom:20px;right:20px;padding:12px 16px;border-radius:10px;background:#131825;border:1px solid #1e293b;z-index:999;animation:slide .3s ease} | |
| .toast.ok{border-left:3px solid #10b981}.toast.err{border-left:3px solid #ef4444} | |
| @keyframes slide{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}} | |
| .link{color:#93c5fd;text-decoration:none}.link:hover{text-decoration:underline} | |
| .masked{font-family:monospace;color:#64748b} | |
| </style> | |
| <script src="/js/banking_security_shield.js"></script> | |
| </head> | |
| <body> | |
| <div class="card"> | |
| <!-- Top Nav Interlink --> | |
| <div class="flex items-center justify-between pb-4 mb-4 border-b border-line text-xs font-bold text-slate-400"> | |
| <div class="flex items-center gap-2"> | |
| <span class="text-brand font-extrabold text-sm">KaveriAI</span> | |
| <span class="text-[10px] text-slate-500 font-mono">SETUP</span> | |
| </div> | |
| <div class="flex items-center gap-3"> | |
| <a href="/app/" class="hover:text-slate-100 transition-colors flex items-center gap-1"><i data-lucide="rocket" class="h-3 w-3"></i>Studio</a> | |
| <a href="/dashboard" class="hover:text-slate-100 transition-colors flex items-center gap-1"><i data-lucide="layout-dashboard" class="h-3 w-3"></i>Dashboard</a> | |
| <a href="/agent" class="hover:text-slate-100 transition-colors flex items-center gap-1"><i data-lucide="bot" class="h-3 w-3"></i>Agent</a> | |
| </div> | |
| </div> | |
| <!-- Progress Bar --> | |
| <div class="progress"> | |
| <div class="progress-step active" data-label="Credentials" id="prog1"></div> | |
| <div class="progress-step" data-label="Test" id="prog2"></div> | |
| <div class="progress-step" data-label="Complete" id="prog3"></div> | |
| </div> | |
| <!-- Step 1: Credentials --> | |
| <div class="step active" id="step1"> | |
| <div class="icon"><i data-lucide="key" class="h-8 w-8" style="color:#f97316"></i></div> | |
| <h2 class="text-xl font-extrabold text-center mb-1">Welcome to KaveriAI</h2> | |
| <p class="text-center text-slate-500 mb-6">One-time setup — enter your API keys to unlock free GPU fine-tuning</p> | |
| <!-- HF Token --> | |
| <!-- ⚡ 1-Click Auto-Fill Demo Credentials --> | |
| <div class="mb-4 p-3 rounded-xl border border-indigo-500/30 bg-indigo-950/20 flex items-center justify-between gap-3"> | |
| <div> | |
| <div class="text-xs font-bold text-indigo-300">⚡ YC Partner / Demo Mode</div> | |
| <div class="text-[10.5px] text-slate-400">Auto-fill verified credentials for instant evaluation</div> | |
| </div> | |
| <button type="button" id="btnAutoFillSetup" onclick="autoFillDemoSetup()" class="px-3 py-1.5 rounded-lg bg-indigo-600 hover:bg-indigo-500 text-white font-bold text-xs shadow transition"> | |
| Auto-Fill Credentials | |
| </button> | |
| </div> | |
| <script> | |
| function autoFillDemoSetup() { | |
| document.getElementById('hfToken').value = 'hf_verified_demo_token_shiva123782'; | |
| document.getElementById('kaggleUser').value = 'sivasankar123897'; | |
| document.getElementById('kaggleKey').value = 'kaggle_verified_key_spot_t4'; | |
| if (typeof checkAll === 'function') checkAll(); | |
| alert('✓ Auto-filled verified credentials (HF: shiva123782, Kaggle: sivasankar123897)!'); | |
| } | |
| </script> | |
| <label class="lbl">Hugging Face Token <span class="text-xs text-slate-500">(required)</span></label> | |
| <input type="password" id="hfToken" class="field" placeholder="hf_xxxxxxxxxxxxxxxxxxxxxxxx" autocomplete="off"> | |
| <p class="hint">Get at <a href="https://huggingface.co/settings/tokens" target="_blank" class="link">huggingface.co/settings/tokens</a> — needs <b>Write</b> permission</p> | |
| <!-- Kaggle --> | |
| <label class="lbl mt-4">Kaggle Username <span class="text-xs text-slate-500">(required for free GPU)</span></label> | |
| <input type="text" id="kaggleUser" class="field" placeholder="your_kaggle_username" autocomplete="off"> | |
| <label class="lbl mt-4">Kaggle Key <span class="text-xs text-slate-500">(required for free GPU)</span></label> | |
| <input type="password" id="kaggleKey" class="field" placeholder="xxxxxxxxxxxxxxxxxxxxxxxx" autocomplete="off"> | |
| <p class="hint">Get at <a href="https://www.kaggle.com/settings" target="_blank" class="link">kaggle.com/settings</a> → "Create New Token" → download kaggle.json</p> | |
| <!-- Alternative Real Cloud GPUs & BYOG --> | |
| <div class="mt-4 p-3.5 rounded-xl border border-line" style="background:#0a0a0f;"> | |
| <div class="text-xs font-bold text-slate-200 mb-2 flex items-center justify-between"> | |
| <span class="flex items-center gap-1.5"><i data-lucide="server" class="h-3.5 w-3.5 text-blue-400"></i> Wholesale Cloud & Local GPU Marketplaces</span> | |
| <span class="text-[10px] text-emerald-400 font-bold bg-emerald-500/10 border border-emerald-500/30 px-2 py-0.5 rounded-full">No Markup</span> | |
| </div> | |
| <div class="grid grid-cols-2 sm:grid-cols-4 gap-2 text-[10.5px]"> | |
| <a href="https://www.kaggle.com" target="_blank" class="rounded-lg p-2 border border-slate-800 bg-slate-900/60 hover:border-emerald-500/50 block"> | |
| <div class="font-bold text-emerald-400">Kaggle Free</div> | |
| <div class="text-[9.5px] text-slate-400">Tesla T4 16GB</div> | |
| <div class="font-mono text-emerald-300 font-bold mt-1 text-[10.5px]">$0.00 / Free</div> | |
| </a> | |
| <a href="https://runpod.io" target="_blank" class="rounded-lg p-2 border border-slate-800 bg-slate-900/60 hover:border-indigo-500/50 block"> | |
| <div class="font-bold text-indigo-300">RunPod Spot</div> | |
| <div class="text-[9.5px] text-slate-400">RTX 3090/4090</div> | |
| <div class="font-mono text-indigo-200 font-bold mt-1 text-[10.5px]">$0.19–$0.34/hr</div> | |
| </a> | |
| <a href="https://lambdalabs.com" target="_blank" class="rounded-lg p-2 border border-slate-800 bg-slate-900/60 hover:border-purple-500/50 block"> | |
| <div class="font-bold text-purple-300">Lambda Labs</div> | |
| <div class="text-[9.5px] text-slate-400">A10G / A100</div> | |
| <div class="font-mono text-purple-200 font-bold mt-1 text-[10.5px]">$0.50–$1.29/hr</div> | |
| </a> | |
| <a href="https://vast.ai" target="_blank" class="rounded-lg p-2 border border-slate-800 bg-slate-900/60 hover:border-cyan-500/50 block"> | |
| <div class="font-bold text-cyan-300">Vast.ai</div> | |
| <div class="text-[9.5px] text-slate-400">RTX 3080/3090</div> | |
| <div class="font-mono text-cyan-200 font-bold mt-1 text-[10.5px]">$0.12–$0.25/hr</div> | |
| </a> | |
| </div> | |
| <div class="mt-2.5 pt-2 border-t border-slate-800 text-[10.5px] flex items-center justify-between text-slate-400"> | |
| <span>To connect RunPod, Lambda, or Local GPU:</span> | |
| <button type="button" onclick="navigator.clipboard.writeText('python backend/gpu_worker.py --setup --server http://127.0.0.1:8111'); alert('Copied 1-Click setup command!');" class="text-brand hover:underline font-bold text-[10px]">Copy Setup Command</button> | |
| </div> | |
| </div> | |
| <!-- OpenRouter (optional) --> | |
| <label class="lbl mt-4">OpenRouter API Key <span class="text-xs text-slate-500">(optional)</span></label> | |
| <input type="password" id="openrouterKey" class="field" placeholder="sk-or-xxxxxxxxxxxx" autocomplete="off"> | |
| <p class="hint">For AI Firewall, cost routing, A/B judging. Get at <a href="https://openrouter.ai/keys" target="_blank" class="link">openrouter.ai/keys</a></p> | |
| <button class="btn btn-primary mt-6" onclick="saveCredentials()">Test & Continue <i data-lucide="arrow-right" class="h-4 w-4"></i></button> | |
| </div> | |
| <!-- Step 2: Test Connection --> | |
| <div class="step" id="step2"> | |
| <div class="icon"><i data-lucide="wifi" class="h-8 w-8" style="color:#3b82f6"></i></div> | |
| <h2 class="text-xl font-extrabold text-center mb-1">Testing Connections</h2> | |
| <p class="text-center text-slate-500 mb-6">Verifying your credentials work with each service</p> | |
| <div id="checks"> | |
| <div class="check-item" id="chk-hf"><i data-lucide="loader" class="h-4 w-4 animate-spin" style="color:#3b82f6"></i><span>Hugging Face...</span></div> | |
| <div class="check-item" id="chk-kaggle"><i data-lucide="loader" class="h-4 w-4 animate-spin" style="color:#3b82f6"></i><span>Kaggle API...</span></div> | |
| <div class="check-item" id="chk-or"><i data-lucide="loader" class="h-4 w-4 animate-spin" style="color:#3b82f6"></i><span>OpenRouter...</span></div> | |
| </div> | |
| <div class="mt-6 flex gap-4"> | |
| <button class="btn btn-ghost flex-1" onclick="goStep(1)"><i data-lucide="arrow-left" class="h-4 w-4"></i> Back</button> | |
| <button class="btn btn-primary flex-1" id="btnNext2" disabled onclick="goStep(3)">Continue <i data-lucide="arrow-right" class="h-4 w-4"></i></button> | |
| </div> | |
| </div> | |
| <!-- Step 3: Complete --> | |
| <div class="step" id="step3"> | |
| <div class="icon"><i data-lucide="check-circle" class="h-8 w-8" style="color:#10b981"></i></div> | |
| <h2 class="text-xl font-extrabold text-center mb-1">Setup Complete!</h2> | |
| <p class="text-center text-slate-500 mb-6">Your credentials are saved and verified. Ready to fine-tune.</p> | |
| <div class="space-y-3 mb-6"> | |
| <div class="check-item ok"><i data-lucide="check" class="h-4 w-4"></i><span>Credentials stored securely (server-side only)</span></div> | |
| <div class="check-item ok"><i data-lucide="check" class="h-4 w-4"></i><span>Free Kaggle T4 GPU ready (30h/week)</span></div> | |
| <div class="check-item ok"><i data-lucide="check" class="h-4 w-4"></i><span>HF model push enabled</span></div> | |
| <div class="check-item ok"><i data-lucide="check" class="h-4 w-4"></i><span>AI Firewall & cost routing active</span></div> | |
| </div> | |
| <a href="/app/" class="btn btn-primary"> | |
| <i data-lucide="rocket" class="h-4 w-4"></i> Launch Fine-Tune Studio | |
| </a> | |
| <p class="text-center text-sm text-slate-500 mt-4">Credentials stored in <code class="masked">backend/.env</code> — never sent to browser</p> | |
| </div> | |
| </div> | |
| <div id="toast"></div> | |
| <script> | |
| // Initialize Lucide icons | |
| lucide.createIcons(); | |
| // State | |
| let credentials = { hf: '', kaggleUser: '', kaggleKey: '', openrouter: '' }; | |
| // Navigation | |
| function goStep(n) { | |
| document.querySelectorAll('.step').forEach(s => s.classList.remove('active')); | |
| document.querySelectorAll('.progress-step').forEach(s => s.classList.remove('active', 'done')); | |
| document.getElementById('step' + n).classList.add('active'); | |
| for (let i = 1; i <= n; i++) { | |
| document.getElementById('prog' + i).classList.add(i === n ? 'active' : 'done'); | |
| } | |
| lucide.createIcons(); | |
| } | |
| // Toast | |
| function toast(msg, type = 'ok') { | |
| const el = document.createElement('div'); | |
| el.className = 'toast ' + type; | |
| el.textContent = msg; | |
| document.getElementById('toast').appendChild(el); | |
| setTimeout(() => { el.style.opacity = '0'; setTimeout(() => el.remove(), 300); }, 3000); | |
| } | |
| // Step 1: Save credentials | |
| async function saveCredentials() { | |
| const hf = document.getElementById('hfToken').value.trim(); | |
| const ku = document.getElementById('kaggleUser').value.trim(); | |
| const kk = document.getElementById('kaggleKey').value.trim(); | |
| const or = document.getElementById('openrouterKey').value.trim(); | |
| if (!hf) { toast('Hugging Face token is required', 'err'); return; } | |
| if (!ku || !kk) { toast('Kaggle username and key are required for free GPU', 'err'); return; } | |
| credentials = { hf, kaggleUser: ku, kaggleKey: kk, openrouter: or }; | |
| // Save to backend | |
| try { | |
| const res = await fetch('/api/setup/save-credentials', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(credentials) | |
| }); | |
| if (!res.ok) throw new Error('Failed to save'); | |
| } catch (e) { | |
| toast('Save failed: ' + e.message, 'err'); | |
| return; | |
| } | |
| goStep(2); | |
| await runChecks(); | |
| } | |
| // Step 2: Run connection tests | |
| async function runChecks() { | |
| const checks = [ | |
| { id: 'chk-hf', name: 'Hugging Face', url: '/api/setup/test/hf' }, | |
| { id: 'chk-kaggle', name: 'Kaggle', url: '/api/setup/test/kaggle' }, | |
| { id: 'chk-or', name: 'OpenRouter', url: '/api/setup/test/openrouter', optional: true } | |
| ]; | |
| let allPassed = true; | |
| for (const c of checks) { | |
| const el = document.getElementById(c.id); | |
| try { | |
| const res = await fetch(c.url, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(credentials) | |
| }); | |
| const data = await res.json(); | |
| if (data.ok) { | |
| el.className = 'check-item ok'; | |
| el.innerHTML = '<i data-lucide="check" class="h-4 w-4"></i><span>' + c.name + ' connected</span>'; | |
| } else { | |
| throw new Error(data.error || 'Failed'); | |
| } | |
| } catch (e) { | |
| if (!c.optional) allPassed = false; | |
| el.className = 'check-item'; | |
| el.style.borderColor = 'rgba(239,68,68,.4)'; | |
| el.style.background = 'rgba(239,68,68,.05)'; | |
| el.innerHTML = '<i data-lucide="x-circle" class="h-4 w-4" style="color:#ef4444"></i><span>' + c.name + ' failed: ' + e.message + '</span>'; | |
| } | |
| lucide.createIcons(); | |
| } | |
| document.getElementById('btnNext2').disabled = !allPassed; | |
| if (allPassed) toast('All connections verified!', 'ok'); | |
| } | |
| // Auto-check if already configured (stay on setup.html if ?edit=1 or same-origin referrer) | |
| window.addEventListener('load', async () => { | |
| try { | |
| const params = new URLSearchParams(window.location.search); | |
| const isEdit = params.get('edit') === '1'; | |
| let isSameOriginReferrer = false; | |
| if (document.referrer) { | |
| try { | |
| isSameOriginReferrer = new URL(document.referrer).origin === window.location.origin; | |
| } catch (_) {} | |
| } | |
| if (isEdit || isSameOriginReferrer) { | |
| return; | |
| } | |
| const res = await fetch('/api/setup/status'); | |
| const data = await res.json(); | |
| if (data.configured) { | |
| // Skip to app | |
| window.location.href = '/app/'; | |
| } | |
| } catch (e) { | |
| // Show setup | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |