Skip to content

Commit bcbd7b3

Browse files
authored
Add submission api for web (#334)
* add api
1 parent 2383854 commit bcbd7b3

16 files changed

Lines changed: 1005 additions & 121 deletions

.github/workflows/lint.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ jobs:
1515
runs-on: ubuntu-latest
1616
steps:
1717
- uses: actions/checkout@v3
18-
18+
1919
- name: Set up Python
2020
uses: actions/setup-python@v4
2121
with:
2222
python-version: '3.10'
23-
23+
2424
- name: Install dependencies
2525
run: |
2626
pip install ruff
27-
27+
2828
- name: Run Ruff check
2929
run: |
30-
ruff check . --exclude examples/
30+
ruff check . --exclude examples/ --line-length 120

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,4 @@ markers = [
5858

5959
[tool.ruff]
6060
line-length = 120
61-
target-version = "py310"
61+
target-version = "py310"

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ better_profanity
1111
PyYAML
1212
fastapi[all]
1313
uvicorn
14-
jinja2
14+
jinja2
15+
pytest-asyncio==1.1.0

src/kernelbot/api/api_utils.py

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
1+
from typing import Any
2+
13
import requests
2-
from fastapi import HTTPException
4+
from fastapi import HTTPException, UploadFile
35

46
from kernelbot.env import env
57
from libkernelbot.backend import KernelBackend
68
from libkernelbot.consts import SubmissionMode
9+
from libkernelbot.leaderboard_db import LeaderboardDB
710
from libkernelbot.report import (
811
Log,
912
MultiProgressReporter,
1013
RunProgressReporter,
1114
RunResultReport,
1215
Text,
1316
)
14-
from libkernelbot.submission import SubmissionRequest, prepare_submission
17+
from libkernelbot.submission import (
18+
SubmissionRequest,
19+
prepare_submission,
20+
)
1521

1622

1723
async def _handle_discord_oauth(code: str, redirect_uri: str) -> tuple[str, str]:
@@ -183,3 +189,103 @@ async def display_report(self, title: str, report: RunResultReport):
183189
elif isinstance(part, Log):
184190
self.long_report += f"\n\n## {part.header}:\n"
185191
self.long_report += f"```\n{part.content}```"
192+
# ruff: noqa: C901
193+
async def to_submit_info(
194+
user_info: Any,
195+
submission_mode: str,
196+
file: UploadFile,
197+
leaderboard_name: str,
198+
gpu_type: str,
199+
db_context: LeaderboardDB,
200+
) -> tuple[SubmissionRequest, SubmissionMode]: # noqa: C901
201+
user_name = user_info["user_name"]
202+
user_id = user_info["user_id"]
203+
204+
try:
205+
submission_mode_enum: SubmissionMode = SubmissionMode(
206+
submission_mode.lower()
207+
)
208+
except ValueError:
209+
raise HTTPException(
210+
status_code=400,
211+
detail=f"Invalid submission mode value: '{submission_mode}'",
212+
) from None
213+
214+
if submission_mode_enum in [SubmissionMode.PROFILE]:
215+
raise HTTPException(
216+
status_code=400,
217+
detail="Profile submissions are not currently supported via API",
218+
)
219+
220+
allowed_modes = [
221+
SubmissionMode.TEST,
222+
SubmissionMode.BENCHMARK,
223+
SubmissionMode.LEADERBOARD,
224+
]
225+
if submission_mode_enum not in allowed_modes:
226+
raise HTTPException(
227+
status_code=400,
228+
detail=f"Submission mode '{submission_mode}' is not supported for this endpoint",
229+
)
230+
231+
try:
232+
with db_context as db:
233+
leaderboard_item = db.get_leaderboard(leaderboard_name)
234+
gpus = leaderboard_item.get("gpu_types", [])
235+
if gpu_type not in gpus:
236+
supported_gpus = ", ".join(gpus) if gpus else "None"
237+
raise HTTPException(
238+
status_code=400,
239+
detail=f"GPU type '{gpu_type}' is not supported for "
240+
f"leaderboard '{leaderboard_name}'. Supported GPUs: {supported_gpus}",
241+
)
242+
except HTTPException:
243+
raise
244+
except Exception as e:
245+
raise HTTPException(
246+
status_code=500,
247+
detail=f"Internal server error while validating leaderboard/GPU: {e}",
248+
) from e
249+
250+
try:
251+
submission_content = await file.read()
252+
if not submission_content:
253+
raise HTTPException(
254+
status_code=400,
255+
detail="Empty file submitted. Please provide a file with code.",
256+
)
257+
if len(submission_content) > 1_000_000:
258+
raise HTTPException(
259+
status_code=413,
260+
detail="Submission file is too large (limit: 1MB).",
261+
)
262+
263+
except HTTPException:
264+
raise
265+
except Exception as e:
266+
raise HTTPException(
267+
status_code=400, detail=f"Error reading submission file: {e}"
268+
) from e
269+
270+
try:
271+
submission_code = submission_content.decode("utf-8")
272+
submission_request = SubmissionRequest(
273+
code=submission_code,
274+
file_name=file.filename or "submission.py",
275+
user_id=user_id,
276+
user_name=user_name,
277+
gpus=[gpu_type],
278+
leaderboard=leaderboard_name,
279+
)
280+
except UnicodeDecodeError:
281+
raise HTTPException(
282+
status_code=400,
283+
detail="Failed to decode submission file content as UTF-8.",
284+
) from None
285+
except Exception as e:
286+
raise HTTPException(
287+
status_code=500,
288+
detail=f"Internal server error creating submission request: {e}",
289+
) from e
290+
291+
return submission_request, submission_mode_enum

0 commit comments

Comments
 (0)