vichter commited on
Commit
be29f4f
·
verified ·
1 Parent(s): e7569f6

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +76 -0
app.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException, Header, Depends, Request
2
+ from pydantic import BaseModel
3
+ from gliner import GLiNER
4
+ from slowapi import Limiter, _rate_limit_exceeded_handler
5
+ from slowapi.util import get_remote_address
6
+ from slowapi.errors import RateLimitExceeded
7
+ import logging
8
+ import os
9
+
10
+ logging.basicConfig(level=logging.INFO)
11
+ logger = logging.getLogger(__name__)
12
+
13
+ limiter = Limiter(key_func=get_remote_address)
14
+
15
+ app = FastAPI(title="Panoptifi NER API")
16
+ app.state.limiter = limiter
17
+ app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
18
+
19
+ API_KEY = os.environ.get("API_KEY", "")
20
+
21
+
22
+ def verify_api_key(x_api_key: str = Header(None, alias="X-API-Key")):
23
+ if API_KEY and x_api_key != API_KEY:
24
+ raise HTTPException(status_code=401, detail="Invalid API key")
25
+ return True
26
+
27
+
28
+ logger.info("Loading GLiNER model...")
29
+ model = GLiNER.from_pretrained("urchade/gliner_small")
30
+ logger.info("Model loaded")
31
+
32
+ DEFAULT_LABELS = ["company", "stock_ticker", "executive", "regulator", "product", "location"]
33
+
34
+
35
+ class NERInput(BaseModel):
36
+ text: str
37
+ labels: list[str] | None = None
38
+
39
+
40
+ class Entity(BaseModel):
41
+ text: str
42
+ label: str
43
+ score: float
44
+ start: int
45
+ end: int
46
+
47
+
48
+ class NERResult(BaseModel):
49
+ entities: list[Entity]
50
+
51
+
52
+ @app.get("/health")
53
+ @limiter.limit("60/minute")
54
+ def health(request: Request):
55
+ return {"status": "healthy", "model": "urchade/gliner_small"}
56
+
57
+
58
+ @app.post("/extract", response_model=NERResult)
59
+ @limiter.limit("30/minute")
60
+ def extract_entities(request: Request, input: NERInput, _: bool = Depends(verify_api_key)):
61
+ if not input.text.strip():
62
+ raise HTTPException(400, "Text cannot be empty")
63
+
64
+ labels = input.labels or DEFAULT_LABELS
65
+ entities = model.predict_entities(input.text[:2000], labels, threshold=0.5)
66
+
67
+ return NERResult(entities=[
68
+ Entity(
69
+ text=e["text"],
70
+ label=e["label"],
71
+ score=e["score"],
72
+ start=e["start"],
73
+ end=e["end"]
74
+ )
75
+ for e in entities
76
+ ])