Download static/index.html from gsstec/fastsdcpu: direct link, hf CLI and curl.
- Browser
- Download file 6.9 kB
-
https://huggingface.co/spaces/gsstec/fastsdcpu/resolve/main/static/index.html
- Command line
-
hf download hf://spaces/gsstec/fastsdcpu/static/index.html
-
curl -L -o index.html https://huggingface.co/spaces/gsstec/fastsdcpu/resolve/main/static/index.html
6.9 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>LFM2-700M Chat</title> | |
| <style> | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| body { | |
| font-family: -apple-system, "Segoe UI", system-ui, sans-serif; | |
| background: #f7f8fa; | |
| color: #1f2328; | |
| height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| padding: 24px 16px; | |
| } | |
| h1 { | |
| font-size: 1.2rem; | |
| font-weight: 600; | |
| margin-bottom: 16px; | |
| color: #1f2328; | |
| } | |
| h1 span { color: #3b82d4; } | |
| #chat-box { | |
| width: 100%; | |
| max-width: 720px; | |
| flex: 1; | |
| overflow-y: auto; | |
| background: #fff; | |
| border: 1px solid #e5e7eb; | |
| border-radius: 10px; | |
| padding: 16px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| margin-bottom: 16px; | |
| } | |
| .msg { | |
| max-width: 85%; | |
| padding: 10px 14px; | |
| border-radius: 10px; | |
| line-height: 1.6; | |
| font-size: 0.93rem; | |
| white-space: pre-wrap; | |
| word-break: break-word; | |
| } | |
| .msg.user { | |
| align-self: flex-end; | |
| background: #3b82d4; | |
| color: #fff; | |
| border-bottom-right-radius: 2px; | |
| } | |
| .msg.assistant { | |
| align-self: flex-start; | |
| background: #f0f2f5; | |
| color: #1f2328; | |
| border-bottom-left-radius: 2px; | |
| } | |
| .msg.thinking { | |
| align-self: flex-start; | |
| background: #f0f2f5; | |
| color: #57606a; | |
| font-style: italic; | |
| animation: pulse 1.2s ease-in-out infinite; | |
| } | |
| @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.4} } | |
| #input-row { | |
| display: flex; | |
| width: 100%; | |
| max-width: 720px; | |
| gap: 8px; | |
| } | |
| #question { | |
| flex: 1; | |
| padding: 10px 14px; | |
| font-size: 0.95rem; | |
| border: 1px solid #e5e7eb; | |
| border-radius: 8px; | |
| outline: none; | |
| background: #fff; | |
| resize: none; | |
| height: 44px; | |
| line-height: 1.4; | |
| font-family: inherit; | |
| } | |
| #question:focus { border-color: #3b82d4; } | |
| #send-btn { | |
| padding: 0 20px; | |
| height: 44px; | |
| background: #3b82d4; | |
| color: #fff; | |
| border: none; | |
| border-radius: 8px; | |
| font-size: 0.95rem; | |
| font-weight: 500; | |
| cursor: pointer; | |
| white-space: nowrap; | |
| } | |
| #send-btn:disabled { background: #a5c4ef; cursor: not-allowed; } | |
| #send-btn:hover:not(:disabled) { background: #2b6cbf; } | |
| #settings { | |
| display: flex; | |
| gap: 12px; | |
| width: 100%; | |
| max-width: 720px; | |
| margin-bottom: 10px; | |
| flex-wrap: wrap; | |
| } | |
| .setting { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| font-size: 0.82rem; | |
| color: #57606a; | |
| } | |
| .setting input[type=range] { width: 80px; } | |
| .setting span { min-width: 28px; text-align: right; font-weight: 500; color: #1f2328; } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>🤖 <span>LFM2-700M</span> Chat</h1> | |
| <div id="settings"> | |
| <div class="setting"> | |
| Max tokens | |
| <input type="range" id="max_tokens" min="64" max="1024" step="64" value="256" | |
| oninput="document.getElementById('mt_val').textContent=this.value"> | |
| <span id="mt_val">256</span> | |
| </div> | |
| <div class="setting"> | |
| Temperature | |
| <input type="range" id="temperature" min="0.01" max="1.5" step="0.01" value="0.3" | |
| oninput="document.getElementById('temp_val').textContent=parseFloat(this.value).toFixed(2)"> | |
| <span id="temp_val">0.30</span> | |
| </div> | |
| <div class="setting"> | |
| Min-P | |
| <input type="range" id="min_p" min="0" max="1" step="0.01" value="0.15" | |
| oninput="document.getElementById('mp_val').textContent=parseFloat(this.value).toFixed(2)"> | |
| <span id="mp_val">0.15</span> | |
| </div> | |
| </div> | |
| <div id="chat-box"></div> | |
| <div id="input-row"> | |
| <textarea id="question" placeholder="Ask anything…" rows="1"></textarea> | |
| <button id="send-btn" onclick="sendQuestion()">Send</button> | |
| </div> | |
| <script> | |
| const API = ''; // same origin — served by FastAPI | |
| // Maintain conversation history for multi-turn chat | |
| const history = []; | |
| const chatBox = document.getElementById('chat-box'); | |
| const input = document.getElementById('question'); | |
| const sendBtn = document.getElementById('send-btn'); | |
| // Send on Enter (Shift+Enter = newline) | |
| input.addEventListener('keydown', e => { | |
| if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendQuestion(); } | |
| }); | |
| function appendMsg(role, text) { | |
| const div = document.createElement('div'); | |
| div.className = `msg ${role}`; | |
| div.textContent = text; | |
| chatBox.appendChild(div); | |
| chatBox.scrollTop = chatBox.scrollHeight; | |
| return div; | |
| } | |
| async function sendQuestion() { | |
| const q = input.value.trim(); | |
| if (!q) return; | |
| input.value = ''; | |
| sendBtn.disabled = true; | |
| // Add user turn to history and UI | |
| history.push({ role: 'user', content: q }); | |
| appendMsg('user', q); | |
| const replyDiv = appendMsg('thinking', '▍'); | |
| const body = { | |
| messages: history.slice(), | |
| max_new_tokens: parseInt(document.getElementById('max_tokens').value), | |
| temperature: parseFloat(document.getElementById('temperature').value), | |
| min_p: parseFloat(document.getElementById('min_p').value), | |
| repetition_penalty: 1.05, | |
| stream: true | |
| }; | |
| let fullText = ''; | |
| try { | |
| const res = await fetch(`${API}/chat`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(body) | |
| }); | |
| if (!res.ok) { | |
| const err = await res.text(); | |
| replyDiv.className = 'msg assistant'; | |
| replyDiv.textContent = `Error ${res.status}: ${err}`; | |
| } else { | |
| // Stream tokens as they arrive | |
| const reader = res.body.getReader(); | |
| const decoder = new TextDecoder(); | |
| replyDiv.className = 'msg assistant'; | |
| replyDiv.textContent = ''; | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| fullText += decoder.decode(value, { stream: true }); | |
| replyDiv.textContent = fullText; | |
| chatBox.scrollTop = chatBox.scrollHeight; | |
| } | |
| // Store assistant reply in history for multi-turn context | |
| history.push({ role: 'assistant', content: fullText }); | |
| } | |
| } catch (err) { | |
| replyDiv.className = 'msg assistant'; | |
| replyDiv.textContent = `Network error: ${err.message}`; | |
| } | |
| sendBtn.disabled = false; | |
| input.focus(); | |
| chatBox.scrollTop = chatBox.scrollHeight; | |
| } | |
| </script> | |
| </body> | |
| </html> | |