Spaces:
Sleeping
Sleeping
feat: Added the rate limiting per route
Browse files- app.py +20 -31
- config.py +1 -1
- features/text_classifier/controller.py +2 -2
- features/text_classifier/routes.py +25 -12
- requirements.txt +1 -1
app.py
CHANGED
|
@@ -1,47 +1,36 @@
|
|
| 1 |
from fastapi import FastAPI, Request
|
| 2 |
-
from contextlib import asynccontextmanager
|
| 3 |
-
from features.text_classifier.routes import router as text_classifier_router
|
| 4 |
-
from features.text_classifier.model_loader import warmup
|
| 5 |
from slowapi import Limiter, _rate_limit_exceeded_handler
|
| 6 |
-
from slowapi.util import get_remote_address
|
| 7 |
-
from slowapi.errors import RateLimitExceeded
|
| 8 |
from slowapi.middleware import SlowAPIMiddleware
|
| 9 |
-
from
|
| 10 |
-
import
|
| 11 |
-
import
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
@asynccontextmanager
|
| 15 |
-
async def lifespan(app: FastAPI):
|
| 16 |
-
warmup()
|
| 17 |
-
yield
|
| 18 |
|
| 19 |
-
# Create the limiter with a global rate from config
|
| 20 |
limiter = Limiter(key_func=get_remote_address, default_limits=[ACCESS_RATE])
|
| 21 |
|
| 22 |
-
|
| 23 |
-
app = FastAPI(lifespan=lifespan)
|
| 24 |
|
| 25 |
-
# Set
|
| 26 |
app.state.limiter = limiter
|
| 27 |
-
app.add_exception_handler(RateLimitExceeded,
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
app.add_middleware(SlowAPIMiddleware)
|
| 31 |
|
| 32 |
-
#
|
| 33 |
app.include_router(text_classifier_router, prefix="/text")
|
| 34 |
|
| 35 |
-
# Root route
|
| 36 |
@app.get("/")
|
| 37 |
-
|
|
|
|
| 38 |
return {
|
| 39 |
-
"
|
| 40 |
-
"
|
| 41 |
}
|
| 42 |
|
| 43 |
-
# Example protected route
|
| 44 |
-
@app.get("/home")
|
| 45 |
-
async def homepage(request: Request):
|
| 46 |
-
return {"msg": "This is a good message"}
|
| 47 |
-
|
|
|
|
| 1 |
from fastapi import FastAPI, Request
|
|
|
|
|
|
|
|
|
|
| 2 |
from slowapi import Limiter, _rate_limit_exceeded_handler
|
|
|
|
|
|
|
| 3 |
from slowapi.middleware import SlowAPIMiddleware
|
| 4 |
+
from slowapi.errors import RateLimitExceeded
|
| 5 |
+
from slowapi.util import get_remote_address
|
| 6 |
+
from fastapi.responses import JSONResponse
|
| 7 |
+
from features.text_classifier.routes import router as text_classifier_router
|
| 8 |
+
from config import ACCESS_RATE
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
|
|
|
| 10 |
limiter = Limiter(key_func=get_remote_address, default_limits=[ACCESS_RATE])
|
| 11 |
|
| 12 |
+
app = FastAPI()
|
|
|
|
| 13 |
|
| 14 |
+
# Set up SlowAPI
|
| 15 |
app.state.limiter = limiter
|
| 16 |
+
app.add_exception_handler(RateLimitExceeded, lambda request, exc: JSONResponse(
|
| 17 |
+
status_code=429,
|
| 18 |
+
content={
|
| 19 |
+
"status_code": 429,
|
| 20 |
+
"error": "Rate limit exceeded",
|
| 21 |
+
"message": "Too many requests. Chill for a bit and try again"
|
| 22 |
+
}
|
| 23 |
+
))
|
| 24 |
app.add_middleware(SlowAPIMiddleware)
|
| 25 |
|
| 26 |
+
# Include your routes
|
| 27 |
app.include_router(text_classifier_router, prefix="/text")
|
| 28 |
|
|
|
|
| 29 |
@app.get("/")
|
| 30 |
+
@limiter.limit(ACCESS_RATE)
|
| 31 |
+
async def root():
|
| 32 |
return {
|
| 33 |
+
"message": "API is working",
|
| 34 |
+
"endpoints": ["/text/analyse", "/text/upload", "/text/analyse-sentences", "/text/analyse-sentance-file"]
|
| 35 |
}
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
config.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
| 1 |
-
ACCESS_RATE = "
|
| 2 |
|
|
|
|
| 1 |
+
ACCESS_RATE = "20/minute"
|
| 2 |
|
features/text_classifier/controller.py
CHANGED
|
@@ -23,10 +23,10 @@ async def verify_token(credentials: HTTPAuthorizationCredentials = Depends(secur
|
|
| 23 |
# Text classification
|
| 24 |
async def handle_text_analysis(text: str):
|
| 25 |
text = text.strip()
|
| 26 |
-
if not text or len(text.split()) <
|
| 27 |
raise HTTPException(status_code=400, detail="Text must contain at least two words")
|
| 28 |
if len(text) > 10000:
|
| 29 |
-
raise HTTPException(status_code=
|
| 30 |
label, perplexity, ai_likelihood = await asyncio.to_thread(classify_text, text)
|
| 31 |
return {"result": label, "perplexity": round(perplexity, 2), "ai_likelihood": ai_likelihood}
|
| 32 |
|
|
|
|
| 23 |
# Text classification
|
| 24 |
async def handle_text_analysis(text: str):
|
| 25 |
text = text.strip()
|
| 26 |
+
if not text or len(text.split()) < 10:
|
| 27 |
raise HTTPException(status_code=400, detail="Text must contain at least two words")
|
| 28 |
if len(text) > 10000:
|
| 29 |
+
raise HTTPException(status_code=413, detail="Text must be less than 10,000 characters.")
|
| 30 |
label, perplexity, ai_likelihood = await asyncio.to_thread(classify_text, text)
|
| 31 |
return {"result": label, "perplexity": round(perplexity, 2), "ai_likelihood": ai_likelihood}
|
| 32 |
|
features/text_classifier/routes.py
CHANGED
|
@@ -1,11 +1,19 @@
|
|
| 1 |
-
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
|
| 2 |
-
from fastapi.security import HTTPBearer
|
| 3 |
from pydantic import BaseModel
|
| 4 |
-
from .
|
| 5 |
-
from
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
router = APIRouter()
|
| 10 |
security = HTTPBearer()
|
| 11 |
|
|
@@ -13,24 +21,29 @@ class TextInput(BaseModel):
|
|
| 13 |
text: str
|
| 14 |
|
| 15 |
@router.post("/analyse")
|
| 16 |
-
|
|
|
|
| 17 |
return await handle_text_analysis(data.text)
|
| 18 |
|
| 19 |
@router.post("/upload")
|
| 20 |
-
|
|
|
|
| 21 |
return await handle_file_upload(file)
|
| 22 |
|
| 23 |
@router.post("/analyse-sentences")
|
| 24 |
-
|
|
|
|
| 25 |
if not data.text:
|
| 26 |
raise HTTPException(status_code=400, detail="Missing 'text' in request body")
|
| 27 |
return await handle_sentence_level_analysis(data.text)
|
| 28 |
|
| 29 |
@router.post("/analyse-sentance-file")
|
| 30 |
-
|
|
|
|
| 31 |
return await handle_file_sentance(file)
|
| 32 |
|
| 33 |
@router.get("/health")
|
| 34 |
-
|
|
|
|
| 35 |
return {"status": "ok"}
|
| 36 |
|
|
|
|
| 1 |
+
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Request
|
| 2 |
+
from fastapi.security import HTTPBearer
|
| 3 |
from pydantic import BaseModel
|
| 4 |
+
from slowapi.util import get_remote_address
|
| 5 |
+
from slowapi import Limiter
|
| 6 |
+
from slowapi.errors import RateLimitExceeded
|
| 7 |
+
from config import ACCESS_RATE
|
| 8 |
+
from .controller import (
|
| 9 |
+
handle_text_analysis,
|
| 10 |
+
handle_file_upload,
|
| 11 |
+
handle_sentence_level_analysis,
|
| 12 |
+
handle_file_sentance,
|
| 13 |
+
verify_token
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
limiter = Limiter(key_func=get_remote_address)
|
| 17 |
router = APIRouter()
|
| 18 |
security = HTTPBearer()
|
| 19 |
|
|
|
|
| 21 |
text: str
|
| 22 |
|
| 23 |
@router.post("/analyse")
|
| 24 |
+
@limiter.limit(ACCESS_RATE)
|
| 25 |
+
async def analyze(request: Request, data: TextInput, token: str = Depends(verify_token)):
|
| 26 |
return await handle_text_analysis(data.text)
|
| 27 |
|
| 28 |
@router.post("/upload")
|
| 29 |
+
@limiter.limit(ACCESS_RATE)
|
| 30 |
+
async def upload_file(request: Request, file: UploadFile = File(...), token: str = Depends(verify_token)):
|
| 31 |
return await handle_file_upload(file)
|
| 32 |
|
| 33 |
@router.post("/analyse-sentences")
|
| 34 |
+
@limiter.limit(ACCESS_RATE)
|
| 35 |
+
async def analyze_sentences(request: Request, data: TextInput, token: str = Depends(verify_token)):
|
| 36 |
if not data.text:
|
| 37 |
raise HTTPException(status_code=400, detail="Missing 'text' in request body")
|
| 38 |
return await handle_sentence_level_analysis(data.text)
|
| 39 |
|
| 40 |
@router.post("/analyse-sentance-file")
|
| 41 |
+
@limiter.limit(ACCESS_RATE)
|
| 42 |
+
async def analyze_sentance_file(request: Request, file: UploadFile = File(...), token: str = Depends(verify_token)):
|
| 43 |
return await handle_file_sentance(file)
|
| 44 |
|
| 45 |
@router.get("/health")
|
| 46 |
+
@limiter.limit(ACCESS_RATE)
|
| 47 |
+
def health(request: Request):
|
| 48 |
return {"status": "ok"}
|
| 49 |
|
requirements.txt
CHANGED
|
@@ -8,6 +8,6 @@ python-docx
|
|
| 8 |
pydantic
|
| 9 |
PyMuPDF
|
| 10 |
nltk
|
| 11 |
-
slowapi
|
| 12 |
python-multipart
|
|
|
|
| 13 |
|
|
|
|
| 8 |
pydantic
|
| 9 |
PyMuPDF
|
| 10 |
nltk
|
|
|
|
| 11 |
python-multipart
|
| 12 |
+
slowapi
|
| 13 |
|