|
<!DOCTYPE html> |
|
<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> |
|
|
|
<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 |
|
}); |
|
let beams = {}; |
|
let completedBeams = []; |
|
let isGenerating = false; |
|
|
|
|
|
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); |
|
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...'); |
|
|
|
|
|
document.getElementById('beams').innerHTML = ''; |
|
document.getElementById('completed-list').innerHTML = ''; |
|
beams = {}; |
|
completedBeams = []; |
|
|
|
|
|
resetConnection(); |
|
|
|
const prompt = document.getElementById('prompt').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, |
|
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> |