mwatson-nvidia commited on
Commit
70b7446
·
1 Parent(s): 21996c1

change approach to docker

Browse files
Files changed (4) hide show
  1. Dockerfile +10 -2
  2. README.md +5 -5
  3. app.py +163 -9
  4. requirements.txt +3 -1
Dockerfile CHANGED
@@ -1,2 +1,10 @@
1
- FROM huggingface/competitions:latest
2
- CMD uvicorn competitions.app:app --host 0.0.0.0 --port 7860 --workers 1
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY . .
9
+
10
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "1"]
README.md CHANGED
@@ -2,9 +2,8 @@
2
  title: AlpaSim Challenge 2026
3
  colorFrom: green
4
  colorTo: blue
5
- sdk: gradio
6
- python_version: 3.11
7
- app_file: app.py
8
  hf_oauth: true
9
  hf_oauth_scopes:
10
  - email
@@ -12,7 +11,7 @@ hf_oauth_scopes:
12
 
13
  # AlpaSim Challenge 2026
14
 
15
- This Gradio Space hosts:
16
 
17
  - team registration
18
  - signed-in participant registration status
@@ -63,4 +62,5 @@ Each track will award two NVIDIA DGX prizes:
63
  - PAI-AV track: two NVIDIA DGX prizes for the top two teams.
64
  - nuPlan track: two NVIDIA DGX prizes for the top two teams.
65
 
66
- The app disables Gradio SSR with `demo.launch(ssr_mode=False)`.
 
 
2
  title: AlpaSim Challenge 2026
3
  colorFrom: green
4
  colorTo: blue
5
+ sdk: docker
6
+ app_port: 7860
 
7
  hf_oauth: true
8
  hf_oauth_scopes:
9
  - email
 
11
 
12
  # AlpaSim Challenge 2026
13
 
14
+ This Docker Space serves a static landing page with an embedded Gradio app for:
15
 
16
  - team registration
17
  - signed-in participant registration status
 
62
  - PAI-AV track: two NVIDIA DGX prizes for the top two teams.
63
  - nuPlan track: two NVIDIA DGX prizes for the top two teams.
64
 
65
+ The app serves the landing page from FastAPI at `/` and mounts Gradio at
66
+ `/gradio`.
app.py CHANGED
@@ -8,6 +8,9 @@ from pathlib import Path
8
  from typing import Any
9
 
10
  import gradio as gr
 
 
 
11
 
12
 
13
  API_BASE_URL = os.environ.get(
@@ -39,11 +42,12 @@ BANNER_HTML = f"""
39
  <a href="#timeline">Timeline</a>
40
  <a href="#prizes">Prizes</a>
41
  <a href="#organizers">Organizers</a>
 
42
  </div>
43
  </div>
44
  <header class="alpasim-hero">
45
  <video autoplay muted loop playsinline preload="auto" aria-label="AlpaSim E2E Closed Loop Challenge 2026 banner">
46
- <source src="/gradio_api/file={BANNER_PATH}" type="video/mp4" />
47
  </video>
48
  <div class="hero-overlay">
49
  <div class="hero-overlay-inner">
@@ -199,7 +203,28 @@ CHALLENGE_OVERVIEW_HTML = """
199
  </section>
200
  </main>
201
  """
 
 
 
 
 
 
 
 
 
 
202
  BANNER_CSS = """
 
 
 
 
 
 
 
 
 
 
 
203
  :root,
204
  .gradio-container {
205
  --bg: #ffffff;
@@ -238,7 +263,7 @@ BANNER_CSS = """
238
  justify-content: space-between;
239
  gap: 20px;
240
  padding: 12px 24px;
241
- margin: -16px -16px 0;
242
  background: rgba(255, 255, 255, 0.92);
243
  backdrop-filter: blur(8px);
244
  border-bottom: 1px solid var(--border);
@@ -272,7 +297,7 @@ BANNER_CSS = """
272
  .alpasim-hero {
273
  position: relative;
274
  overflow: hidden;
275
- margin: 0 -16px;
276
  background: #000000;
277
  border-bottom: 1px solid var(--border);
278
  }
@@ -331,6 +356,7 @@ BANNER_CSS = """
331
  .challenge-info section {
332
  padding: 48px 0;
333
  border-bottom: 1px solid var(--border);
 
334
  }
335
 
336
  .challenge-info section:last-child {
@@ -496,6 +522,37 @@ BANNER_CSS = """
496
  margin: 0 auto;
497
  }
498
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
  @media (max-width: 720px) {
500
  .topline {
501
  align-items: flex-start;
@@ -530,11 +587,87 @@ BANNER_CSS = """
530
  padding: 0 18px;
531
  }
532
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
533
  .identity-strip {
534
  padding: 0 18px 18px;
535
  }
536
  }
537
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
538
 
539
 
540
  def split_multivalue(value: str) -> list[str]:
@@ -563,7 +696,7 @@ def current_identity(profile: gr.OAuthProfile | None) -> str:
563
  "To register a team, connect this Space to your Hugging Face account. "
564
  "If you are already signed in on huggingface.co, this just authorizes "
565
  "the Space to read your Hugging Face profile. "
566
- "[Connect Hugging Face account](/login/huggingface?_target_url=/)"
567
  )
568
 
569
  username = profile_value(profile, "preferred_username", "name")
@@ -574,7 +707,7 @@ def current_identity(profile: gr.OAuthProfile | None) -> str:
574
  parts.append(f"Stable HF user ID: `{user_id}`.")
575
  if email:
576
  parts.append(f"HF email: `{email}`.")
577
- parts.append("[Disconnect](/logout?_target_url=/).")
578
  return " ".join(parts)
579
 
580
 
@@ -1188,11 +1321,9 @@ def add_oauth_route_marker() -> None:
1188
  with gr.Blocks(
1189
  title="AlpaSim E2E Closed Loop Challenge 2026",
1190
  theme=APP_THEME,
1191
- css=BANNER_CSS,
1192
  ) as demo:
1193
  add_oauth_route_marker()
1194
- gr.HTML(BANNER_HTML)
1195
- gr.HTML(CHALLENGE_OVERVIEW_HTML)
1196
  with gr.Group(elem_classes=["identity-strip"]):
1197
  identity = gr.Markdown()
1198
 
@@ -1449,5 +1580,28 @@ with gr.Blocks(
1449
  ],
1450
  )
1451
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1452
  if __name__ == "__main__":
1453
- demo.launch(ssr_mode=False, allowed_paths=[str(BANNER_FILE)])
 
 
 
 
 
 
 
8
  from typing import Any
9
 
10
  import gradio as gr
11
+ from fastapi import FastAPI
12
+ from fastapi.responses import HTMLResponse
13
+ from fastapi.staticfiles import StaticFiles
14
 
15
 
16
  API_BASE_URL = os.environ.get(
 
42
  <a href="#timeline">Timeline</a>
43
  <a href="#prizes">Prizes</a>
44
  <a href="#organizers">Organizers</a>
45
+ <a href="#registration">Registration</a>
46
  </div>
47
  </div>
48
  <header class="alpasim-hero">
49
  <video autoplay muted loop playsinline preload="auto" aria-label="AlpaSim E2E Closed Loop Challenge 2026 banner">
50
+ <source src="/{BANNER_PATH}" type="video/mp4" />
51
  </video>
52
  <div class="hero-overlay">
53
  <div class="hero-overlay-inner">
 
203
  </section>
204
  </main>
205
  """
206
+ APP_EMBED_HTML = """
207
+ <section id="registration" class="challenge-app">
208
+ <h2><span>07</span> Registration</h2>
209
+ <iframe
210
+ class="gradio-frame"
211
+ src="/gradio/"
212
+ title="AlpaSim registration, leaderboard, and review app"
213
+ ></iframe>
214
+ </section>
215
+ """
216
  BANNER_CSS = """
217
+ html {
218
+ scroll-behavior: smooth;
219
+ }
220
+
221
+ body {
222
+ margin: 0;
223
+ background: var(--bg);
224
+ color: var(--text);
225
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
226
+ }
227
+
228
  :root,
229
  .gradio-container {
230
  --bg: #ffffff;
 
263
  justify-content: space-between;
264
  gap: 20px;
265
  padding: 12px 24px;
266
+ margin: 0;
267
  background: rgba(255, 255, 255, 0.92);
268
  backdrop-filter: blur(8px);
269
  border-bottom: 1px solid var(--border);
 
297
  .alpasim-hero {
298
  position: relative;
299
  overflow: hidden;
300
+ margin: 0;
301
  background: #000000;
302
  border-bottom: 1px solid var(--border);
303
  }
 
356
  .challenge-info section {
357
  padding: 48px 0;
358
  border-bottom: 1px solid var(--border);
359
+ scroll-margin-top: 72px;
360
  }
361
 
362
  .challenge-info section:last-child {
 
522
  margin: 0 auto;
523
  }
524
 
525
+ .challenge-app {
526
+ max-width: 1100px;
527
+ margin: 0 auto;
528
+ padding: 48px 24px 72px;
529
+ scroll-margin-top: 72px;
530
+ }
531
+
532
+ .challenge-app h2 {
533
+ display: flex;
534
+ align-items: baseline;
535
+ gap: 12px;
536
+ margin: 0 0 18px;
537
+ font-size: 28px;
538
+ }
539
+
540
+ .challenge-app h2 span {
541
+ color: #76B900;
542
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
543
+ font-size: 0.7em;
544
+ letter-spacing: 0.08em;
545
+ }
546
+
547
+ .gradio-frame {
548
+ display: block;
549
+ width: 100%;
550
+ min-height: 940px;
551
+ border: 1px solid var(--border);
552
+ border-radius: 8px;
553
+ background: #ffffff;
554
+ }
555
+
556
  @media (max-width: 720px) {
557
  .topline {
558
  align-items: flex-start;
 
587
  padding: 0 18px;
588
  }
589
 
590
+ .challenge-info section,
591
+ .challenge-app {
592
+ scroll-margin-top: 112px;
593
+ }
594
+
595
+ .challenge-app {
596
+ padding: 48px 18px 64px;
597
+ }
598
+
599
+ .gradio-frame {
600
+ min-height: 1040px;
601
+ }
602
+
603
+ .identity-strip {
604
+ padding: 0 18px 18px;
605
+ }
606
+ }
607
+ """
608
+ GRADIO_CSS = """
609
+ :root,
610
+ .gradio-container {
611
+ --bg: #ffffff;
612
+ --panel: #f8f9fb;
613
+ --panel-2: #f1f3f7;
614
+ --border: #e4e7ec;
615
+ --text: #1a1d23;
616
+ --muted: #6b7280;
617
+ --button-primary-background-fill: #76B900;
618
+ --button-primary-background-fill-hover: #5F9500;
619
+ --button-primary-border-color: #76B900;
620
+ --button-primary-text-color: white;
621
+ --color-accent: #76B900;
622
+ --link-text-color: #1f6feb;
623
+ --loader-color: #76B900;
624
+ --slider-color: #76B900;
625
+ color: var(--text);
626
+ }
627
+
628
+ .gradio-container {
629
+ background: var(--bg);
630
+ max-width: none !important;
631
+ padding-top: 0 !important;
632
+ }
633
+
634
+ .identity-strip {
635
+ max-width: 1100px;
636
+ margin: 0 auto;
637
+ padding: 0 24px 18px;
638
+ }
639
+
640
+ .identity-strip p {
641
+ margin: 0;
642
+ color: var(--muted);
643
+ }
644
+
645
+ .tabs {
646
+ max-width: 1100px;
647
+ margin: 0 auto;
648
+ }
649
+
650
+ @media (max-width: 720px) {
651
  .identity-strip {
652
  padding: 0 18px 18px;
653
  }
654
  }
655
  """
656
+ LANDING_HTML = f"""<!doctype html>
657
+ <html lang="en">
658
+ <head>
659
+ <meta charset="utf-8" />
660
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
661
+ <title>AlpaSim E2E Closed Loop Challenge 2026</title>
662
+ <style>{BANNER_CSS}</style>
663
+ </head>
664
+ <body>
665
+ {BANNER_HTML}
666
+ {CHALLENGE_OVERVIEW_HTML}
667
+ {APP_EMBED_HTML}
668
+ </body>
669
+ </html>
670
+ """
671
 
672
 
673
  def split_multivalue(value: str) -> list[str]:
 
696
  "To register a team, connect this Space to your Hugging Face account. "
697
  "If you are already signed in on huggingface.co, this just authorizes "
698
  "the Space to read your Hugging Face profile. "
699
+ "[Connect Hugging Face account](/gradio/login/huggingface?_target_url=/gradio/)"
700
  )
701
 
702
  username = profile_value(profile, "preferred_username", "name")
 
707
  parts.append(f"Stable HF user ID: `{user_id}`.")
708
  if email:
709
  parts.append(f"HF email: `{email}`.")
710
+ parts.append("[Disconnect](/gradio/logout?_target_url=/gradio/).")
711
  return " ".join(parts)
712
 
713
 
 
1321
  with gr.Blocks(
1322
  title="AlpaSim E2E Closed Loop Challenge 2026",
1323
  theme=APP_THEME,
1324
+ css=GRADIO_CSS,
1325
  ) as demo:
1326
  add_oauth_route_marker()
 
 
1327
  with gr.Group(elem_classes=["identity-strip"]):
1328
  identity = gr.Markdown()
1329
 
 
1580
  ],
1581
  )
1582
 
1583
+ app = FastAPI(title="AlpaSim E2E Closed Loop Challenge 2026")
1584
+ app.mount("/assets", StaticFiles(directory=str(BANNER_DIR)), name="assets")
1585
+
1586
+
1587
+ @app.get("/", response_class=HTMLResponse, include_in_schema=False)
1588
+ def landing_page() -> HTMLResponse:
1589
+ return HTMLResponse(LANDING_HTML)
1590
+
1591
+
1592
+ app = gr.mount_gradio_app(
1593
+ app,
1594
+ demo,
1595
+ path="/gradio",
1596
+ allowed_paths=[str(BANNER_FILE)],
1597
+ )
1598
+
1599
+
1600
  if __name__ == "__main__":
1601
+ import uvicorn
1602
+
1603
+ uvicorn.run(
1604
+ app,
1605
+ host="0.0.0.0",
1606
+ port=int(os.environ.get("PORT", "7860")),
1607
+ )
requirements.txt CHANGED
@@ -1 +1,3 @@
1
- gradio[oauth]>=5.0
 
 
 
1
+ gradio[oauth]>=5.0,<6.0
2
+ fastapi>=0.115
3
+ uvicorn>=0.30