Manoj Thapa commited on
Commit
a452d25
·
1 Parent(s): d6f4d0e

Update README for Hugging Face Deployment

Browse files
.gitignore CHANGED
@@ -1,8 +1,3 @@
1
- # Node
2
- node_modules/
3
- dist/
4
- npm-debug.log
5
-
6
  # Byte-compiled / optimized / DLL files
7
  __pycache__/
8
  *.py[cod]
@@ -13,14 +8,22 @@ venv/
13
  .venv/
14
  env/
15
 
 
 
 
 
 
 
 
16
  # Environment variables
17
  .env
 
18
 
19
  # IDE
20
  .vscode/
21
  .idea/
22
 
23
- # Data directories
24
  data/
25
  uploads/
26
 
 
 
 
 
 
 
1
  # Byte-compiled / optimized / DLL files
2
  __pycache__/
3
  *.py[cod]
 
8
  .venv/
9
  env/
10
 
11
+ # Node.js
12
+ node_modules/
13
+ npm-debug.log
14
+ yarn-error.log
15
+ frontend/dist/
16
+ dist/
17
+
18
  # Environment variables
19
  .env
20
+ .env.local
21
 
22
  # IDE
23
  .vscode/
24
  .idea/
25
 
26
+ # Data directories (Local persistence)
27
  data/
28
  uploads/
29
 
Dockerfile CHANGED
@@ -1,37 +1,35 @@
1
- # Stage 1: Build React Frontend
2
- FROM node:18-alpine as build-step
3
-
4
  WORKDIR /app/frontend
5
  COPY frontend/package*.json ./
6
  RUN npm install
7
  COPY frontend/ ./
8
  RUN npm run build
9
 
10
- # Stage 2: Python Backend with Static Files
11
- FROM python:3.11-slim-bookworm
 
12
 
13
- # Install system dependencies (Poppler for PDF, Tesseract for OCR if needed)
14
  RUN apt-get update && apt-get install -y \
15
- poppler-utils \
16
  tesseract-ocr \
 
17
  libgl1-mesa-glx \
18
  && rm -rf /var/lib/apt/lists/*
19
 
20
- WORKDIR /app
21
-
22
- # Copy backend requirements
23
  COPY backend/requirements.txt .
24
  RUN pip install --no-cache-dir -r requirements.txt
25
 
26
- # Copy backend code
27
- COPY backend/ ./backend/
28
 
29
- # Copy built frontend from Stage 1 to backend/static
30
- COPY --from=build-step /app/frontend/dist ./backend/static
31
 
32
- # Set Environment Variables
33
- ENV PORT=7860
34
- ENV PYTHONPATH=/app/backend
35
 
36
- # Run FastAPI (Serving Frontend + Backend)
 
37
  CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "7860"]
 
1
+ # Stage 1: Build Frontend
2
+ FROM node:18-alpine as frontend-build
 
3
  WORKDIR /app/frontend
4
  COPY frontend/package*.json ./
5
  RUN npm install
6
  COPY frontend/ ./
7
  RUN npm run build
8
 
9
+ # Stage 2: Runtime
10
+ FROM python:3.11-slim
11
+ WORKDIR /app
12
 
13
+ # Install system dependencies
14
  RUN apt-get update && apt-get install -y \
 
15
  tesseract-ocr \
16
+ poppler-utils \
17
  libgl1-mesa-glx \
18
  && rm -rf /var/lib/apt/lists/*
19
 
20
+ # Install Python deps
 
 
21
  COPY backend/requirements.txt .
22
  RUN pip install --no-cache-dir -r requirements.txt
23
 
24
+ # Copy Backend Code
25
+ COPY backend/ ./backend
26
 
27
+ # Copy Frontend Build from Stage 1
28
+ COPY --from=frontend-build /app/frontend/dist ./frontend/dist
29
 
30
+ # Expose port 7860 (Standard for HF Spaces)
31
+ EXPOSE 7860
 
32
 
33
+ # Run the app
34
+ # Note: We run from root, so module is backend.main
35
  CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,6 +1,6 @@
1
  ---
2
  title: Planck AI
3
- emoji: 🧠
4
  colorFrom: gray
5
  colorTo: blue
6
  sdk: docker
 
1
  ---
2
  title: Planck AI
3
+ emoji: 🪐
4
  colorFrom: gray
5
  colorTo: blue
6
  sdk: docker
backend/main.py CHANGED
@@ -43,36 +43,6 @@ memory = ConversationMemory()
43
  UPLOAD_DIR = Path(__file__).parent / "uploads"
44
  UPLOAD_DIR.mkdir(exist_ok=True, parents=True)
45
 
46
- # -------------------------------------------------------------------------
47
- # Static File Serving (For Production/Docker)
48
- # -------------------------------------------------------------------------
49
- from fastapi.staticfiles import StaticFiles
50
- from fastapi.responses import FileResponse
51
-
52
- # Helper to find static directory (handles local run vs docker structure)
53
- # In Docker, we copied dist to ./backend/static, so it's relative to main.py
54
- static_dir = Path(__file__).parent / "static"
55
-
56
- if static_dir.exists():
57
- app.mount("/assets", StaticFiles(directory=static_dir / "assets"), name="assets")
58
-
59
- # Catch-all for React Router (SPA)
60
- @app.get("/{full_path:path}")
61
- async def serve_frontend(full_path: str):
62
- # Allow API routes to pass through
63
- if full_path.startswith("api/") or full_path.startswith("docs") or full_path.startswith("openapi.json"):
64
- raise HTTPException(status_code=404)
65
-
66
- # Check if file exists in static (e.g. favicon.ico, plain images)
67
- file_path = static_dir / full_path
68
- if file_path.exists() and file_path.is_file():
69
- return FileResponse(file_path)
70
-
71
- # Fallback to index.html for React Routes
72
- return FileResponse(static_dir / "index.html")
73
- else:
74
- print("WARNING: Static directory not found. Frontend will not be served (API Only Mode).")
75
-
76
 
77
  # Request/Response models
78
  class ChatRequest(BaseModel):
@@ -253,6 +223,46 @@ def delete_conversation(conversation_id: str):
253
  return {"status": "success"}
254
  raise HTTPException(status_code=404, detail="Conversation not found or could not be deleted")
255
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  if __name__ == "__main__":
257
  uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
258
 
 
43
  UPLOAD_DIR = Path(__file__).parent / "uploads"
44
  UPLOAD_DIR.mkdir(exist_ok=True, parents=True)
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
  # Request/Response models
48
  class ChatRequest(BaseModel):
 
223
  return {"status": "success"}
224
  raise HTTPException(status_code=404, detail="Conversation not found or could not be deleted")
225
 
226
+ # -------------------------------------------------------------------------
227
+ # Static Files & SPA Handling (Production/Docker)
228
+ # -------------------------------------------------------------------------
229
+ from fastapi.staticfiles import StaticFiles
230
+ from fastapi.responses import FileResponse
231
+
232
+ # Check for frontend build
233
+ frontend_dist = Path("frontend/dist")
234
+
235
+ if frontend_dist.exists():
236
+ # Mount assets
237
+ app.mount("/assets", StaticFiles(directory=frontend_dist / "assets"), name="assets")
238
+
239
+ # Manifest and other root files
240
+ @app.get("/manifest.json")
241
+ async def manifest():
242
+ return FileResponse(frontend_dist / "manifest.json")
243
+
244
+ @app.get("/favicon.ico")
245
+ async def favicon():
246
+ # Fallback if favicon isn't in root
247
+ if (frontend_dist / "favicon.ico").exists():
248
+ return FileResponse(frontend_dist / "favicon.ico")
249
+ return {"error": "not found"}
250
+
251
+ # Catch-all for SPA (must be last)
252
+ @app.get("/{full_path:path}")
253
+ async def serve_spa(full_path: str):
254
+ # Skip API routes (handled above)
255
+ if full_path.startswith("api"):
256
+ raise HTTPException(status_code=404, detail="API route not found")
257
+
258
+ # Check if file exists (e.g. manoj.png)
259
+ file_path = frontend_dist / full_path
260
+ if file_path.exists() and file_path.is_file():
261
+ return FileResponse(file_path)
262
+
263
+ # Default to index.html for React Router
264
+ return FileResponse(frontend_dist / "index.html")
265
+
266
  if __name__ == "__main__":
267
  uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
268
 
frontend/src/components/Header.jsx CHANGED
@@ -17,7 +17,7 @@ export default function Header({ onToggleSidebar, onNewChat }) {
17
  <Sparkles className="w-4 h-4 text-white" />
18
  </div>
19
  <div>
20
- <h1 className="text-xl font-bold gradient-text">Planck AI</h1>
21
  <p className="text-xs text-pplx-muted">Autonomous Reasoning Engine</p>
22
  </div>
23
  </div>
 
17
  <Sparkles className="w-4 h-4 text-white" />
18
  </div>
19
  <div>
20
+ <h1 className="text-xl font-bold gradient-text flex items-center gap-1">Planck AI <Sparkles className="w-3 h-3 text-cyan-400" /></h1>
21
  <p className="text-xs text-pplx-muted">Autonomous Reasoning Engine</p>
22
  </div>
23
  </div>
frontend/src/components/Sidebar.jsx CHANGED
@@ -49,7 +49,7 @@ export default function Sidebar({
49
  <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-cyan-500 to-pplx-accent flex items-center justify-center cursor-pointer hover:opacity-80 transition-opacity" title="Planck AI">
50
  <Sparkles className="w-4 h-4 text-white" />
51
  </div>
52
- <span className="text-xl font-serif font-medium text-pplx-text tracking-wide">Planck <span className="text-pplx-accent">AI</span></span>
53
  </div>
54
  </div>
55
 
 
49
  <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-cyan-500 to-pplx-accent flex items-center justify-center cursor-pointer hover:opacity-80 transition-opacity" title="Planck AI">
50
  <Sparkles className="w-4 h-4 text-white" />
51
  </div>
52
+ <span className="text-xl font-serif font-medium text-pplx-text tracking-wide flex items-center gap-1">Planck <span className="text-pplx-accent">AI</span> <Sparkles className="w-3 h-3 text-cyan-400" /></span>
53
  </div>
54
  </div>
55