Spaces:
Running
on
Zero
Running
on
Zero
Commit
·
347797e
1
Parent(s):
e7fd2d9
update data pipeline
Browse files- app.py +61 -32
- utils/arena_df.csv +0 -0
- utils/context_processor.py +57 -467
- utils/data_loader.py +91 -80
app.py
CHANGED
@@ -37,6 +37,7 @@ def load_context(set_interrupt=False):
|
|
37 |
generation_interrupt.clear()
|
38 |
example = get_random_example()
|
39 |
|
|
|
40 |
context_desc = example.get('processed_context_desc', '')
|
41 |
if context_desc:
|
42 |
context_desc = f"<div class='context-topic'><span class='topic-label'>The question and context are about:</span> {context_desc}</div>"
|
@@ -75,14 +76,17 @@ def generate_model_summaries(example):
|
|
75 |
|
76 |
try:
|
77 |
m_a_name, m_b_name = random.sample(model_names, 2)
|
|
|
|
|
|
|
|
|
|
|
78 |
s_a, s_b = generate_summaries(example, m_a_name, m_b_name)
|
79 |
|
80 |
if not generation_interrupt.is_set():
|
81 |
-
result["model_a"] = m_a_name
|
82 |
-
result["model_b"] = m_b_name
|
83 |
result["summary_a"] = s_a
|
84 |
result["summary_b"] = s_b
|
85 |
-
result["completed"] =
|
86 |
except Exception as e:
|
87 |
print(f"Error in generation: {e}")
|
88 |
|
@@ -90,16 +94,20 @@ def generate_model_summaries(example):
|
|
90 |
|
91 |
def process_generation_result(result):
|
92 |
"""Process the results from the generation function"""
|
93 |
-
if not result["completed"]:
|
94 |
-
#
|
95 |
return [
|
96 |
-
"", "",
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
gr.update(
|
102 |
-
gr.update(
|
|
|
|
|
|
|
|
|
103 |
gr.update(choices=[], value=[], interactive=False, visible=False),
|
104 |
gr.update(visible=False),
|
105 |
gr.update(interactive=False, visible=True),
|
@@ -108,6 +116,9 @@ def process_generation_result(result):
|
|
108 |
gr.update(elem_classes=[])
|
109 |
]
|
110 |
|
|
|
|
|
|
|
111 |
# Generation completed successfully
|
112 |
agg_results = load_leaderboard_data()
|
113 |
return [
|
@@ -116,10 +127,10 @@ def process_generation_result(result):
|
|
116 |
None, [], False, agg_results,
|
117 |
gr.update(value=result["summary_a"]),
|
118 |
gr.update(value=result["summary_b"]),
|
119 |
-
gr.update(interactive=
|
120 |
-
gr.update(interactive=
|
121 |
-
gr.update(interactive=
|
122 |
-
gr.update(interactive=
|
123 |
gr.update(choices=[], value=[], interactive=False, visible=False),
|
124 |
gr.update(visible=False),
|
125 |
gr.update(interactive=False, visible=True),
|
@@ -178,10 +189,10 @@ def show_loading_state():
|
|
178 |
return [
|
179 |
gr.update(value="Loading new question and summaries...", interactive=False),
|
180 |
gr.update(value="Loading new question and summaries...", interactive=False),
|
181 |
-
gr.update(interactive=False),
|
182 |
-
gr.update(interactive=False),
|
183 |
-
gr.update(interactive=False),
|
184 |
-
gr.update(interactive=False)
|
185 |
]
|
186 |
|
187 |
def handle_new_example_click():
|
@@ -191,9 +202,14 @@ def handle_new_example_click():
|
|
191 |
|
192 |
def update_ui_for_new_context(example):
|
193 |
"""Update UI with new context information"""
|
|
|
|
|
|
|
|
|
|
|
194 |
return [
|
195 |
gr.update(value=example['question']),
|
196 |
-
gr.update(value=
|
197 |
gr.update(value=get_context_html(example, False)),
|
198 |
gr.update(value="Show Full Context", elem_classes=["context-toggle-button"]),
|
199 |
False
|
@@ -204,6 +220,8 @@ def cleanup_on_disconnect():
|
|
204 |
"""Clean up resources when browser disconnects"""
|
205 |
print(f"Browser disconnected. Cleaning up resources...")
|
206 |
generation_interrupt.set()
|
|
|
|
|
207 |
|
208 |
# Create Gradio interface
|
209 |
with gr.Blocks(theme=gr.themes.Default(
|
@@ -213,11 +231,11 @@ with gr.Blocks(theme=gr.themes.Default(
|
|
213 |
# Load CSS
|
214 |
css_path = os.path.join(os.getcwd(), 'static', 'styles.css')
|
215 |
|
216 |
-
# Load the
|
217 |
with open(css_path, 'r') as f:
|
218 |
css_content = f.read()
|
219 |
|
220 |
-
# Create HTML components with CSS
|
221 |
gr.HTML(f"<style>{css_content}</style>")
|
222 |
|
223 |
# Add JavaScript to handle browser unload events
|
@@ -309,10 +327,10 @@ with gr.Blocks(theme=gr.themes.Default(
|
|
309 |
# Voting section
|
310 |
gr.Markdown("### 🏅 Cast Your Vote", elem_classes="section-heading")
|
311 |
with gr.Row():
|
312 |
-
vote_button_a = gr.Button("⬅️ Summary A is Better", elem_classes=["vote-button"])
|
313 |
-
vote_button_tie = gr.Button("🤝 Tie / Equally Good", elem_classes=["vote-button"])
|
314 |
-
vote_button_b = gr.Button("➡️ Summary B is Better", elem_classes=["vote-button"])
|
315 |
-
vote_button_neither = gr.Button("❌ Neither is Good", elem_classes=["vote-button", "vote-button-neither"])
|
316 |
|
317 |
# Feedback and Submit sections
|
318 |
with gr.Group(elem_classes=["feedback-section"], visible=False) as feedback_section:
|
@@ -388,15 +406,17 @@ The Elo rating system provides a more accurate ranking than simple win rates:
|
|
388 |
outputs=[results_table_display]
|
389 |
)
|
390 |
|
391 |
-
#
|
|
|
392 |
for btn in [random_question_btn, try_another_btn]:
|
393 |
-
|
394 |
-
|
|
|
395 |
inputs=[],
|
396 |
outputs=[summary_a_display, summary_b_display, vote_button_a,
|
397 |
vote_button_b, vote_button_tie, vote_button_neither]
|
398 |
).then(
|
399 |
-
fn=handle_new_example_click,
|
400 |
inputs=[],
|
401 |
outputs=[current_example]
|
402 |
).then(
|
@@ -404,9 +424,18 @@ The Elo rating system provides a more accurate ranking than simple win rates:
|
|
404 |
inputs=[current_example],
|
405 |
outputs=[query_display, context_description, context_display,
|
406 |
context_toggle_btn, show_full_context]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
407 |
).then(
|
408 |
fn=process_example,
|
409 |
-
inputs=[current_example],
|
410 |
outputs=[model_a_name, model_b_name, summary_a_text, summary_b_text,
|
411 |
selected_winner, feedback_list, show_results_state, results_agg,
|
412 |
summary_a_display, summary_b_display, vote_button_a, vote_button_b,
|
|
|
37 |
generation_interrupt.clear()
|
38 |
example = get_random_example()
|
39 |
|
40 |
+
# Format the context description
|
41 |
context_desc = example.get('processed_context_desc', '')
|
42 |
if context_desc:
|
43 |
context_desc = f"<div class='context-topic'><span class='topic-label'>The question and context are about:</span> {context_desc}</div>"
|
|
|
76 |
|
77 |
try:
|
78 |
m_a_name, m_b_name = random.sample(model_names, 2)
|
79 |
+
|
80 |
+
# Track the partial completion state
|
81 |
+
result["model_a"] = m_a_name
|
82 |
+
result["model_b"] = m_b_name
|
83 |
+
|
84 |
s_a, s_b = generate_summaries(example, m_a_name, m_b_name)
|
85 |
|
86 |
if not generation_interrupt.is_set():
|
|
|
|
|
87 |
result["summary_a"] = s_a
|
88 |
result["summary_b"] = s_b
|
89 |
+
result["completed"] = bool(s_a and s_b) # Only mark complete if both have content
|
90 |
except Exception as e:
|
91 |
print(f"Error in generation: {e}")
|
92 |
|
|
|
94 |
|
95 |
def process_generation_result(result):
|
96 |
"""Process the results from the generation function"""
|
97 |
+
if not result["completed"] or not result["summary_a"] or not result["summary_b"]:
|
98 |
+
# Either generation was interrupted or both summaries aren't ready
|
99 |
return [
|
100 |
+
result.get("model_a", ""),
|
101 |
+
result.get("model_b", ""),
|
102 |
+
result.get("summary_a", ""),
|
103 |
+
result.get("summary_b", ""),
|
104 |
+
None, [], False, load_leaderboard_data(),
|
105 |
+
gr.update(value=result.get("summary_a", "Generation was interrupted or failed.")),
|
106 |
+
gr.update(value=result.get("summary_b", "Generation was interrupted or failed.")),
|
107 |
+
gr.update(interactive=False, elem_classes=["vote-button"]), # Explicitly disable
|
108 |
+
gr.update(interactive=False, elem_classes=["vote-button"]),
|
109 |
+
gr.update(interactive=False, elem_classes=["vote-button"]),
|
110 |
+
gr.update(interactive=False, elem_classes=["vote-button", "vote-button-neither"]),
|
111 |
gr.update(choices=[], value=[], interactive=False, visible=False),
|
112 |
gr.update(visible=False),
|
113 |
gr.update(interactive=False, visible=True),
|
|
|
116 |
gr.update(elem_classes=[])
|
117 |
]
|
118 |
|
119 |
+
# Only enable voting when both summaries are complete and non-empty
|
120 |
+
buttons_interactive = bool(result["summary_a"] and result["summary_b"])
|
121 |
+
|
122 |
# Generation completed successfully
|
123 |
agg_results = load_leaderboard_data()
|
124 |
return [
|
|
|
127 |
None, [], False, agg_results,
|
128 |
gr.update(value=result["summary_a"]),
|
129 |
gr.update(value=result["summary_b"]),
|
130 |
+
gr.update(interactive=buttons_interactive, elem_classes=["vote-button"]),
|
131 |
+
gr.update(interactive=buttons_interactive, elem_classes=["vote-button"]),
|
132 |
+
gr.update(interactive=buttons_interactive, elem_classes=["vote-button"]),
|
133 |
+
gr.update(interactive=buttons_interactive, elem_classes=["vote-button", "vote-button-neither"]),
|
134 |
gr.update(choices=[], value=[], interactive=False, visible=False),
|
135 |
gr.update(visible=False),
|
136 |
gr.update(interactive=False, visible=True),
|
|
|
189 |
return [
|
190 |
gr.update(value="Loading new question and summaries...", interactive=False),
|
191 |
gr.update(value="Loading new question and summaries...", interactive=False),
|
192 |
+
gr.update(interactive=False), # For vote_button_a
|
193 |
+
gr.update(interactive=False), # For vote_button_b
|
194 |
+
gr.update(interactive=False), # For vote_button_tie
|
195 |
+
gr.update(interactive=False) # For vote_button_neither
|
196 |
]
|
197 |
|
198 |
def handle_new_example_click():
|
|
|
202 |
|
203 |
def update_ui_for_new_context(example):
|
204 |
"""Update UI with new context information"""
|
205 |
+
# Format the context description
|
206 |
+
context_desc = example.get('processed_context_desc', '')
|
207 |
+
if context_desc:
|
208 |
+
context_desc = f"<div class='context-topic'><span class='topic-label'>The question and context are about:</span> {context_desc}</div>"
|
209 |
+
|
210 |
return [
|
211 |
gr.update(value=example['question']),
|
212 |
+
gr.update(value=context_desc, visible=bool(context_desc)),
|
213 |
gr.update(value=get_context_html(example, False)),
|
214 |
gr.update(value="Show Full Context", elem_classes=["context-toggle-button"]),
|
215 |
False
|
|
|
220 |
"""Clean up resources when browser disconnects"""
|
221 |
print(f"Browser disconnected. Cleaning up resources...")
|
222 |
generation_interrupt.set()
|
223 |
+
# No need for time.sleep here as this is just setting the flag
|
224 |
+
# Threads will detect it on their next check
|
225 |
|
226 |
# Create Gradio interface
|
227 |
with gr.Blocks(theme=gr.themes.Default(
|
|
|
231 |
# Load CSS
|
232 |
css_path = os.path.join(os.getcwd(), 'static', 'styles.css')
|
233 |
|
234 |
+
# Load the CSS file
|
235 |
with open(css_path, 'r') as f:
|
236 |
css_content = f.read()
|
237 |
|
238 |
+
# Create HTML components with CSS
|
239 |
gr.HTML(f"<style>{css_content}</style>")
|
240 |
|
241 |
# Add JavaScript to handle browser unload events
|
|
|
327 |
# Voting section
|
328 |
gr.Markdown("### 🏅 Cast Your Vote", elem_classes="section-heading")
|
329 |
with gr.Row():
|
330 |
+
vote_button_a = gr.Button("⬅️ Summary A is Better", elem_classes=["vote-button"], interactive=False)
|
331 |
+
vote_button_tie = gr.Button("🤝 Tie / Equally Good", elem_classes=["vote-button"], interactive=False)
|
332 |
+
vote_button_b = gr.Button("➡️ Summary B is Better", elem_classes=["vote-button"], interactive=False)
|
333 |
+
vote_button_neither = gr.Button("❌ Neither is Good", elem_classes=["vote-button", "vote-button-neither"], interactive=False)
|
334 |
|
335 |
# Feedback and Submit sections
|
336 |
with gr.Group(elem_classes=["feedback-section"], visible=False) as feedback_section:
|
|
|
406 |
outputs=[results_table_display]
|
407 |
)
|
408 |
|
409 |
+
# Alternative approach: use two separate clicks for each button
|
410 |
+
# First click event: Update UI immediately
|
411 |
for btn in [random_question_btn, try_another_btn]:
|
412 |
+
# Handle UI updates first
|
413 |
+
event1 = btn.click(
|
414 |
+
fn=show_loading_state,
|
415 |
inputs=[],
|
416 |
outputs=[summary_a_display, summary_b_display, vote_button_a,
|
417 |
vote_button_b, vote_button_tie, vote_button_neither]
|
418 |
).then(
|
419 |
+
fn=handle_new_example_click,
|
420 |
inputs=[],
|
421 |
outputs=[current_example]
|
422 |
).then(
|
|
|
424 |
inputs=[current_example],
|
425 |
outputs=[query_display, context_description, context_display,
|
426 |
context_toggle_btn, show_full_context]
|
427 |
+
)
|
428 |
+
|
429 |
+
# Second click event for each button runs in parallel with the first
|
430 |
+
for btn in [random_question_btn, try_another_btn]:
|
431 |
+
# Generate model outputs (potentially slower operation)
|
432 |
+
event2 = btn.click(
|
433 |
+
fn=handle_new_example_click, # This will be called separately from the first event
|
434 |
+
inputs=[],
|
435 |
+
outputs=[current_example]
|
436 |
).then(
|
437 |
fn=process_example,
|
438 |
+
inputs=[current_example],
|
439 |
outputs=[model_a_name, model_b_name, summary_a_text, summary_b_text,
|
440 |
selected_winner, feedback_list, show_results_state, results_agg,
|
441 |
summary_a_display, summary_b_display, vote_button_a, vote_button_b,
|
utils/arena_df.csv
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
utils/context_processor.py
CHANGED
@@ -1,460 +1,51 @@
|
|
1 |
import re
|
2 |
import html
|
3 |
import json
|
4 |
-
from typing import Dict, List, Tuple, Optional, Any, Union
|
5 |
|
6 |
-
|
7 |
-
"""
|
8 |
-
|
9 |
-
# Common HTML entities that might be incomplete
|
10 |
-
INCOMPLETE_ENTITIES = {
|
11 |
-
''': ''',
|
12 |
-
'"': '"',
|
13 |
-
'<': '<',
|
14 |
-
'>': '>',
|
15 |
-
'&': '&'
|
16 |
-
}
|
17 |
-
|
18 |
-
@staticmethod
|
19 |
-
def clean_text(text: str) -> str:
|
20 |
-
"""Cleans text by fixing HTML entities and handling escaped characters"""
|
21 |
-
if not text or not isinstance(text, str):
|
22 |
-
return text
|
23 |
-
|
24 |
-
# Fix incomplete HTML entities
|
25 |
-
for incomplete, complete in ContextProcessor.INCOMPLETE_ENTITIES.items():
|
26 |
-
text = re.sub(f"{re.escape(incomplete)}(?!;)", complete, text)
|
27 |
-
|
28 |
-
# Convert HTML entities to characters
|
29 |
-
try:
|
30 |
-
text = html.unescape(text)
|
31 |
-
except Exception:
|
32 |
-
pass
|
33 |
-
|
34 |
-
# Handle escaped quotes and special characters
|
35 |
-
replacements = {
|
36 |
-
r'\"': '"', r"\'": "'", r"\n": "\n", r"\t": "\t", r"\\": "\\",
|
37 |
-
'"': '"', '"': '"', ''': "'", ''': "'", '`': "'", '´': "'"
|
38 |
-
}
|
39 |
-
for pattern, replacement in replacements.items():
|
40 |
-
text = text.replace(pattern, replacement)
|
41 |
-
|
42 |
-
# Remove trailing backslash if present
|
43 |
-
if text.rstrip().endswith('\\'):
|
44 |
-
text = text.rstrip().rstrip('\\')
|
45 |
-
|
46 |
-
return text
|
47 |
-
|
48 |
-
@staticmethod
|
49 |
-
def balance_highlight_tags(text: str) -> str:
|
50 |
-
"""Ensures highlight tags are properly balanced"""
|
51 |
-
if not text or not isinstance(text, str):
|
52 |
-
return text
|
53 |
-
|
54 |
-
# Define highlight tag patterns
|
55 |
-
highlight_pairs = [
|
56 |
-
('[[start_highlight]]', '[[end_highlight]]'),
|
57 |
-
('[[highlight_start]]', '[[highlight_end]]'),
|
58 |
-
('<span class="highlight">', '</span>')
|
59 |
-
]
|
60 |
-
|
61 |
-
# Check and balance each pair
|
62 |
-
for start_tag, end_tag in highlight_pairs:
|
63 |
-
start_count = text.count(start_tag)
|
64 |
-
end_count = text.count(end_tag)
|
65 |
-
|
66 |
-
# Add missing tags if needed
|
67 |
-
if start_count > end_count:
|
68 |
-
text += end_tag * (start_count - end_count)
|
69 |
-
elif end_count > start_count:
|
70 |
-
text = start_tag * (end_count - start_count) + text
|
71 |
-
|
72 |
-
return text
|
73 |
-
|
74 |
-
@staticmethod
|
75 |
-
def balance_quotes(text: str) -> str:
|
76 |
-
"""Ensures quotes are properly balanced"""
|
77 |
-
if not text or not isinstance(text, str):
|
78 |
-
return text
|
79 |
-
|
80 |
-
# First, remove escaped quotes from the count
|
81 |
-
plain_text = text.replace('\\"', '')
|
82 |
-
|
83 |
-
# Count quotes and balance if needed
|
84 |
-
quote_count = plain_text.count('"')
|
85 |
-
if quote_count % 2 == 1:
|
86 |
-
text += '"'
|
87 |
-
|
88 |
return text
|
89 |
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
"""
|
95 |
-
# Ensure highlight tags are balanced
|
96 |
-
text = ContextProcessor.balance_highlight_tags(text)
|
97 |
-
|
98 |
-
# Define all highlight patterns
|
99 |
-
highlight_patterns = [
|
100 |
-
('[[start_highlight]]', '[[end_highlight]]'),
|
101 |
-
('[[highlight_start]]', '[[highlight_end]]'),
|
102 |
-
('<span class="highlight">', '</span>')
|
103 |
-
]
|
104 |
-
|
105 |
-
# Collect all highlight sections with their positions
|
106 |
-
all_highlights = []
|
107 |
-
|
108 |
-
for start_tag, end_tag in highlight_patterns:
|
109 |
-
# Escape special regex characters if needed
|
110 |
-
start_esc = re.escape(start_tag)
|
111 |
-
end_esc = re.escape(end_tag)
|
112 |
-
|
113 |
-
# Find all occurrences of this highlight pattern
|
114 |
-
for match in re.finditer(f"{start_esc}(.*?){end_esc}", text, re.DOTALL):
|
115 |
-
all_highlights.append({
|
116 |
-
'start': match.start(),
|
117 |
-
'end': match.end(),
|
118 |
-
'content': match.group(1),
|
119 |
-
'start_tag': start_tag,
|
120 |
-
'end_tag': end_tag
|
121 |
-
})
|
122 |
-
|
123 |
-
# If no highlights found, return the whole text as unhighlighted
|
124 |
-
if not all_highlights:
|
125 |
-
return [(False, text)]
|
126 |
-
|
127 |
-
# Sort highlights by start position
|
128 |
-
all_highlights.sort(key=lambda x: x['start'])
|
129 |
-
|
130 |
-
# Build the parts list by processing text portions between and including highlights
|
131 |
-
parts = []
|
132 |
-
current_pos = 0
|
133 |
-
|
134 |
-
for highlight in all_highlights:
|
135 |
-
# Add non-highlighted text before this highlight
|
136 |
-
if highlight['start'] > current_pos:
|
137 |
-
parts.append((False, text[current_pos:highlight['start']]))
|
138 |
-
|
139 |
-
# Add the highlighted text
|
140 |
-
parts.append((True, highlight['content']))
|
141 |
-
|
142 |
-
# Update position to end of this highlight
|
143 |
-
current_pos = highlight['end']
|
144 |
-
|
145 |
-
# Add any remaining text after the last highlight
|
146 |
-
if current_pos < len(text):
|
147 |
-
parts.append((False, text[current_pos:]))
|
148 |
-
|
149 |
-
return parts
|
150 |
-
|
151 |
-
@staticmethod
|
152 |
-
def is_markdown_table(text: str) -> bool:
|
153 |
-
"""Checks if text looks like a markdown table"""
|
154 |
-
if not text or not isinstance(text, str):
|
155 |
-
return False
|
156 |
-
|
157 |
-
if '|' in text and '\n' in text:
|
158 |
-
lines = text.strip().split('\n')
|
159 |
-
pipe_lines = sum(1 for line in lines if line.strip().startswith('|'))
|
160 |
-
return pipe_lines >= 2
|
161 |
-
|
162 |
-
return False
|
163 |
-
|
164 |
-
@staticmethod
|
165 |
-
def process_cell_content(cell_text: str) -> str:
|
166 |
-
"""Processes a single table cell, handling highlights if present"""
|
167 |
-
# Clean and prepare the text
|
168 |
-
cell_text = ContextProcessor.clean_text(cell_text)
|
169 |
-
cell_text = ContextProcessor.balance_quotes(cell_text)
|
170 |
-
|
171 |
-
# Check if cell has any highlight tags
|
172 |
-
has_highlights = False
|
173 |
-
highlight_patterns = [
|
174 |
-
'[[start_highlight]]', '[[end_highlight]]',
|
175 |
-
'[[highlight_start]]', '[[highlight_end]]',
|
176 |
-
'<span class="highlight">', '</span>'
|
177 |
-
]
|
178 |
-
|
179 |
-
for pattern in highlight_patterns:
|
180 |
-
if pattern in cell_text:
|
181 |
-
has_highlights = True
|
182 |
-
break
|
183 |
-
|
184 |
-
if has_highlights:
|
185 |
-
# Extract and process highlight parts
|
186 |
-
parts = ContextProcessor.extract_highlight_parts(cell_text)
|
187 |
-
|
188 |
-
# Build the result
|
189 |
-
result = ""
|
190 |
-
for is_highlighted, part in parts:
|
191 |
-
if is_highlighted:
|
192 |
-
result += f'<span class="highlight">{html.escape(part)}</span>'
|
193 |
-
else:
|
194 |
-
result += html.escape(part)
|
195 |
-
|
196 |
-
return result
|
197 |
-
else:
|
198 |
-
# Just escape HTML in regular cells
|
199 |
-
return html.escape(cell_text)
|
200 |
-
|
201 |
-
@staticmethod
|
202 |
-
def convert_table_to_html(text: str) -> str:
|
203 |
-
"""Converts markdown table to HTML with support for highlights in cells"""
|
204 |
-
# Clean the text
|
205 |
-
text = ContextProcessor.clean_text(text)
|
206 |
-
|
207 |
-
# Split into lines and get table rows
|
208 |
-
lines = text.strip().split('\n')
|
209 |
-
table_lines = [line for line in lines if line.strip().startswith('|')]
|
210 |
-
|
211 |
-
# Check if it's a proper table
|
212 |
-
if len(table_lines) < 2:
|
213 |
-
return ContextProcessor.process_text(text)
|
214 |
-
|
215 |
-
# Check if second line is a separator (----)
|
216 |
-
has_header = False
|
217 |
-
if len(table_lines) >= 2 and '---' in table_lines[1]:
|
218 |
-
has_header = True
|
219 |
-
|
220 |
-
# Start building HTML table
|
221 |
-
html_output = '<table class="md-table">'
|
222 |
-
|
223 |
-
if has_header:
|
224 |
-
# Process header row
|
225 |
-
header_line = table_lines[0]
|
226 |
-
# Split by pipe and remove empty first and last elements
|
227 |
-
cells = [cell.strip() for cell in header_line.split('|')]
|
228 |
-
if cells and not cells[0]:
|
229 |
-
cells.pop(0)
|
230 |
-
if cells and not cells[-1]:
|
231 |
-
cells.pop()
|
232 |
-
|
233 |
-
html_output += '<thead><tr>'
|
234 |
-
for cell in cells:
|
235 |
-
cell_html = ContextProcessor.process_cell_content(cell)
|
236 |
-
html_output += f'<th>{cell_html}</th>'
|
237 |
-
html_output += '</tr></thead>'
|
238 |
-
|
239 |
-
# Process data rows (skip header and separator)
|
240 |
-
html_output += '<tbody>'
|
241 |
-
for line in table_lines[2:]:
|
242 |
-
cells = [cell.strip() for cell in line.split('|')]
|
243 |
-
if cells and not cells[0]:
|
244 |
-
cells.pop(0)
|
245 |
-
if cells and not cells[-1]:
|
246 |
-
cells.pop()
|
247 |
-
|
248 |
-
html_output += '<tr>'
|
249 |
-
for cell in cells:
|
250 |
-
cell_html = ContextProcessor.process_cell_content(cell)
|
251 |
-
html_output += f'<td>{cell_html}</td>'
|
252 |
-
html_output += '</tr>'
|
253 |
-
html_output += '</tbody>'
|
254 |
-
else:
|
255 |
-
# All rows are data
|
256 |
-
html_output += '<tbody>'
|
257 |
-
for line in table_lines:
|
258 |
-
cells = [cell.strip() for cell in line.split('|')]
|
259 |
-
if cells and not cells[0]:
|
260 |
-
cells.pop(0)
|
261 |
-
if cells and not cells[-1]:
|
262 |
-
cells.pop()
|
263 |
-
|
264 |
-
html_output += '<tr>'
|
265 |
-
for cell in cells:
|
266 |
-
cell_html = ContextProcessor.process_cell_content(cell)
|
267 |
-
html_output += f'<td>{cell_html}</td>'
|
268 |
-
html_output += '</tr>'
|
269 |
-
html_output += '</tbody>'
|
270 |
-
|
271 |
-
html_output += '</table>'
|
272 |
-
return html_output
|
273 |
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
text = ContextProcessor.balance_quotes(text)
|
280 |
-
text = ContextProcessor.balance_highlight_tags(text)
|
281 |
-
|
282 |
-
# Extract and process highlight parts
|
283 |
-
parts = ContextProcessor.extract_highlight_parts(text)
|
284 |
-
|
285 |
-
# Build the result
|
286 |
-
result = ""
|
287 |
-
for is_highlighted, part in parts:
|
288 |
-
if is_highlighted:
|
289 |
-
escaped_part = html.escape(part)
|
290 |
-
result += f'<span class="highlight">{escaped_part}</span>'
|
291 |
-
else:
|
292 |
-
result += html.escape(part)
|
293 |
-
|
294 |
-
return result
|
295 |
|
296 |
-
|
297 |
-
|
298 |
-
"""
|
299 |
-
#
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
special_cases = [
|
305 |
-
lambda c: c.strip() == "In Oklahoma,",
|
306 |
-
lambda c: c.strip().startswith('"') and c.count('"') == 1,
|
307 |
-
lambda c: c.rstrip().endswith('\\'),
|
308 |
-
lambda c: (c.replace('\\"', '').count('"') % 2) == 1,
|
309 |
-
lambda c: any((c.count(start) != c.count(end)) for start, end in [
|
310 |
-
('[[start_highlight]]', '[[end_highlight]]'),
|
311 |
-
('[[highlight_start]]', '[[highlight_end]]'),
|
312 |
-
('<span class="highlight">', '</span>')
|
313 |
-
])
|
314 |
-
]
|
315 |
-
|
316 |
-
# Check if we need to use abbreviated content
|
317 |
-
needs_abbreviated = any(check(content) for check in special_cases)
|
318 |
-
|
319 |
-
# If content needs help and we have abbreviated content, use it
|
320 |
-
if needs_abbreviated and abbreviated_content:
|
321 |
-
# Handle abbreviated content that might be a JSON string
|
322 |
-
if abbreviated_content.strip().startswith('{') and abbreviated_content.strip().endswith('}'):
|
323 |
-
try:
|
324 |
-
data = json.loads(abbreviated_content)
|
325 |
-
if "abbreviatedContent" in data:
|
326 |
-
abbreviated_content = data["abbreviatedContent"]
|
327 |
-
except json.JSONDecodeError:
|
328 |
-
pass
|
329 |
-
|
330 |
-
# Clean and prepare the abbreviated content
|
331 |
-
abbreviated_content = ContextProcessor.clean_text(abbreviated_content)
|
332 |
-
abbreviated_content = ContextProcessor.balance_quotes(abbreviated_content)
|
333 |
-
abbreviated_content = ContextProcessor.balance_highlight_tags(abbreviated_content)
|
334 |
-
|
335 |
-
# Use abbreviated content instead
|
336 |
-
content = abbreviated_content
|
337 |
-
|
338 |
-
# Check if content is a markdown table
|
339 |
-
if ContextProcessor.is_markdown_table(content):
|
340 |
-
return ContextProcessor.convert_table_to_html(content)
|
341 |
-
else:
|
342 |
-
return ContextProcessor.process_text(content)
|
343 |
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
contexts = []
|
348 |
-
|
349 |
-
# First try standard JSON parsing
|
350 |
-
try:
|
351 |
-
contexts = json.loads(context_json)
|
352 |
-
if not isinstance(contexts, list):
|
353 |
-
contexts = []
|
354 |
-
except json.JSONDecodeError:
|
355 |
-
# If standard parsing fails, use regex to extract the data
|
356 |
-
try:
|
357 |
-
# Extract type field
|
358 |
-
type_pattern = r'"type":\s*"(primary|secondary)"'
|
359 |
-
types = re.findall(type_pattern, context_json)
|
360 |
-
|
361 |
-
# Extract abbreviatedContent field - more robustly handle quotes
|
362 |
-
content_pattern = r'"abbreviatedContent":\s*"((?:\\.|[^"])*?)"'
|
363 |
-
contents = re.findall(content_pattern, context_json)
|
364 |
-
|
365 |
-
# Build context objects
|
366 |
-
for i, (ctx_type, content) in enumerate(zip(types, contents)):
|
367 |
-
contexts.append({
|
368 |
-
'type': ctx_type,
|
369 |
-
'abbreviatedContent': content.replace('\\"', '"')
|
370 |
-
})
|
371 |
-
except Exception as e:
|
372 |
-
print(f"Error extracting contexts with regex: {e}")
|
373 |
-
|
374 |
-
return contexts
|
375 |
|
376 |
-
|
377 |
-
def process_json_contexts(context_json: str) -> List[Dict[str, Any]]:
|
378 |
-
"""Process JSON-formatted highlighted contexts"""
|
379 |
-
processed_contexts = []
|
380 |
-
|
381 |
-
try:
|
382 |
-
# Parse the JSON contexts
|
383 |
-
contexts = ContextProcessor.parse_json_contexts(context_json)
|
384 |
-
|
385 |
-
# Process each context item
|
386 |
-
for i, item in enumerate(contexts):
|
387 |
-
if isinstance(item, dict):
|
388 |
-
context_type = item.get('type', 'secondary')
|
389 |
-
content = item.get('abbreviatedContent', '')
|
390 |
-
|
391 |
-
# Process the content
|
392 |
-
processed_content = ContextProcessor.process_content(content)
|
393 |
-
|
394 |
-
# Create processed context item
|
395 |
-
processed_contexts.append({
|
396 |
-
'chunk_num': i + 1,
|
397 |
-
'content': processed_content,
|
398 |
-
'is_primary': context_type == 'primary'
|
399 |
-
})
|
400 |
-
except Exception as e:
|
401 |
-
print(f"Error processing JSON contexts: {e}")
|
402 |
-
|
403 |
-
return processed_contexts
|
404 |
-
|
405 |
-
|
406 |
-
# Module-level functions for backward compatibility
|
407 |
-
def clean_text(text):
|
408 |
-
return ContextProcessor.clean_text(text)
|
409 |
-
|
410 |
-
def balance_highlight_tags(text):
|
411 |
-
return ContextProcessor.balance_highlight_tags(text)
|
412 |
-
|
413 |
-
def balance_quotes(text):
|
414 |
-
return ContextProcessor.balance_quotes(text)
|
415 |
-
|
416 |
-
def extract_highlight_parts(text):
|
417 |
-
return ContextProcessor.extract_highlight_parts(text)
|
418 |
-
|
419 |
-
def is_markdown_table(text):
|
420 |
-
return ContextProcessor.is_markdown_table(text)
|
421 |
-
|
422 |
-
def process_cell_content(cell_text):
|
423 |
-
return ContextProcessor.process_cell_content(cell_text)
|
424 |
-
|
425 |
-
def convert_table_to_html(text):
|
426 |
-
return ContextProcessor.convert_table_to_html(text)
|
427 |
-
|
428 |
-
def process_text(text):
|
429 |
-
return ContextProcessor.process_text(text)
|
430 |
-
|
431 |
-
def process_content(content, abbreviated_content=None):
|
432 |
-
return ContextProcessor.process_content(content, abbreviated_content)
|
433 |
-
|
434 |
-
def process_highlights(text):
|
435 |
-
"""Main entry point called from data_loader.py"""
|
436 |
-
return ContextProcessor.process_content(text)
|
437 |
|
438 |
def get_context_html(example, show_full=False):
|
439 |
-
"""Format context chunks into HTML for display"""
|
440 |
html_output = ""
|
441 |
|
442 |
# Process insufficient context warning if needed
|
443 |
if example.get("insufficient", False):
|
444 |
insufficient_reason = example.get("insufficient_reason", "")
|
445 |
-
reason_html =
|
446 |
-
f"<p>{insufficient_reason}</p>" if insufficient_reason else
|
447 |
-
"<p>The context may not contain enough information to fully answer the question, "
|
448 |
-
"or the question might be ambiguous. Models should ideally indicate this limitation "
|
449 |
-
"or refuse to answer.</p>"
|
450 |
-
)
|
451 |
|
452 |
html_output += f"""
|
453 |
<div class="insufficient-alert">
|
454 |
<strong>
|
455 |
-
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
|
456 |
-
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
457 |
-
style="vertical-align: middle; margin-right: 5px;">
|
458 |
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"></path>
|
459 |
<line x1="12" y1="9" x2="12" y2="13"></line>
|
460 |
<line x1="12" y1="17" x2="12.01" y2="17"></line>
|
@@ -467,40 +58,39 @@ def get_context_html(example, show_full=False):
|
|
467 |
|
468 |
html_output += '<div class="context-items-container">'
|
469 |
|
470 |
-
# Display full contexts
|
471 |
-
if show_full
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
480 |
else:
|
481 |
-
#
|
482 |
if "contexts" in example and example["contexts"]:
|
483 |
for context_item in example["contexts"]:
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
elif "contexts_highlighted" in example and example["contexts_highlighted"]:
|
497 |
-
processed_contexts = ContextProcessor.process_json_contexts(example["contexts_highlighted"])
|
498 |
-
|
499 |
-
for context_item in processed_contexts:
|
500 |
-
is_primary = context_item.get('is_primary', False)
|
501 |
-
extra_class = " primary-context" if is_primary else ""
|
502 |
-
|
503 |
-
html_output += f'<div class="context-item{extra_class}">{context_item["content"]}</div>'
|
504 |
else:
|
505 |
html_output += '<div class="context-item">No context available. Try toggling to full context view.</div>'
|
506 |
|
|
|
1 |
import re
|
2 |
import html
|
3 |
import json
|
|
|
4 |
|
5 |
+
def clean_text(text):
|
6 |
+
"""Clean text with common issues like HTML entities and escaped quotes."""
|
7 |
+
if not text or not isinstance(text, str):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
return text
|
9 |
|
10 |
+
# Fix incomplete HTML entities
|
11 |
+
incomplete_entities = {''': ''', '"': '"', '<': '<', '>': '>', '&': '&'}
|
12 |
+
for incomplete, complete in incomplete_entities.items():
|
13 |
+
text = re.sub(f"{re.escape(incomplete)}(?!;)", complete, text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
15 |
+
# Convert HTML entities to characters
|
16 |
+
try:
|
17 |
+
text = html.unescape(text)
|
18 |
+
except Exception:
|
19 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
+
# Handle escaped quotes and other special characters
|
22 |
+
replacements = {
|
23 |
+
r'\"': '"', r"\'": "'", r"\n": "\n", r"\t": "\t", r"\\": "\\",
|
24 |
+
# Also normalize fancy quotes
|
25 |
+
'"': '"', '"': '"', ''': "'", ''': "'", '`': "'", '´': "'"
|
26 |
+
}
|
27 |
+
for pattern, replacement in replacements.items():
|
28 |
+
text = text.replace(pattern, replacement)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
+
# Remove trailing backslash if present
|
31 |
+
if text.rstrip().endswith('\\'):
|
32 |
+
text = text.rstrip().rstrip('\\')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
|
34 |
+
return text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
def get_context_html(example, show_full=False):
|
37 |
+
"""Format context chunks into HTML for display."""
|
38 |
html_output = ""
|
39 |
|
40 |
# Process insufficient context warning if needed
|
41 |
if example.get("insufficient", False):
|
42 |
insufficient_reason = example.get("insufficient_reason", "")
|
43 |
+
reason_html = f"<p>{insufficient_reason}</p>" if insufficient_reason else "<p>The context may not contain enough information to fully answer the question, or the question might be ambiguous. Models should ideally indicate this limitation or refuse to answer.</p>"
|
|
|
|
|
|
|
|
|
|
|
44 |
|
45 |
html_output += f"""
|
46 |
<div class="insufficient-alert">
|
47 |
<strong>
|
48 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 5px;">
|
|
|
|
|
49 |
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"></path>
|
50 |
<line x1="12" y1="9" x2="12" y2="13"></line>
|
51 |
<line x1="12" y1="17" x2="12.01" y2="17"></line>
|
|
|
58 |
|
59 |
html_output += '<div class="context-items-container">'
|
60 |
|
61 |
+
# Display full contexts or highlighted contexts based on toggle
|
62 |
+
if show_full:
|
63 |
+
# Show full context - directly use the strings from the list in full_contexts
|
64 |
+
if "full_contexts" in example and example["full_contexts"]:
|
65 |
+
for context_item in example["full_contexts"]:
|
66 |
+
if isinstance(context_item, dict) and 'content' in context_item:
|
67 |
+
content = context_item.get('content', '')
|
68 |
+
elif isinstance(context_item, str):
|
69 |
+
content = context_item
|
70 |
+
else:
|
71 |
+
content = str(context_item)
|
72 |
+
|
73 |
+
# Escape HTML entities for safe display
|
74 |
+
escaped_content = html.escape(content)
|
75 |
+
|
76 |
+
# Create the context item box - no headers
|
77 |
+
html_output += f'<div class="context-item">{escaped_content}</div>'
|
78 |
else:
|
79 |
+
# Show highlighted contexts
|
80 |
if "contexts" in example and example["contexts"]:
|
81 |
for context_item in example["contexts"]:
|
82 |
+
if isinstance(context_item, dict):
|
83 |
+
content = context_item.get('content', '')
|
84 |
+
is_primary = context_item.get('is_primary', False)
|
85 |
+
|
86 |
+
# Extra class for primary context styling
|
87 |
+
extra_class = " primary-context" if is_primary else ""
|
88 |
+
|
89 |
+
# Use content directly as it already has HTML highlighting
|
90 |
+
html_output += f'<div class="context-item{extra_class}">{content}</div>'
|
91 |
+
elif isinstance(context_item, str):
|
92 |
+
# For direct string contexts
|
93 |
+
html_output += f'<div class="context-item">{context_item}</div>'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
else:
|
95 |
html_output += '<div class="context-item">No context available. Try toggling to full context view.</div>'
|
96 |
|
utils/data_loader.py
CHANGED
@@ -3,7 +3,6 @@ import json
|
|
3 |
import pandas as pd
|
4 |
import random
|
5 |
import re
|
6 |
-
from .context_processor import process_highlights
|
7 |
|
8 |
# Global data store - loaded once at import time
|
9 |
_ARENA_DATA = None
|
@@ -40,10 +39,11 @@ def create_dummy_example():
|
|
40 |
return {
|
41 |
"question": "Could not load questions from the dataset. Please check the data file.",
|
42 |
"processed_context_desc": "Error: Data not available",
|
43 |
-
"contexts": [
|
44 |
-
"
|
45 |
"Answerable": False,
|
46 |
-
"insufficient": True
|
|
|
47 |
}
|
48 |
|
49 |
def get_random_example():
|
@@ -64,102 +64,113 @@ def get_random_example():
|
|
64 |
# Process the example data
|
65 |
processed_example = {
|
66 |
"question": example['question'],
|
67 |
-
"
|
68 |
-
"Answerable": example.get('Answerable', True), # Default to True unless specified otherwise
|
69 |
"insufficient": example.get('insufficient', False),
|
70 |
-
"insufficient_reason": example.get('insufficient_reason', '')
|
|
|
71 |
}
|
72 |
|
73 |
-
# Process
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
try:
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
except Exception as e:
|
91 |
-
print(f"Error processing contexts: {e}")
|
92 |
-
processed_example["full_contexts"] = []
|
93 |
|
94 |
-
# Process highlighted contexts
|
95 |
contexts_highlighted = []
|
96 |
-
|
97 |
try:
|
98 |
-
#
|
99 |
if 'contexts_highlighted' in example and example['contexts_highlighted']:
|
100 |
-
|
101 |
|
102 |
-
if isinstance(
|
103 |
try:
|
104 |
-
# Try
|
105 |
-
|
106 |
-
|
107 |
-
# First, manually parse the highlighted contexts using regex
|
108 |
-
# This is a more robust approach for our specific format
|
109 |
-
type_pattern = r'"type":\s*"(primary|secondary)"'
|
110 |
-
content_pattern = r'"abbreviatedContent":\s*"([^"]*)"|"abbreviatedContent":\s*"([^"]*)'
|
111 |
-
|
112 |
-
types = re.findall(type_pattern, raw_str)
|
113 |
-
# Handle both regular quotes and escaped quotes in content
|
114 |
-
raw_contents = re.findall(content_pattern, raw_str)
|
115 |
|
116 |
-
#
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
highlighted_contexts = example['contexts_highlighted']
|
135 |
-
|
136 |
-
# Process each context item
|
137 |
-
for i, item in enumerate(highlighted_contexts):
|
138 |
-
if isinstance(item, dict):
|
139 |
-
ctx_type = item.get('type', 'secondary')
|
140 |
-
content = item.get('abbreviatedContent', '')
|
141 |
-
|
142 |
-
# Process highlights using the standard format
|
143 |
-
content = process_highlights(content)
|
144 |
-
|
145 |
-
contexts_highlighted.append({
|
146 |
-
'chunk_num': i + 1,
|
147 |
-
'content': content,
|
148 |
-
'is_primary': ctx_type == 'primary'
|
149 |
-
})
|
150 |
except Exception as e:
|
151 |
print(f"Error processing highlighted contexts: {e}")
|
152 |
|
153 |
-
#
|
154 |
-
if not contexts_highlighted and
|
155 |
-
for
|
156 |
contexts_highlighted.append({
|
157 |
-
'
|
158 |
-
'content':
|
159 |
-
'is_primary': False
|
160 |
})
|
161 |
|
162 |
processed_example["contexts"] = contexts_highlighted
|
|
|
163 |
|
164 |
return processed_example
|
165 |
|
|
|
3 |
import pandas as pd
|
4 |
import random
|
5 |
import re
|
|
|
6 |
|
7 |
# Global data store - loaded once at import time
|
8 |
_ARENA_DATA = None
|
|
|
39 |
return {
|
40 |
"question": "Could not load questions from the dataset. Please check the data file.",
|
41 |
"processed_context_desc": "Error: Data not available",
|
42 |
+
"contexts": [],
|
43 |
+
"full_contexts": [],
|
44 |
"Answerable": False,
|
45 |
+
"insufficient": True,
|
46 |
+
"insufficient_reason": "Data loading error"
|
47 |
}
|
48 |
|
49 |
def get_random_example():
|
|
|
64 |
# Process the example data
|
65 |
processed_example = {
|
66 |
"question": example['question'],
|
67 |
+
"Answerable": not example.get('insufficient', False),
|
|
|
68 |
"insufficient": example.get('insufficient', False),
|
69 |
+
"insufficient_reason": example.get('insufficient_reason', ''),
|
70 |
+
"sample_id": example.get('sample_id', 0)
|
71 |
}
|
72 |
|
73 |
+
# Process the context description - ensure it's a non-empty string
|
74 |
+
context_desc = example.get('processed_context_desc', '')
|
75 |
+
if pd.isna(context_desc):
|
76 |
+
context_desc = ""
|
77 |
+
# Add the description to the processed example
|
78 |
+
processed_example["processed_context_desc"] = context_desc
|
79 |
+
|
80 |
+
# Process full contexts - from the 'contexts' column
|
81 |
+
full_contexts = []
|
82 |
try:
|
83 |
+
if 'contexts' in example and example['contexts']:
|
84 |
+
# Try to parse contexts as JSON if it's a string
|
85 |
+
contexts_str = example['contexts']
|
86 |
+
|
87 |
+
if isinstance(contexts_str, str):
|
88 |
+
# Try to parse as list literal first (for Python list representation)
|
89 |
+
if contexts_str.strip().startswith('[') and contexts_str.strip().endswith(']'):
|
90 |
+
try:
|
91 |
+
# This is for handling Python list literals like "['string1', 'string2']"
|
92 |
+
import ast
|
93 |
+
contexts_list = ast.literal_eval(contexts_str)
|
94 |
+
|
95 |
+
# Process each context string in the list
|
96 |
+
for ctx in contexts_list:
|
97 |
+
full_contexts.append(ctx)
|
98 |
+
except (SyntaxError, ValueError) as e:
|
99 |
+
# If ast.literal_eval fails, try JSON
|
100 |
+
try:
|
101 |
+
contexts_list = json.loads(contexts_str)
|
102 |
+
|
103 |
+
# Process each context in the list
|
104 |
+
for ctx in contexts_list:
|
105 |
+
if isinstance(ctx, str):
|
106 |
+
full_contexts.append(ctx)
|
107 |
+
elif isinstance(ctx, dict) and 'content' in ctx:
|
108 |
+
full_contexts.append(ctx.get('content', ''))
|
109 |
+
except json.JSONDecodeError:
|
110 |
+
# Not valid JSON, treat as a single context
|
111 |
+
full_contexts.append(contexts_str)
|
112 |
+
else:
|
113 |
+
# Single context string (not JSON array or list literal)
|
114 |
+
full_contexts.append(contexts_str)
|
115 |
+
elif isinstance(contexts_str, list):
|
116 |
+
# Already a list, process directly
|
117 |
+
for ctx in contexts_str:
|
118 |
+
if isinstance(ctx, str):
|
119 |
+
full_contexts.append(ctx)
|
120 |
+
elif isinstance(ctx, dict) and 'content' in ctx:
|
121 |
+
full_contexts.append(ctx.get('content', ''))
|
122 |
except Exception as e:
|
123 |
+
print(f"Error processing full contexts: {e}")
|
|
|
124 |
|
125 |
+
# Process highlighted contexts - from contexts_highlighted column
|
126 |
contexts_highlighted = []
|
|
|
127 |
try:
|
128 |
+
# Process contexts_highlighted - this is stored as a string in CSV
|
129 |
if 'contexts_highlighted' in example and example['contexts_highlighted']:
|
130 |
+
highlights_str = example['contexts_highlighted']
|
131 |
|
132 |
+
if isinstance(highlights_str, str):
|
133 |
try:
|
134 |
+
# Try to parse as JSON array
|
135 |
+
highlights_list = json.loads(highlights_str)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
|
137 |
+
# Process each highlighted context
|
138 |
+
for i, ctx in enumerate(highlights_list):
|
139 |
+
if isinstance(ctx, dict):
|
140 |
+
ctx_type = ctx.get('type', 'secondary')
|
141 |
+
content = ctx.get('abbreviatedContent', '')
|
142 |
+
|
143 |
+
# The content already has HTML span tags for highlights
|
144 |
+
contexts_highlighted.append({
|
145 |
+
'is_primary': ctx_type == 'primary',
|
146 |
+
'content': content
|
147 |
+
})
|
148 |
+
except json.JSONDecodeError:
|
149 |
+
print(f"Error parsing contexts_highlighted JSON: {highlights_str[:100]}...")
|
150 |
+
elif isinstance(highlights_str, list):
|
151 |
+
# Already a list, process directly
|
152 |
+
for ctx in highlights_str:
|
153 |
+
if isinstance(ctx, dict):
|
154 |
+
ctx_type = ctx.get('type', 'secondary')
|
155 |
+
content = ctx.get('abbreviatedContent', '')
|
156 |
|
157 |
+
contexts_highlighted.append({
|
158 |
+
'is_primary': ctx_type == 'primary',
|
159 |
+
'content': content
|
160 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
except Exception as e:
|
162 |
print(f"Error processing highlighted contexts: {e}")
|
163 |
|
164 |
+
# Make sure we have the highlighted contexts populated even if there are no contexts_highlighted
|
165 |
+
if not contexts_highlighted and full_contexts:
|
166 |
+
for content in full_contexts:
|
167 |
contexts_highlighted.append({
|
168 |
+
'is_primary': False,
|
169 |
+
'content': content
|
|
|
170 |
})
|
171 |
|
172 |
processed_example["contexts"] = contexts_highlighted
|
173 |
+
processed_example["full_contexts"] = full_contexts
|
174 |
|
175 |
return processed_example
|
176 |
|