Pujan-Dev commited on
Commit
805e1e5
·
1 Parent(s): df01ffa

feat: Added the rate limiting per route

Browse files
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 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 {
39
- "Message": "FastAPI is running...",
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
-
 
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 = "100/minute"
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()) < 2:
27
  raise HTTPException(status_code=400, detail="Text must contain at least two words")
28
  if len(text) > 10000:
29
- raise HTTPException(status_code=400, detail="The text should 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
 
 
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 .controller import handle_text_analysis, handle_file_upload, handle_sentence_level_analysis, handle_file_sentance
5
- from .controller import verify_token
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
- async def analyze(data: TextInput, token: str = Depends(verify_token)):
 
17
  return await handle_text_analysis(data.text)
18
 
19
  @router.post("/upload")
20
- async def upload_file(file: UploadFile = File(...), token: str = Depends(verify_token)):
 
21
  return await handle_file_upload(file)
22
 
23
  @router.post("/analyse-sentences")
24
- async def analyze_sentences(data: TextInput, token: str = Depends(verify_token)):
 
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
- async def AnalyzeSentanceFile(file: UploadFile = File(...), token: str = Depends(verify_token)):
 
31
  return await handle_file_sentance(file)
32
 
33
  @router.get("/health")
34
- def health():
 
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