Spaces:
Sleeping
Sleeping
feat: improve UI
Browse files- src/utils/ui.py +64 -16
src/utils/ui.py
CHANGED
|
@@ -5,6 +5,8 @@ UI components and helper functions for the Defensor application.
|
|
| 5 |
import gradio as gr
|
| 6 |
import time
|
| 7 |
import traceback
|
|
|
|
|
|
|
| 8 |
|
| 9 |
def create_fallback_ui(custom_css):
|
| 10 |
"""Create a fallback UI for when data loading fails."""
|
|
@@ -243,24 +245,70 @@ def format_progress_stages(stage, is_error=False):
|
|
| 243 |
return ""
|
| 244 |
|
| 245 |
def format_answer_html(answer, sources=None, reasoning_steps=None, debug=False):
|
| 246 |
-
"""Format the answer into well-structured HTML."""
|
|
|
|
|
|
|
| 247 |
answer_html = ""
|
| 248 |
|
| 249 |
-
#
|
| 250 |
for paragraph in answer.split("\n\n"):
|
| 251 |
-
if paragraph.strip():
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 264 |
|
| 265 |
# Add sources if available
|
| 266 |
if sources:
|
|
@@ -286,7 +334,7 @@ def format_answer_html(answer, sources=None, reasoning_steps=None, debug=False):
|
|
| 286 |
"""
|
| 287 |
answer_html += reasoning_html
|
| 288 |
|
| 289 |
-
# Return the final formatted HTML
|
| 290 |
return f"""
|
| 291 |
<div class="response-wrapper">
|
| 292 |
<div class="answer-container">
|
|
|
|
| 5 |
import gradio as gr
|
| 6 |
import time
|
| 7 |
import traceback
|
| 8 |
+
import uuid
|
| 9 |
+
import re
|
| 10 |
|
| 11 |
def create_fallback_ui(custom_css):
|
| 12 |
"""Create a fallback UI for when data loading fails."""
|
|
|
|
| 245 |
return ""
|
| 246 |
|
| 247 |
def format_answer_html(answer, sources=None, reasoning_steps=None, debug=False):
|
| 248 |
+
"""Format the answer into well-structured HTML with improved Markdown rendering."""
|
| 249 |
+
|
| 250 |
+
# Process Markdown more comprehensively
|
| 251 |
answer_html = ""
|
| 252 |
|
| 253 |
+
# Process paragraphs and add proper formatting
|
| 254 |
for paragraph in answer.split("\n\n"):
|
| 255 |
+
if not paragraph.strip():
|
| 256 |
+
continue
|
| 257 |
+
|
| 258 |
+
# Process headers
|
| 259 |
+
if paragraph.startswith("# "):
|
| 260 |
+
heading = paragraph[2:].strip()
|
| 261 |
+
answer_html += f"<h2>{heading}</h2>"
|
| 262 |
+
elif paragraph.startswith("## "):
|
| 263 |
+
heading = paragraph[3:].strip()
|
| 264 |
+
answer_html += f"<h3>{heading}</h3>"
|
| 265 |
+
elif paragraph.startswith("### "):
|
| 266 |
+
heading = paragraph[4:].strip()
|
| 267 |
+
answer_html += f"<h4>{heading}</h4>"
|
| 268 |
+
# Process lists
|
| 269 |
+
elif re.match(r'^\d+\.\s', paragraph):
|
| 270 |
+
# Ordered list
|
| 271 |
+
items = paragraph.split("\n")
|
| 272 |
+
answer_html += "<ol>"
|
| 273 |
+
for item in items:
|
| 274 |
+
if re.match(r'^\d+\.\s', item):
|
| 275 |
+
item_content = re.sub(r'^\d+\.\s', '', item)
|
| 276 |
+
answer_html += f"<li>{item_content}</li>"
|
| 277 |
+
answer_html += "</ol>"
|
| 278 |
+
elif re.match(r'^[*-]\s', paragraph):
|
| 279 |
+
# Unordered list
|
| 280 |
+
items = paragraph.split("\n")
|
| 281 |
+
answer_html += "<ul>"
|
| 282 |
+
for item in items:
|
| 283 |
+
if re.match(r'^[*-]\s', item):
|
| 284 |
+
item_content = re.sub(r'^[*-]\s', '', item)
|
| 285 |
+
answer_html += f"<li>{item_content}</li>"
|
| 286 |
+
answer_html += "</ul>"
|
| 287 |
+
# Process code blocks
|
| 288 |
+
elif paragraph.startswith("```") and paragraph.endswith("```"):
|
| 289 |
+
code = paragraph[3:-3].strip()
|
| 290 |
+
language = ""
|
| 291 |
+
if "\n" in code:
|
| 292 |
+
first_line = code.split("\n")[0].strip()
|
| 293 |
+
if first_line and not first_line.startswith("```"):
|
| 294 |
+
language = first_line
|
| 295 |
+
code = "\n".join(code.split("\n")[1:])
|
| 296 |
+
|
| 297 |
+
answer_html += f'<pre class="code-block"><code class="language-{language}">{code}</code></pre>'
|
| 298 |
+
# Process blockquotes
|
| 299 |
+
elif paragraph.startswith(">"):
|
| 300 |
+
content = paragraph.replace("\n>", "\n").replace(">", "")
|
| 301 |
+
answer_html += f'<blockquote>{content}</blockquote>'
|
| 302 |
+
# Standard paragraph
|
| 303 |
+
else:
|
| 304 |
+
# Process inline code
|
| 305 |
+
paragraph = re.sub(r'`([^`]+)`', r'<code>\1</code>', paragraph)
|
| 306 |
+
# Process bold text
|
| 307 |
+
paragraph = re.sub(r'\*\*([^*]+)\*\*', r'<strong>\1</strong>', paragraph)
|
| 308 |
+
# Process italic text
|
| 309 |
+
paragraph = re.sub(r'\*([^*]+)\*', r'<em>\1</em>', paragraph)
|
| 310 |
+
|
| 311 |
+
answer_html += f"<p>{paragraph}</p>"
|
| 312 |
|
| 313 |
# Add sources if available
|
| 314 |
if sources:
|
|
|
|
| 334 |
"""
|
| 335 |
answer_html += reasoning_html
|
| 336 |
|
| 337 |
+
# Return the final formatted HTML without any copy functionality
|
| 338 |
return f"""
|
| 339 |
<div class="response-wrapper">
|
| 340 |
<div class="answer-container">
|