relbench 3.0.0__tar.gz → 3.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {relbench-3.0.0 → relbench-3.0.2}/PKG-INFO +13 -39
  2. {relbench-3.0.0 → relbench-3.0.2}/README.md +12 -38
  3. {relbench-3.0.0 → relbench-3.0.2}/pyproject.toml +1 -1
  4. {relbench-3.0.0 → relbench-3.0.2}/relbench/base/task_autocomplete.py +1 -1
  5. {relbench-3.0.0 → relbench-3.0.2}/relbench/submit.py +208 -10
  6. {relbench-3.0.0 → relbench-3.0.2}/LICENSE +0 -0
  7. {relbench-3.0.0 → relbench-3.0.2}/relbench/__init__.py +0 -0
  8. {relbench-3.0.0 → relbench-3.0.2}/relbench/base/__init__.py +0 -0
  9. {relbench-3.0.0 → relbench-3.0.2}/relbench/base/database.py +0 -0
  10. {relbench-3.0.0 → relbench-3.0.2}/relbench/base/dataset.py +0 -0
  11. {relbench-3.0.0 → relbench-3.0.2}/relbench/base/table.py +0 -0
  12. {relbench-3.0.0 → relbench-3.0.2}/relbench/base/task_base.py +0 -0
  13. {relbench-3.0.0 → relbench-3.0.2}/relbench/base/task_entity.py +0 -0
  14. {relbench-3.0.0 → relbench-3.0.2}/relbench/base/task_recommendation.py +0 -0
  15. {relbench-3.0.0 → relbench-3.0.2}/relbench/hf.py +0 -0
  16. {relbench-3.0.0 → relbench-3.0.2}/relbench/load.py +0 -0
  17. {relbench-3.0.0 → relbench-3.0.2}/relbench/manifest.py +0 -0
  18. {relbench-3.0.0 → relbench-3.0.2}/relbench/metrics.py +0 -0
  19. {relbench-3.0.0 → relbench-3.0.2}/relbench/modeling/__init__.py +0 -0
  20. {relbench-3.0.0 → relbench-3.0.2}/relbench/modeling/graph.py +0 -0
  21. {relbench-3.0.0 → relbench-3.0.2}/relbench/modeling/loader.py +0 -0
  22. {relbench-3.0.0 → relbench-3.0.2}/relbench/modeling/nn.py +0 -0
  23. {relbench-3.0.0 → relbench-3.0.2}/relbench/modeling/utils.py +0 -0
  24. {relbench-3.0.0 → relbench-3.0.2}/relbench/schema.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: relbench
3
- Version: 3.0.0
3
+ Version: 3.0.2
4
4
  Summary: RelBench: Relational Deep Learning Benchmark
5
5
  Keywords: relational deep learning,graph neural networks,relational databases,benchmark,machine learning
6
6
  Author-email: RelBench Team <relbench@cs.stanford.edu>
@@ -60,16 +60,7 @@ Provides-Extra: schema
60
60
 
61
61
  ## News
62
62
 
63
- RelBench 3 loads every dataset family below from the Hugging Face Hub with one call —
64
- `relbench.load_dataset("<org>/<repo>/<name>")` — the core databases
65
- ([`stanford-star/relbench-v1`](https://huggingface.co/datasets/stanford-star/relbench-v1),
66
- [`stanford-star/relbench-v2-extra`](https://huggingface.co/datasets/stanford-star/relbench-v2-extra)),
67
- CTU/ReDeLEx ([`stanford-star/redelex`](https://huggingface.co/datasets/stanford-star/redelex)),
68
- 4DBInfer ([`stanford-star/dbinfer`](https://huggingface.co/datasets/stanford-star/dbinfer)) and
69
- TGB ([`stanford-star/tgb`](https://huggingface.co/datasets/stanford-star/tgb)); the `[ctu]` extra is
70
- gone. MIMIC-IV requires PhysioNet credentials and is not hosted. TGB and 4DBInfer tasks that
71
- use their own scoring protocols load as data only (see [`MIGRATION.md`](MIGRATION.md)).
72
-
63
+ - **Oct 2026** — [Agentic leaderboard submissions](leaderboard/README.md#automatically-eg-from-an-ai-agent): point your AI agent at the [leaderboard README](leaderboard/README.md) and it can submit end to end. Entries can now carry an optional repro link for reproducibility.
73
64
  - **Aug 2026** — RelBench v3 released: datasets and tasks load straight from [Hugging Face](https://huggingface.co/stanford-star), a new [leaderboard](https://star-project.stanford.edu/relbench/leaderboard/) with automated submissions, and bug fixes ([migration guide](MIGRATION.md)).
74
65
  - **Jun 2026** — Datasets, models, and community hub migrated to [Hugging Face](https://huggingface.co/stanford-star).
75
66
  - **Mar 2026** — [RelBench v2 paper](https://arxiv.org/abs/2602.12606) accepted at the [ICLR 2026 DATA-FM workshop](https://data-fm-iclr2026.github.io/).
@@ -123,31 +114,15 @@ Open these directly in Google Colab — no setup required:
123
114
  ## Leaderboard
124
115
 
125
116
  The [**RelBench leaderboard**](https://star-project.stanford.edu/relbench/leaderboard/)
126
- ranks methods by their test-set performance, averaged over a fixed task set. There are
127
- three independent boards — **classification** (12 tasks), **regression** (9), and
128
- **recommendation** (10); the task lists are in `relbench.submit.LEADERBOARD_TASKS`.
129
- You can submit to any of them; each requires predictions for *all* of its tasks.
130
-
131
- To submit:
132
-
133
- 1. **Write one prediction CSV per task**, named `<dataset>__<task>.csv`, into a directory:
134
-
135
- ```python
136
- relbench.submit.write_prediction_table(task, test_pred, "preds/rel-f1__driver-position.csv")
137
- ```
138
-
139
- 2. **Validate and package** the directory — this scores every CSV against the test tables,
140
- prints a verdict per leaderboard, and writes clean submission zip file(s):
141
-
142
- ```bash
143
- python -m relbench.submit preds/
144
- ```
145
-
146
- 3. **[Open a submission issue](https://github.com/stanford-star/relbench/issues/new?template=submit.yml)**
147
- on this repository: fill in the short form and upload the zip file(s) into it.
117
+ ranks methods by their test-set performance, averaged over a fixed task set, with three
118
+ independent boards: **classification**, **regression**, and **recommendation**.
119
+ To submit, write one prediction CSV per task, validate and package them with
120
+ `python -m relbench.submit`, and open a
121
+ [submission issue](https://github.com/stanford-star/relbench/issues/new?template=submit.yml)
122
+ — or let `python -m relbench.submit --submit` do the upload and open the issue for you.
148
123
 
149
- The submission is validated automatically and the report is posted on the issue; once a
150
- maintainer approves, your entry appears on the leaderboard.
124
+ [**`leaderboard/README.md`**](leaderboard/README.md) has the full instructions, including
125
+ the prediction CSV format for writing predictions with your own code.
151
126
 
152
127
  ## BYOD (Bring Your Own Data)
153
128
 
@@ -178,13 +153,12 @@ If you use RelBench, please cite the benchmark papers:
178
153
  year={2024}
179
154
  }
180
155
 
181
- @misc{relbenchv2,
156
+ @inproceedings{relbenchv2,
182
157
  title={RelBench v2: A Large-Scale Benchmark and Repository for Relational Data},
183
158
  author={Gu, Justin and Ranjan, Rishabh and Kanatsoulis, Charilaos and Tang, Haiming and Jurkovic, Martin and Hudovernik, Valter and Znidar, Mark and Chaturvedi, Pranshu and Shroff, Parth and Li, Fengyu and Leskovec, Jure},
159
+ booktitle={3rd Workshop on Navigating and Addressing Data Problems for Foundation Models (DATA-FM) at ICLR 2026},
184
160
  year={2026},
185
- eprint={2602.12606},
186
- archivePrefix={arXiv},
187
- primaryClass={cs.LG},
161
+ note={arXiv:2602.12606 [cs.LG]},
188
162
  url={https://arxiv.org/abs/2602.12606}
189
163
  }
190
164
  ```
@@ -21,16 +21,7 @@
21
21
 
22
22
  ## News
23
23
 
24
- RelBench 3 loads every dataset family below from the Hugging Face Hub with one call —
25
- `relbench.load_dataset("<org>/<repo>/<name>")` — the core databases
26
- ([`stanford-star/relbench-v1`](https://huggingface.co/datasets/stanford-star/relbench-v1),
27
- [`stanford-star/relbench-v2-extra`](https://huggingface.co/datasets/stanford-star/relbench-v2-extra)),
28
- CTU/ReDeLEx ([`stanford-star/redelex`](https://huggingface.co/datasets/stanford-star/redelex)),
29
- 4DBInfer ([`stanford-star/dbinfer`](https://huggingface.co/datasets/stanford-star/dbinfer)) and
30
- TGB ([`stanford-star/tgb`](https://huggingface.co/datasets/stanford-star/tgb)); the `[ctu]` extra is
31
- gone. MIMIC-IV requires PhysioNet credentials and is not hosted. TGB and 4DBInfer tasks that
32
- use their own scoring protocols load as data only (see [`MIGRATION.md`](MIGRATION.md)).
33
-
24
+ - **Oct 2026** — [Agentic leaderboard submissions](leaderboard/README.md#automatically-eg-from-an-ai-agent): point your AI agent at the [leaderboard README](leaderboard/README.md) and it can submit end to end. Entries can now carry an optional repro link for reproducibility.
34
25
  - **Aug 2026** — RelBench v3 released: datasets and tasks load straight from [Hugging Face](https://huggingface.co/stanford-star), a new [leaderboard](https://star-project.stanford.edu/relbench/leaderboard/) with automated submissions, and bug fixes ([migration guide](MIGRATION.md)).
35
26
  - **Jun 2026** — Datasets, models, and community hub migrated to [Hugging Face](https://huggingface.co/stanford-star).
36
27
  - **Mar 2026** — [RelBench v2 paper](https://arxiv.org/abs/2602.12606) accepted at the [ICLR 2026 DATA-FM workshop](https://data-fm-iclr2026.github.io/).
@@ -84,31 +75,15 @@ Open these directly in Google Colab — no setup required:
84
75
  ## Leaderboard
85
76
 
86
77
  The [**RelBench leaderboard**](https://star-project.stanford.edu/relbench/leaderboard/)
87
- ranks methods by their test-set performance, averaged over a fixed task set. There are
88
- three independent boards — **classification** (12 tasks), **regression** (9), and
89
- **recommendation** (10); the task lists are in `relbench.submit.LEADERBOARD_TASKS`.
90
- You can submit to any of them; each requires predictions for *all* of its tasks.
91
-
92
- To submit:
93
-
94
- 1. **Write one prediction CSV per task**, named `<dataset>__<task>.csv`, into a directory:
95
-
96
- ```python
97
- relbench.submit.write_prediction_table(task, test_pred, "preds/rel-f1__driver-position.csv")
98
- ```
99
-
100
- 2. **Validate and package** the directory — this scores every CSV against the test tables,
101
- prints a verdict per leaderboard, and writes clean submission zip file(s):
102
-
103
- ```bash
104
- python -m relbench.submit preds/
105
- ```
106
-
107
- 3. **[Open a submission issue](https://github.com/stanford-star/relbench/issues/new?template=submit.yml)**
108
- on this repository: fill in the short form and upload the zip file(s) into it.
78
+ ranks methods by their test-set performance, averaged over a fixed task set, with three
79
+ independent boards: **classification**, **regression**, and **recommendation**.
80
+ To submit, write one prediction CSV per task, validate and package them with
81
+ `python -m relbench.submit`, and open a
82
+ [submission issue](https://github.com/stanford-star/relbench/issues/new?template=submit.yml)
83
+ — or let `python -m relbench.submit --submit` do the upload and open the issue for you.
109
84
 
110
- The submission is validated automatically and the report is posted on the issue; once a
111
- maintainer approves, your entry appears on the leaderboard.
85
+ [**`leaderboard/README.md`**](leaderboard/README.md) has the full instructions, including
86
+ the prediction CSV format for writing predictions with your own code.
112
87
 
113
88
  ## BYOD (Bring Your Own Data)
114
89
 
@@ -139,13 +114,12 @@ If you use RelBench, please cite the benchmark papers:
139
114
  year={2024}
140
115
  }
141
116
 
142
- @misc{relbenchv2,
117
+ @inproceedings{relbenchv2,
143
118
  title={RelBench v2: A Large-Scale Benchmark and Repository for Relational Data},
144
119
  author={Gu, Justin and Ranjan, Rishabh and Kanatsoulis, Charilaos and Tang, Haiming and Jurkovic, Martin and Hudovernik, Valter and Znidar, Mark and Chaturvedi, Pranshu and Shroff, Parth and Li, Fengyu and Leskovec, Jure},
120
+ booktitle={3rd Workshop on Navigating and Addressing Data Problems for Foundation Models (DATA-FM) at ICLR 2026},
145
121
  year={2026},
146
- eprint={2602.12606},
147
- archivePrefix={arXiv},
148
- primaryClass={cs.LG},
122
+ note={arXiv:2602.12606 [cs.LG]},
149
123
  url={https://arxiv.org/abs/2602.12606}
150
124
  }
151
125
  ```
@@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi"
4
4
 
5
5
  [project]
6
6
  name = "relbench"
7
- version = "3.0.0"
7
+ version = "3.0.2"
8
8
  description = "RelBench: Relational Deep Learning Benchmark"
9
9
  authors = [{name = "RelBench Team", email = "relbench@cs.stanford.edu"}]
10
10
  readme = "README.md"
@@ -139,7 +139,7 @@ class AutoCompleteTask(EntityTask):
139
139
  db = full_db if split == "test" else full_db.upto(self.dataset.test_timestamp)
140
140
 
141
141
  if split == "train":
142
- start = db.min_timestamp - pd.Timedelta(1, "ns")
142
+ start = db.min_timestamp - self.timedelta
143
143
  end = self.dataset.val_timestamp
144
144
  elif split == "val":
145
145
  start, end = self.dataset.val_timestamp, self.dataset.test_timestamp
@@ -5,8 +5,8 @@ library, ``pandas``/``numpy`` and the rest of RelBench's data layer (no torch /
5
5
  torch_geometric / pyg), so it imports cleanly in a minimal environment and can score a
6
6
  submission produced by any modelling stack.
7
7
 
8
- Concept -- the prediction table
9
- ================================
8
+ The prediction table
9
+ ====================
10
10
  The test split of a task is a *task table*. A *prediction table* is the same table with
11
11
  the target column replaced by predictions:
12
12
 
@@ -46,6 +46,7 @@ from __future__ import annotations
46
46
  import argparse
47
47
  import json
48
48
  import os
49
+ import sys
49
50
  from concurrent.futures import ProcessPoolExecutor
50
51
  from pathlib import Path
51
52
  from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
@@ -806,6 +807,15 @@ def _use_color() -> bool:
806
807
  return False
807
808
 
808
809
 
810
+ def _glyph(unicode: str, ascii_fallback: str) -> str:
811
+ encoding = getattr(sys.stdout, "encoding", None) or "ascii"
812
+ try:
813
+ unicode.encode(encoding)
814
+ except (UnicodeEncodeError, LookupError):
815
+ return ascii_fallback
816
+ return unicode
817
+
818
+
809
819
  class _Style:
810
820
  r"""Tiny ANSI styler; every method degrades to the identity without a TTY."""
811
821
 
@@ -933,18 +943,18 @@ def _print_report(result: Dict[str, Any]) -> None:
933
943
  for task_name in group_tasks:
934
944
  entry = tasks[task_name]
935
945
  if entry["status"] == "ok":
936
- mark = st.green("✓")
946
+ mark = st.green(_glyph("✓", "OK"))
937
947
  value = _format_value(entry["metric_name"], entry["metric"])
938
948
  print(f" {mark} {task_name.ljust(name_w)} {value.rjust(7)}")
939
949
  else:
940
- mark = st.red("✗")
950
+ mark = st.red(_glyph("✗", "X"))
941
951
  print(f" {mark} {task_name.ljust(name_w)} {st.red('failed')}")
942
952
  for line in _wrap_text(entry["error"] or "unknown error", indent=6):
943
953
  print(st.dim(line))
944
954
  if fam is not None and fam["missing"]:
945
955
  for task_name in fam["missing"]:
946
956
  print(
947
- f" {st.yellow('·')} {task_name.ljust(name_w)} "
957
+ f" {st.yellow(_glyph('·', '-'))} {task_name.ljust(name_w)} "
948
958
  + st.dim("missing")
949
959
  )
950
960
  if fam is not None and fam["aggregate"] is not None:
@@ -1045,9 +1055,11 @@ def _markdown_report(result: Dict[str, Any]) -> str:
1045
1055
  # Where validated submissions are turned into leaderboard entries: a submission is a GitHub
1046
1056
  # issue on the RelBench repository, created from the submission form with the prediction
1047
1057
  # tables attached. CI validates it and a maintainer approval publishes the entry.
1058
+ SUBMISSION_REPO = "stanford-star/relbench"
1048
1059
  SUBMISSION_ISSUE_URL = (
1049
- "https://github.com/stanford-star/relbench/issues/new?template=submit.yml"
1060
+ f"https://github.com/{SUBMISSION_REPO}/issues/new?template=submit.yml"
1050
1061
  )
1062
+ SUBMISSION_HF_REPO = "relbench-submissions"
1051
1063
 
1052
1064
 
1053
1065
  def _zip_files(pred_dir: Path, filenames: Sequence[str]) -> bytes:
@@ -1068,7 +1080,8 @@ def _package(
1068
1080
  validated: Sequence[str],
1069
1081
  extra: Sequence[str],
1070
1082
  out: Path,
1071
- ) -> None:
1083
+ show_next_step: bool = True,
1084
+ ) -> List[Path]:
1072
1085
  r"""Write one submission zip per validated family and print how to submit them.
1073
1086
 
1074
1087
  A single all-in-one zip easily exceeds GitHub's issue-attachment size limit; the
@@ -1091,11 +1104,134 @@ def _package(
1091
1104
  fam_out.write_bytes(zip_bytes)
1092
1105
  outputs.append((fam_out, len(zip_bytes)))
1093
1106
  print("\r", end="", flush=True)
1107
+ if not show_next_step:
1108
+ return [p for p, _ in outputs]
1094
1109
  print(st.bold("Next step "))
1095
1110
  print(f" Upload these files at {st.underline_cyan(SUBMISSION_ISSUE_URL)}:")
1096
1111
  for fam_out, size in outputs:
1097
1112
  print(f" {st.bold(str(fam_out))} " + st.italic(f"({size / 1e6:.1f} MB)"))
1098
1113
  print()
1114
+ return [p for p, _ in outputs]
1115
+
1116
+
1117
+ def _github_token() -> str:
1118
+ import subprocess
1119
+
1120
+ for var in ("GH_TOKEN", "GITHUB_TOKEN"):
1121
+ if os.environ.get(var):
1122
+ return os.environ[var]
1123
+ try:
1124
+ token = subprocess.run(
1125
+ ["gh", "auth", "token"], capture_output=True, text=True, check=True
1126
+ ).stdout.strip()
1127
+ except (OSError, subprocess.CalledProcessError):
1128
+ token = ""
1129
+ if not token:
1130
+ raise RuntimeError(
1131
+ "no GitHub token: set GH_TOKEN or GITHUB_TOKEN, or run `gh auth login`"
1132
+ )
1133
+ return token
1134
+
1135
+
1136
+ def _upload_to_hf(zips: Sequence[Path]) -> List[str]:
1137
+ import tempfile
1138
+ from datetime import datetime, timezone
1139
+
1140
+ from huggingface_hub import CommitOperationAdd, HfApi
1141
+
1142
+ api = HfApi()
1143
+ user = api.whoami()["name"]
1144
+ repo_id = f"{user}/{SUBMISSION_HF_REPO}"
1145
+ api.create_repo(repo_id, repo_type="dataset", private=False, exist_ok=True)
1146
+ folder = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
1147
+ ops = [
1148
+ CommitOperationAdd(path_in_repo=f"{folder}/{z.name}", path_or_fileobj=str(z))
1149
+ for z in zips
1150
+ ]
1151
+ commit = api.create_commit(
1152
+ repo_id,
1153
+ operations=ops,
1154
+ repo_type="dataset",
1155
+ commit_message=f"RelBench leaderboard submission {folder}",
1156
+ )
1157
+ return [
1158
+ f"https://huggingface.co/datasets/{repo_id}/resolve/{commit.oid}/{op.path_in_repo}"
1159
+ for op in ops
1160
+ ]
1161
+
1162
+
1163
+ def _issue_body(
1164
+ name: str,
1165
+ url: str,
1166
+ in_context: bool,
1167
+ note: str,
1168
+ links: Sequence[str],
1169
+ repro_link: str = "",
1170
+ ) -> str:
1171
+ files = "\n".join(f"[{u.rsplit('/', 1)[-1]}]({u})" for u in links)
1172
+ return (
1173
+ f"### Name\n\n{name}\n\n"
1174
+ f"### Note\n\n{note or '_No response_'}\n\n"
1175
+ f"### In-context?\n\n{'Yes' if in_context else 'No'}\n\n"
1176
+ f"### URL\n\n{url}\n\n"
1177
+ f"### Repro link\n\n{repro_link or '_No response_'}\n\n"
1178
+ f"### File(s)\n\n{files}\n"
1179
+ )
1180
+
1181
+
1182
+ def _edit_issue_body(
1183
+ entry: int, name: str = "", url: str = "", repro_link: str = "", note: str = ""
1184
+ ) -> str:
1185
+ return "".join(
1186
+ f"### {h}\n\n{v or '_No response_'}\n\n"
1187
+ for h, v in [
1188
+ ("Entry", str(entry)),
1189
+ ("Name", name),
1190
+ ("URL", url),
1191
+ ("Repro link", repro_link),
1192
+ ("Note", note),
1193
+ ]
1194
+ )
1195
+
1196
+
1197
+ def _open_issue(
1198
+ body: str, title: str = "Leaderboard submission", label: str = "submit"
1199
+ ) -> str:
1200
+ import urllib.request
1201
+
1202
+ req = urllib.request.Request(
1203
+ f"https://api.github.com/repos/{SUBMISSION_REPO}/issues",
1204
+ data=json.dumps({"title": title, "body": body, "labels": [label]}).encode(),
1205
+ headers={
1206
+ "Authorization": f"Bearer {_github_token()}",
1207
+ "Accept": "application/vnd.github+json",
1208
+ "User-Agent": "relbench-submit",
1209
+ },
1210
+ method="POST",
1211
+ )
1212
+ with urllib.request.urlopen(req, timeout=60) as r:
1213
+ return json.load(r)["html_url"]
1214
+
1215
+
1216
+ def _submit(zips: Sequence[Path], args: argparse.Namespace) -> None:
1217
+ st = _Style(_use_color())
1218
+ print("Uploading to the Hugging Face Hub...", flush=True)
1219
+ links = _upload_to_hf(zips)
1220
+ for u in links:
1221
+ print(f" {u}")
1222
+ body = _issue_body(
1223
+ args.name,
1224
+ args.url,
1225
+ args.in_context == "yes",
1226
+ args.note or "",
1227
+ links,
1228
+ args.repro_link or "",
1229
+ )
1230
+ issue = _open_issue(body)
1231
+ print()
1232
+ print(st.bold("Submitted ") + st.underline_cyan(issue))
1233
+ print(" Validation results will be posted on the issue.")
1234
+ print()
1099
1235
 
1100
1236
 
1101
1237
  def main(argv: Optional[Sequence[str]] = None) -> int:
@@ -1113,7 +1249,9 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
1113
1249
  ),
1114
1250
  )
1115
1251
  parser.add_argument(
1116
- "pred_dir", help="directory of <dataset>__<task>.csv prediction files"
1252
+ "pred_dir",
1253
+ nargs="?",
1254
+ help="directory of <dataset>__<task>.csv prediction files",
1117
1255
  )
1118
1256
  parser.add_argument(
1119
1257
  "--num-workers",
@@ -1129,7 +1267,64 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
1129
1267
  "<base>-<family>.zip (default base: <dir-name>.zip in the cwd)"
1130
1268
  ),
1131
1269
  )
1270
+ parser.add_argument(
1271
+ "--submit",
1272
+ action="store_true",
1273
+ help=(
1274
+ "upload the zips to <hf-user>/relbench-submissions on the Hugging Face Hub "
1275
+ "(needs a Hugging Face login) and open the submission issue on GitHub "
1276
+ "(needs GH_TOKEN / GITHUB_TOKEN or `gh auth login`)"
1277
+ ),
1278
+ )
1279
+ parser.add_argument("--name", help="with --submit: display name on the leaderboard")
1280
+ parser.add_argument("--url", help="with --submit: project / code / paper link")
1281
+ parser.add_argument(
1282
+ "--in-context",
1283
+ choices=["yes", "no"],
1284
+ help="with --submit: yes if the method did NOT train on the target database",
1285
+ )
1286
+ parser.add_argument("--note", help="with --submit: optional note, shown on hover")
1287
+ parser.add_argument(
1288
+ "--repro-link",
1289
+ help="with --submit: optional link to code / instructions to reproduce the results",
1290
+ )
1291
+ parser.add_argument(
1292
+ "--edit",
1293
+ type=int,
1294
+ metavar="ISSUE",
1295
+ help=(
1296
+ "instead of submitting, open an issue that updates the --name, --url, "
1297
+ "--repro-link, and/or --note of the leaderboard entry published from "
1298
+ "submission issue ISSUE (needs a GitHub token, as for --submit)"
1299
+ ),
1300
+ )
1132
1301
  args = parser.parse_args(argv)
1302
+ if args.edit is not None:
1303
+ if not (args.name or args.url or args.repro_link or args.note):
1304
+ parser.error("--edit requires --name, --url, --repro-link, or --note")
1305
+ issue = _open_issue(
1306
+ _edit_issue_body(
1307
+ args.edit,
1308
+ args.name or "",
1309
+ args.url or "",
1310
+ args.repro_link or "",
1311
+ args.note or "",
1312
+ ),
1313
+ title="Leaderboard entry edit",
1314
+ label="edit",
1315
+ )
1316
+ print(f"Opened {issue}")
1317
+ return 0
1318
+ if args.pred_dir is None:
1319
+ parser.error("pred_dir is required unless --edit is given")
1320
+ if args.submit:
1321
+ missing = [
1322
+ f"--{k.replace('_', '-')}"
1323
+ for k in ("name", "url", "in_context")
1324
+ if not getattr(args, k)
1325
+ ]
1326
+ if missing:
1327
+ parser.error("--submit requires " + ", ".join(missing))
1133
1328
  if args.out is not None and not args.out.endswith(".zip"):
1134
1329
  args.out += ".zip"
1135
1330
 
@@ -1139,18 +1334,21 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
1139
1334
  st = _Style(_use_color())
1140
1335
  print(
1141
1336
  st.bold_red("Cannot package")
1142
- + " — no leaderboard was validated; fix the prediction tables first."
1337
+ + f" {_glyph('—', '-')} no leaderboard was validated; fix the prediction tables first."
1143
1338
  )
1144
1339
  print()
1145
1340
  return 1
1146
1341
  out = Path(args.out or f"{Path(args.pred_dir).resolve().name}.zip")
1147
- _package(
1342
+ zips = _package(
1148
1343
  args.pred_dir,
1149
1344
  result["families"],
1150
1345
  result["validated"],
1151
1346
  result.get("extra_files") or [],
1152
1347
  out,
1348
+ show_next_step=not args.submit,
1153
1349
  )
1350
+ if args.submit:
1351
+ _submit(zips, args)
1154
1352
  return 0
1155
1353
 
1156
1354
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes