Download sglang/docs/release_lookup/index.html from FasterDFlash/Hanrui: direct link, hf CLI and curl.
- Browser
- Download file 16 kB
-
https://huggingface.co/FasterDFlash/Hanrui/resolve/main/sglang/docs/release_lookup/index.html
- Command line
-
hf download hf://FasterDFlash/Hanrui/sglang/docs/release_lookup/index.html
-
curl -L -o index.html https://huggingface.co/FasterDFlash/Hanrui/resolve/main/sglang/docs/release_lookup/index.html
16 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>SGLang Release Lookup</title> | |
| <style> | |
| :root { | |
| --primary: #3b82f6; | |
| --primary-hover: #2563eb; | |
| --bg: #f8fafc; | |
| --card-bg: #ffffff; | |
| --text-main: #1e293b; | |
| --text-secondary: #64748b; | |
| --border: #e2e8f0; | |
| --success-bg: #f0fdf4; | |
| --success-border: #bbf7d0; | |
| --success-text: #166534; | |
| --error-bg: #fef2f2; | |
| --error-border: #fecaca; | |
| --error-text: #991b1b; | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | |
| background-color: var(--bg); | |
| color: var(--text-main); | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| margin: 0; | |
| padding: 20px; | |
| } | |
| .container { | |
| background-color: var(--card-bg); | |
| padding: 2.5rem; | |
| border-radius: 16px; | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); | |
| width: 100%; | |
| max-width: 550px; | |
| text-align: center; | |
| transition: transform 0.2s; | |
| } | |
| h1 { | |
| margin-top: 0; | |
| margin-bottom: 0.5rem; | |
| color: var(--text-main); | |
| font-size: 1.8rem; | |
| font-weight: 700; | |
| } | |
| p.subtitle { | |
| margin-bottom: 2rem; | |
| color: var(--text-secondary); | |
| font-size: 0.95rem; | |
| } | |
| .input-group { | |
| display: flex; | |
| gap: 12px; | |
| margin-bottom: 1.5rem; | |
| position: relative; | |
| } | |
| input { | |
| flex: 1; | |
| padding: 12px 16px; | |
| border: 2px solid var(--border); | |
| border-radius: 8px; | |
| font-size: 1rem; | |
| outline: none; | |
| transition: all 0.2s ease; | |
| color: var(--text-main); | |
| } | |
| input:focus { | |
| border-color: var(--primary); | |
| box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); | |
| } | |
| input::placeholder { | |
| color: #94a3b8; | |
| } | |
| button { | |
| padding: 12px 24px; | |
| background-color: var(--primary); | |
| color: white; | |
| border: none; | |
| border-radius: 8px; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: background-color 0.2s, transform 0.1s; | |
| } | |
| button:hover { | |
| background-color: var(--primary-hover); | |
| } | |
| button:active { | |
| transform: translateY(1px); | |
| } | |
| button:disabled { | |
| background-color: #cbd5e1; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| #result { | |
| margin-top: 1.5rem; | |
| text-align: left; | |
| border-radius: 8px; | |
| display: none; | |
| opacity: 0; | |
| transition: opacity 0.3s ease; | |
| } | |
| #result.visible { | |
| opacity: 1; | |
| } | |
| .result-content { | |
| padding: 1.25rem; | |
| border-radius: 8px; | |
| } | |
| .result-success { | |
| background-color: var(--success-bg); | |
| border: 1px solid var(--success-border); | |
| color: var(--success-text); | |
| } | |
| .result-error { | |
| background-color: var(--error-bg); | |
| border: 1px solid var(--error-border); | |
| color: var(--error-text); | |
| } | |
| .result-row { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-bottom: 0.5rem; | |
| align-items: baseline; | |
| } | |
| .result-row:last-child { | |
| margin-bottom: 0; | |
| } | |
| .result-label { | |
| font-weight: 600; | |
| margin-right: 1rem; | |
| min-width: 80px; | |
| } | |
| .tag-link { | |
| color: var(--primary); | |
| text-decoration: none; | |
| font-weight: bold; | |
| font-size: 1.1rem; | |
| } | |
| .tag-link:hover { | |
| text-decoration: underline; | |
| } | |
| .loader { | |
| display: inline-block; | |
| width: 18px; | |
| height: 18px; | |
| border: 3px solid rgba(59, 130, 246, 0.2); | |
| border-radius: 50%; | |
| border-top-color: var(--primary); | |
| animation: spin 1s linear infinite; | |
| margin-right: 8px; | |
| vertical-align: text-bottom; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| .status-msg { | |
| margin-top: 1rem; | |
| font-size: 0.85rem; | |
| color: var(--text-secondary); | |
| min-height: 20px; | |
| } | |
| .badge { | |
| display: inline-block; | |
| padding: 2px 8px; | |
| border-radius: 12px; | |
| font-size: 0.75rem; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| } | |
| .badge-main { | |
| background-color: #dbeafe; | |
| color: #1e40af; | |
| } | |
| .badge-gateway { | |
| background-color: #f3e8ff; | |
| color: #6b21a8; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>Release Lookup</h1> | |
| <p class="subtitle">Find which SGLang release first included your PR or commit.</p> | |
| <div class="input-group"> | |
| <input type="text" id="queryInput" placeholder="PR # (e.g. 1425), URL, or Commit Hash" autocomplete="off" /> | |
| <button id="searchBtn" disabled>Search</button> | |
| </div> | |
| <div id="loading" style="display: none; margin-bottom: 1rem; color: var(--text-secondary);"> | |
| <span class="loader"></span> Loading index... | |
| </div> | |
| <div id="result"></div> | |
| <div id="indexStatus" class="status-msg">Initializing...</div> | |
| </div> | |
| <script> | |
| let tagIndex = null; | |
| let tagsArray = null; // Compact format: array of [name, date, type] | |
| let sortedCommitKeys = null; // Sorted keys for binary prefix search | |
| const INDEX_FILE = 'release_index.json'; | |
| const SHORT_HASH_LEN = 8; | |
| const input = document.getElementById('queryInput'); | |
| const btn = document.getElementById('searchBtn'); | |
| const resultDiv = document.getElementById('result'); | |
| const loadingDiv = document.getElementById('loading'); | |
| const statusDiv = document.getElementById('indexStatus'); | |
| // Format date nicely (always in English) | |
| function formatDate(isoString) { | |
| if (!isoString) return 'Unknown'; | |
| try { | |
| return new Date(isoString).toLocaleDateString('en-US', { | |
| year: 'numeric', month: 'long', day: 'numeric' | |
| }); | |
| } catch(e) { return isoString; } | |
| } | |
| // Check if index is in compact format | |
| function isCompactFormat(data) { | |
| return Array.isArray(data.t); | |
| } | |
| // Get tag info by index (compact) or name (legacy) | |
| function getTagInfo(tagRef) { | |
| if (tagsArray) { | |
| // Compact format: tagRef is index | |
| const tag = tagsArray[tagRef]; | |
| return { | |
| name: tag[0], | |
| date: tag[1], | |
| type: tag[2] === 1 ? 'gateway' : 'main' | |
| }; | |
| } else { | |
| // Legacy format: tagRef is name | |
| const info = tagIndex.tags[tagRef]; | |
| return { name: tagRef, ...info }; | |
| } | |
| } | |
| // Parse compact tag reference: "m5" -> {type: 'm', idx: 5} | |
| function parseTagRef(ref) { | |
| if (typeof ref === 'string' && /^[mg]\d+$/.test(ref)) { | |
| return { | |
| type: ref[0], | |
| idx: parseInt(ref.slice(1)) | |
| }; | |
| } | |
| return null; | |
| } | |
| async function loadIndex() { | |
| loadingDiv.style.display = 'block'; | |
| statusDiv.innerText = 'Downloading index...'; | |
| try { | |
| const response = await fetch(INDEX_FILE); | |
| if (!response.ok) { | |
| throw new Error("No index file found. Please run generate_index.py."); | |
| } | |
| const data = await response.json(); | |
| // Handle both compact and legacy formats | |
| if (isCompactFormat(data)) { | |
| tagsArray = data.t; | |
| tagIndex = { | |
| prs: data.p, | |
| commits: data.c | |
| }; | |
| } else { | |
| tagIndex = data; | |
| tagsArray = null; | |
| } | |
| // Pre-sort commit keys for binary prefix search | |
| sortedCommitKeys = Object.keys(tagIndex.commits).sort(); | |
| const tagCount = tagsArray ? tagsArray.length : Object.keys(tagIndex.tags).length; | |
| const prCount = Object.keys(tagIndex.prs).length; | |
| statusDiv.innerText = `Ready. Indexed ${tagCount} releases and ${prCount} PRs.`; | |
| btn.disabled = false; | |
| } catch (e) { | |
| statusDiv.textContent = ''; | |
| const errorSpan = document.createElement('span'); | |
| errorSpan.style.color = 'var(--error-text)'; | |
| errorSpan.textContent = `Error: ${e.message}`; | |
| statusDiv.appendChild(errorSpan); | |
| btn.disabled = true; | |
| } finally { | |
| loadingDiv.style.display = 'none'; | |
| } | |
| } | |
| // Binary search for first commit key matching the given prefix (O(log n)) | |
| function prefixSearchCommit(prefix) { | |
| if (!sortedCommitKeys) return null; | |
| let lo = 0, hi = sortedCommitKeys.length; | |
| while (lo < hi) { | |
| const mid = (lo + hi) >>> 1; | |
| if (sortedCommitKeys[mid] < prefix) lo = mid + 1; | |
| else hi = mid; | |
| } | |
| if (lo < sortedCommitKeys.length && sortedCommitKeys[lo].startsWith(prefix)) { | |
| return sortedCommitKeys[lo]; | |
| } | |
| return null; | |
| } | |
| // Start loading | |
| loadIndex(); | |
| // Event listeners | |
| btn.addEventListener('click', performSearch); | |
| input.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') performSearch(); | |
| }); | |
| // Auto-focus input | |
| input.focus(); | |
| function performSearch() { | |
| if (!tagIndex) return; | |
| const rawQuery = input.value.trim(); | |
| if (!rawQuery) return; | |
| // Hide previous result | |
| resultDiv.style.display = 'none'; | |
| resultDiv.classList.remove('visible'); | |
| let queryType = 'unknown'; | |
| let key = rawQuery; | |
| // Parse query | |
| // 1. PR URL: https://github.com/.../pull/1234 | |
| const urlMatch = rawQuery.match(/\/pull\/(\d+)/); | |
| if (urlMatch) { | |
| key = urlMatch[1]; | |
| queryType = 'pr'; | |
| } | |
| // 2. PR Number: #1234 or 1234 | |
| else if (rawQuery.match(/^#?\d+$/)) { | |
| key = rawQuery.replace('#', ''); | |
| queryType = 'pr'; | |
| } | |
| // 3. Commit Hash: usually hex string (min 7 chars) | |
| else if (rawQuery.match(/^[0-9a-fA-F]{7,40}$/)) { | |
| key = rawQuery.toLowerCase(); | |
| queryType = 'commit'; | |
| } | |
| let tagData = null; | |
| if (queryType === 'pr') { | |
| tagData = tagIndex.prs[key]; | |
| } else if (queryType === 'commit') { | |
| // Use short hash for lookup | |
| const shortKey = key.slice(0, SHORT_HASH_LEN); | |
| tagData = tagIndex.commits[shortKey]; | |
| // If not found with short hash, try prefix match (binary search) | |
| if (!tagData) { | |
| const matchKey = prefixSearchCommit(shortKey); | |
| if (matchKey) { | |
| tagData = tagIndex.commits[matchKey]; | |
| } | |
| } | |
| } | |
| renderResult(tagData, queryType, key); | |
| } | |
| function renderResult(tagData, queryType, key) { | |
| resultDiv.innerHTML = ''; | |
| resultDiv.style.display = 'block'; | |
| // Trigger reflow for animation | |
| void resultDiv.offsetWidth; | |
| resultDiv.classList.add('visible'); | |
| // Collect tag references | |
| let tagRefs = []; | |
| if (!tagData) { | |
| // Not found | |
| } else if (typeof tagData === 'string') { | |
| // Compact format: "m5" or "g3" | |
| const parsed = parseTagRef(tagData); | |
| if (parsed) { | |
| tagRefs.push(parsed.idx); | |
| } else { | |
| // Legacy format: tag name directly | |
| tagRefs.push(tagData); | |
| } | |
| } else if (typeof tagData === 'object') { | |
| // Object format: {m: 5, g: 3} or {main: "v0.5.8", gateway: "..."} | |
| if ('m' in tagData) tagRefs.push(tagData.m); | |
| if ('g' in tagData) tagRefs.push(tagData.g); | |
| if ('main' in tagData) tagRefs.push(tagData.main); | |
| if ('gateway' in tagData) tagRefs.push(tagData.gateway); | |
| } | |
| if (tagRefs.length === 0) { | |
| const label = queryType === 'pr' ? `PR #${key}` : `Commit ${key.substring(0, 7)}`; | |
| const container = document.createElement('div'); | |
| container.className = 'result-content result-error'; | |
| const statusRow = document.createElement('div'); | |
| statusRow.className = 'result-row'; | |
| const statusLabel = document.createElement('span'); | |
| statusLabel.className = 'result-label'; | |
| statusLabel.textContent = 'Status'; | |
| const statusValue = document.createElement('span'); | |
| statusValue.textContent = 'Not Found'; | |
| statusRow.appendChild(statusLabel); | |
| statusRow.appendChild(statusValue); | |
| const msgDiv = document.createElement('div'); | |
| msgDiv.style.marginTop = '8px'; | |
| const strongEl = document.createElement('strong'); | |
| strongEl.textContent = label; | |
| msgDiv.append( | |
| `The ${queryType} `, | |
| strongEl, | |
| ' has not been included in any release yet, or is not in the index.' | |
| ); | |
| container.appendChild(statusRow); | |
| container.appendChild(msgDiv); | |
| resultDiv.appendChild(container); | |
| return; | |
| } | |
| const repoUrl = "https://github.com/sgl-project/sglang"; | |
| resultDiv.innerHTML = ''; // Clear previous results | |
| for (const tagRef of tagRefs) { | |
| const tagInfo = getTagInfo(tagRef); | |
| const dateStr = formatDate(tagInfo.date); | |
| const tagUrl = `${repoUrl}/releases/tag/${encodeURIComponent(tagInfo.name)}`; | |
| const badgeClass = tagInfo.type === 'gateway' ? 'badge-gateway' : 'badge-main'; | |
| const container = document.createElement('div'); | |
| container.className = 'result-content result-success'; | |
| container.style.marginBottom = '0.75rem'; | |
| container.innerHTML = ` | |
| <div class="result-row"> | |
| <span class="result-label">Release</span> | |
| <a target="_blank" class="tag-link"></a> | |
| </div> | |
| <div class="result-row"> | |
| <span class="result-label">Date</span> | |
| <span class="date-value"></span> | |
| </div> | |
| <div class="result-row"> | |
| <span class="result-label">Module</span> | |
| <span class="badge ${badgeClass} module-value"></span> | |
| </div> | |
| `; | |
| // Set dynamic content safely via textContent | |
| const link = container.querySelector('.tag-link'); | |
| link.href = tagUrl; | |
| link.textContent = tagInfo.name; | |
| container.querySelector('.date-value').textContent = dateStr; | |
| container.querySelector('.module-value').textContent = tagInfo.type; | |
| resultDiv.appendChild(container); | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |