chore: vendor sglang v0.5.10 snapshot
This commit is contained in:
515
third_party/sglang/docs/release_lookup/index.html
vendored
Normal file
515
third_party/sglang/docs/release_lookup/index.html
vendored
Normal file
@@ -0,0 +1,515 @@
|
||||
<!DOCTYPE html>
|
||||
<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>
|
||||
Reference in New Issue
Block a user