Spaces:
Running
Running
Upload app.py
Browse files
app.py
CHANGED
|
@@ -487,6 +487,10 @@ def is_final_answer(text: str) -> bool:
|
|
| 487 |
# ============================================================
|
| 488 |
def render_citations(text: str, browser: SimpleBrowser) -> str:
|
| 489 |
"""Convert citation markers to clickable HTML links with tooltips."""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 490 |
def replace_citation(m):
|
| 491 |
cursor_str = m.group(1)
|
| 492 |
full_match = m.group(0) # Get the full match to extract line info
|
|
@@ -527,7 +531,13 @@ def render_citations(text: str, browser: SimpleBrowser) -> str:
|
|
| 527 |
# Create citation with tooltip (single line to avoid markdown conversion issues)
|
| 528 |
tooltip_html = f'<span class="citation-tooltip"><div class="citation-tooltip-title">{title_display}</div>{line_html}<div class="citation-tooltip-snippet">{snippet_display}</div><div class="citation-tooltip-url">π {html.escape(domain)}</div></span>'
|
| 529 |
|
| 530 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 531 |
|
| 532 |
# Fallback if no URL
|
| 533 |
return f'<span class="citation-link">[{index}]</span>'
|
|
@@ -536,23 +546,14 @@ def render_citations(text: str, browser: SimpleBrowser) -> str:
|
|
| 536 |
pass
|
| 537 |
return m.group(0)
|
| 538 |
|
| 539 |
-
# First pass: replace citations with
|
| 540 |
result = re.sub(r'[γ\[](\d+)β .*?[γ\]]', replace_citation, text)
|
| 541 |
|
| 542 |
# Second pass: Remove standalone URLs that appear after text (common pattern)
|
| 543 |
# This removes URLs that directly follow sentences without proper citation
|
| 544 |
result = re.sub(r'(?<=[.!?])\s+(https?://[^\s]+)', '', result)
|
| 545 |
|
| 546 |
-
# Third pass:
|
| 547 |
-
# Matches: <span class="citation-wrapper">...</span> followed by optional whitespace and same wrapper
|
| 548 |
-
while True:
|
| 549 |
-
# Match citation wrapper and deduplicate
|
| 550 |
-
new_result = re.sub(r'(<span class="citation-wrapper">.*?</span>)(\s*)\1', r'\1', result)
|
| 551 |
-
if new_result == result:
|
| 552 |
-
break
|
| 553 |
-
result = new_result
|
| 554 |
-
|
| 555 |
-
# Convert basic markdown to HTML
|
| 556 |
result = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', result)
|
| 557 |
result = re.sub(r'\*(.+?)\*', r'<em>\1</em>', result)
|
| 558 |
result = re.sub(r'`(.+?)`', r'<code>\1</code>', result)
|
|
@@ -560,6 +561,17 @@ def render_citations(text: str, browser: SimpleBrowser) -> str:
|
|
| 560 |
if not result.startswith('<p>'):
|
| 561 |
result = f'<p>{result}</p>'
|
| 562 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 563 |
return result
|
| 564 |
|
| 565 |
def render_thinking_streaming(text: str) -> str:
|
|
@@ -1545,7 +1557,7 @@ def create_interface():
|
|
| 1545 |
bottom: 125%;
|
| 1546 |
left: 50%;
|
| 1547 |
transform: translateX(-50%);
|
| 1548 |
-
z-index:
|
| 1549 |
width: 280px;
|
| 1550 |
background: linear-gradient(135deg, #ffffff 0%, #f9fafb 100%);
|
| 1551 |
border: 2px solid #10a37f;
|
|
|
|
| 487 |
# ============================================================
|
| 488 |
def render_citations(text: str, browser: SimpleBrowser) -> str:
|
| 489 |
"""Convert citation markers to clickable HTML links with tooltips."""
|
| 490 |
+
# Store citation HTML to protect from markdown conversion
|
| 491 |
+
citation_store = {}
|
| 492 |
+
citation_counter = [0]
|
| 493 |
+
|
| 494 |
def replace_citation(m):
|
| 495 |
cursor_str = m.group(1)
|
| 496 |
full_match = m.group(0) # Get the full match to extract line info
|
|
|
|
| 531 |
# Create citation with tooltip (single line to avoid markdown conversion issues)
|
| 532 |
tooltip_html = f'<span class="citation-tooltip"><div class="citation-tooltip-title">{title_display}</div>{line_html}<div class="citation-tooltip-snippet">{snippet_display}</div><div class="citation-tooltip-url">π {html.escape(domain)}</div></span>'
|
| 533 |
|
| 534 |
+
citation_html = f'<span class="citation-wrapper"><a href="{html.escape(url)}" target="_blank" class="citation-link">[{index}]</a>{tooltip_html}</span>'
|
| 535 |
+
|
| 536 |
+
# Store citation HTML and return placeholder
|
| 537 |
+
placeholder = f'___CITATION_{citation_counter[0]}___'
|
| 538 |
+
citation_store[placeholder] = citation_html
|
| 539 |
+
citation_counter[0] += 1
|
| 540 |
+
return placeholder
|
| 541 |
|
| 542 |
# Fallback if no URL
|
| 543 |
return f'<span class="citation-link">[{index}]</span>'
|
|
|
|
| 546 |
pass
|
| 547 |
return m.group(0)
|
| 548 |
|
| 549 |
+
# First pass: replace citations with placeholders
|
| 550 |
result = re.sub(r'[γ\[](\d+)β .*?[γ\]]', replace_citation, text)
|
| 551 |
|
| 552 |
# Second pass: Remove standalone URLs that appear after text (common pattern)
|
| 553 |
# This removes URLs that directly follow sentences without proper citation
|
| 554 |
result = re.sub(r'(?<=[.!?])\s+(https?://[^\s]+)', '', result)
|
| 555 |
|
| 556 |
+
# Third pass: Convert basic markdown to HTML
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 557 |
result = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', result)
|
| 558 |
result = re.sub(r'\*(.+?)\*', r'<em>\1</em>', result)
|
| 559 |
result = re.sub(r'`(.+?)`', r'<code>\1</code>', result)
|
|
|
|
| 561 |
if not result.startswith('<p>'):
|
| 562 |
result = f'<p>{result}</p>'
|
| 563 |
|
| 564 |
+
# Fourth pass: Restore citation HTML from placeholders
|
| 565 |
+
for placeholder, citation_html in citation_store.items():
|
| 566 |
+
result = result.replace(placeholder, citation_html)
|
| 567 |
+
|
| 568 |
+
# Fifth pass: Deduplicate adjacent identical citations
|
| 569 |
+
while True:
|
| 570 |
+
new_result = re.sub(r'(<span class="citation-wrapper">.*?</span>)(\s*)\1', r'\1', result)
|
| 571 |
+
if new_result == result:
|
| 572 |
+
break
|
| 573 |
+
result = new_result
|
| 574 |
+
|
| 575 |
return result
|
| 576 |
|
| 577 |
def render_thinking_streaming(text: str) -> str:
|
|
|
|
| 1557 |
bottom: 125%;
|
| 1558 |
left: 50%;
|
| 1559 |
transform: translateX(-50%);
|
| 1560 |
+
z-index: 999999;
|
| 1561 |
width: 280px;
|
| 1562 |
background: linear-gradient(135deg, #ffffff 0%, #f9fafb 100%);
|
| 1563 |
border: 2px solid #10a37f;
|