VladBoyko commited on
Commit
02f680e
Β·
verified Β·
1 Parent(s): 126f5e1

Update app.py

Browse files

fixed column issue with gradio 5

Files changed (1) hide show
  1. app.py +146 -89
app.py CHANGED
@@ -2,18 +2,16 @@ import gradio as gr
2
  import re
3
  from vllm import LLM, SamplingParams
4
  import spaces
5
- import torch
6
 
7
  class VibeThinkerVLLM:
8
  def __init__(self, model_path="WeiboAI/VibeThinker-1.5B"):
9
  self.model_path = model_path
10
  print("Loading model with vLLM... This may take a minute.")
11
 
12
- # T4 GPU has compute capability 7.5, which doesn't support bfloat16
13
- # We need to use float16 instead
14
  self.model = LLM(
15
  model=self.model_path,
16
- dtype="float16", # Changed from bfloat16 to float16 for T4 compatibility
17
  gpu_memory_utilization=0.9,
18
  max_model_len=40960,
19
  trust_remote_code=True
@@ -46,9 +44,7 @@ class VibeThinkerVLLM:
46
 
47
 
48
  def parse_model_output(text):
49
- """
50
- Parse model output into structured components
51
- """
52
  sections = []
53
 
54
  # Patterns
@@ -124,21 +120,129 @@ def parse_text_with_code(text):
124
  return sections
125
 
126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  # Initialize model
128
  print("Initializing VibeThinker-1.5B with vLLM...")
129
  model = VibeThinkerVLLM()
130
 
131
 
132
  def generate_response(prompt, temperature, max_tokens, top_p):
133
- """
134
- Generate and parse response - yields components for streaming display
135
- """
136
  if not prompt.strip():
137
- return [gr.Markdown("⚠️ Please enter a question.")]
138
 
139
  try:
140
  # Show generating message
141
- yield [gr.Markdown("πŸ€– Generating response...")]
142
 
143
  # Generate raw response
144
  raw_response = model.infer_text(
@@ -151,52 +255,20 @@ def generate_response(prompt, temperature, max_tokens, top_p):
151
  # Parse the response
152
  sections = parse_model_output(raw_response)
153
 
154
- # Build component list
155
- output_components = []
156
 
157
- for i, section in enumerate(sections):
158
- if section['type'] == 'thinking':
159
- # Thinking section with Accordion
160
- output_components.append(
161
- gr.Accordion("πŸ€” Thinking Process", open=False)
162
- )
163
- output_components.append(
164
- gr.Textbox(
165
- value=section['content'],
166
- lines=20,
167
- max_lines=50,
168
- show_label=False,
169
- container=False,
170
- interactive=False,
171
- elem_classes=["thinking-box"]
172
- )
173
- )
174
-
175
- elif section['type'] == 'code':
176
- # Code section with native Code component
177
- output_components.append(
178
- gr.Accordion(f"πŸ’» Code ({section['language']})", open=True)
179
- )
180
- output_components.append(
181
- gr.Code(
182
- value=section['content'],
183
- language=section['language'],
184
- lines=20,
185
- show_label=False,
186
- interactive=False,
187
- elem_classes=["code-box"]
188
- )
189
- )
190
-
191
- else: # text
192
- output_components.append(
193
- gr.Markdown(section['content'])
194
- )
195
-
196
- yield output_components
197
 
198
  except Exception as e:
199
- yield [gr.Markdown(f"❌ **Error:** {str(e)}\n\nPlease try again or adjust the parameters.")]
 
 
 
 
 
 
 
200
 
201
 
202
  # Custom theme for Gradio 5
@@ -218,16 +290,6 @@ with gr.Blocks(
218
  title="VibeThinker-1.5B Advanced",
219
  theme=theme,
220
  fill_height=False,
221
- css="""
222
- .thinking-box {
223
- font-family: 'Courier New', monospace;
224
- font-size: 13px;
225
- line-height: 1.6;
226
- }
227
- .code-box {
228
- font-family: 'Fira Code', 'Courier New', monospace;
229
- }
230
- """,
231
  ) as demo:
232
 
233
  gr.Markdown("""
@@ -237,8 +299,8 @@ with gr.Blocks(
237
 
238
  ### ✨ Features:
239
  - πŸ€” **Collapsible Thinking Sections** - Explore the model's reasoning process
240
- - πŸ’» **Syntax-Highlighted Code** - Native code display with copy functionality
241
- - πŸ“ **Clean Markdown Output** - Beautiful formatting for text responses
242
 
243
  **Best for:** Competitive math problems and algorithm coding challenges
244
 
@@ -295,18 +357,15 @@ with gr.Blocks(
295
  )
296
 
297
  with gr.Column(scale=1):
298
- # Output area
299
- output_area = gr.Column()
300
-
301
- with output_area:
302
- initial_message = gr.Markdown(
303
- """
304
- <div style='text-align: center; padding: 60px; color: #7f8c8d;'>
305
- <h3>πŸ‘‹ Ready to solve problems!</h3>
306
- <p>Enter your question and click Generate Solution</p>
307
- </div>
308
- """
309
- )
310
 
311
  # Example problems
312
  gr.Examples(
@@ -337,26 +396,24 @@ with gr.Blocks(
337
 
338
  # Event handlers
339
  def clear_interface():
340
- return "", [gr.Markdown(
341
- """
342
- <div style='text-align: center; padding: 60px; color: #7f8c8d;'>
343
- <h3>πŸ‘‹ Ready to solve problems!</h3>
344
- <p>Enter your question and click Generate Solution</p>
345
- </div>
346
- """
347
- )]
348
 
349
  submit_btn.click(
350
  fn=generate_response,
351
  inputs=[prompt_input, temperature_slider, max_tokens_slider, top_p_slider],
352
- outputs=[output_area],
353
  show_progress="full"
354
  )
355
 
356
  clear_btn.click(
357
  fn=clear_interface,
358
  inputs=[],
359
- outputs=[prompt_input, output_area]
360
  )
361
 
362
 
 
2
  import re
3
  from vllm import LLM, SamplingParams
4
  import spaces
 
5
 
6
  class VibeThinkerVLLM:
7
  def __init__(self, model_path="WeiboAI/VibeThinker-1.5B"):
8
  self.model_path = model_path
9
  print("Loading model with vLLM... This may take a minute.")
10
 
11
+ # T4 GPU compatible - using float16
 
12
  self.model = LLM(
13
  model=self.model_path,
14
+ dtype="float16",
15
  gpu_memory_utilization=0.9,
16
  max_model_len=40960,
17
  trust_remote_code=True
 
44
 
45
 
46
  def parse_model_output(text):
47
+ """Parse model output into structured components"""
 
 
48
  sections = []
49
 
50
  # Patterns
 
120
  return sections
121
 
122
 
123
+ def format_sections_to_html(sections):
124
+ """
125
+ Convert parsed sections to rich HTML with collapsible elements
126
+ This approach works reliably with Gradio 5's HTML component
127
+ """
128
+ html_parts = []
129
+
130
+ # Add JavaScript for interactivity
131
+ html_parts.append("""
132
+ <script>
133
+ function copyCode(elementId) {
134
+ const codeElement = document.getElementById(elementId);
135
+ const code = codeElement.textContent;
136
+ navigator.clipboard.writeText(code).then(() => {
137
+ // Show temporary success message
138
+ const btn = event.target;
139
+ const originalText = btn.textContent;
140
+ btn.textContent = 'βœ… Copied!';
141
+ setTimeout(() => { btn.textContent = originalText; }, 2000);
142
+ }).catch(err => {
143
+ console.error('Failed to copy:', err);
144
+ alert('Failed to copy code');
145
+ });
146
+ }
147
+
148
+ function downloadCode(elementId, language) {
149
+ const codeElement = document.getElementById(elementId);
150
+ const code = codeElement.textContent;
151
+
152
+ const extensions = {
153
+ 'python': 'py', 'javascript': 'js', 'typescript': 'ts',
154
+ 'html': 'html', 'css': 'css', 'java': 'java',
155
+ 'cpp': 'cpp', 'c': 'c', 'ruby': 'rb',
156
+ 'go': 'go', 'rust': 'rs', 'swift': 'swift',
157
+ 'kotlin': 'kt', 'plaintext': 'txt'
158
+ };
159
+
160
+ const ext = extensions[language.toLowerCase()] || 'txt';
161
+ const filename = `code_snippet.${ext}`;
162
+
163
+ const blob = new Blob([code], { type: 'text/plain' });
164
+ const url = window.URL.createObjectURL(blob);
165
+ const a = document.createElement('a');
166
+ a.href = url;
167
+ a.download = filename;
168
+ document.body.appendChild(a);
169
+ a.click();
170
+ document.body.removeChild(a);
171
+ window.URL.revokeObjectURL(url);
172
+ }
173
+ </script>
174
+ """)
175
+
176
+ for i, section in enumerate(sections):
177
+ if section['type'] == 'thinking':
178
+ # Collapsible thinking section
179
+ html_parts.append(f"""
180
+ <details class="thinking-section" style="margin: 15px 0; border: 2px solid #f39c12; border-radius: 8px; background-color: #fff9e6;">
181
+ <summary style="padding: 12px; cursor: pointer; font-weight: bold; color: #d68910; user-select: none;">
182
+ πŸ€” Thinking Process (Click to expand)
183
+ </summary>
184
+ <div style="padding: 15px; border-top: 1px solid #f39c12; background-color: #fffef7; white-space: pre-wrap; font-family: 'Courier New', monospace; font-size: 13px; color: #333; line-height: 1.6; max-height: 500px; overflow-y: auto;">
185
+ {section['content']}
186
+ </div>
187
+ </details>
188
+ """)
189
+
190
+ elif section['type'] == 'code':
191
+ # Code block with copy/download buttons
192
+ code_id = f"code-{i}"
193
+ # Escape HTML in code
194
+ escaped_code = section['content'].replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
195
+
196
+ html_parts.append(f"""
197
+ <details class="code-section" open style="margin: 15px 0; border: 2px solid #3498db; border-radius: 8px; background-color: #e8f4fd;">
198
+ <summary style="padding: 12px; cursor: pointer; font-weight: bold; color: #2874a6; user-select: none;">
199
+ πŸ’» Code ({section['language']}) - Click to collapse
200
+ </summary>
201
+ <div style="position: relative; padding: 0;">
202
+ <div style="position: absolute; top: 10px; right: 10px; z-index: 10;">
203
+ <button onclick="copyCode('{code_id}')" style="padding: 6px 12px; margin-right: 5px; background-color: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px;">
204
+ πŸ“‹ Copy
205
+ </button>
206
+ <button onclick="downloadCode('{code_id}', '{section['language']}')" style="padding: 6px 12px; background-color: #27ae60; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px;">
207
+ ⬇️ Download
208
+ </button>
209
+ </div>
210
+ <pre id="{code_id}" style="margin: 0; padding: 40px 15px 15px 15px; background-color: #f8f9fa; border-top: 1px solid #3498db; overflow-x: auto; font-family: 'Courier New', monospace; font-size: 13px; line-height: 1.5;"><code class="language-{section['language']}">{escaped_code}</code></pre>
211
+ </div>
212
+ </details>
213
+ """)
214
+
215
+ else: # text
216
+ # Regular text output with markdown-style rendering
217
+ # Convert markdown to HTML
218
+ text_html = section['content']
219
+ # Basic markdown conversions
220
+ text_html = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', text_html)
221
+ text_html = re.sub(r'\*(.*?)\*', r'<em>\1</em>', text_html)
222
+ text_html = re.sub(r'`(.*?)`', r'<code style="background-color: #f4f4f4; padding: 2px 5px; border-radius: 3px;">\1</code>', text_html)
223
+
224
+ html_parts.append(f"""
225
+ <div class="text-section" style="margin: 15px 0; padding: 15px; border: 1px solid #bdc3c7; border-radius: 8px; background-color: #ffffff; white-space: pre-wrap; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; font-size: 14px; line-height: 1.8; color: #2c3e50;">
226
+ {text_html}
227
+ </div>
228
+ """)
229
+
230
+ return "\n".join(html_parts)
231
+
232
+
233
  # Initialize model
234
  print("Initializing VibeThinker-1.5B with vLLM...")
235
  model = VibeThinkerVLLM()
236
 
237
 
238
  def generate_response(prompt, temperature, max_tokens, top_p):
239
+ """Generate and return formatted HTML response"""
 
 
240
  if not prompt.strip():
241
+ return "<div style='color: #e74c3c; padding: 20px; text-align: center;'>⚠️ Please enter a question.</div>"
242
 
243
  try:
244
  # Show generating message
245
+ yield "<div style='text-align: center; padding: 40px; color: #3498db;'><h3>πŸ€– Generating response...</h3><p>This may take a moment...</p></div>"
246
 
247
  # Generate raw response
248
  raw_response = model.infer_text(
 
255
  # Parse the response
256
  sections = parse_model_output(raw_response)
257
 
258
+ # Convert to HTML
259
+ html_output = format_sections_to_html(sections)
260
 
261
+ yield html_output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
 
263
  except Exception as e:
264
+ error_html = f"""
265
+ <div style='color: #e74c3c; padding: 20px; border: 2px solid #e74c3c; border-radius: 8px; background-color: #fadbd8; margin: 15px 0;'>
266
+ <h3>❌ Error</h3>
267
+ <p><strong>{str(e)}</strong></p>
268
+ <p>Please try again or adjust the parameters.</p>
269
+ </div>
270
+ """
271
+ yield error_html
272
 
273
 
274
  # Custom theme for Gradio 5
 
290
  title="VibeThinker-1.5B Advanced",
291
  theme=theme,
292
  fill_height=False,
 
 
 
 
 
 
 
 
 
 
293
  ) as demo:
294
 
295
  gr.Markdown("""
 
299
 
300
  ### ✨ Features:
301
  - πŸ€” **Collapsible Thinking Sections** - Explore the model's reasoning process
302
+ - πŸ’» **Interactive Code Blocks** - Copy or download code with one click
303
+ - πŸ“ **Clean Formatted Output** - Beautiful rendering for all content types
304
 
305
  **Best for:** Competitive math problems and algorithm coding challenges
306
 
 
357
  )
358
 
359
  with gr.Column(scale=1):
360
+ # Output area using HTML component
361
+ output_html = gr.HTML(
362
+ value="""
363
+ <div style='text-align: center; padding: 60px; color: #7f8c8d;'>
364
+ <h3>πŸ‘‹ Ready to solve problems!</h3>
365
+ <p>Enter your question and click Generate Solution</p>
366
+ </div>
367
+ """
368
+ )
 
 
 
369
 
370
  # Example problems
371
  gr.Examples(
 
396
 
397
  # Event handlers
398
  def clear_interface():
399
+ return "", """
400
+ <div style='text-align: center; padding: 60px; color: #7f8c8d;'>
401
+ <h3>πŸ‘‹ Ready to solve problems!</h3>
402
+ <p>Enter your question and click Generate Solution</p>
403
+ </div>
404
+ """
 
 
405
 
406
  submit_btn.click(
407
  fn=generate_response,
408
  inputs=[prompt_input, temperature_slider, max_tokens_slider, top_p_slider],
409
+ outputs=output_html,
410
  show_progress="full"
411
  )
412
 
413
  clear_btn.click(
414
  fn=clear_interface,
415
  inputs=[],
416
+ outputs=[prompt_input, output_html]
417
  )
418
 
419