Spaces:
Running
Running
Sean Yu commited on
Commit ·
1622ebb
1
Parent(s): 2850a82
Derive submission model name from verified repository URL
Browse files- README.md +2 -0
- static/submit.html +0 -1
- submission_backend.py +1 -3
- tests/test_submissions.py +11 -1
README.md
CHANGED
|
@@ -58,3 +58,5 @@ Limits: ZIP ≤8 GiB, expanded contents ≤24 GiB, each member ≤128 MiB, ≤40
|
|
| 58 |
Local development of storage only: set `INTREAL_LOCAL_STORAGE=1` and `SUBMISSIONS_DIR` to a scratch directory. This never enables a fake sign-in and is ignored in a Space. Production OAuth must be tested on the Space before enabling real submissions. Use `python -m pytest tests` for backend checks.
|
| 59 |
|
| 60 |
Storage admission checks account for the configured 32 GiB intake quota, existing files, pending reservations, and filesystem free space before receiving a ZIP. Disk-full/quota errors during upload are rejected and incomplete files removed. The form disables submission when full and directs users to `shaohanyu628@gmail.com` to request storage management and retry later. Filesystem capacity is not a direct measurement of the HF account-wide Hub storage quota; remote quota write failures are also handled.
|
|
|
|
|
|
|
|
|
| 58 |
Local development of storage only: set `INTREAL_LOCAL_STORAGE=1` and `SUBMISSIONS_DIR` to a scratch directory. This never enables a fake sign-in and is ignored in a Space. Production OAuth must be tested on the Space before enabling real submissions. Use `python -m pytest tests` for backend checks.
|
| 59 |
|
| 60 |
Storage admission checks account for the configured 32 GiB intake quota, existing files, pending reservations, and filesystem free space before receiving a ZIP. Disk-full/quota errors during upload are rejected and incomplete files removed. The form disables submission when full and directs users to `shaohanyu628@gmail.com` to request storage management and retry later. Filesystem capacity is not a direct measurement of the HF account-wide Hub storage quota; remote quota write failures are also handled.
|
| 61 |
+
|
| 62 |
+
Submission model names are derived server-side from the verified HF repository URL; submitters do not enter a separate name.
|
static/submit.html
CHANGED
|
@@ -20,7 +20,6 @@
|
|
| 20 |
<form id="submission-form">
|
| 21 |
<fieldset id="submission-fields" disabled>
|
| 22 |
<div class="form-grid">
|
| 23 |
-
<label class="field full"><span>Model name *</span><input name="model_name" required maxlength="160" placeholder="Your model name"></label>
|
| 24 |
<label class="field full"><span>Hugging Face model URL *</span><input name="model_url" type="url" required maxlength="240" placeholder="https://huggingface.co/your-username/model-name"><small>Public, ungated, with weights and an open license. The owner must match your signed-in account.</small></label>
|
| 25 |
<label class="field"><span>Hugging Face username *</span><input id="hf-username" name="username" readonly required autocomplete="off"><small>Automatically verified through Hugging Face sign-in.</small></label>
|
| 26 |
<label class="field"><span>Contact email *</span><input name="email" type="email" required maxlength="254" autocomplete="email" placeholder="you@example.com"></label>
|
|
|
|
| 20 |
<form id="submission-form">
|
| 21 |
<fieldset id="submission-fields" disabled>
|
| 22 |
<div class="form-grid">
|
|
|
|
| 23 |
<label class="field full"><span>Hugging Face model URL *</span><input name="model_url" type="url" required maxlength="240" placeholder="https://huggingface.co/your-username/model-name"><small>Public, ungated, with weights and an open license. The owner must match your signed-in account.</small></label>
|
| 24 |
<label class="field"><span>Hugging Face username *</span><input id="hf-username" name="username" readonly required autocomplete="off"><small>Automatically verified through Hugging Face sign-in.</small></label>
|
| 25 |
<label class="field"><span>Contact email *</span><input name="email" type="email" required maxlength="254" autocomplete="email" placeholder="you@example.com"></label>
|
submission_backend.py
CHANGED
|
@@ -302,13 +302,11 @@ def install_submissions(app):
|
|
| 302 |
raise HTTPException(413, "Form data is too large.")
|
| 303 |
try:
|
| 304 |
body = json.loads(body_bytes)
|
| 305 |
-
name = body.get("model_name", "").strip()
|
| 306 |
-
if not 1 <= len(name) <= 160:
|
| 307 |
-
raise ValueError("Model name is required (maximum 160 characters).")
|
| 308 |
email = validate_email(body.get("email", ""), check_deliverability=False).normalized
|
| 309 |
if body.get("username") != user["username"]:
|
| 310 |
raise ValueError("Submitted HF username must match your signed-in account.")
|
| 311 |
repo_id = model_id_from_url(body.get("model_url", ""), user["username"])
|
|
|
|
| 312 |
filename = body.get("filename", "")
|
| 313 |
if not filename.lower().endswith(".zip") or "/" in filename or "\\" in filename or len(filename) > 200:
|
| 314 |
raise ValueError("Choose a .zip archive.")
|
|
|
|
| 302 |
raise HTTPException(413, "Form data is too large.")
|
| 303 |
try:
|
| 304 |
body = json.loads(body_bytes)
|
|
|
|
|
|
|
|
|
|
| 305 |
email = validate_email(body.get("email", ""), check_deliverability=False).normalized
|
| 306 |
if body.get("username") != user["username"]:
|
| 307 |
raise ValueError("Submitted HF username must match your signed-in account.")
|
| 308 |
repo_id = model_id_from_url(body.get("model_url", ""), user["username"])
|
| 309 |
+
name = repo_id.split("/", 1)[1]
|
| 310 |
filename = body.get("filename", "")
|
| 311 |
if not filename.lower().endswith(".zip") or "/" in filename or "\\" in filename or len(filename) > 200:
|
| 312 |
raise ValueError("Choose a .zip archive.")
|
tests/test_submissions.py
CHANGED
|
@@ -116,7 +116,7 @@ def service(tmp_path, monkeypatch):
|
|
| 116 |
|
| 117 |
|
| 118 |
def form(data):
|
| 119 |
-
return {'
|
| 120 |
|
| 121 |
|
| 122 |
def sign_in(client, username='alice'):
|
|
@@ -289,3 +289,13 @@ def test_disk_fills_during_upload_cleans_partial(service, monkeypatch):
|
|
| 289 |
assert 'shaohanyu628@gmail.com' in response.json()['detail']
|
| 290 |
assert not (root/sid/'outputs.zip.part').exists()
|
| 291 |
assert not (root/sid/'outputs.zip').exists()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
|
| 118 |
def form(data):
|
| 119 |
+
return {'model_url': 'https://huggingface.co/alice/model', 'username': 'alice', 'email': 'alice@example.com', 'filename': 'outputs.zip', 'size': len(data)}
|
| 120 |
|
| 121 |
|
| 122 |
def sign_in(client, username='alice'):
|
|
|
|
| 289 |
assert 'shaohanyu628@gmail.com' in response.json()['detail']
|
| 290 |
assert not (root/sid/'outputs.zip.part').exists()
|
| 291 |
assert not (root/sid/'outputs.zip').exists()
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
def test_model_name_is_derived_and_not_user_controlled(service):
|
| 295 |
+
client, root = service
|
| 296 |
+
sign_in(client)
|
| 297 |
+
payload = form(archive_bytes()) | {'model_name': 'forged display name'}
|
| 298 |
+
response = client.post('/api/submissions', json=payload, headers={'X-CSRF-Token':'csrf-test'})
|
| 299 |
+
assert response.status_code == 200
|
| 300 |
+
record = json.loads((root / response.json()['id'] / 'metadata.json').read_text())
|
| 301 |
+
assert record['model_name'] == 'model'
|