Spaces:
Running
Running
Manoj Thapa
commited on
Commit
·
a452d25
1
Parent(s):
d6f4d0e
Update README for Hugging Face Deployment
Browse files- .gitignore +9 -6
- Dockerfile +16 -18
- README.md +1 -1
- backend/main.py +40 -30
- frontend/src/components/Header.jsx +1 -1
- frontend/src/components/Sidebar.jsx +1 -1
.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
|
| 2 |
-
FROM node:18-alpine as build
|
| 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:
|
| 11 |
-
FROM python:3.11-slim
|
|
|
|
| 12 |
|
| 13 |
-
# Install system dependencies
|
| 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 |
-
|
| 21 |
-
|
| 22 |
-
# Copy backend requirements
|
| 23 |
COPY backend/requirements.txt .
|
| 24 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 25 |
|
| 26 |
-
# Copy
|
| 27 |
-
COPY backend/ ./backend
|
| 28 |
|
| 29 |
-
# Copy
|
| 30 |
-
COPY --from=build
|
| 31 |
|
| 32 |
-
#
|
| 33 |
-
|
| 34 |
-
ENV PYTHONPATH=/app/backend
|
| 35 |
|
| 36 |
-
# Run
|
|
|
|
| 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
|
| 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
|
| 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 |
|