hassoudi commited on
Commit
d2237d8
·
verified ·
1 Parent(s): b3cdc28

Upload 6 files

Browse files
Files changed (6) hide show
  1. Dockerfile +11 -0
  2. README.md +34 -11
  3. api_keys.json +7 -0
  4. app.py +70 -0
  5. model.py +10 -0
  6. requirements.txt +5 -0
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY . .
9
+
10
+ EXPOSE 8000
11
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
README.md CHANGED
@@ -1,11 +1,34 @@
1
- ---
2
- title: Magbert Ner Api
3
- emoji: 🏃
4
- colorFrom: yellow
5
- colorTo: indigo
6
- sdk: docker
7
- pinned: false
8
- short_description: magbert-ner-fr-api
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Private BERT NER API on Hugging Face Space
2
+
3
+ This repo contains a private FastAPI-based NER API built on a Hugging Face BERT model.
4
+
5
+ ## Features
6
+ - Private Hugging Face model loading
7
+ - FastAPI REST endpoint for NER
8
+ - Token-based authentication (`X-API-KEY`)
9
+ - Rate limiting per IP using `slowapi`
10
+ - Usage tracking per API key
11
+
12
+ ## Run locally
13
+ ```bash
14
+ export HF_TOKEN=your_hf_token
15
+ export X-API-KEY=your-secret-key
16
+ uvicorn app:app --reload
17
+ ```
18
+
19
+ ## Example request
20
+ ```bash
21
+ curl -X POST http://localhost:8000/ner \
22
+ -H "X-API-KEY: your-secret-key" \
23
+ -H "Content-Type: application/json" \
24
+ -d '{"text": "Barack Obama was the president of the United States."}'
25
+ ```
26
+
27
+ ## Deployment
28
+ Can be deployed to Hugging Face Spaces (with Docker option enabled) or any cloud with Docker support.
29
+
30
+ ### Hugging Face Space Setup
31
+ - Make sure to enable **Docker** runtime
32
+ - Add two secrets:
33
+ - `HF_TOKEN`
34
+ - `X-API-KEY`
api_keys.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "users": {
3
+ "test-key": {
4
+ "usage_count": 0
5
+ }
6
+ }
7
+ }
app.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Depends, HTTPException, Request
2
+ from fastapi.security import APIKeyHeader
3
+ from pydantic import BaseModel
4
+ from model import ner_pipeline
5
+ import logging
6
+ import time
7
+ import json
8
+ import os
9
+ import secrets
10
+ from slowapi import Limiter, _rate_limit_exceeded_handler
11
+ from slowapi.util import get_remote_address
12
+ from slowapi.errors import RateLimitExceeded
13
+
14
+ logging.basicConfig(level=logging.INFO)
15
+ logger = logging.getLogger(__name__)
16
+
17
+ app = FastAPI()
18
+ limiter = Limiter(key_func=get_remote_address)
19
+ app.state.limiter = limiter
20
+ app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
21
+
22
+ API_KEY_FILE = "api_keys.json"
23
+ token_header = APIKeyHeader(name="X-API-KEY")
24
+
25
+ if os.path.exists(API_KEY_FILE):
26
+ with open(API_KEY_FILE, "r") as f:
27
+ API_KEYS_STORE = json.load(f)
28
+ else:
29
+ API_KEYS_STORE = {"users": {}}
30
+
31
+ API_KEYS = API_KEYS_STORE.get("users", {})
32
+ ADMIN_KEY = os.getenv("ADMIN_KEY")
33
+
34
+ class TextRequest(BaseModel):
35
+ text: str
36
+
37
+ class RegisterRequest(BaseModel):
38
+ label: str = "user"
39
+
40
+ def save_keys():
41
+ API_KEYS_STORE["users"] = API_KEYS
42
+ with open(API_KEY_FILE, "w") as f:
43
+ json.dump(API_KEYS_STORE, f, indent=2)
44
+
45
+ def verify_token(x_api_key: str = Depends(token_header)):
46
+ if x_api_key not in API_KEYS:
47
+ raise HTTPException(status_code=403, detail="Unauthorized")
48
+ return x_api_key
49
+
50
+ @app.post("/ner")
51
+ @limiter.limit("10/minute")
52
+ def ner_predict(request: TextRequest, api_key: str = Depends(verify_token), req: Request = None):
53
+ logger.info("Received NER request from IP: %s", get_remote_address(req))
54
+ predictions = ner_pipeline(request.text)
55
+ API_KEYS[api_key]["usage_count"] = API_KEYS[api_key].get("usage_count", 0) + 1
56
+ save_keys()
57
+ return {
58
+ "entities": predictions,
59
+ "usage": API_KEYS[api_key]["usage_count"]
60
+ }
61
+
62
+ @app.post("/register_user")
63
+ def register_user(request: RegisterRequest, x_api_key: str = Depends(token_header)):
64
+ if x_api_key != ADMIN_KEY:
65
+ raise HTTPException(status_code=403, detail="Admin access required")
66
+
67
+ new_key = secrets.token_urlsafe(32)
68
+ API_KEYS[new_key] = {"usage_count": 0, "label": request.label}
69
+ save_keys()
70
+ return {"message": "User registered", "api_key": new_key}
model.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from transformers import AutoTokenizer, AutoModelForTokenClassification, pipeline
3
+
4
+ HF_TOKEN = os.getenv("HF_TOKEN")
5
+ MODEL_ID = "TypicaAI/magbert-ner"
6
+
7
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, use_auth_token=HF_TOKEN)
8
+ model = AutoModelForTokenClassification.from_pretrained(MODEL_ID, use_auth_token=HF_TOKEN)
9
+
10
+ ner_pipeline = pipeline("ner", model=model, tokenizer=tokenizer, aggregation_strategy="first")
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ transformers
2
+ fastapi
3
+ uvicorn
4
+ python-dotenv
5
+ slowapi