AhmedSamir1598 Cursor commited on
Commit
bcd2ced
·
1 Parent(s): 7477316

Add Hugging Face Spaces deployment support

Browse files

Co-authored-by: Cursor <cursoragent@cursor.com>

Files changed (4) hide show
  1. .dockerignore +13 -1
  2. Dockerfile +49 -0
  3. README.md +9 -0
  4. config.py +3 -2
.dockerignore CHANGED
@@ -36,5 +36,17 @@ optiq.db
36
  # Environment secrets
37
  .env
38
 
39
- # Misc
 
 
 
 
 
 
 
 
 
40
  EcoHackathon/
 
 
 
 
36
  # Environment secrets
37
  .env
38
 
39
+ # Built frontend (rebuilt in Docker Stage 1)
40
+ frontend/dist/
41
+
42
+ # Nginx (separate deployment config, not needed in main image)
43
+ nginx/
44
+
45
+ # Cursor IDE
46
+ .cursor/
47
+
48
+ # Research documents
49
  EcoHackathon/
50
+
51
+ # Scripts (not needed at runtime)
52
+ scripts/
Dockerfile ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ──────────────────────────────────────────────────
2
+ # OptiQ — Full-stack Docker image for Hugging Face Spaces
3
+ # Multi-stage: Node (frontend build) → Python (backend)
4
+ # ──────────────────────────────────────────────────
5
+
6
+ # ── Stage 1: Build the React frontend ────────────
7
+ FROM node:20-alpine AS frontend
8
+ WORKDIR /app/frontend
9
+ COPY frontend/package*.json ./
10
+ RUN npm ci --no-audit --no-fund
11
+ COPY frontend/ .
12
+ RUN npm run build
13
+
14
+ # ── Stage 2: Python backend + built frontend ─────
15
+ FROM python:3.12-slim
16
+ WORKDIR /app
17
+
18
+ # System dependencies needed by some Python packages (pandapower, scipy, etc.)
19
+ RUN apt-get update && \
20
+ apt-get install -y --no-install-recommends gcc g++ && \
21
+ rm -rf /var/lib/apt/lists/*
22
+
23
+ # Install PyTorch CPU first (not in requirements.txt — installed via conda locally)
24
+ RUN pip install --no-cache-dir torch --index-url https://download.pytorch.org/whl/cpu
25
+
26
+ # Install remaining Python dependencies
27
+ COPY requirements.txt .
28
+ RUN pip install --no-cache-dir -r requirements.txt
29
+
30
+ # Copy application source
31
+ COPY api/ api/
32
+ COPY src/ src/
33
+ COPY config.py .
34
+
35
+ # Copy pre-trained model checkpoint if it exists (models/ may only contain .gitkeep)
36
+ COPY models/ models/
37
+
38
+ # Copy built frontend from Stage 1
39
+ COPY --from=frontend /app/frontend/dist frontend/dist/
40
+
41
+ # Create writable directory for SQLite database
42
+ RUN mkdir -p /app/data
43
+
44
+ # HF Spaces requires port 7860
45
+ ENV PORT=7860
46
+ EXPOSE 7860
47
+
48
+ # Run the application
49
+ CMD ["python", "-m", "uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,3 +1,12 @@
 
 
 
 
 
 
 
 
 
1
  <p align="center">
2
  <img src="https://img.shields.io/badge/Python-3.12-blue?style=flat-square&logo=python" />
3
  <img src="https://img.shields.io/badge/Qiskit-2.3-6929C4?style=flat-square&logo=ibm" />
 
1
+ ---
2
+ title: OptiQ
3
+ emoji: ⚡
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
  <p align="center">
11
  <img src="https://img.shields.io/badge/Python-3.12-blue?style=flat-square&logo=python" />
12
  <img src="https://img.shields.io/badge/Qiskit-2.3-6929C4?style=flat-square&logo=ibm" />
config.py CHANGED
@@ -5,6 +5,7 @@ emission factors, and API settings.
5
  """
6
  from dataclasses import dataclass, field
7
  from typing import Literal
 
8
 
9
 
10
  # ---------------------------------------------------------------------------
@@ -121,8 +122,8 @@ class EgyptConfig:
121
  class APIConfig:
122
  """FastAPI server settings."""
123
  host: str = "0.0.0.0"
124
- port: int = 8000
125
- reload: bool = True
126
  cors_origins: list[str] = field(default_factory=lambda: ["*"])
127
 
128
 
 
5
  """
6
  from dataclasses import dataclass, field
7
  from typing import Literal
8
+ import os
9
 
10
 
11
  # ---------------------------------------------------------------------------
 
122
  class APIConfig:
123
  """FastAPI server settings."""
124
  host: str = "0.0.0.0"
125
+ port: int = int(os.environ.get("PORT", "8000"))
126
+ reload: bool = os.environ.get("OPTIQ_RELOAD", "false").lower() == "true"
127
  cors_origins: list[str] = field(default_factory=lambda: ["*"])
128
 
129