Moshe Ofer commited on
Commit
a1b31ed
·
1 Parent(s): 2e30eb9

Initial commit for Hugging Face Space

Browse files
Files changed (5) hide show
  1. Dockerfile +23 -0
  2. app.py +113 -0
  3. requirements.txt +7 -0
  4. temp.py +175 -0
  5. templates/index.html +530 -0
Dockerfile ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use a lightweight Python base image
2
+ FROM python:3.9-slim
3
+
4
+ # Set the working directory in the container
5
+ WORKDIR /app
6
+
7
+ # Install system dependencies required for the application
8
+ RUN apt-get update && apt-get install -y --no-install-recommends \
9
+ build-essential && \
10
+ rm -rf /var/lib/apt/lists/*
11
+
12
+ # Copy the application files into the container
13
+ COPY . /app
14
+
15
+ # Install Python dependencies
16
+ RUN pip install --no-cache-dir --upgrade pip
17
+ RUN pip install --no-cache-dir -r requirements.txt
18
+
19
+ # Expose the application port
20
+ EXPOSE 7860
21
+
22
+ # Command to run the application using Gunicorn with Eventlet
23
+ CMD ["gunicorn", "--worker-class", "eventlet", "-w", "1", "app:app", "-b", "0.0.0.0:7860"]
app.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template
2
+ from flask_socketio import SocketIO
3
+ from transformers import MultiBeamTextStreamer, AutoTokenizer, AutoModelForCausalLM
4
+ import torch
5
+ import time
6
+ import eventlet
7
+
8
+ eventlet.monkey_patch()
9
+ app = Flask(__name__)
10
+ socketio = SocketIO(app, ping_timeout=60)
11
+
12
+ # Initialize model and tokenizer
13
+ MODEL_NAME = "Qwen/Qwen2.5-0.5B-Instruct"
14
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
15
+ model = AutoModelForCausalLM.from_pretrained(
16
+ MODEL_NAME,
17
+ torch_dtype="auto",
18
+ device_map="auto"
19
+ )
20
+
21
+
22
+ class WebSocketBeamStreamer(MultiBeamTextStreamer):
23
+ """Custom streamer that sends updates through websockets with adjustable speed"""
24
+
25
+ def __init__(self, tokenizer, num_beams, sleep_time=0, skip_prompt=True):
26
+ super().__init__(
27
+ tokenizer,
28
+ num_beams=num_beams,
29
+ skip_prompt=skip_prompt,
30
+ on_beam_update=self.on_beam_update,
31
+ on_beam_finished=self.on_beam_finished
32
+ )
33
+ self.beam_texts = {i: "" for i in range(num_beams)}
34
+ self.sleep_time = sleep_time # Sleep time in milliseconds
35
+
36
+ def on_beam_update(self, beam_idx: int, new_text: str):
37
+ """Send beam updates through websocket with delay"""
38
+ self.beam_texts[beam_idx] = new_text
39
+ if self.sleep_time > 0:
40
+ time.sleep(self.sleep_time / 1000) # Convert milliseconds to seconds
41
+ socketio.emit('beam_update', {
42
+ 'beam_idx': beam_idx,
43
+ 'text': new_text
44
+ })
45
+
46
+ def on_beam_finished(self, final_text: str):
47
+ """Send completion notification through websocket"""
48
+ socketio.emit('beam_finished', {
49
+ 'text': final_text
50
+ })
51
+
52
+
53
+ @app.route('/')
54
+ def index():
55
+ return render_template('index.html')
56
+
57
+
58
+ @socketio.on('generate')
59
+ def handle_generation(data):
60
+ # Emit a generation start event
61
+ socketio.emit('generation_started')
62
+
63
+ prompt = data['prompt']
64
+ num_beams = data.get('num_beams', 5)
65
+ max_new_tokens = data.get('max_tokens', 512)
66
+ sleep_time = data.get('sleep_time', 0) # Get sleep time from frontend
67
+
68
+ # Create messages format
69
+ messages = [
70
+ {"role": "system", "content": "You are a helpful assistant."},
71
+ {"role": "user", "content": prompt}
72
+ ]
73
+
74
+ # Apply chat template
75
+ text = tokenizer.apply_chat_template(
76
+ messages,
77
+ tokenize=False,
78
+ add_generation_prompt=True
79
+ )
80
+
81
+ # Prepare inputs
82
+ model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
83
+
84
+ # Initialize streamer with sleep time
85
+ streamer = WebSocketBeamStreamer(
86
+ tokenizer=tokenizer,
87
+ num_beams=num_beams,
88
+ sleep_time=sleep_time,
89
+ skip_prompt=True
90
+ )
91
+
92
+ try:
93
+ # Generate with beam search
94
+ with torch.no_grad():
95
+ model.generate(
96
+ **model_inputs,
97
+ num_beams=num_beams,
98
+ num_return_sequences=num_beams,
99
+ max_new_tokens=max_new_tokens,
100
+ output_scores=True,
101
+ return_dict_in_generate=True,
102
+ early_stopping=True,
103
+ streamer=streamer
104
+ )
105
+ except Exception as e:
106
+ socketio.emit('generation_error', {'error': str(e)})
107
+ finally:
108
+ # Emit generation completed event
109
+ socketio.emit('generation_completed')
110
+
111
+
112
+ if __name__ == '__main__':
113
+ socketio.run(app, host='0.0.0.0', port=7860)
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ torch
2
+ eventlet
3
+ Flask
4
+ Flask-SocketIO
5
+ accelerate
6
+ gunicorn
7
+ git+https://github.com/MosheOfer1/transformers.git
temp.py ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import os
3
+
4
+ from transformers import MultiBeamTextStreamer, AutoTokenizer, AutoModelForCausalLM
5
+
6
+
7
+ class BeamOutputManager:
8
+ """Manages file handlers for beam outputs"""
9
+
10
+ def __init__(self, output_dir: str, num_beams: int):
11
+ self.output_dir = output_dir
12
+ self.num_beams = num_beams
13
+ self.counter = 0
14
+
15
+ # Create main output directory and closed beams directory
16
+ os.makedirs(output_dir, exist_ok=True)
17
+ self.closed_beams_dir = os.path.join(output_dir, "closed_beams")
18
+ os.makedirs(self.closed_beams_dir, exist_ok=True)
19
+
20
+ # Store complete text for each beam
21
+ self.beam_texts = {i: "" for i in range(num_beams)}
22
+ self.active_beams = set(range(num_beams))
23
+
24
+ # Initialize empty files
25
+ for beam_idx in range(num_beams):
26
+ filename = os.path.join(output_dir, f'beam_{beam_idx}.txt')
27
+ with open(filename, 'w', encoding='utf-8') as f:
28
+ f.write('')
29
+
30
+ def write_to_beam(self, beam_idx: int, text: str):
31
+ """Write text to the specified beam's file"""
32
+ if 0 <= beam_idx < self.num_beams and beam_idx in self.active_beams:
33
+ # Update stored text
34
+ self.beam_texts[beam_idx] = text
35
+
36
+ # Write complete text to file
37
+ filename = os.path.join(self.output_dir, f'beam_{beam_idx}.txt')
38
+ with open(filename, 'w', encoding='utf-8') as f:
39
+ f.write(self.beam_texts[beam_idx])
40
+
41
+ def finalize_beam(self, final_text: str):
42
+ """
43
+ Handle a completed beam by creating a new file in the closed_beams directory.
44
+
45
+ Args:
46
+ final_text (str): The complete text generated by the finished beam
47
+ """
48
+ # Create a timestamp-based filename to ensure uniqueness
49
+ self.counter += 1
50
+ filename = os.path.join(self.closed_beams_dir, f'completed_beam_{self.counter}.txt')
51
+
52
+ # Write the final text to the completed beam file
53
+ with open(filename, 'w', encoding='utf-8') as f:
54
+ f.write(final_text)
55
+
56
+ return filename
57
+
58
+
59
+ def setup_model_and_tokenizer(model_name):
60
+ """
61
+ Initialize the model and tokenizer.
62
+
63
+ Args:
64
+ model_name (str): Name of the model to use
65
+
66
+ Returns:
67
+ tuple: (model, tokenizer)
68
+ """
69
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
70
+ model = AutoModelForCausalLM.from_pretrained(
71
+ model_name,
72
+ torch_dtype="auto",
73
+ device_map="auto"
74
+ )
75
+ return model, tokenizer
76
+
77
+
78
+ def generate_with_beam_search(model, tokenizer, user_prompt, output_dir, num_beams=5, max_new_tokens=512):
79
+ """
80
+ Generate responses using beam search and write results to files.
81
+
82
+ Args:
83
+ model: The language model
84
+ tokenizer: The tokenizer
85
+ user_prompt (str): Input prompt
86
+ output_dir (str): Directory to save beam outputs
87
+ num_beams (int): Number of beams to use
88
+ max_new_tokens (int): Maximum number of new tokens to generate
89
+ """
90
+ # Initialize the output manager
91
+ output_manager = BeamOutputManager(output_dir, num_beams)
92
+
93
+ def on_beam_update(beam_idx: int, new_text: str):
94
+ """Handler for beam updates - write new text to file"""
95
+ output_manager.write_to_beam(beam_idx, new_text)
96
+
97
+ def on_beam_finished(final_text: str):
98
+ """Handler for completed beams - create final output file"""
99
+ final_path = output_manager.finalize_beam(final_text)
100
+ print(f"\nCompleted beam saved to: {final_path}")
101
+
102
+ # Create messages format
103
+ messages = [
104
+ {"role": "system", "content": "You are a helpful assistant."},
105
+ {"role": "user", "content": user_prompt}
106
+ ]
107
+
108
+ # Apply chat template
109
+ text = tokenizer.apply_chat_template(
110
+ messages,
111
+ tokenize=False,
112
+ add_generation_prompt=True
113
+ )
114
+
115
+ # Prepare inputs
116
+ model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
117
+
118
+ # Initialize streamer with handlers
119
+ streamer = MultiBeamTextStreamer(
120
+ tokenizer=tokenizer,
121
+ num_beams=num_beams,
122
+ on_beam_update=on_beam_update,
123
+ on_beam_finished=on_beam_finished,
124
+ skip_prompt=True
125
+ )
126
+
127
+ # Generate with beam search
128
+ model.generate(
129
+ **model_inputs,
130
+ num_beams=num_beams,
131
+ num_return_sequences=num_beams,
132
+ max_new_tokens=max_new_tokens,
133
+ output_scores=True,
134
+ return_dict_in_generate=True,
135
+ early_stopping=True,
136
+ streamer=streamer
137
+ )
138
+
139
+
140
+ def main():
141
+ # Setup command line arguments
142
+ parser = argparse.ArgumentParser(description='Language Model Text Generation with Beam Search')
143
+ parser.add_argument('--model', type=str, default='Qwen/Qwen2.5-0.5B-Instruct',
144
+ help='Name of the model to use')
145
+ parser.add_argument('--num_beams', type=int, default=5,
146
+ help='Number of beams for beam search')
147
+ parser.add_argument('--max_tokens', type=int, default=512,
148
+ help='Maximum number of new tokens to generate')
149
+ parser.add_argument('--output_dir', type=str, default='beam_outputs',
150
+ help='Directory to save beam outputs')
151
+
152
+ args = parser.parse_args()
153
+
154
+ # Initialize model and tokenizer
155
+ model, tokenizer = setup_model_and_tokenizer(args.model)
156
+
157
+ # Interactive loop
158
+ while True:
159
+ prompt = input("\nEnter your prompt (or 'quit' to exit): ")
160
+ if prompt.lower() == 'quit':
161
+ break
162
+
163
+ generate_with_beam_search(
164
+ model,
165
+ tokenizer,
166
+ prompt,
167
+ args.output_dir,
168
+ num_beams=args.num_beams,
169
+ max_new_tokens=args.max_tokens
170
+ )
171
+ print(f"\nOutputs written to: {args.output_dir}/beam_*.txt")
172
+
173
+
174
+ if __name__ == "__main__":
175
+ main()
templates/index.html ADDED
@@ -0,0 +1,530 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Beam Search Generation</title>
5
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
6
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
7
+ <style>
8
+ :root {
9
+ --primary-color: #4F46E5;
10
+ --secondary-color: #818CF8;
11
+ --background-color: #F3F4F6;
12
+ --card-background: #FFFFFF;
13
+ --text-primary: #111827;
14
+ --text-secondary: #4B5563;
15
+ --accent-color: #3730A3;
16
+ --success-color: #059669;
17
+ --border-radius: 12px;
18
+ }
19
+
20
+ * {
21
+ margin: 0;
22
+ padding: 0;
23
+ box-sizing: border-box;
24
+ font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
25
+ }
26
+
27
+ body {
28
+ background-color: var(--background-color);
29
+ color: var(--text-primary);
30
+ line-height: 1.5;
31
+ min-height: 100vh;
32
+ }
33
+
34
+ .header {
35
+ background: var(--card-background);
36
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
37
+ margin-bottom: 1rem;
38
+ }
39
+
40
+ h1 {
41
+ font-size: 2rem;
42
+ font-weight: 700;
43
+ color: var(--accent-color);
44
+ text-align: center;
45
+ text-shadow: 0 1px 2px rgba(0,0,0,0.1);
46
+ padding: 1.5rem;
47
+ margin: 0;
48
+ }
49
+
50
+ .input-section {
51
+ background: var(--card-background);
52
+ padding: 1.5rem 2rem;
53
+ border-bottom: 1px solid #E5E7EB;
54
+ }
55
+
56
+ textarea {
57
+ width: 100%;
58
+ padding: 1rem;
59
+ border: 2px solid #E5E7EB;
60
+ border-radius: var(--border-radius);
61
+ font-size: 1rem;
62
+ transition: border-color 0.3s ease;
63
+ resize: vertical;
64
+ margin-bottom: 1rem;
65
+ }
66
+
67
+ textarea:focus {
68
+ outline: none;
69
+ border-color: var(--primary-color);
70
+ box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
71
+ }
72
+
73
+ .controls {
74
+ display: grid;
75
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
76
+ gap: 1rem;
77
+ margin-bottom: 1rem;
78
+ }
79
+
80
+ .input-group {
81
+ display: flex;
82
+ flex-direction: column;
83
+ }
84
+
85
+ label {
86
+ font-weight: 600;
87
+ margin-bottom: 0.5rem;
88
+ color: var(--text-secondary);
89
+ }
90
+
91
+ input[type="number"] {
92
+ padding: 0.75rem;
93
+ border: 2px solid #E5E7EB;
94
+ border-radius: var(--border-radius);
95
+ font-size: 1rem;
96
+ transition: all 0.3s ease;
97
+ }
98
+
99
+ input[type="number"]:focus {
100
+ outline: none;
101
+ border-color: var(--primary-color);
102
+ box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
103
+ }
104
+
105
+ .slider-container {
106
+ margin: 1rem 0;
107
+ padding: 1rem;
108
+ background: var(--background-color);
109
+ border-radius: var(--border-radius);
110
+ }
111
+
112
+ .slider-group {
113
+ display: flex;
114
+ align-items: center;
115
+ gap: 1rem;
116
+ margin-top: 0.5rem;
117
+ }
118
+
119
+ input[type="range"] {
120
+ flex: 1;
121
+ height: 8px;
122
+ -webkit-appearance: none;
123
+ background: #E5E7EB;
124
+ border-radius: 4px;
125
+ outline: none;
126
+ transition: all 0.3s ease;
127
+ }
128
+
129
+ input[type="range"]::-webkit-slider-thumb {
130
+ -webkit-appearance: none;
131
+ width: 20px;
132
+ height: 20px;
133
+ background: var(--primary-color);
134
+ border-radius: 50%;
135
+ cursor: pointer;
136
+ transition: all 0.3s ease;
137
+ }
138
+
139
+ input[type="range"]::-webkit-slider-thumb:hover {
140
+ background: var(--accent-color);
141
+ transform: scale(1.1);
142
+ }
143
+
144
+ .slider-value {
145
+ min-width: 100px;
146
+ padding: 0.5rem 1rem;
147
+ background: var(--primary-color);
148
+ color: white;
149
+ border-radius: var(--border-radius);
150
+ text-align: center;
151
+ font-weight: 600;
152
+ font-size: 0.9rem;
153
+ }
154
+
155
+ #generate-btn {
156
+ background-color: var(--primary-color);
157
+ color: white;
158
+ border: none;
159
+ padding: 1rem 2rem;
160
+ border-radius: var(--border-radius);
161
+ font-weight: 600;
162
+ cursor: pointer;
163
+ transition: all 0.3s ease;
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ gap: 0.5rem;
168
+ width: 100%;
169
+ margin-top: 1rem;
170
+ }
171
+
172
+ #generate-btn:hover {
173
+ background-color: var(--accent-color);
174
+ transform: translateY(-1px);
175
+ }
176
+
177
+ #generate-btn:disabled {
178
+ background-color: #D1D5DB;
179
+ cursor: not-allowed;
180
+ transform: none;
181
+ }
182
+
183
+ .loading {
184
+ display: none;
185
+ text-align: center;
186
+ color: var(--text-secondary);
187
+ font-weight: 600;
188
+ padding: 0.5rem;
189
+ }
190
+
191
+ .container {
192
+ padding: 0 2rem;
193
+ }
194
+
195
+ .split-container {
196
+ display: flex;
197
+ gap: 2rem;
198
+ padding-bottom: 2rem;
199
+ }
200
+
201
+ .left-panel, .right-panel {
202
+ flex: 1;
203
+ background: var(--card-background);
204
+ border-radius: var(--border-radius);
205
+ padding: 1.5rem;
206
+ min-height: 300px;
207
+ }
208
+
209
+ .panel-title {
210
+ font-size: 1.25rem;
211
+ color: var(--accent-color);
212
+ margin-bottom: 1rem;
213
+ padding-bottom: 0.5rem;
214
+ border-bottom: 2px solid var(--secondary-color);
215
+ }
216
+
217
+ .beam-container {
218
+ background: var(--background-color);
219
+ margin-bottom: 1rem;
220
+ padding: 1.5rem;
221
+ border-radius: var(--border-radius);
222
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
223
+ transition: transform 0.3s ease;
224
+ }
225
+
226
+ .beam-container:hover {
227
+ transform: translateY(-2px);
228
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
229
+ }
230
+
231
+ .beam-container h3, .beam-container h4 {
232
+ color: var(--accent-color);
233
+ margin-bottom: 1rem;
234
+ font-weight: 600;
235
+ }
236
+
237
+ .beam-text {
238
+ white-space: pre-wrap;
239
+ font-family: 'Cascadia Code', 'Source Code Pro', monospace;
240
+ line-height: 1.6;
241
+ color: var(--text-secondary);
242
+ background: var(--card-background);
243
+ padding: 1rem;
244
+ border-radius: calc(var(--border-radius) - 4px);
245
+ }
246
+
247
+ @media (max-width: 768px) {
248
+ .container {
249
+ padding: 0 1rem;
250
+ }
251
+
252
+ .split-container {
253
+ flex-direction: column;
254
+ gap: 1rem;
255
+ }
256
+
257
+ .controls {
258
+ grid-template-columns: 1fr;
259
+ }
260
+
261
+ .header {
262
+ margin-bottom: 1rem;
263
+ }
264
+ }
265
+ .footer {
266
+ background: var(--card-background);
267
+ padding: 2rem;
268
+ margin-top: 2rem;
269
+ box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.05);
270
+ }
271
+
272
+ .footer-content {
273
+ max-width: 1200px;
274
+ margin: 0 auto;
275
+ display: grid;
276
+ grid-template-columns: 2fr 1fr;
277
+ gap: 2rem;
278
+ }
279
+
280
+ .project-info h3 {
281
+ color: var(--accent-color);
282
+ margin-bottom: 1rem;
283
+ font-size: 1.25rem;
284
+ }
285
+
286
+ .project-info p {
287
+ color: var(--text-secondary);
288
+ line-height: 1.6;
289
+ margin-bottom: 1rem;
290
+ }
291
+
292
+ .credit {
293
+ display: flex;
294
+ flex-direction: column;
295
+ align-items: flex-end;
296
+ justify-content: center;
297
+ }
298
+
299
+ .credit p {
300
+ color: var(--text-secondary);
301
+ margin-bottom: 1rem;
302
+ }
303
+
304
+ .credit a {
305
+ color: var(--primary-color);
306
+ text-decoration: none;
307
+ font-weight: 600;
308
+ transition: color 0.3s ease;
309
+ }
310
+
311
+ .credit a:hover {
312
+ color: var(--accent-color);
313
+ }
314
+
315
+ .social-links {
316
+ display: flex;
317
+ gap: 1rem;
318
+ }
319
+
320
+ .social-links a {
321
+ color: var(--text-secondary);
322
+ font-size: 1.5rem;
323
+ transition: all 0.3s ease;
324
+ }
325
+
326
+ .social-links a:hover {
327
+ color: var(--primary-color);
328
+ transform: translateY(-2px);
329
+ }
330
+
331
+ @media (max-width: 768px) {
332
+ .footer-content {
333
+ grid-template-columns: 1fr;
334
+ gap: 1rem;
335
+ }
336
+
337
+ .credit {
338
+ align-items: flex-start;
339
+ }
340
+ }
341
+ </style>
342
+ </head>
343
+ <body>
344
+ <div class="header">
345
+ <h1>Beam Search Generator</h1>
346
+
347
+ <div class="input-section">
348
+ <textarea id="prompt" rows="4" placeholder="Enter your prompt here..."></textarea>
349
+
350
+ <div class="controls">
351
+ <div class="input-group">
352
+ <label for="num_beams">Number of beams</label>
353
+ <input type="number" id="num_beams" value="5" min="2" max="10">
354
+ </div>
355
+
356
+ <div class="input-group">
357
+ <label for="max_tokens">Max tokens</label>
358
+ <input type="number" id="max_tokens" value="512" min="1">
359
+ </div>
360
+ </div>
361
+
362
+ <div class="slider-container">
363
+ <label for="sleep_time">Generation Speed</label>
364
+ <div class="slider-group">
365
+ <i class="fas fa-bolt" title="Fast"></i>
366
+ <input type="range" id="sleep_time" min="0" max="500" value="0" step="10">
367
+ <i class="fas fa-hourglass" title="Slow"></i>
368
+ <div class="slider-value" id="sleep_value">0ms delay</div>
369
+ </div>
370
+ </div>
371
+
372
+ <button id="generate-btn" onclick="generate()">
373
+ <i class="fas fa-wand-magic-sparkles"></i>
374
+ Generate
375
+ </button>
376
+
377
+ <div id="loading" class="loading">
378
+ <i class="fas fa-spinner"></i>
379
+ Generating amazing content...
380
+ </div>
381
+ </div>
382
+ </div>
383
+
384
+ <div class="container">
385
+ <div class="split-container">
386
+ <div class="left-panel">
387
+ <h2 class="panel-title">Active Beams</h2>
388
+ <div id="beams"></div>
389
+ </div>
390
+
391
+ <div class="right-panel">
392
+ <h2 class="panel-title">Completed Beams</h2>
393
+ <div id="completed-list"></div>
394
+ </div>
395
+ </div>
396
+ </div>
397
+
398
+ <script>
399
+ let socket = io();
400
+ let beams = {};
401
+ let completedBeams = [];
402
+ let isGenerating = false;
403
+
404
+ // Add slider value update
405
+ const sleepSlider = document.getElementById('sleep_time');
406
+ const sleepValue = document.getElementById('sleep_value');
407
+
408
+ sleepSlider.addEventListener('input', function() {
409
+ const value = parseInt(this.value);
410
+ sleepValue.textContent = value === 0 ? 'No delay' : `${value}ms delay`;
411
+ });
412
+
413
+ function setupSocketListeners() {
414
+ socket.on('beam_update', function(data) {
415
+ const { beam_idx, text } = data;
416
+ if (!beams[beam_idx]) {
417
+ createBeamContainer(beam_idx);
418
+ }
419
+ document.getElementById(`beam-${beam_idx}`).textContent = text;
420
+ });
421
+
422
+ socket.on('beam_finished', function(data) {
423
+ completedBeams.push(data.text);
424
+ updateCompletedBeams();
425
+ });
426
+
427
+ socket.on('generation_started', function() {
428
+ isGenerating = true;
429
+ document.getElementById('generate-btn').disabled = true;
430
+ document.getElementById('loading').style.display = 'block';
431
+ document.getElementById('generate-btn').innerHTML = '<i class="fas fa-spinner fa-spin"></i> Generating...';
432
+ });
433
+
434
+ socket.on('generation_completed', function() {
435
+ isGenerating = false;
436
+ document.getElementById('generate-btn').disabled = false;
437
+ document.getElementById('loading').style.display = 'none';
438
+ document.getElementById('generate-btn').innerHTML = '<i class="fas fa-wand-magic-sparkles"></i> Generate';
439
+ });
440
+
441
+ socket.on('generation_error', function(data) {
442
+ alert('Error during generation: ' + data.error);
443
+ isGenerating = false;
444
+ document.getElementById('generate-btn').disabled = false;
445
+ document.getElementById('loading').style.display = 'none';
446
+ document.getElementById('generate-btn').innerHTML = '<i class="fas fa-wand-magic-sparkles"></i> Generate';
447
+ });
448
+
449
+ socket.on('connect_error', function(error) {
450
+ console.error('Connection error:', error);
451
+ resetConnection();
452
+ });
453
+ }
454
+
455
+ function createBeamContainer(beamIdx) {
456
+ const container = document.createElement('div');
457
+ container.className = 'beam-container';
458
+ container.innerHTML = `
459
+ <h3>Beam ${beamIdx + 1}</h3>
460
+ <div id="beam-${beamIdx}" class="beam-text"></div>
461
+ `;
462
+ document.getElementById('beams').appendChild(container);
463
+ beams[beamIdx] = container;
464
+ }
465
+
466
+ function updateCompletedBeams() {
467
+ const completedList = document.getElementById('completed-list');
468
+ completedList.innerHTML = completedBeams.map((text, idx) => `
469
+ <div class="beam-container">
470
+ <h4>Completed Beam ${idx + 1}</h4>
471
+ <div class="beam-text">${text}</div>
472
+ </div>
473
+ `).join('');
474
+ }
475
+
476
+ function resetConnection() {
477
+ if (socket) {
478
+ socket.disconnect();
479
+ }
480
+ socket = io();
481
+ setupSocketListeners();
482
+ }
483
+
484
+ function generate() {
485
+ if (isGenerating) return;
486
+
487
+ document.getElementById('beams').innerHTML = '';
488
+ document.getElementById('completed-list').innerHTML = '';
489
+ beams = {};
490
+ completedBeams = [];
491
+
492
+ resetConnection();
493
+
494
+ const prompt = document.getElementById('prompt').value;
495
+ const numBeams = parseInt(document.getElementById('num_beams').value);
496
+ const maxTokens = parseInt(document.getElementById('max_tokens').value);
497
+ const sleepTime = parseInt(document.getElementById('sleep_time').value);
498
+
499
+ socket.emit('generate', {
500
+ prompt: prompt,
501
+ num_beams: numBeams,
502
+ max_tokens: maxTokens,
503
+ sleep_time: sleepTime
504
+ });
505
+ }
506
+
507
+ setupSocketListeners();
508
+ </script>
509
+ <footer class="footer">
510
+ <div class="footer-content">
511
+ <div class="project-info">
512
+ <h3>About This Project</h3>
513
+ <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>
514
+ <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>
515
+ </div>
516
+ <div class="credit">
517
+ <p>Created by <a href="https://github.com/mosheofer1" target="_blank" rel="noopener noreferrer">Moshe Ofer</a></p>
518
+ <div class="social-links">
519
+ <a href="https://github.com/mosheofer1" target="_blank" rel="noopener noreferrer" title="GitHub">
520
+ <i class="fab fa-github"></i>
521
+ </a>
522
+ <a href="https://www.linkedin.com/in/moshe-ofer/" target="_blank" rel="noopener noreferrer" title="linkedin">
523
+ <i class="fas fa-rocket"></i>
524
+ </a>
525
+ </div>
526
+ </div>
527
+ </div>
528
+ </footer>
529
+ </body>
530
+ </html>