Spaces:
Running
Running
vibe coding the fix now basically added more logs and timeouts
Browse files- Dockerfile +2 -4
- backend/main.py +67 -32
- frontend/src/App.js +7 -0
Dockerfile
CHANGED
|
@@ -69,7 +69,5 @@ WORKDIR /home/user/app/backend
|
|
| 69 |
# Expose port for FastAPI on Hugging Face
|
| 70 |
EXPOSE 7860
|
| 71 |
|
| 72 |
-
# Start the FastAPI server
|
| 73 |
-
|
| 74 |
-
# Update this line in your Dockerfile
|
| 75 |
-
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860", "--proxy-headers", "--forwarded-allow-ips=*"]
|
|
|
|
| 69 |
# Expose port for FastAPI on Hugging Face
|
| 70 |
EXPOSE 7860
|
| 71 |
|
| 72 |
+
# Start the FastAPI server with optimized settings for Hugging Face Spaces
|
| 73 |
+
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860", "--proxy-headers", "--forwarded-allow-ips=*", "--timeout-keep-alive", "75"]
|
|
|
|
|
|
backend/main.py
CHANGED
|
@@ -34,32 +34,50 @@ from aimakerspace.utils.session_manager import SessionManager
|
|
| 34 |
from dotenv import load_dotenv
|
| 35 |
load_dotenv()
|
| 36 |
|
| 37 |
-
app = FastAPI(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
-
#
|
| 40 |
from starlette.middleware.base import BaseHTTPMiddleware
|
|
|
|
| 41 |
|
| 42 |
-
class
|
| 43 |
async def dispatch(self, request, call_next):
|
| 44 |
-
#
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
-
# Add
|
| 52 |
-
|
| 53 |
-
app.add_middleware(HTTPSRedirectMiddleware)
|
| 54 |
|
| 55 |
-
# Configure CORS -
|
| 56 |
app.add_middleware(
|
| 57 |
CORSMiddleware,
|
| 58 |
-
allow_origins=["*"], #
|
| 59 |
allow_credentials=True,
|
| 60 |
-
allow_methods=["
|
| 61 |
-
allow_headers=["*"],
|
| 62 |
-
expose_headers=["
|
|
|
|
| 63 |
)
|
| 64 |
|
| 65 |
# Initialize session manager
|
|
@@ -167,6 +185,11 @@ async def process_file_background(temp_path: str, filename: str, session_id: str
|
|
| 167 |
async def upload_file(background_tasks: BackgroundTasks, file: UploadFile = File(...)):
|
| 168 |
try:
|
| 169 |
logger.info(f"Received upload request for file: {file.filename}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
|
| 171 |
# Check file size first
|
| 172 |
file_size = 0
|
|
@@ -174,20 +197,32 @@ async def upload_file(background_tasks: BackgroundTasks, file: UploadFile = File
|
|
| 174 |
contents = bytearray()
|
| 175 |
|
| 176 |
# Read file in chunks to avoid memory issues
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
|
| 192 |
logger.info(f"File size: {file_size/1024/1024:.2f}MB")
|
| 193 |
|
|
@@ -214,7 +249,7 @@ async def upload_file(background_tasks: BackgroundTasks, file: UploadFile = File
|
|
| 214 |
session_id
|
| 215 |
)
|
| 216 |
|
| 217 |
-
return {"session_id": session_id, "message": "File uploaded and processing started"}
|
| 218 |
|
| 219 |
except Exception as e:
|
| 220 |
logger.error(f"Error processing upload: {str(e)}")
|
|
|
|
| 34 |
from dotenv import load_dotenv
|
| 35 |
load_dotenv()
|
| 36 |
|
| 37 |
+
app = FastAPI(
|
| 38 |
+
title="RAG Application",
|
| 39 |
+
description="Retrieval Augmented Generation with FastAPI and React",
|
| 40 |
+
version="0.1.0",
|
| 41 |
+
root_path="", # Important for proxy environments
|
| 42 |
+
)
|
| 43 |
|
| 44 |
+
# More robust middleware for handling HTTPS
|
| 45 |
from starlette.middleware.base import BaseHTTPMiddleware
|
| 46 |
+
from starlette.responses import RedirectResponse, JSONResponse
|
| 47 |
|
| 48 |
+
class ProxyMiddleware(BaseHTTPMiddleware):
|
| 49 |
async def dispatch(self, request, call_next):
|
| 50 |
+
# Log request details for debugging
|
| 51 |
+
logger.info(f"Request path: {request.url.path}")
|
| 52 |
+
logger.info(f"Request headers: {request.headers}")
|
| 53 |
+
|
| 54 |
+
# Validate request before processing
|
| 55 |
+
try:
|
| 56 |
+
start_time = time.time()
|
| 57 |
+
response = await call_next(request)
|
| 58 |
+
process_time = time.time() - start_time
|
| 59 |
+
response.headers["X-Process-Time"] = str(process_time)
|
| 60 |
+
return response
|
| 61 |
+
except Exception as e:
|
| 62 |
+
logger.error(f"Request failed: {str(e)}")
|
| 63 |
+
logger.error(traceback.format_exc())
|
| 64 |
+
return JSONResponse(
|
| 65 |
+
status_code=500,
|
| 66 |
+
content={"detail": f"Internal server error: {str(e)}"}
|
| 67 |
+
)
|
| 68 |
|
| 69 |
+
# Add custom middleware
|
| 70 |
+
app.add_middleware(ProxyMiddleware)
|
|
|
|
| 71 |
|
| 72 |
+
# Configure CORS - more specific configuration for Hugging Face
|
| 73 |
app.add_middleware(
|
| 74 |
CORSMiddleware,
|
| 75 |
+
allow_origins=["*"], # In production, you should restrict this
|
| 76 |
allow_credentials=True,
|
| 77 |
+
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
| 78 |
+
allow_headers=["*"],
|
| 79 |
+
expose_headers=["Content-Length", "X-Process-Time"],
|
| 80 |
+
max_age=600, # 10 minutes cache for preflight requests
|
| 81 |
)
|
| 82 |
|
| 83 |
# Initialize session manager
|
|
|
|
| 185 |
async def upload_file(background_tasks: BackgroundTasks, file: UploadFile = File(...)):
|
| 186 |
try:
|
| 187 |
logger.info(f"Received upload request for file: {file.filename}")
|
| 188 |
+
logger.info(f"Content type: {file.content_type}")
|
| 189 |
+
|
| 190 |
+
# Create a unique ID for this upload
|
| 191 |
+
upload_id = str(uuid.uuid4())
|
| 192 |
+
logger.info(f"Assigned upload ID: {upload_id}")
|
| 193 |
|
| 194 |
# Check file size first
|
| 195 |
file_size = 0
|
|
|
|
| 197 |
contents = bytearray()
|
| 198 |
|
| 199 |
# Read file in chunks to avoid memory issues
|
| 200 |
+
try:
|
| 201 |
+
while True:
|
| 202 |
+
chunk = await asyncio.wait_for(file.read(chunk_size), timeout=60.0)
|
| 203 |
+
if not chunk:
|
| 204 |
+
break
|
| 205 |
+
file_size += len(chunk)
|
| 206 |
+
contents.extend(chunk)
|
| 207 |
+
|
| 208 |
+
# Check size limit
|
| 209 |
+
if file_size > FILE_SIZE_LIMIT:
|
| 210 |
+
logger.warning(f"File too large: {file_size/1024/1024:.2f}MB exceeds limit of {FILE_SIZE_LIMIT/1024/1024}MB")
|
| 211 |
+
return HTTPException(
|
| 212 |
+
status_code=413,
|
| 213 |
+
detail=f"File too large. Maximum size is {FILE_SIZE_LIMIT/1024/1024}MB"
|
| 214 |
+
)
|
| 215 |
+
|
| 216 |
+
# Log progress for large files
|
| 217 |
+
if file_size % (5 * 1024 * 1024) == 0: # Log every 5MB
|
| 218 |
+
logger.info(f"Upload progress: {file_size/1024/1024:.2f}MB read so far...")
|
| 219 |
+
|
| 220 |
+
except asyncio.TimeoutError:
|
| 221 |
+
logger.error(f"Timeout reading file: {file.filename}")
|
| 222 |
+
raise HTTPException(
|
| 223 |
+
status_code=408,
|
| 224 |
+
detail="Request timeout while reading file. Please try again."
|
| 225 |
+
)
|
| 226 |
|
| 227 |
logger.info(f"File size: {file_size/1024/1024:.2f}MB")
|
| 228 |
|
|
|
|
| 249 |
session_id
|
| 250 |
)
|
| 251 |
|
| 252 |
+
return {"session_id": session_id, "message": "File uploaded and processing started", "upload_id": upload_id}
|
| 253 |
|
| 254 |
except Exception as e:
|
| 255 |
logger.error(f"Error processing upload: {str(e)}")
|
frontend/src/App.js
CHANGED
|
@@ -197,9 +197,16 @@ function FileUploader({ onFileUpload }) {
|
|
| 197 |
headers: {
|
| 198 |
'Content-Type': 'multipart/form-data',
|
| 199 |
},
|
|
|
|
|
|
|
|
|
|
| 200 |
onUploadProgress: (progressEvent) => {
|
| 201 |
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
| 202 |
setUploadProgress(percentCompleted);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
}
|
| 204 |
});
|
| 205 |
|
|
|
|
| 197 |
headers: {
|
| 198 |
'Content-Type': 'multipart/form-data',
|
| 199 |
},
|
| 200 |
+
timeout: 180000, // 3 minutes timeout for large files
|
| 201 |
+
maxContentLength: Infinity,
|
| 202 |
+
maxBodyLength: Infinity,
|
| 203 |
onUploadProgress: (progressEvent) => {
|
| 204 |
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
| 205 |
setUploadProgress(percentCompleted);
|
| 206 |
+
},
|
| 207 |
+
// Add retry logic for network errors
|
| 208 |
+
validateStatus: function (status) {
|
| 209 |
+
return status >= 200 && status < 500; // Handle 4xx errors in our own logic
|
| 210 |
}
|
| 211 |
});
|
| 212 |
|