feat: added the rate limiter
Browse files- app.py +16 -5
- config.py +2 -1
- features/text_classifier/controller.py +5 -5
app.py
CHANGED
|
@@ -1,27 +1,38 @@
|
|
| 1 |
-
# app.py
|
| 2 |
-
|
| 3 |
from fastapi import FastAPI, Request
|
| 4 |
from contextlib import asynccontextmanager
|
| 5 |
from features.text_classifier.routes import router as text_classifier_router
|
| 6 |
from features.text_classifier.model_loader import warmup
|
| 7 |
-
import nltk
|
| 8 |
from slowapi import Limiter, _rate_limit_exceeded_handler
|
| 9 |
from slowapi.util import get_remote_address
|
| 10 |
from slowapi.errors import RateLimitExceeded
|
|
|
|
|
|
|
|
|
|
| 11 |
import requests
|
|
|
|
|
|
|
| 12 |
@asynccontextmanager
|
| 13 |
async def lifespan(app: FastAPI):
|
| 14 |
warmup()
|
| 15 |
yield
|
| 16 |
|
| 17 |
-
limiter
|
|
|
|
| 18 |
|
|
|
|
| 19 |
app = FastAPI(lifespan=lifespan)
|
|
|
|
|
|
|
| 20 |
app.state.limiter = limiter
|
| 21 |
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
| 22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
app.include_router(text_classifier_router, prefix="/text")
|
| 24 |
|
|
|
|
| 25 |
@app.get("/")
|
| 26 |
def index():
|
| 27 |
return {
|
|
@@ -29,8 +40,8 @@ def index():
|
|
| 29 |
"Try": "/text/analyse or /text/analyse-sentences"
|
| 30 |
}
|
| 31 |
|
|
|
|
| 32 |
@app.get("/home")
|
| 33 |
-
@limiter.limit("5/minute")
|
| 34 |
async def homepage(request: Request):
|
| 35 |
return {"msg": "This is a good message"}
|
| 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 config import ACCESS_RATE # E.g., "100/minute"
|
| 10 |
+
import nltk
|
| 11 |
import requests
|
| 12 |
+
|
| 13 |
+
# Warmup logic
|
| 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 |
+
# Initialize FastAPI with lifespan
|
| 23 |
app = FastAPI(lifespan=lifespan)
|
| 24 |
+
|
| 25 |
+
# Set limiter and exception handler
|
| 26 |
app.state.limiter = limiter
|
| 27 |
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
| 28 |
|
| 29 |
+
# Add SlowAPI middleware globally
|
| 30 |
+
app.add_middleware(SlowAPIMiddleware)
|
| 31 |
+
|
| 32 |
+
# Register routers
|
| 33 |
app.include_router(text_classifier_router, prefix="/text")
|
| 34 |
|
| 35 |
+
# Root route
|
| 36 |
@app.get("/")
|
| 37 |
def index():
|
| 38 |
return {
|
|
|
|
| 40 |
"Try": "/text/analyse or /text/analyse-sentences"
|
| 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 |
|
config.py
CHANGED
|
@@ -1 +1,2 @@
|
|
| 1 |
-
ACCESS_RATE=
|
|
|
|
|
|
| 1 |
+
ACCESS_RATE = "100/minute"
|
| 2 |
+
|
features/text_classifier/controller.py
CHANGED
|
@@ -38,7 +38,7 @@ async def handle_file_sentance(file: UploadFile):
|
|
| 38 |
return {"message": "File contains more than 10,000 characters."}
|
| 39 |
cleaned_text = file_contents.replace("\n", " ").replace("\t", " ").strip()
|
| 40 |
if not cleaned_text:
|
| 41 |
-
raise HTTPException(status_code=
|
| 42 |
result = await handle_sentence_level_analysis(cleaned_text)
|
| 43 |
return {"content": file_contents, **result}
|
| 44 |
except Exception as e:
|
|
@@ -53,7 +53,7 @@ async def handle_file_upload(file: UploadFile):
|
|
| 53 |
return {"message": "File contains more than 10,000 characters."}
|
| 54 |
cleaned_text = file_contents.replace("\n", " ").replace("\t", " ").strip()
|
| 55 |
if not cleaned_text:
|
| 56 |
-
raise HTTPException(status_code=
|
| 57 |
label, perplexity, ai_likelihood = await asyncio.to_thread(classify_text, cleaned_text)
|
| 58 |
return {
|
| 59 |
"content": file_contents,
|
|
@@ -77,7 +77,7 @@ async def extract_file_contents(file: UploadFile):
|
|
| 77 |
return parse_txt(file_stream)
|
| 78 |
else:
|
| 79 |
raise HTTPException(
|
| 80 |
-
status_code=
|
| 81 |
detail="Invalid file type. Only .docx, .pdf, and .txt are allowed."
|
| 82 |
)
|
| 83 |
|
|
@@ -85,10 +85,10 @@ async def extract_file_contents(file: UploadFile):
|
|
| 85 |
async def handle_sentence_level_analysis(text: str):
|
| 86 |
text = text.strip()
|
| 87 |
if not text or len(text.split()) < 2:
|
| 88 |
-
raise HTTPException(status_code=
|
| 89 |
|
| 90 |
if len(text) > 10000:
|
| 91 |
-
raise HTTPException(status_code=
|
| 92 |
|
| 93 |
sentences = sent_tokenize(text, language="english")
|
| 94 |
results = []
|
|
|
|
| 38 |
return {"message": "File contains more than 10,000 characters."}
|
| 39 |
cleaned_text = file_contents.replace("\n", " ").replace("\t", " ").strip()
|
| 40 |
if not cleaned_text:
|
| 41 |
+
raise HTTPException(status_code=404, detail="The file is empty or only contains whitespace.")
|
| 42 |
result = await handle_sentence_level_analysis(cleaned_text)
|
| 43 |
return {"content": file_contents, **result}
|
| 44 |
except Exception as e:
|
|
|
|
| 53 |
return {"message": "File contains more than 10,000 characters."}
|
| 54 |
cleaned_text = file_contents.replace("\n", " ").replace("\t", " ").strip()
|
| 55 |
if not cleaned_text:
|
| 56 |
+
raise HTTPException(status_code=404, detail="The file is empty or only contains whitespace.")
|
| 57 |
label, perplexity, ai_likelihood = await asyncio.to_thread(classify_text, cleaned_text)
|
| 58 |
return {
|
| 59 |
"content": file_contents,
|
|
|
|
| 77 |
return parse_txt(file_stream)
|
| 78 |
else:
|
| 79 |
raise HTTPException(
|
| 80 |
+
status_code=404,
|
| 81 |
detail="Invalid file type. Only .docx, .pdf, and .txt are allowed."
|
| 82 |
)
|
| 83 |
|
|
|
|
| 85 |
async def handle_sentence_level_analysis(text: str):
|
| 86 |
text = text.strip()
|
| 87 |
if not text or len(text.split()) < 2:
|
| 88 |
+
raise HTTPException(status_code=413, detail="Text must contain at least two words")
|
| 89 |
|
| 90 |
if len(text) > 10000:
|
| 91 |
+
raise HTTPException(status_code=413, detail="Text must be less than 10,000 characters.")
|
| 92 |
|
| 93 |
sentences = sent_tokenize(text, language="english")
|
| 94 |
results = []
|