khasi-ner-api / app.py
RansMairoi's picture
Initial NER service: FastAPI + spaCy/RoBERTa pipeline
e0abc36
Raw
History Blame Contribute Delete
1.66 kB
"""
Khasi NER service β€” runs the trained spaCy/RoBERTa pipeline behind a tiny
JSON API. Deployed to a Hugging Face Space; called by the Khasi NLP backend
on Render so the heavy model never has to live on the (small) Render box.
"""
import os
from typing import Optional
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
import spacy
# Optional shared secret. Set NER_API_KEY in the Space's "Variables and
# secrets" panel β€” Render sends the same value in the X-Api-Key header.
# If unset, the endpoint is open (fine for local dev, not production).
API_KEY = os.environ.get("NER_API_KEY", "")
# Load once at process boot so every request reuses the warm model.
nlp = spacy.load("model-best")
NER_LABELS = list(nlp.get_pipe("ner").labels)
print(f"[khasi-ner] loaded β€” components: {nlp.pipe_names} | labels: {NER_LABELS}")
app = FastAPI(title="Khasi NER", version="1.0.0")
class NerRequest(BaseModel):
text: str
@app.get("/")
def root():
return {"service": "khasi-ner", "endpoints": ["/health", "/ner"]}
@app.get("/health")
def health():
return {"ok": True, "labels": NER_LABELS}
@app.post("/ner")
def ner(req: NerRequest, x_api_key: Optional[str] = Header(default=None)):
if API_KEY and x_api_key != API_KEY:
raise HTTPException(status_code=401, detail="invalid api key")
doc = nlp(req.text)
return {
"text": doc.text,
"entities": [
{
"text": ent.text,
"label": ent.label_,
"start": ent.start_char,
"end": ent.end_char,
}
for ent in doc.ents
],
}