Spaces:
Sleeping
Sleeping
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 |
|