arjunguha commited on
Commit
2ab660f
·
unverified ·
1 Parent(s): fec4f75

Initial commit

Browse files
Files changed (4) hide show
  1. README.md +3 -3
  2. app.py +554 -0
  3. tasks.jsonl +0 -0
  4. validated_tasks.jsonl +0 -0
README.md CHANGED
@@ -1,6 +1,6 @@
1
  ---
2
- title: Buildabench Workshop
3
- emoji: 🔥
4
  colorFrom: gray
5
  colorTo: gray
6
  sdk: gradio
@@ -10,4 +10,4 @@ pinned: false
10
  license: bsd-3-clause
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: Build-A-Bench Workshop
3
+ emoji: 🧸
4
  colorFrom: gray
5
  colorTo: gray
6
  sdk: gradio
 
10
  license: bsd-3-clause
11
  ---
12
 
13
+ See https://github.com/nuprl/buildabench_workshop/ for the code.
app.py ADDED
@@ -0,0 +1,554 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Gradio app to view tasks.jsonl and validated_tasks.jsonl side by side.
3
+
4
+ Run with:
5
+ uv --with gradio python -m buildabench_workshop.view_tasks_gradio tasks.jsonl validated_tasks.jsonl
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import difflib
12
+ import json
13
+ from pathlib import Path
14
+ from typing import Any, Dict, Iterable, List, Optional, Set, Tuple
15
+
16
+
17
+ def normalize_task(task: Dict[str, Any]) -> Dict[str, Any]:
18
+ """Normalize a task by removing unwanted fields and cleaning structure."""
19
+ # Fields to remove completely
20
+ fields_to_remove = {
21
+ "matching_files",
22
+ "commit_sha",
23
+ "task_id",
24
+ "repo",
25
+ "log",
26
+ "tips",
27
+ "container",
28
+ }
29
+
30
+ def clean_dict(obj: Any) -> Any:
31
+ """Recursively clean dictionaries."""
32
+ if isinstance(obj, dict):
33
+ result = {}
34
+ for key, value in obj.items():
35
+ # Skip unwanted top-level fields
36
+ if key in fields_to_remove:
37
+ continue
38
+ # Skip nested fields with these names
39
+ if "matching_files" in key or "log" in key or "tips" in key or "container" in key:
40
+ continue
41
+ result[key] = clean_dict(value)
42
+ return result
43
+ elif isinstance(obj, list):
44
+ return [clean_dict(item) for item in obj]
45
+ else:
46
+ return obj
47
+
48
+ return clean_dict(task)
49
+
50
+
51
+ def load_jsonl(filepath: Path) -> List[Dict[str, Any]]:
52
+ """Load a JSONL file and return a list of normalized dictionaries."""
53
+ data = []
54
+ with open(filepath, "r", encoding="utf-8") as f:
55
+ for line in f:
56
+ line = line.strip()
57
+ if line:
58
+ task = json.loads(line)
59
+ # Normalize the task (removes unwanted fields)
60
+ task = normalize_task(task)
61
+ data.append(task)
62
+ return data
63
+
64
+
65
+ def load_and_join_tasks(tasks_path: Path, validated_path: Path) -> Dict[str, Dict[str, Any]]:
66
+ """Load both files, normalize, and join by task_id. Returns clean structure."""
67
+ result: Dict[str, Dict[str, Any]] = {}
68
+
69
+ # Load tasks.jsonl
70
+ with open(tasks_path, "r", encoding="utf-8") as f:
71
+ for line in f:
72
+ line = line.strip()
73
+ if line:
74
+ task = json.loads(line)
75
+ task_id = task["task_id"]
76
+ normalized = normalize_task(task)
77
+ # Preserve repo separately for descriptions
78
+ repo = task["repo"]
79
+ result[task_id] = {"task": normalized, "validated": None, "_repo": repo}
80
+
81
+ # Load validated_tasks.jsonl
82
+ with open(validated_path, "r", encoding="utf-8") as f:
83
+ for line in f:
84
+ line = line.strip()
85
+ if line:
86
+ validated = json.loads(line)
87
+ task_id = validated["task_id"]
88
+ normalized = normalize_task(validated)
89
+ if task_id not in result:
90
+ repo = validated["repo"]
91
+ result[task_id] = {"task": None, "validated": normalized, "_repo": repo}
92
+ else:
93
+ # Preserve repo if not already set
94
+ if "_repo" not in result[task_id] or not result[task_id]["_repo"]:
95
+ repo = validated["repo"]
96
+ result[task_id]["_repo"] = repo
97
+ result[task_id]["validated"] = normalized
98
+
99
+ return result
100
+
101
+
102
+ def build_paths(
103
+ data: Any,
104
+ prefix: str = "",
105
+ max_list_items: int = 5,
106
+ paths: Optional[Set[str]] = None,
107
+ ) -> Set[str]:
108
+ """Collect dotted paths for dict/list structures."""
109
+ if paths is None:
110
+ paths = set()
111
+
112
+ if isinstance(data, dict):
113
+ for key in sorted(data.keys()):
114
+ path = f"{prefix}.{key}" if prefix else key
115
+ paths.add(path)
116
+ build_paths(data[key], path, max_list_items=max_list_items, paths=paths)
117
+ elif isinstance(data, list):
118
+ for i, item in enumerate(data[:max_list_items]):
119
+ path = f"{prefix}[{i}]" if prefix else f"[{i}]"
120
+ paths.add(path)
121
+ build_paths(item, path, max_list_items=max_list_items, paths=paths)
122
+
123
+ return paths
124
+
125
+
126
+ def get_value_by_path(obj: Any, path: str) -> Any:
127
+ """Get a value from nested dict/list using dot notation with [idx]."""
128
+ if obj is None:
129
+ return None
130
+ if isinstance(obj, dict) and path in obj:
131
+ return obj[path]
132
+
133
+ parts = path.split(".") if path else []
134
+ current = obj
135
+
136
+ for i, part in enumerate(parts):
137
+ if not isinstance(current, (dict, list)):
138
+ return None
139
+
140
+ # If a full remaining path exists as a key, return it
141
+ if isinstance(current, dict) and part not in current:
142
+ remaining = ".".join(parts[i:])
143
+ if remaining in current:
144
+ return current[remaining]
145
+
146
+ key = part.split("[", 1)[0] if "[" in part else part
147
+ if key:
148
+ if not isinstance(current, dict) or key not in current:
149
+ return None
150
+ current = current[key]
151
+
152
+ # Handle list indexes like key[0][1]
153
+ indices: List[int] = []
154
+ start = 0
155
+ while True:
156
+ open_idx = part.find("[", start)
157
+ if open_idx == -1:
158
+ break
159
+ close_idx = part.find("]", open_idx)
160
+ if close_idx == -1:
161
+ break
162
+ idx_str = part[open_idx + 1 : close_idx]
163
+ if idx_str.isdigit():
164
+ indices.append(int(idx_str))
165
+ start = close_idx + 1
166
+
167
+ for idx in indices:
168
+ if not isinstance(current, list) or idx >= len(current):
169
+ return None
170
+ current = current[idx]
171
+
172
+ return current
173
+
174
+
175
+ def format_value(value: Any) -> str:
176
+ if value is None:
177
+ return "null"
178
+ if isinstance(value, (dict, list)):
179
+ return json.dumps(value, indent=2, ensure_ascii=False)
180
+ return str(value)
181
+
182
+
183
+ def diff_values(left: Any, right: Any) -> str:
184
+ left_text = format_value(left).splitlines()
185
+ right_text = format_value(right).splitlines()
186
+ diff = difflib.unified_diff(
187
+ left_text,
188
+ right_text,
189
+ fromfile="task",
190
+ tofile="validated",
191
+ lineterm="",
192
+ )
193
+ return "\n".join(diff) if left is not None or right is not None else ""
194
+
195
+
196
+ def build_tree_items(
197
+ data: Any,
198
+ prefix: str = "",
199
+ max_list_items: int = 5,
200
+ items: Optional[List[Tuple[str, str]]] = None,
201
+ ) -> List[Tuple[str, str]]:
202
+ """Build tree items as (path, display_label) tuples, matching view_tasks.py structure."""
203
+ if items is None:
204
+ items = []
205
+
206
+ if isinstance(data, dict):
207
+ for key in sorted(data.keys()):
208
+ path = f"{prefix}.{key}" if prefix else key
209
+ display = key
210
+
211
+ # Special handling for 'task' and 'validated' wrappers - show their children directly
212
+ if key in ("task", "validated"):
213
+ value = data[key]
214
+ if value and isinstance(value, dict):
215
+ wrapper_path = path
216
+ for sub_key in sorted(value.keys()):
217
+ sub_path = f"{wrapper_path}.{sub_key}"
218
+ sub_value = value[sub_key]
219
+
220
+ # For matching_files, flatten it
221
+ if sub_key == "matching_files" and isinstance(sub_value, list):
222
+ items.append((sub_path, sub_key))
223
+ continue
224
+
225
+ has_children = isinstance(sub_value, dict) and not isinstance(sub_value, list)
226
+ items.append((sub_path, sub_key))
227
+ if has_children:
228
+ build_tree_items(sub_value, sub_path, max_list_items=max_list_items, items=items)
229
+ elif isinstance(sub_value, list) and len(sub_value) > 0:
230
+ for i, item in enumerate(sub_value[:max_list_items]):
231
+ item_path = f"{sub_path}[{i}]"
232
+ items.append((item_path, f"[{i}]"))
233
+ if isinstance(item, (dict, list)):
234
+ build_tree_items(item, item_path, max_list_items=max_list_items, items=items)
235
+ if len(sub_value) > max_list_items:
236
+ items.append((f"{sub_path}[...]", f"[... ({len(sub_value)} total)]"))
237
+ continue
238
+
239
+ # For matching_files, flatten it
240
+ if key == "matching_files" and isinstance(data[key], list):
241
+ items.append((path, display))
242
+ continue
243
+
244
+ value = data[key]
245
+ has_children = isinstance(value, dict) and not isinstance(value, list)
246
+ items.append((path, display))
247
+ if has_children:
248
+ build_tree_items(value, path, max_list_items=max_list_items, items=items)
249
+ elif isinstance(value, list) and len(value) > 0:
250
+ for i, item in enumerate(value[:max_list_items]):
251
+ item_path = f"{path}[{i}]"
252
+ items.append((item_path, f"[{i}]"))
253
+ if isinstance(item, (dict, list)):
254
+ build_tree_items(item, item_path, max_list_items=max_list_items, items=items)
255
+ if len(value) > max_list_items:
256
+ items.append((f"{path}[...]", f"[... ({len(value)} total)]"))
257
+ elif isinstance(data, list):
258
+ for i, item in enumerate(data[:max_list_items]):
259
+ path = f"{prefix}[{i}]" if prefix else f"[{i}]"
260
+ items.append((path, f"[{i}]"))
261
+ if isinstance(item, (dict, list)):
262
+ build_tree_items(item, path, max_list_items=max_list_items, items=items)
263
+ if len(data) > max_list_items:
264
+ items.append((f"{prefix}[...]", f"[... ({len(data)} total)]"))
265
+
266
+ return items
267
+
268
+
269
+ def build_app(joined_data: Dict[str, Dict[str, Any]]):
270
+ import gradio as gr
271
+
272
+ task_ids = sorted(joined_data.keys())
273
+
274
+ def get_field_paths(task_id: str) -> List[str]:
275
+ """Get list of field paths to display, in order."""
276
+ entry = joined_data[task_id]
277
+
278
+ # Get all paths
279
+ paths: Set[str] = set()
280
+ if entry["task"] is not None:
281
+ paths |= build_paths(entry["task"])
282
+ if entry["validated"] is not None:
283
+ paths |= build_paths(entry["validated"])
284
+
285
+ # Filter out unwanted fields
286
+ filtered_paths = []
287
+ reasoning_path = None
288
+
289
+ for path in sorted(paths):
290
+ path_parts = path.split(".")
291
+ # Skip log, repo, tips, container, matching_files, commit_message, commit_sha, task_id, patches, subject
292
+ if ("log" in path_parts or "repo" in path_parts or
293
+ "tips" in path_parts or "container" in path_parts or
294
+ "matching_files" in path_parts or
295
+ path == "commit_message" or path == "commit_sha" or path == "task_id" or
296
+ path == "patches" or path == "subject"):
297
+ continue
298
+
299
+ # Track reasoning separately to put it last
300
+ if path == "reasoning" or path.endswith(".reasoning"):
301
+ reasoning_path = path
302
+ continue
303
+
304
+ filtered_paths.append(path)
305
+
306
+ # Put task_description first
307
+ if "task_description" in filtered_paths:
308
+ filtered_paths.remove("task_description")
309
+ filtered_paths.insert(0, "task_description")
310
+
311
+ # Add reasoning last
312
+ if reasoning_path:
313
+ filtered_paths.append(reasoning_path)
314
+
315
+ return filtered_paths
316
+
317
+ def render_field(task_id: str, path: str) -> str:
318
+ """Render a single field's content as markdown."""
319
+ entry = joined_data[task_id]
320
+
321
+ task_value = get_value_by_path(entry["task"], path)
322
+ validated_value = get_value_by_path(entry["validated"], path)
323
+
324
+ # Skip if both values are None
325
+ if task_value is None and validated_value is None:
326
+ return ""
327
+
328
+ # Determine if this is a code field (diff or patch)
329
+ is_diff = path.endswith(".diff") or "diff" in path.lower()
330
+ is_patch = "patch" in path.lower() and not is_diff
331
+ is_code_field = is_diff or is_patch
332
+
333
+ # Check if values are identical
334
+ task_str = format_value(task_value) if task_value is not None else None
335
+ validated_str = format_value(validated_value) if validated_value is not None else None
336
+ values_identical = task_str == validated_str
337
+
338
+ lines = []
339
+
340
+ # Special handling for task_description - plain text (no code fences)
341
+ if path == "task_description":
342
+ if task_value is not None:
343
+ return task_str
344
+ return ""
345
+
346
+ # If values are identical, show only once
347
+ if values_identical and task_value is not None:
348
+ value_str = task_str
349
+ if is_code_field:
350
+ if is_diff:
351
+ lines.append(f"```diff\n{value_str}\n```")
352
+ else: # patch
353
+ lines.append(f"```patch\n{value_str}\n```")
354
+ else:
355
+ lines.append(value_str)
356
+ else:
357
+ # Values differ or one is missing - show both
358
+ if task_value is not None:
359
+ value_str = task_str
360
+ if is_code_field:
361
+ if is_diff:
362
+ lines.append(f"```diff\n{value_str}\n```")
363
+ else: # patch
364
+ lines.append(f"```patch\n{value_str}\n```")
365
+ else:
366
+ lines.append(value_str)
367
+
368
+ if validated_value is not None:
369
+ value_str = validated_str
370
+ if is_code_field:
371
+ if is_diff:
372
+ lines.append(f"```diff\n{value_str}\n```")
373
+ else: # patch
374
+ lines.append(f"```patch\n{value_str}\n```")
375
+ else:
376
+ lines.append(value_str)
377
+
378
+ # Show diff if both values exist and differ
379
+ if task_value is not None and validated_value is not None:
380
+ diff_text = diff_values(task_value, validated_value)
381
+ if diff_text.strip():
382
+ lines.append(f"```diff\n{diff_text}\n```")
383
+
384
+ return "\n\n".join(lines)
385
+
386
+ def extract_repo_from_task_id(task_id: str) -> str:
387
+ """Extract and format repo from task_id.
388
+
389
+ Example: "JuliaORNL#JACC.jl.tar/0" -> "JuliaORNL/JACC.jl"
390
+ """
391
+ try:
392
+ # Split by "/" to get the part before the number
393
+ parts = task_id.split("/")
394
+ if len(parts) < 2:
395
+ return task_id
396
+
397
+ repo_part = parts[0] # "JuliaORNL#JACC.jl.tar"
398
+ # Split by # to separate org and repo name
399
+ org_repo = repo_part.split("#")
400
+ if len(org_repo) < 2:
401
+ return task_id
402
+
403
+ org = org_repo[0] # "JuliaORNL"
404
+ repo_with_ext = org_repo[1] # "JACC.jl.tar"
405
+ # Split by . and take first two parts (JACC.jl)
406
+ repo_parts = repo_with_ext.split(".")
407
+ if len(repo_parts) >= 2:
408
+ repo_name = f"{repo_parts[0]}.{repo_parts[1]}" # "JACC.jl"
409
+ return f"{org}/{repo_name}" # "JuliaORNL/JACC.jl"
410
+ # Fallback: just use the repo_with_ext
411
+ return f"{org}/{repo_with_ext}"
412
+ except Exception:
413
+ # If parsing fails, return task_id as-is
414
+ return task_id
415
+
416
+ def get_task_display_name(task_id: str) -> str:
417
+ """Get display name for task including formatted repo and subject."""
418
+ entry = joined_data[task_id]
419
+ subject = None
420
+
421
+ # Try to get subject from task (tasks.jsonl has "subject" field)
422
+ if entry["task"] is not None and "subject" in entry["task"]:
423
+ subject = entry["task"]["subject"]
424
+ elif entry["validated"] is not None and "subject" in entry["validated"]:
425
+ subject = entry["validated"]["subject"]
426
+
427
+ # Extract and format repo from task_id
428
+ repo = extract_repo_from_task_id(task_id)
429
+
430
+ if subject:
431
+ # Extract first line if it's multi-line
432
+ subject_line = subject.split("\n")[0].strip()
433
+ return f"{repo} - {subject_line}"
434
+ return repo
435
+
436
+ def get_field_description(task_id: str, path: str) -> str:
437
+ """Get description text for a field."""
438
+ entry = joined_data[task_id]
439
+
440
+ if path == "task_description":
441
+ return "*This is the prompt to the agent, asking it to implement an existing feature in the repository.*"
442
+ elif path == "src.diff":
443
+ return "*This patch removes the feature from the repository. The goal is to ensure the repo is in a working state.*"
444
+ elif path == "tests.diff":
445
+ return "*This patch adds tests for the feature to the repository. After the agent solves the task, we run these tests to see if it did it correctly*"
446
+ elif path == "reasoning":
447
+ return "*This is the model's reasoning for why this is a good task and how to do it. It's for debugging.*"
448
+ return ""
449
+
450
+ def update_task(task_id: str):
451
+ """Update UI when task changes. Returns content for all field tabs."""
452
+ if not task_id:
453
+ return [""] * len(field_paths)
454
+
455
+ return [render_field(task_id, path) for path in field_paths]
456
+
457
+ # Create dropdown choices with display names
458
+ task_choices = [(get_task_display_name(tid), tid) for tid in task_ids]
459
+
460
+ # Get field paths from first task (or collect from all tasks)
461
+ field_paths = []
462
+ if task_ids:
463
+ # Get fields from first task
464
+ field_paths = get_field_paths(task_ids[0])
465
+ # Also check other tasks to get all possible fields
466
+ all_paths = set(field_paths)
467
+ for tid in task_ids[1:]:
468
+ all_paths.update(get_field_paths(tid))
469
+ # Sort and maintain order: task_description first, reasoning last
470
+ field_paths = []
471
+ if "task_description" in all_paths:
472
+ field_paths.append("task_description")
473
+ for path in sorted(all_paths):
474
+ if path not in ["task_description", "reasoning"]:
475
+ field_paths.append(path)
476
+ if "reasoning" in all_paths:
477
+ field_paths.append("reasoning")
478
+
479
+ with gr.Blocks(title="Task Viewer") as demo:
480
+ gr.Markdown("# Task Viewer")
481
+
482
+ # Task dropdown at the top
483
+ task_list = gr.Dropdown(
484
+ label="Task",
485
+ choices=task_choices,
486
+ value=task_ids[0] if task_ids else None,
487
+ interactive=True,
488
+ )
489
+
490
+ # Tabs for each field
491
+ if field_paths:
492
+ with gr.Tabs() as field_tabs:
493
+ field_components = []
494
+ description_components = []
495
+ for path in field_paths:
496
+ with gr.Tab(path):
497
+ # Description component (will be updated dynamically)
498
+ desc_comp = gr.Markdown(value="")
499
+ description_components.append(desc_comp)
500
+ # Use Markdown for all fields (supports code fences)
501
+ comp = gr.Markdown(value="")
502
+ field_components.append(comp)
503
+ else:
504
+ gr.Markdown("No fields available")
505
+ field_components = []
506
+ description_components = []
507
+
508
+ def update_task_with_descriptions(task_id: str):
509
+ """Update UI when task changes. Returns content for all field tabs and descriptions."""
510
+ if not task_id:
511
+ field_contents = [""] * len(field_paths)
512
+ descriptions = [""] * len(field_paths)
513
+ else:
514
+ field_contents = [render_field(task_id, path) for path in field_paths]
515
+ descriptions = [get_field_description(task_id, path) for path in field_paths]
516
+ return field_contents + descriptions
517
+
518
+ # Initialize with first task on load
519
+ def on_load():
520
+ if task_ids and field_components:
521
+ return update_task_with_descriptions(task_ids[0])
522
+ return [""] * (len(field_components) + len(description_components))
523
+
524
+ if field_components:
525
+ all_outputs = field_components + description_components
526
+ demo.load(on_load, outputs=all_outputs)
527
+
528
+ # Update fields and descriptions when task changes
529
+ task_list.change(
530
+ update_task_with_descriptions,
531
+ inputs=[task_list],
532
+ outputs=all_outputs,
533
+ )
534
+
535
+ return demo
536
+
537
+
538
+ def main() -> None:
539
+ parser = argparse.ArgumentParser(description="View tasks.jsonl and validated_tasks.jsonl in Gradio")
540
+ parser.add_argument("--tasks", type=Path, default=Path("tasks.jsonl"), help="Path to tasks.jsonl file")
541
+ parser.add_argument("--validated", type=Path, default=Path("validated_tasks.jsonl"), help="Path to validated_tasks.jsonl file")
542
+ parser.add_argument("--host", type=str, default="0.0.0.0", help="Host for web server")
543
+ parser.add_argument("--port", type=int, default=7860, help="Port for web server")
544
+ args = parser.parse_args()
545
+
546
+
547
+ joined_data = load_and_join_tasks(args.tasks, args.validated)
548
+
549
+ app = build_app(joined_data)
550
+ app.launch(server_name=args.host, server_port=args.port)
551
+
552
+
553
+ if __name__ == "__main__":
554
+ main()
tasks.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
validated_tasks.jsonl ADDED
The diff for this file is too large to render. See raw diff