Spaces:
Sleeping
Sleeping
<html> | |
<head> | |
<title>Beam Search Generation</title> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> | |
<style> | |
:root { | |
--primary-color: #4F46E5; | |
--secondary-color: #818CF8; | |
--background-color: #F3F4F6; | |
--card-background: #FFFFFF; | |
--text-primary: #111827; | |
--text-secondary: #4B5563; | |
--accent-color: #3730A3; | |
--success-color: #059669; | |
--border-radius: 12px; | |
} | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; | |
} | |
body { | |
background-color: var(--background-color); | |
color: var(--text-primary); | |
line-height: 1.5; | |
min-height: 100vh; | |
} | |
.header { | |
background: var(--card-background); | |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
margin-bottom: 1rem; | |
} | |
h1 { | |
font-size: 2rem; | |
font-weight: 700; | |
color: var(--accent-color); | |
text-align: center; | |
text-shadow: 0 1px 2px rgba(0,0,0,0.1); | |
padding: 1.5rem; | |
margin: 0; | |
} | |
.input-section { | |
background: var(--card-background); | |
padding: 1.5rem 2rem; | |
border-bottom: 1px solid #E5E7EB; | |
} | |
textarea { | |
width: 100%; | |
padding: 1rem; | |
border: 2px solid #E5E7EB; | |
border-radius: var(--border-radius); | |
font-size: 1rem; | |
transition: border-color 0.3s ease; | |
resize: vertical; | |
margin-bottom: 1rem; | |
} | |
textarea:focus { | |
outline: none; | |
border-color: var(--primary-color); | |
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1); | |
} | |
.controls { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
gap: 1rem; | |
margin-bottom: 1rem; | |
} | |
.input-group { | |
display: flex; | |
flex-direction: column; | |
} | |
label { | |
font-weight: 600; | |
margin-bottom: 0.5rem; | |
color: var(--text-secondary); | |
} | |
input[type="number"] { | |
padding: 0.75rem; | |
border: 2px solid #E5E7EB; | |
border-radius: var(--border-radius); | |
font-size: 1rem; | |
transition: all 0.3s ease; | |
} | |
input[type="number"]:focus { | |
outline: none; | |
border-color: var(--primary-color); | |
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1); | |
} | |
.slider-container { | |
margin: 1rem 0; | |
padding: 1rem; | |
background: var(--background-color); | |
border-radius: var(--border-radius); | |
} | |
.slider-group { | |
display: flex; | |
align-items: center; | |
gap: 1rem; | |
margin-top: 0.5rem; | |
} | |
input[type="range"] { | |
flex: 1; | |
height: 8px; | |
-webkit-appearance: none; | |
background: #E5E7EB; | |
border-radius: 4px; | |
outline: none; | |
transition: all 0.3s ease; | |
} | |
input[type="range"]::-webkit-slider-thumb { | |
-webkit-appearance: none; | |
width: 20px; | |
height: 20px; | |
background: var(--primary-color); | |
border-radius: 50%; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
} | |
input[type="range"]::-webkit-slider-thumb:hover { | |
background: var(--accent-color); | |
transform: scale(1.1); | |
} | |
.slider-value { | |
min-width: 100px; | |
padding: 0.5rem 1rem; | |
background: var(--primary-color); | |
color: white; | |
border-radius: var(--border-radius); | |
text-align: center; | |
font-weight: 600; | |
font-size: 0.9rem; | |
} | |
#generate-btn { | |
background-color: var(--primary-color); | |
color: white; | |
border: none; | |
padding: 1rem 2rem; | |
border-radius: var(--border-radius); | |
font-weight: 600; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
gap: 0.5rem; | |
width: 100%; | |
margin-top: 1rem; | |
} | |
#generate-btn:hover { | |
background-color: var(--accent-color); | |
transform: translateY(-1px); | |
} | |
#generate-btn:disabled { | |
background-color: #D1D5DB; | |
cursor: not-allowed; | |
transform: none; | |
} | |
.loading { | |
display: none; | |
text-align: center; | |
color: var(--text-secondary); | |
font-weight: 600; | |
padding: 0.5rem; | |
} | |
.container { | |
padding: 0 2rem; | |
} | |
.split-container { | |
display: flex; | |
gap: 2rem; | |
padding-bottom: 2rem; | |
} | |
.left-panel, .right-panel { | |
flex: 1; | |
background: var(--card-background); | |
border-radius: var(--border-radius); | |
padding: 1.5rem; | |
min-height: 300px; | |
} | |
.panel-title { | |
font-size: 1.25rem; | |
color: var(--accent-color); | |
margin-bottom: 1rem; | |
padding-bottom: 0.5rem; | |
border-bottom: 2px solid var(--secondary-color); | |
} | |
.beam-container { | |
background: var(--background-color); | |
margin-bottom: 1rem; | |
padding: 1.5rem; | |
border-radius: var(--border-radius); | |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); | |
transition: transform 0.3s ease; | |
} | |
.beam-container:hover { | |
transform: translateY(-2px); | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
} | |
.beam-container h3, .beam-container h4 { | |
color: var(--accent-color); | |
margin-bottom: 1rem; | |
font-weight: 600; | |
} | |
.beam-text { | |
white-space: pre-wrap; | |
font-family: 'Cascadia Code', 'Source Code Pro', monospace; | |
line-height: 1.6; | |
color: var(--text-secondary); | |
background: var(--card-background); | |
padding: 1rem; | |
border-radius: calc(var(--border-radius) - 4px); | |
} | |
@media (max-width: 768px) { | |
.container { | |
padding: 0 1rem; | |
} | |
.split-container { | |
flex-direction: column; | |
gap: 1rem; | |
} | |
.controls { | |
grid-template-columns: 1fr; | |
} | |
.header { | |
margin-bottom: 1rem; | |
} | |
} | |
.footer { | |
background: var(--card-background); | |
padding: 2rem; | |
margin-top: 2rem; | |
box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.05); | |
} | |
.footer-content { | |
max-width: 1200px; | |
margin: 0 auto; | |
display: grid; | |
grid-template-columns: 2fr 1fr; | |
gap: 2rem; | |
} | |
.project-info h3 { | |
color: var(--accent-color); | |
margin-bottom: 1rem; | |
font-size: 1.25rem; | |
} | |
.project-info p { | |
color: var(--text-secondary); | |
line-height: 1.6; | |
margin-bottom: 1rem; | |
} | |
.credit { | |
display: flex; | |
flex-direction: column; | |
align-items: flex-end; | |
justify-content: center; | |
} | |
.credit p { | |
color: var(--text-secondary); | |
margin-bottom: 1rem; | |
} | |
.credit a { | |
color: var(--primary-color); | |
text-decoration: none; | |
font-weight: 600; | |
transition: color 0.3s ease; | |
} | |
.credit a:hover { | |
color: var(--accent-color); | |
} | |
.social-links { | |
display: flex; | |
gap: 1rem; | |
} | |
.social-links a { | |
color: var(--text-secondary); | |
font-size: 1.5rem; | |
transition: all 0.3s ease; | |
} | |
.social-links a:hover { | |
color: var(--primary-color); | |
transform: translateY(-2px); | |
} | |
@media (max-width: 768px) { | |
.footer-content { | |
grid-template-columns: 1fr; | |
gap: 1rem; | |
} | |
.credit { | |
align-items: flex-start; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div class="header"> | |
<h1>Beam Search Generator</h1> | |
<div class="input-section"> | |
<textarea id="prompt" rows="4" placeholder="Enter your prompt here..."></textarea> | |
<div class="controls"> | |
<div class="input-group"> | |
<label for="num_beams">Number of beams</label> | |
<input type="number" id="num_beams" value="5" min="2" max="10"> | |
</div> | |
<div class="input-group"> | |
<label for="max_tokens">Max tokens</label> | |
<input type="number" id="max_tokens" value="512" min="1"> | |
</div> | |
<div class="input-group"> | |
<label for="model_select">Model</label> | |
<select id="model_select" class="form-select"> | |
<option value="gpt2">GPT-2</option> | |
<option value="qwen">Qwen</option> | |
</select> | |
</div> | |
</div> | |
<div class="slider-container"> | |
<label for="sleep_time">Generation Speed</label> | |
<div class="slider-group"> | |
<i class="fas fa-bolt" title="Fast"></i> | |
<input type="range" id="sleep_time" min="0" max="500" value="0" step="10"> | |
<i class="fas fa-hourglass" title="Slow"></i> | |
<div class="slider-value" id="sleep_value">0ms delay</div> | |
</div> | |
</div> | |
<button id="generate-btn" onclick="generate()"> | |
<i class="fas fa-wand-magic-sparkles"></i> | |
Generate | |
</button> | |
<div id="loading" class="loading"> | |
<i class="fas fa-spinner"></i> | |
Generating amazing content... | |
</div> | |
</div> | |
</div> | |
<div class="container"> | |
<div class="split-container"> | |
<div class="left-panel"> | |
<h2 class="panel-title">Active Beams</h2> | |
<div id="beams"></div> | |
</div> | |
<div class="right-panel"> | |
<h2 class="panel-title">Completed Beams</h2> | |
<div id="completed-list"></div> | |
</div> | |
</div> | |
</div> | |
<script> | |
let socket = io({ | |
transports: ['websocket'], | |
reconnection: true, | |
reconnectionAttempts: 5, | |
reconnectionDelay: 1000, | |
path: '/socket.io/', | |
upgrade: false, | |
forceNew: true, | |
pingTimeout: 60000, | |
pingInterval: 25000 | |
}); | |
let beams = {}; | |
let completedBeams = []; | |
let isGenerating = false; | |
// Add slider value update | |
const sleepSlider = document.getElementById('sleep_time'); | |
const sleepValue = document.getElementById('sleep_value'); | |
sleepSlider.addEventListener('input', function() { | |
const value = parseInt(this.value); | |
sleepValue.textContent = value === 0 ? 'No delay' : `${value}ms delay`; | |
}); | |
function setupSocketListeners() { | |
socket.on('beam_update', function(data) { | |
console.log('Received beam update:', data); // Add logging | |
const { beam_idx, text } = data; | |
if (!beams[beam_idx]) { | |
createBeamContainer(beam_idx); | |
} | |
const beamElement = document.getElementById(`beam-${beam_idx}`); | |
if (beamElement) { | |
beamElement.textContent = text; | |
} | |
}); | |
socket.on('beam_finished', function(data) { | |
completedBeams.push(data.text); | |
updateCompletedBeams(); | |
}); | |
socket.on('generation_started', function() { | |
isGenerating = true; | |
document.getElementById('generate-btn').disabled = true; | |
document.getElementById('loading').style.display = 'block'; | |
document.getElementById('generate-btn').innerHTML = '<i class="fas fa-spinner fa-spin"></i> Generating...'; | |
}); | |
socket.on('generation_completed', function() { | |
isGenerating = false; | |
document.getElementById('generate-btn').disabled = false; | |
document.getElementById('loading').style.display = 'none'; | |
document.getElementById('generate-btn').innerHTML = '<i class="fas fa-wand-magic-sparkles"></i> Generate'; | |
}); | |
socket.on('generation_error', function(data) { | |
alert('Error during generation: ' + data.error); | |
isGenerating = false; | |
document.getElementById('generate-btn').disabled = false; | |
document.getElementById('loading').style.display = 'none'; | |
document.getElementById('generate-btn').innerHTML = '<i class="fas fa-wand-magic-sparkles"></i> Generate'; | |
}); | |
socket.on('connect_error', function(error) { | |
console.error('Connection error:', error); | |
resetConnection(); | |
}); | |
} | |
function createBeamContainer(beamIdx) { | |
const container = document.createElement('div'); | |
container.className = 'beam-container'; | |
container.innerHTML = ` | |
<h3>Beam ${beamIdx + 1}</h3> | |
<div id="beam-${beamIdx}" class="beam-text"></div> | |
`; | |
document.getElementById('beams').appendChild(container); | |
beams[beamIdx] = container; | |
} | |
function updateCompletedBeams() { | |
const completedList = document.getElementById('completed-list'); | |
completedList.innerHTML = completedBeams.map((text, idx) => ` | |
<div class="beam-container"> | |
<h4>Completed Beam ${idx + 1}</h4> | |
<div class="beam-text">${text}</div> | |
</div> | |
`).join(''); | |
} | |
function resetConnection() { | |
if (socket) { | |
socket.removeAllListeners(); | |
socket.close(); | |
} | |
socket = io({ | |
transports: ['websocket'], | |
reconnection: true, | |
reconnectionAttempts: 5, | |
reconnectionDelay: 1000 | |
}); | |
setupSocketListeners(); | |
} | |
function generate() { | |
if (isGenerating) return; | |
console.log('Starting generation...'); // Add logging | |
// Clear previous state | |
document.getElementById('beams').innerHTML = ''; | |
document.getElementById('completed-list').innerHTML = ''; | |
beams = {}; | |
completedBeams = []; | |
// Reset connection before each generation | |
resetConnection(); | |
const prompt = document.getElementById('prompt').value; | |
const model = document.getElementById('model_select').value; | |
const numBeams = parseInt(document.getElementById('num_beams').value); | |
const maxTokens = parseInt(document.getElementById('max_tokens').value); | |
const sleepTime = parseInt(document.getElementById('sleep_time').value); | |
console.log('Emitting generate event with params:', { | |
prompt, numBeams, maxTokens, sleepTime | |
}); | |
socket.emit('generate', { | |
prompt: prompt, | |
model: model, | |
num_beams: numBeams, | |
max_tokens: maxTokens, | |
sleep_time: sleepTime | |
}); | |
} | |
setupSocketListeners(); | |
</script> | |
<footer class="footer"> | |
<div class="footer-content"> | |
<div class="project-info"> | |
<h3>About This Project</h3> | |
<p>This website demonstrates the MultiBeamTextStreamer feature proposed in a pull request to the Hugging Face Transformers library. The MultiBeamTextStreamer enables real-time visualization of beam search generation, providing insights into how language models explore different text completion possibilities.</p> | |
<p>The implementation showcases how beam search works by displaying multiple candidate sequences simultaneously, making it a valuable educational tool for understanding text generation algorithms.</p> | |
</div> | |
<div class="credit"> | |
<p>Created by <a href="https://github.com/mosheofer1" target="_blank" rel="noopener noreferrer">Moshe Ofer</a></p> | |
<div class="social-links"> | |
<a href="https://github.com/mosheofer1" target="_blank" rel="noopener noreferrer" title="GitHub"> | |
<i class="fab fa-github"></i> | |
</a> | |
<a href="https://www.linkedin.com/in/moshe-ofer/" target="_blank" rel="noopener noreferrer" title="linkedin"> | |
<i class="fas fa-rocket"></i> | |
</a> | |
</div> | |
</div> | |
</div> | |
</footer> | |
</body> | |
</html> |