Libra-1995 commited on
Commit
cfbf272
·
1 Parent(s): 90b78ed

feat: update code

Browse files
competitions/evaluate.py DELETED
@@ -1,135 +0,0 @@
1
- import argparse
2
- import json
3
- import os
4
- import shlex
5
- import shutil
6
- import subprocess
7
-
8
- from huggingface_hub import HfApi, hf_hub_download, snapshot_download
9
- from huggingface_hub.utils._errors import EntryNotFoundError
10
- from loguru import logger
11
-
12
- from competitions import utils
13
- from competitions.compute_metrics import compute_metrics
14
- from competitions.enums import SubmissionStatus
15
- from competitions.params import EvalParams
16
-
17
-
18
- def parse_args():
19
- parser = argparse.ArgumentParser()
20
- parser.add_argument("--config", type=str, required=True)
21
- return parser.parse_args()
22
-
23
-
24
- def upload_submission_file(params, file_path):
25
- logger.info("Uploading submission file")
26
- pass
27
-
28
-
29
- def generate_submission_file(params):
30
- logger.info("Downloading submission dataset")
31
- submission_dir = snapshot_download(
32
- repo_id=params.submission_repo,
33
- local_dir=params.output_path,
34
- token=os.environ.get("USER_TOKEN"),
35
- repo_type="model",
36
- )
37
- # submission_dir has a script.py file
38
- # start a subprocess to run the script.py
39
- # the script.py will generate a submission.csv file in the submission_dir
40
- # push the submission.csv file to the repo using upload_submission_file
41
- logger.info("Generating submission file")
42
-
43
- # invalidate USER_TOKEN env var
44
- os.environ["USER_TOKEN"] = ""
45
-
46
- # Copy sandbox to submission_dir
47
- shutil.copyfile("sandbox", f"{submission_dir}/sandbox")
48
- sandbox_path = f"{submission_dir}/sandbox"
49
- os.chmod(sandbox_path, 0o755)
50
- os.chown(sandbox_path, os.getuid(), os.getgid())
51
-
52
- # Define your command
53
- cmd = f"{sandbox_path} python script.py"
54
- cmd = shlex.split(cmd)
55
-
56
- # Copy the current environment and modify it
57
- env = os.environ.copy()
58
-
59
- # Start the subprocess
60
- process = subprocess.Popen(cmd, cwd=submission_dir, env=env)
61
-
62
- # Wait for the process to complete or timeout
63
- try:
64
- process.wait(timeout=params.time_limit)
65
- except subprocess.TimeoutExpired:
66
- logger.info(f"Process exceeded {params.time_limit} seconds time limit. Terminating...")
67
- process.kill()
68
- process.wait()
69
-
70
- # Check if process terminated due to timeout
71
- if process.returncode and process.returncode != 0:
72
- logger.error("Subprocess didn't terminate successfully")
73
- else:
74
- logger.info("Subprocess terminated successfully")
75
-
76
- logger.info("contents of submission_dir")
77
- logger.info(os.listdir(submission_dir))
78
-
79
- api = HfApi(token=params.token)
80
- for sub_file in params.submission_filenames:
81
- logger.info(f"Uploading {sub_file} to the repository")
82
- sub_file_ext = sub_file.split(".")[-1]
83
- api.upload_file(
84
- path_or_fileobj=f"{submission_dir}/{sub_file}",
85
- path_in_repo=f"submissions/{params.team_id}-{params.submission_id}.{sub_file_ext}",
86
- repo_id=params.competition_id,
87
- repo_type="dataset",
88
- )
89
-
90
-
91
- @utils.monitor
92
- def run(params):
93
- logger.info(params)
94
- if isinstance(params, dict):
95
- params = EvalParams(**params)
96
-
97
- utils.update_submission_status(params, SubmissionStatus.PROCESSING.value)
98
-
99
- if params.competition_type == "script":
100
- try:
101
- requirements_fname = hf_hub_download(
102
- repo_id=params.competition_id,
103
- filename="requirements.txt",
104
- token=params.token,
105
- repo_type="dataset",
106
- )
107
- except EntryNotFoundError:
108
- requirements_fname = None
109
-
110
- if requirements_fname:
111
- logger.info("Installing requirements")
112
- utils.uninstall_requirements(requirements_fname)
113
- utils.install_requirements(requirements_fname)
114
- if len(str(params.dataset).strip()) > 0:
115
- # _ = Repository(local_dir="/tmp/data", clone_from=params.dataset, token=params.token)
116
- _ = snapshot_download(
117
- repo_id=params.dataset,
118
- local_dir="/tmp/data",
119
- token=params.token,
120
- repo_type="dataset",
121
- )
122
- generate_submission_file(params)
123
-
124
- evaluation = compute_metrics(params)
125
-
126
- utils.update_submission_score(params, evaluation["public_score"], evaluation["private_score"])
127
- utils.update_submission_status(params, SubmissionStatus.SUCCESS.value)
128
- utils.delete_space(params)
129
-
130
-
131
- if __name__ == "__main__":
132
- args = parse_args()
133
- _params = json.load(open(args.config, encoding="utf-8"))
134
- _params = EvalParams(**_params)
135
- run(_params)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
competitions/runner.py CHANGED
@@ -4,7 +4,10 @@ import json
4
  import os
5
  import time
6
  import uuid
 
7
  from dataclasses import dataclass
 
 
8
 
9
  import pandas as pd
10
  from huggingface_hub import HfApi, hf_hub_download, snapshot_download
@@ -12,17 +15,7 @@ from loguru import logger
12
 
13
  from competitions.enums import SubmissionStatus
14
  from competitions.info import CompetitionInfo
15
- from competitions.utils import run_evaluation, user_token_api
16
-
17
-
18
- _DOCKERFILE = """
19
- FROM huggingface/competitions:latest
20
-
21
- CMD uvicorn competitions.api:api --port 7860 --host 0.0.0.0
22
- """
23
-
24
- # format _DOCKERFILE
25
- _DOCKERFILE = _DOCKERFILE.replace("\n", " ").replace(" ", "\n").strip()
26
 
27
 
28
  @dataclass
@@ -43,7 +36,7 @@ class JobRunner:
43
  self.dataset = self.competition_info.dataset
44
  self.submission_filenames = self.competition_info.submission_filenames
45
 
46
- def get_pending_subs(self):
47
  submission_jsons = snapshot_download(
48
  repo_id=self.competition_id,
49
  allow_patterns="submission_info/*.json",
@@ -51,21 +44,32 @@ class JobRunner:
51
  repo_type="dataset",
52
  )
53
  submission_jsons = glob.glob(os.path.join(submission_jsons, "submission_info/*.json"))
54
- pending_submissions = []
55
- for _json in submission_jsons:
56
- _json = json.load(open(_json, "r", encoding="utf-8"))
 
57
  team_id = _json["id"]
58
  for sub in _json["submissions"]:
59
- if sub["status"] == SubmissionStatus.PENDING.value:
60
- pending_submissions.append(
61
- {
62
- "team_id": team_id,
63
- "submission_id": sub["submission_id"],
64
- "datetime": sub["datetime"],
65
- "submission_repo": sub["submission_repo"],
66
- "space_id": sub["space_id"],
67
- }
68
- )
 
 
 
 
 
 
 
 
 
 
69
  if len(pending_submissions) == 0:
70
  return None
71
  logger.info(f"Found {len(pending_submissions)} pending submissions.")
@@ -75,6 +79,13 @@ class JobRunner:
75
  pending_submissions = pending_submissions.reset_index(drop=True)
76
  return pending_submissions
77
 
 
 
 
 
 
 
 
78
  def _queue_submission(self, team_id, submission_id):
79
  team_fname = hf_hub_download(
80
  repo_id=self.competition_id,
@@ -127,29 +138,7 @@ class JobRunner:
127
  repo_type="dataset",
128
  )
129
 
130
- def run_local(self, team_id, submission_id, submission_repo):
131
- self._queue_submission(team_id, submission_id)
132
- eval_params = {
133
- "competition_id": self.competition_id,
134
- "competition_type": self.competition_type,
135
- "metric": self.metric,
136
- "token": self.token,
137
- "team_id": team_id,
138
- "submission_id": submission_id,
139
- "submission_id_col": self.submission_id_col,
140
- "submission_cols": self.submission_cols,
141
- "submission_rows": self.submission_rows,
142
- "output_path": self.output_path,
143
- "submission_repo": submission_repo,
144
- "time_limit": self.time_limit,
145
- "dataset": self.dataset,
146
- "submission_filenames": self.submission_filenames,
147
- }
148
- eval_params = json.dumps(eval_params)
149
- eval_pid = run_evaluation(eval_params, local=True, wait=True)
150
- logger.info(f"New evaluation process started with pid {eval_pid}.")
151
-
152
- def _create_readme(self, project_name):
153
  _readme = "---\n"
154
  _readme += f"title: {project_name}\n"
155
  _readme += "emoji: 🚀\n"
@@ -158,92 +147,107 @@ class JobRunner:
158
  _readme += "sdk: docker\n"
159
  _readme += "pinned: false\n"
160
  _readme += "---\n"
161
- _readme = io.BytesIO(_readme.encode())
162
  return _readme
163
 
164
- def create_space(self, team_id, submission_id, submission_repo, space_id):
165
- server_space_id = space_id + "-server"
166
- client_space_id = space_id + "-client"
167
- space_auth_token = uuid.uuid4().hex
168
  user_token = user_token_api.get(team_id)
169
 
170
  api = HfApi(token=self.token)
171
  params = {
 
 
172
  "competition_id": self.competition_id,
173
- "competition_type": self.competition_type,
174
- "metric": self.metric,
175
- "token": self.token,
176
  "team_id": team_id,
177
  "submission_id": submission_id,
178
- "submission_id_col": self.submission_id_col,
179
- "submission_cols": self.submission_cols,
180
- "submission_rows": self.submission_rows,
181
  "output_path": self.output_path,
182
  "submission_repo": submission_repo,
183
  "time_limit": self.time_limit,
184
  "dataset": self.dataset,
185
  "submission_filenames": self.submission_filenames,
186
  }
 
 
 
 
 
 
187
 
188
- api.add_space_secret(repo_id=server_space_id, key="PARAMS", value=json.dumps(params))
189
- api.add_space_secret(repo_id=server_space_id, key="HUGSIM_AUTH_TOKEN", value=space_auth_token)
190
- api.add_space_secret(repo_id=server_space_id, key="HF_TOKEN", value=self.token)
191
- readme = self._create_readme(space_id.split("/")[-1])
192
  api.upload_file(
193
- path_or_fileobj=readme,
194
- path_in_repo="README.md",
195
- repo_id=server_space_id,
196
- repo_type="space",
197
  )
198
- api.upload_file(
199
- path_or_fileobj=io.BytesIO(_DOCKERFILE.encode()),
200
- path_in_repo="Dockerfile",
201
  repo_id=space_id,
202
  repo_type="space",
 
 
 
203
  )
204
 
205
- api.add_space_secret(repo_id=client_space_id, key="HUGSIM_AUTH_TOKEN", value=space_auth_token)
 
 
 
206
  api.snapshot_download(
207
  repo_id=submission_repo,
208
  repo_type="model",
209
- revision="main",
210
  token=user_token,
211
- local_dir="/tmp/data/user_repo",
212
  allow_patterns=["*"],
213
  )
214
- api.upload_folder(
215
- repo_id=client_space_id,
216
- repo_type="space",
217
- folder_path="/tmp/data/user_repo",
218
- )
 
 
 
 
 
219
  self._queue_submission(team_id, submission_id)
220
 
221
  def run(self):
 
222
  while True:
223
- pending_submissions = self.get_pending_subs()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  if pending_submissions is None:
225
- time.sleep(5)
226
  continue
227
- if self.competition_type == "generic":
228
- for _, row in pending_submissions.iterrows():
229
- team_id = row["team_id"]
230
- submission_id = row["submission_id"]
231
- submission_repo = row["submission_repo"]
232
- self.run_local(team_id, submission_id, submission_repo)
233
- elif self.competition_type == "script":
234
- for _, row in pending_submissions.iterrows():
235
- team_id = row["team_id"]
236
- submission_id = row["submission_id"]
237
- submission_repo = row["submission_repo"]
238
- space_id = row["space_id"]
239
- try:
240
- self.create_space(team_id, submission_id, submission_repo, space_id)
241
- except Exception as e:
242
- logger.error(
243
- f"Failed to create space for {team_id} {submission_id} {submission_repo} {space_id}: {e}"
244
- )
245
- # mark submission as failed
246
- self.mark_submission_failed(team_id, submission_id)
247
- logger.error(f"Marked submission {submission_id} as failed.")
248
- continue
249
- time.sleep(5)
 
4
  import os
5
  import time
6
  import uuid
7
+ import shutil
8
  from dataclasses import dataclass
9
+ from typing import List, Dict, Any
10
+ from collections import defaultdict
11
 
12
  import pandas as pd
13
  from huggingface_hub import HfApi, hf_hub_download, snapshot_download
 
15
 
16
  from competitions.enums import SubmissionStatus
17
  from competitions.info import CompetitionInfo
18
+ from competitions.utils import user_token_api, space_cleaner
 
 
 
 
 
 
 
 
 
 
19
 
20
 
21
  @dataclass
 
36
  self.dataset = self.competition_info.dataset
37
  self.submission_filenames = self.competition_info.submission_filenames
38
 
39
+ def _get_all_submissions(self) -> List[Dict[str, Any]]:
40
  submission_jsons = snapshot_download(
41
  repo_id=self.competition_id,
42
  allow_patterns="submission_info/*.json",
 
44
  repo_type="dataset",
45
  )
46
  submission_jsons = glob.glob(os.path.join(submission_jsons, "submission_info/*.json"))
47
+ all_submissions = []
48
+ for _json_path in submission_jsons:
49
+ with open(_json_path, "r", encoding="utf-8") as f:
50
+ _json = json.load(f)
51
  team_id = _json["id"]
52
  for sub in _json["submissions"]:
53
+ all_submissions.append(
54
+ {
55
+ "team_id": team_id,
56
+ "submission_id": sub["submission_id"],
57
+ "datetime": sub["datetime"],
58
+ "status": sub["status"],
59
+ "submission_repo": sub["submission_repo"],
60
+ "space_id": sub["space_id"],
61
+ "server_url": sub["server_url"],
62
+ "hardware": sub["hardware"],
63
+ }
64
+ )
65
+ return all_submissions
66
+
67
+
68
+ def _get_pending_subs(self, submissions: List[Dict[str, Any]]) -> pd.DataFrame:
69
+ pending_submissions = []
70
+ for sub in submissions:
71
+ if sub["status"] == SubmissionStatus.PENDING.value:
72
+ pending_submissions.append(sub)
73
  if len(pending_submissions) == 0:
74
  return None
75
  logger.info(f"Found {len(pending_submissions)} pending submissions.")
 
79
  pending_submissions = pending_submissions.reset_index(drop=True)
80
  return pending_submissions
81
 
82
+ def _get_server_active_count(self, submissions: List[Dict[str, Any]]) -> Dict[str, int]:
83
+ server_active_count = defaultdict(int)
84
+ for sub in submissions:
85
+ if sub["status"] in {SubmissionStatus.PROCESSING.value, SubmissionStatus.QUEUED.value}:
86
+ server_active_count[sub["server_url"]] += 1
87
+ return server_active_count
88
+
89
  def _queue_submission(self, team_id, submission_id):
90
  team_fname = hf_hub_download(
91
  repo_id=self.competition_id,
 
138
  repo_type="dataset",
139
  )
140
 
141
+ def _create_readme(self, project_name: str) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  _readme = "---\n"
143
  _readme += f"title: {project_name}\n"
144
  _readme += "emoji: 🚀\n"
 
147
  _readme += "sdk: docker\n"
148
  _readme += "pinned: false\n"
149
  _readme += "---\n"
 
150
  return _readme
151
 
152
+ def create_space(self, team_id, submission_id, submission_repo, space_id, server_url, hardware):
 
 
 
153
  user_token = user_token_api.get(team_id)
154
 
155
  api = HfApi(token=self.token)
156
  params = {
157
+ "space_id": space_id,
158
+ "client_space_id": space_id,
159
  "competition_id": self.competition_id,
 
 
 
160
  "team_id": team_id,
161
  "submission_id": submission_id,
 
 
 
162
  "output_path": self.output_path,
163
  "submission_repo": submission_repo,
164
  "time_limit": self.time_limit,
165
  "dataset": self.dataset,
166
  "submission_filenames": self.submission_filenames,
167
  }
168
+ token_info_json = json.dumps(params, indent=4)
169
+ token_info_json_bytes = token_info_json.encode("utf-8")
170
+ token_info_json_buffer = io.BytesIO(token_info_json_bytes)
171
+
172
+ api = HfApi(token=self.token)
173
+ client_token = uuid.uuid4().hex + uuid.uuid4().hex
174
 
 
 
 
 
175
  api.upload_file(
176
+ path_or_fileobj=token_info_json_buffer,
177
+ path_in_repo=f"token_data_info/{client_token}.json",
178
+ repo_id=self.competition_id,
179
+ repo_type="dataset",
180
  )
181
+ api.create_repo(
 
 
182
  repo_id=space_id,
183
  repo_type="space",
184
+ space_sdk="docker",
185
+ space_hardware=hardware,
186
+ private=True,
187
  )
188
 
189
+ api.add_space_secret(repo_id=space_id, key="HUGSIM_API_TOKEN", value=client_token)
190
+ api.add_space_secret(repo_id=space_id, key="HUGSIM_SERVER_HOST", value=server_url)
191
+ client_code_local_dir = f"/tmp/data/client_repo/{space_id}"
192
+ client_commits = api.list_repo_commits(submission_repo, repo_type="model")
193
  api.snapshot_download(
194
  repo_id=submission_repo,
195
  repo_type="model",
196
+ revision=client_commits[0].commit_id,
197
  token=user_token,
198
+ local_dir=client_code_local_dir,
199
  allow_patterns=["*"],
200
  )
201
+ with open(f"{client_code_local_dir}/README.md", "w", encoding="utf-8") as f:
202
+ f.write(self._create_readme(space_id))
203
+ try:
204
+ api.upload_folder(
205
+ repo_id=space_id,
206
+ repo_type="space",
207
+ folder_path=client_code_local_dir,
208
+ )
209
+ finally:
210
+ shutil.rmtree(client_code_local_dir, ignore_errors=True)
211
  self._queue_submission(team_id, submission_id)
212
 
213
  def run(self):
214
+ cur = 0
215
  while True:
216
+ time.sleep(5)
217
+ if cur == 10000:
218
+ cur = 0
219
+ cur += 1
220
+ all_submissions = self._get_all_submissions()
221
+
222
+ # Clean up spaces every 100 iterations
223
+ if cur % 100 == 0:
224
+ for space in all_submissions:
225
+ if space["status"] == SubmissionStatus.QUEUED.value:
226
+ space_cleaner.clean_space(
227
+ space["space_id"],
228
+ space["team_id"],
229
+ space["submission_id"],
230
+ )
231
+
232
+ pending_submissions = self._get_pending_subs(all_submissions)
233
  if pending_submissions is None:
 
234
  continue
235
+ first_pending_sub = pending_submissions.iloc[0]
236
+ server_active_count = self._get_server_active_count(all_submissions)
237
+
238
+ if server_active_count[first_pending_sub["server_url"]] >= 1:
239
+ continue
240
+ try:
241
+ self.create_space(first_pending_sub["team_id"], first_pending_sub["submission_id"], first_pending_sub["submission_repo"], first_pending_sub["space_id"], first_pending_sub["server_url"], first_pending_sub["hardware"])
242
+ except Exception as e:
243
+ logger.error(
244
+ f"Failed to create space for {first_pending_sub['submission_id']}: {e}"
245
+ )
246
+ # mark submission as failed
247
+ self.mark_submission_failed(first_pending_sub['team_id'], first_pending_sub['submission_id'])
248
+ try:
249
+ space_cleaner.delete_space(first_pending_sub["space_id"])
250
+ except Exception as e:
251
+ logger.error(f"Failed to delete space {first_pending_sub['space_id']}: {e}")
252
+ logger.error(f"Marked submission {first_pending_sub['submission_id']} as failed.")
253
+ continue
 
 
 
 
competitions/submissions.py CHANGED
@@ -5,12 +5,12 @@ from dataclasses import dataclass
5
  from datetime import datetime
6
 
7
  import pandas as pd
8
- from huggingface_hub import HfApi, hf_hub_download
9
  from huggingface_hub.utils._errors import EntryNotFoundError
10
 
11
  from competitions.enums import SubmissionStatus
12
  from competitions.errors import AuthenticationError, PastDeadlineError, SubmissionError, SubmissionLimitError
13
- from competitions.utils import token_information, team_file_api, user_token_api
14
 
15
 
16
  @dataclass
@@ -86,6 +86,8 @@ class Submissions:
86
  "selected": False,
87
  "public_score": {},
88
  "private_score": {},
 
 
89
  }
90
  )
91
  # count the number of times user has submitted today
@@ -179,6 +181,15 @@ class Submissions:
179
  return self._get_team_subs(team_id, private=private)
180
 
181
  def _create_submission(self, team_id: str):
 
 
 
 
 
 
 
 
 
182
  team_submission_info = {}
183
  team_submission_info["id"] = team_id
184
  team_submission_info["submissions"] = []
@@ -186,7 +197,6 @@ class Submissions:
186
  team_submission_info_json_bytes = team_submission_info_json.encode("utf-8")
187
  team_submission_info_json_buffer = io.BytesIO(team_submission_info_json_bytes)
188
 
189
- api = HfApi(token=self.token)
190
  api.upload_file(
191
  path_or_fileobj=team_submission_info_json_buffer,
192
  path_in_repo=f"submission_info/{team_id}.json",
@@ -231,31 +241,11 @@ class Submissions:
231
  return self.submission_limit - submissions_made
232
 
233
  user_api = HfApi(token=user_token)
234
- # submission_id is the sha of the submitted model repo + "__" + submission_id
235
  submission_id = user_api.model_info(repo_id=uploaded_file).sha + "__" + submission_id
236
  competition_organizer = self.competition_id.split("/")[0]
237
  space_id = f"{competition_organizer}/comp-{submission_id}"
238
- server_space_id = space_id + "-server"
239
- client_space_id = space_id + "-client"
240
 
241
- api = HfApi(token=self.token)
242
- api.create_repo(
243
- repo_id=server_space_id,
244
- repo_type="space",
245
- space_sdk="docker",
246
- space_hardware=self.hardware,
247
- private=False,
248
- )
249
- api.create_repo(
250
- repo_id=client_space_id,
251
- repo_type="space",
252
- space_sdk="docker",
253
- space_hardware=self.hardware,
254
- private=True,
255
- )
256
  user_token_api.put(team_id, user_token)
257
-
258
- # api.add_space_secret(repo_id=space_id, key="USER_TOKEN", value=user_token)
259
  submissions_made = self._increment_submissions(
260
  team_id=team_id,
261
  user_id=user_id,
 
5
  from datetime import datetime
6
 
7
  import pandas as pd
8
+ from huggingface_hub import HfApi, hf_hub_download, SpaceHardware
9
  from huggingface_hub.utils._errors import EntryNotFoundError
10
 
11
  from competitions.enums import SubmissionStatus
12
  from competitions.errors import AuthenticationError, PastDeadlineError, SubmissionError, SubmissionLimitError
13
+ from competitions.utils import token_information, team_file_api, user_token_api, server_manager
14
 
15
 
16
  @dataclass
 
86
  "selected": False,
87
  "public_score": {},
88
  "private_score": {},
89
+ "server_url": server_manager.get_next_server(),
90
+ "hardware": self.hardware,
91
  }
92
  )
93
  # count the number of times user has submitted today
 
181
  return self._get_team_subs(team_id, private=private)
182
 
183
  def _create_submission(self, team_id: str):
184
+ api = HfApi(token=self.token)
185
+
186
+ if api.file_exists(
187
+ repo_id=self.competition_id,
188
+ filename=f"submission_info/{team_id}.json",
189
+ repo_type="dataset",
190
+ ):
191
+ return
192
+
193
  team_submission_info = {}
194
  team_submission_info["id"] = team_id
195
  team_submission_info["submissions"] = []
 
197
  team_submission_info_json_bytes = team_submission_info_json.encode("utf-8")
198
  team_submission_info_json_buffer = io.BytesIO(team_submission_info_json_bytes)
199
 
 
200
  api.upload_file(
201
  path_or_fileobj=team_submission_info_json_buffer,
202
  path_in_repo=f"submission_info/{team_id}.json",
 
241
  return self.submission_limit - submissions_made
242
 
243
  user_api = HfApi(token=user_token)
 
244
  submission_id = user_api.model_info(repo_id=uploaded_file).sha + "__" + submission_id
245
  competition_organizer = self.competition_id.split("/")[0]
246
  space_id = f"{competition_organizer}/comp-{submission_id}"
 
 
247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  user_token_api.put(team_id, user_token)
 
 
249
  submissions_made = self._increment_submissions(
250
  team_id=team_id,
251
  user_id=user_id,
competitions/utils.py CHANGED
@@ -7,13 +7,15 @@ import traceback
7
  import threading
8
  import uuid
9
  import base64
10
- from typing import Optional, Dict, Any
 
11
 
12
  import requests
13
  from fastapi import Request
14
  from huggingface_hub import HfApi, hf_hub_download
15
  from loguru import logger
16
  from cryptography.hazmat.primitives.ciphers.aead import AESGCM
 
17
 
18
  from competitions.enums import SubmissionStatus
19
  from competitions.params import EvalParams
@@ -521,3 +523,109 @@ user_token_api = UserTokenApi(
521
  os.environ.get("USER_TOKEN_KEY_BASE64"),
522
  os.environ.get("COMPETITION_ID")
523
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  import threading
8
  import uuid
9
  import base64
10
+ from typing import Optional, Dict, Any, List
11
+ from collections import defaultdict
12
 
13
  import requests
14
  from fastapi import Request
15
  from huggingface_hub import HfApi, hf_hub_download
16
  from loguru import logger
17
  from cryptography.hazmat.primitives.ciphers.aead import AESGCM
18
+ from huggingface_hub import SpaceStage
19
 
20
  from competitions.enums import SubmissionStatus
21
  from competitions.params import EvalParams
 
523
  os.environ.get("USER_TOKEN_KEY_BASE64"),
524
  os.environ.get("COMPETITION_ID")
525
  )
526
+
527
+
528
+ class ServerManager:
529
+ def __init__(self, server_url_list: List[str]):
530
+ self.server_url_list = server_url_list
531
+ self._cur_index = 0
532
+ self._lock = threading.Lock()
533
+
534
+ def get_next_server(self) -> str:
535
+ with self._lock:
536
+ server_url = self.server_url_list[self._cur_index]
537
+ self._cur_index = (self._cur_index + 1) % len(self.server_url_list)
538
+ return server_url
539
+
540
+
541
+ server_manager = ServerManager(["https://xdimlab-hugsim-web-server-0.hf.space"])
542
+
543
+
544
+ class SubmissionApi:
545
+ def __init__(self, hf_token: str, competition_id: str):
546
+ self.hf_token = hf_token
547
+ self.competition_id = competition_id
548
+ self.api = HfApi(token=hf_token)
549
+
550
+ def download_submission_info(self, team_id: str) -> Dict[str, Any]:
551
+ """
552
+ Download the submission info from Hugging Face Hub.
553
+ Args:
554
+ team_id (str): The team ID.
555
+ Returns:
556
+ Dict[str, Any]: The submission info.
557
+ """
558
+ submission_info_path = self.api.hf_hub_download(
559
+ repo_id=self.competition_id,
560
+ filename=f"submission_info/{team_id}.json",
561
+ repo_type="dataset",
562
+ )
563
+ with open(submission_info_path, 'r') as f:
564
+ submission_info = json.load(f)
565
+
566
+ return submission_info
567
+
568
+ def upload_submission_info(self, team_id: str, user_submission_info: Dict[str, Any]):
569
+ user_submission_info_json = json.dumps(user_submission_info, indent=4)
570
+ user_submission_info_json_bytes = user_submission_info_json.encode("utf-8")
571
+ user_submission_info_json_buffer = io.BytesIO(user_submission_info_json_bytes)
572
+ self.api.upload_file(
573
+ path_or_fileobj=user_submission_info_json_buffer,
574
+ path_in_repo=f"submission_info/{team_id}.json",
575
+ repo_id=self.competition_id,
576
+ repo_type="dataset",
577
+ )
578
+
579
+ def update_submission_status(self, team_id: str, submission_id: str, status: int):
580
+ user_submission_info = self.download_submission_info(team_id)
581
+ for submission in user_submission_info["submissions"]:
582
+ if submission["submission_id"] == submission_id:
583
+ submission["status"] = status
584
+ break
585
+ self.upload_submission_info(team_id, user_submission_info)
586
+
587
+
588
+ submission_api = SubmissionApi(
589
+ hf_token=os.environ.get("HF_TOKEN", None),
590
+ competition_id=os.environ.get("COMPETITION_ID")
591
+ )
592
+
593
+
594
+ class SpaceCleaner:
595
+ def __init__(self, hf_token: str):
596
+ self.hf_token = hf_token
597
+ self.api = HfApi(token=hf_token)
598
+ self.space_build_error_count = defaultdict(int)
599
+
600
+ def delete_space(self, space_id: str):
601
+ """Delete a space by its ID."""
602
+ self.api.delete_repo(repo_id=space_id, repo_type="space")
603
+
604
+ def clean_space(self, space_id: str, team_id: str, submission_id: str):
605
+ space_info = self.api.space_info(repo_id=space_id)
606
+ if space_info.runtime.stage == SpaceStage.BUILD_ERROR:
607
+ self.space_build_error_count[space_id] += 1
608
+ if self.space_build_error_count[space_id] >= 3:
609
+ self.delete_space(space_id)
610
+ submission_api.update_submission_status(
611
+ team_id=team_id,
612
+ submission_id=submission_id,
613
+ status=SubmissionStatus.FAILED.value
614
+ )
615
+ else:
616
+ self.api.restart_space(repo_id=space_id)
617
+ return
618
+
619
+ if space_info.runtime.stage == SpaceStage.RUNTIME_ERROR:
620
+ self.delete_space(space_id)
621
+ submission_api.update_submission_status(
622
+ team_id=team_id,
623
+ submission_id=submission_id,
624
+ status=SubmissionStatus.FAILED.value
625
+ )
626
+ return
627
+
628
+
629
+ space_cleaner = SpaceCleaner(
630
+ os.environ.get("HF_TOKEN", None),
631
+ )