AdnanElAssadi commited on
Commit
e4647ff
·
verified ·
1 Parent(s): a3a2c22

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +208 -215
app.py CHANGED
@@ -4,64 +4,109 @@ import os
4
  from pathlib import Path
5
 
6
  def create_reranking_interface(task_data):
7
- """Create a Gradio interface for reranking evaluation."""
8
  samples = task_data["samples"]
9
  results = {"task_name": task_data["task_name"], "task_type": "reranking", "annotations": []}
10
  completed_samples = {s["id"]: False for s in samples}
11
 
12
- def save_ranking(rankings, sample_id):
13
- """Save the current set of rankings."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  try:
15
- # Convert to integers with error handling
16
- processed_rankings = []
17
- for r in rankings:
18
- if r is None or r == "":
19
- processed_rankings.append(None)
 
 
 
 
 
20
  else:
21
- try:
22
- processed_rankings.append(int(r))
23
- except ValueError:
24
- return "⚠️ Invalid ranking value. Please use only numbers.", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
25
-
26
- # Check if all documents have rankings
27
- if None in processed_rankings:
28
- return "⚠️ Please assign a rank to all documents before submitting", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
29
-
30
- # Check for duplicate rankings
31
- if len(set(processed_rankings)) != len(processed_rankings):
32
- return "⚠️ Each document must have a unique rank. Please review your rankings.", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
33
-
34
- # Store this annotation in memory
35
  existing_idx = next((i for i, a in enumerate(results["annotations"]) if a["sample_id"] == sample_id), None)
36
  if existing_idx is not None:
37
- results["annotations"][existing_idx] = {
38
- "sample_id": sample_id,
39
- "rankings": processed_rankings
40
- }
41
  else:
42
- results["annotations"].append({
43
- "sample_id": sample_id,
44
- "rankings": processed_rankings
45
- })
46
-
47
  completed_samples[sample_id] = True
48
-
49
- # Try to save to file, but continue even if it fails
50
- try:
51
- output_path = f"{task_data['task_name']}_human_results.json"
52
- with open(output_path, "w") as f:
53
- json.dump(results, f, indent=2)
54
- return f"✅ Rankings saved successfully (in memory and to file)", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
55
- except:
56
- # If file saving fails, still mark as success since we saved in memory
57
- return f"✅ Rankings saved in memory (file save failed)", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
58
  except Exception as e:
59
- # Return specific error message
60
- return f"Error: {str(e)}", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
63
  gr.Markdown(f"# {task_data['task_name']} - Human Reranking Evaluation")
64
-
65
  with gr.Accordion("Instructions", open=True):
66
  gr.Markdown("""
67
  ## Task Instructions
@@ -70,12 +115,11 @@ def create_reranking_interface(task_data):
70
 
71
  ### How to use this interface:
72
  1. Read the query at the top
73
- 2. Review each document carefully
74
- 3. Assign a rank to each document (1 = most relevant, higher numbers = less relevant)
75
- 4. Each document must have a unique rank
76
- 5. Click "Submit Rankings" when you're done with the current query
77
- 6. Use "Previous" and "Next" to navigate between queries
78
- 7. Click "Save All Results" periodically to ensure your work is saved
79
  """.format(instructions=task_data["instructions"]))
80
 
81
  current_sample_id = gr.State(value=samples[0]["id"])
@@ -87,200 +131,149 @@ def create_reranking_interface(task_data):
87
  with gr.Group():
88
  gr.Markdown("## Query:")
89
  query_text = gr.Textbox(value=samples[0]["query"], label="", interactive=False)
90
-
91
- gr.Markdown("## Documents to Rank:")
92
-
93
- # Create document displays and ranking inputs in synchronized pairs
94
- doc_containers = []
95
- ranking_inputs = []
96
-
97
- with gr.Column():
98
- for i, doc in enumerate(samples[0]["candidates"]):
99
- with gr.Box():
100
- gr.Markdown(f"### Document {i+1}")
101
- doc_box = gr.Textbox(
102
- value=doc,
103
- label=None,
104
- interactive=False,
105
- lines=4
106
- )
107
- doc_containers.append(doc_box)
108
-
109
- rank_input = gr.Number(
110
- value=None,
111
- label=f"Rank (1 = highest, {len(samples[0]['candidates'])} = lowest)",
112
- minimum=1,
113
- maximum=len(samples[0]['candidates']),
114
- step=1
115
- )
116
- ranking_inputs.append(rank_input)
117
-
118
- # Add simple instructions for ranking
119
- with gr.Accordion("Ranking Instructions", open=True):
120
- gr.Markdown("""
121
- ### Ranking Documents:
122
- - Enter a number from 1 to {max_rank} for each document
123
- - 1 = most relevant document
124
- - Higher numbers = less relevant documents
125
- - Each document must have a unique rank
126
- """.format(max_rank=len(samples[0]["candidates"])))
127
-
128
  with gr.Row():
129
  prev_btn = gr.Button("← Previous Query", size="sm", elem_id="prev-btn")
130
  submit_btn = gr.Button("Submit Rankings", size="lg", variant="primary", elem_id="submit-btn")
131
  next_btn = gr.Button("Next Query →", size="sm", elem_id="next-btn")
132
-
133
  save_btn = gr.Button("💾 Save All Results", variant="secondary")
134
 
135
- def load_sample(sample_id):
136
- """Load a specific sample into the interface."""
137
- sample = next((s for s in samples if s["id"] == sample_id), None)
138
- if not sample:
139
- return [query_text.value] + [d.value for d in doc_containers] + [None] * len(ranking_inputs) + [current_sample_id.value, progress_text.value, status_box.value]
140
-
141
- # Update query
142
- new_query = sample["query"]
143
-
144
- # Update documents
145
- new_docs = []
146
- for i, doc in enumerate(sample["candidates"]):
147
- if i < len(doc_containers):
148
- new_docs.append(doc)
149
-
150
- # Initialize rankings
151
- new_rankings = [None] * len(ranking_inputs)
152
-
153
- # Check if this sample has already been annotated
154
- existing_annotation = next((a for a in results["annotations"] if a["sample_id"] == sample_id), None)
155
- if existing_annotation:
156
- # Restore previous rankings
157
- for i, rank in enumerate(existing_annotation["rankings"]):
158
- if i < len(new_rankings) and rank is not None:
159
- new_rankings[i] = rank
160
-
161
- # Update progress
162
- current_idx = samples.index(sample)
163
- new_progress = f"Progress: {sum(completed_samples.values())}/{len(samples)}"
164
-
165
- new_status = f"Viewing query {current_idx + 1} of {len(samples)}"
166
- if completed_samples[sample_id]:
167
- new_status += " (already completed)"
168
-
169
- return [new_query] + new_docs + new_rankings + [sample["id"], new_progress, new_status]
170
-
171
- def next_sample(current_id):
172
- """Load the next sample."""
173
- current_sample = next((s for s in samples if s["id"] == current_id), None)
174
- if not current_sample:
175
- return current_id
176
-
177
- current_idx = samples.index(current_sample)
178
- if current_idx < len(samples) - 1:
179
- next_sample = samples[current_idx + 1]
180
- return next_sample["id"]
181
- return current_id
182
 
183
- def prev_sample(current_id):
184
- """Load the previous sample."""
185
- current_sample = next((s for s in samples if s["id"] == current_id), None)
186
- if not current_sample:
187
- return current_id
188
-
189
- current_idx = samples.index(current_sample)
190
- if current_idx > 0:
191
- prev_sample = samples[current_idx - 1]
192
- return prev_sample["id"]
193
- return current_id
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
 
195
- def save_results():
196
- """Save all collected results to a file."""
197
- output_path = f"{task_data['task_name']}_human_results.json"
198
- with open(output_path, "w") as f:
199
- json.dump(results, f, indent=2)
200
- return f"✅ Results saved to {output_path} ({len(results['annotations'])} annotations)"
 
 
 
 
 
 
 
 
 
201
 
202
- # Resolve rank conflicts automatically
203
- def resolve_rank_conflicts(*ranks):
204
- ranks = list(ranks)
205
- # Convert to integers with validation
206
- int_ranks = []
207
- for r in ranks:
208
- try:
209
- r_int = int(r) if r is not None else None
210
- if r_int is not None and (r_int < 1 or r_int > len(ranks)):
211
- r_int = None
212
- int_ranks.append(r_int)
213
- except:
214
- int_ranks.append(None)
215
-
216
- # Find duplicates
217
- seen = set()
218
- duplicates = set()
219
- for i, r in enumerate(int_ranks):
220
- if r is None:
221
- continue
222
- if r in seen:
223
- duplicates.add(r)
224
- seen.add(r)
225
-
226
- # Resolve duplicates by incrementing/shifting
227
- for dup in sorted(duplicates):
228
- indices = [i for i, r in enumerate(int_ranks) if r == dup]
229
- # Keep the first occurrence, shift others
230
- for idx in indices[1:]:
231
- # Find the next available rank
232
- next_rank = dup + 1
233
- while next_rank in int_ranks and next_rank <= len(ranks):
234
- next_rank += 1
235
- if next_rank <= len(ranks):
236
- int_ranks[idx] = next_rank
237
- else:
238
- # If no ranks available, find the first empty spot
239
- for j in range(1, len(ranks) + 1):
240
- if j not in int_ranks:
241
- int_ranks[idx] = j
242
- break
243
-
244
- # Convert back to original type
245
- return int_ranks
246
 
247
- # Connect events
248
- for i, rank_input in enumerate(ranking_inputs):
249
- rank_input.change(
250
- resolve_rank_conflicts,
251
- inputs=ranking_inputs,
252
- outputs=ranking_inputs
253
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
- # Connect events
256
  submit_btn.click(
257
  save_ranking,
258
- inputs=ranking_inputs + [current_sample_id],
259
  outputs=[status_box, progress_text]
260
  )
261
 
262
  next_btn.click(
263
- next_sample,
264
- inputs=[current_sample_id],
265
- outputs=[current_sample_id]
266
  ).then(
267
  load_sample,
268
  inputs=[current_sample_id],
269
- outputs=[query_text] + doc_containers + ranking_inputs + [current_sample_id, progress_text, status_box]
270
  )
271
 
272
  prev_btn.click(
273
- prev_sample,
274
- inputs=[current_sample_id],
275
- outputs=[current_sample_id]
276
  ).then(
277
  load_sample,
278
  inputs=[current_sample_id],
279
- outputs=[query_text] + doc_containers + ranking_inputs + [current_sample_id, progress_text, status_box]
280
  )
281
 
282
  save_btn.click(save_results, outputs=[status_box])
283
-
 
 
 
284
  return demo
285
 
286
  # Main app with file upload capability
 
4
  from pathlib import Path
5
 
6
  def create_reranking_interface(task_data):
7
+ """Create a Gradio interface for reranking evaluation using drag and drop."""
8
  samples = task_data["samples"]
9
  results = {"task_name": task_data["task_name"], "task_type": "reranking", "annotations": []}
10
  completed_samples = {s["id"]: False for s in samples}
11
 
12
+ # Define helper functions before UI elements are created
13
+ def generate_sortable_html(candidates, existing_ranks=None):
14
+ """Generate the HTML for the sortable list."""
15
+ if existing_ranks and len(existing_ranks) == len(candidates):
16
+ order = sorted(range(len(candidates)), key=lambda i: existing_ranks[i])
17
+ else:
18
+ order = list(range(len(candidates)))
19
+
20
+ html = '<div id="sortable-container" class="sortable-container">'
21
+ for rank_minus_1, idx in enumerate(order):
22
+ if idx < len(candidates):
23
+ doc = candidates[idx]
24
+ rank = rank_minus_1 + 1
25
+ import html as html_escaper
26
+ escaped_doc = html_escaper.escape(doc)
27
+ html += f'''\
28
+ <div class="sortable-item rank-bg-{rank}" data-doc-id="{idx}">
29
+ <div class="rank-badge">{rank}</div>
30
+ <div class="doc-content">{escaped_doc}</div>
31
+ </div>
32
+ '''
33
+ html += '</div>'
34
+ return html
35
+
36
+ def save_ranking(order_json, sample_id):
37
+ """Save the current ranking to results."""
38
  try:
39
+ if not order_json or order_json == "[]":
40
+ return "⚠️ Drag documents to set the ranking before submitting.", progress_text.value
41
+ order = json.loads(order_json)
42
+ num_candidates = len(next(s["candidates"] for s in samples if s["id"] == sample_id))
43
+ if len(order) != num_candidates:
44
+ return f"⚠️ Ranking order length mismatch. Expected {num_candidates}, got {len(order)}.", progress_text.value
45
+ rankings = [0] * num_candidates
46
+ for rank_minus_1, doc_idx in enumerate(order):
47
+ if doc_idx < num_candidates:
48
+ rankings[doc_idx] = rank_minus_1 + 1
49
  else:
50
+ raise ValueError(f"Invalid document index {doc_idx} found in order.")
51
+ if sorted(rankings) != list(range(1, num_candidates + 1)):
52
+ return "⚠️ Ranking validation failed. Ranks are not 1 to N.", progress_text.value
53
+ annotation = {"sample_id": sample_id, "rankings": rankings}
 
 
 
 
 
 
 
 
 
 
54
  existing_idx = next((i for i, a in enumerate(results["annotations"]) if a["sample_id"] == sample_id), None)
55
  if existing_idx is not None:
56
+ results["annotations"][existing_idx] = annotation
 
 
 
57
  else:
58
+ results["annotations"].append(annotation)
 
 
 
 
59
  completed_samples[sample_id] = True
60
+ output_path = f"{task_data['task_name']}_human_results.json"
61
+ with open(output_path, "w") as f:
62
+ json.dump(results, f, indent=2)
63
+ return f"✅ Rankings saved successfully ({len(results['annotations'])}/{len(samples)} completed)", f"Progress: {sum(completed_samples.values())}/{len(samples)}"
64
+ except json.JSONDecodeError:
65
+ return "⚠️ Error decoding ranking order. Please try again.", progress_text.value
 
 
 
 
66
  except Exception as e:
67
+ import traceback
68
+ print(traceback.format_exc())
69
+ return f"Error saving ranking: {str(e)}", progress_text.value
70
+
71
+ def load_sample(sample_id):
72
+ """Load a sample into the interface."""
73
+ try:
74
+ sample = next((s for s in samples if s["id"] == sample_id), None)
75
+ if not sample:
76
+ return gr.update(), gr.update(value="[]"), gr.update(), gr.update()
77
+ existing_ranking = next((anno["rankings"] for anno in results["annotations"] if anno["sample_id"] == sample_id), None)
78
+ new_html = generate_sortable_html(sample["candidates"], existing_ranking)
79
+ status = "Ready to rank" if not completed_samples.get(sample_id, False) else "Already ranked"
80
+ progress = f"Progress: {sum(completed_samples.values())}/{len(samples)}"
81
+ return sample["query"], new_html, "[]", progress, status
82
+ except Exception as e:
83
+ return gr.update(), gr.update(value="[]"), gr.update(), gr.update(value=f"Error loading sample: {str(e)}")
84
+
85
+ def next_sample_id(current_id):
86
+ current_idx = next((i for i, s in enumerate(samples) if s["id"] == current_id), -1)
87
+ if current_idx == -1:
88
+ return current_id
89
+ next_idx = min(current_idx + 1, len(samples) - 1)
90
+ return samples[next_idx]["id"]
91
+
92
+ def prev_sample_id(current_id):
93
+ current_idx = next((i for i, s in enumerate(samples) if s["id"] == current_id), -1)
94
+ if current_idx == -1:
95
+ return current_id
96
+ prev_idx = max(current_idx - 1, 0)
97
+ return samples[prev_idx]["id"]
98
+
99
+ def save_results():
100
+ output_path = f"{task_data['task_name']}_human_results.json"
101
+ try:
102
+ with open(output_path, "w") as f:
103
+ json.dump(results, f, indent=2)
104
+ return f"✅ Results saved to {output_path} ({len(results['annotations'])} annotations)"
105
+ except Exception as e:
106
+ return f"⚠️ Error saving results file: {str(e)}"
107
 
108
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
109
  gr.Markdown(f"# {task_data['task_name']} - Human Reranking Evaluation")
 
110
  with gr.Accordion("Instructions", open=True):
111
  gr.Markdown("""
112
  ## Task Instructions
 
115
 
116
  ### How to use this interface:
117
  1. Read the query at the top
118
+ 2. Drag and drop documents to reorder them based on relevance
119
+ 3. Top document = Rank 1, Second = Rank 2, etc.
120
+ 4. Click "Submit Rankings" when you're done with the current query
121
+ 5. Use "Previous" and "Next" to navigate between queries
122
+ 6. Click "Save All Results" periodically to ensure your work is saved
 
123
  """.format(instructions=task_data["instructions"]))
124
 
125
  current_sample_id = gr.State(value=samples[0]["id"])
 
131
  with gr.Group():
132
  gr.Markdown("## Query:")
133
  query_text = gr.Textbox(value=samples[0]["query"], label="", interactive=False)
134
+ gr.Markdown("## Documents to Rank (Drag to Reorder):")
135
+ sortable_list = gr.HTML(generate_sortable_html(samples[0]["candidates"], []), elem_id="sortable-list-container")
136
+ order_state = gr.Textbox(value="[]", visible=False, elem_id="current-order")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  with gr.Row():
138
  prev_btn = gr.Button("← Previous Query", size="sm", elem_id="prev-btn")
139
  submit_btn = gr.Button("Submit Rankings", size="lg", variant="primary", elem_id="submit-btn")
140
  next_btn = gr.Button("Next Query →", size="sm", elem_id="next-btn")
 
141
  save_btn = gr.Button("💾 Save All Results", variant="secondary")
142
 
143
+ js_code = """
144
+ <script src=\"https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js\"></script>
145
+ <script>
146
+ function initializeSortableForApp() {
147
+ const container = document.getElementById('sortable-container');
148
+ if (!container) {
149
+ setTimeout(initializeSortableForApp, 200);
150
+ return;
151
+ }
152
+ if (typeof Sortable === 'undefined') {
153
+ setTimeout(initializeSortableForApp, 200);
154
+ return;
155
+ }
156
+ if (container.sortableInstance) {
157
+ return;
158
+ }
159
+ container.sortableInstance = new Sortable(container, {
160
+ animation: 150,
161
+ ghostClass: "sortable-ghost",
162
+ onEnd: function() {
163
+ updateRanksAndSyncState();
164
+ }
165
+ });
166
+ updateRanksAndSyncState();
167
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
169
+ function updateRanksAndSyncState() {
170
+ const container = document.getElementById('sortable-container');
171
+ if (!container) return;
172
+
173
+ const items = container.querySelectorAll('.sortable-item');
174
+ const orderInput = document.querySelector('#current-order textarea');
175
+ if (!orderInput) {
176
+ return;
177
+ }
178
+
179
+ const order = [];
180
+ items.forEach((item, index) => {
181
+ const rankBadge = item.querySelector('.rank-badge');
182
+ if (rankBadge) rankBadge.textContent = (index + 1);
183
+ item.className = item.className.replace(/rank-bg-\d+/g, '').trim();
184
+ item.classList.add(`rank-bg-${index + 1}`);
185
+ const docId = parseInt(item.getAttribute('data-doc-id'));
186
+ order.push(docId);
187
+ });
188
+
189
+ const newOrderValue = JSON.stringify(order);
190
+ if (orderInput.value !== newOrderValue) {
191
+ orderInput.value = newOrderValue;
192
+ const event = new Event('input', { bubbles: true });
193
+ orderInput.dispatchEvent(event);
194
+ }
195
+ }
196
 
197
+ const targetNode = document.getElementById('sortable-list-container');
198
+ if (targetNode) {
199
+ const config = { childList: true, subtree: true };
200
+ const callback = function(mutationsList, observer) {
201
+ for(const mutation of mutationsList) {
202
+ if (mutation.type === 'childList') {
203
+ if (document.getElementById('sortable-container')){
204
+ initializeSortableForApp();
205
+ }
206
+ }
207
+ }
208
+ };
209
+ const observer = new MutationObserver(callback);
210
+ observer.observe(targetNode, config);
211
+ }
212
 
213
+ setTimeout(initializeSortableForApp, 500);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
+ </script>
216
+ <style>
217
+ .sortable-container { display: flex; flex-direction: column; gap: 8px; }
218
+ .sortable-item {
219
+ padding: 10px 15px; background-color: #fff; border: 1px solid #e0e0e0;
220
+ border-radius: 6px; cursor: grab; display: flex; align-items: center;
221
+ transition: background-color 0.2s ease;
222
+ }
223
+ .sortable-item:hover { background-color: #f8f9fa; }
224
+ .sortable-ghost { background-color: #d3e3fd !important; border-style: dashed !important; opacity: 0.6; }
225
+ .sortable-chosen { cursor: grabbing; }
226
+ .rank-badge {
227
+ display: flex; align-items: center; justify-content: center;
228
+ width: 28px; height: 28px; border-radius: 50%;
229
+ background-color: #6c757d; color: white; font-weight: bold; margin-right: 15px;
230
+ flex-shrink: 0;
231
+ }
232
+ .doc-content { flex: 1; line-height: 1.4; }
233
+ .rank-bg-1 .rank-badge { background-color: #198754; }
234
+ .rank-bg-2 .rank-badge { background-color: #20c997; }
235
+ .rank-bg-3 .rank-badge { background-color: #ffc107; color: #333; }
236
+ .rank-bg-4 .rank-badge { background-color: #fd7e14; }
237
+ .rank-bg-5 .rank-badge { background-color: #dc3545; }
238
+ .rank-bg-6 .rank-badge, .rank-bg-7 .rank-badge { background-color: #6f42c1; }
239
+ .rank-bg-8 .rank-badge, .rank-bg-9 .rank-badge { background-color: #d63384; }
240
+ .rank-bg-10 .rank-badge, .rank-bg-11 .rank-badge, .rank-bg-12 .rank-badge,
241
+ .rank-bg-13 .rank-badge, .rank-bg-14 .rank-badge, .rank-bg-15 .rank-badge,
242
+ .rank-bg-16 .rank-badge, .rank-bg-17 .rank-badge, .rank-bg-18 .rank-badge,
243
+ .rank-bg-19 .rank-badge, .rank-bg-20 .rank-badge {
244
+ background-color: #6c757d;
245
+ }
246
+ </style>
247
+ """
248
+ gr.HTML(js_code)
249
 
 
250
  submit_btn.click(
251
  save_ranking,
252
+ inputs=[order_state, current_sample_id],
253
  outputs=[status_box, progress_text]
254
  )
255
 
256
  next_btn.click(
257
+ next_sample_id, inputs=[current_sample_id], outputs=[current_sample_id]
 
 
258
  ).then(
259
  load_sample,
260
  inputs=[current_sample_id],
261
+ outputs=[query_text, sortable_list, order_state, progress_text, status_box]
262
  )
263
 
264
  prev_btn.click(
265
+ prev_sample_id, inputs=[current_sample_id], outputs=[current_sample_id]
 
 
266
  ).then(
267
  load_sample,
268
  inputs=[current_sample_id],
269
+ outputs=[query_text, sortable_list, order_state, progress_text, status_box]
270
  )
271
 
272
  save_btn.click(save_results, outputs=[status_box])
273
+
274
+ demo.load(lambda: load_sample(samples[0]['id']),
275
+ outputs=[query_text, sortable_list, order_state, progress_text, status_box])
276
+
277
  return demo
278
 
279
  # Main app with file upload capability