File size: 4,615 Bytes
1cca871 1fce961 1cca871 11d1836 d420fe2 1fce961 1cca871 1fce961 1cca871 d420fe2 3a03e86 1fce961 3a03e86 1fce961 3a03e86 1fce961 9a16674 3a03e86 9a16674 3a03e86 9a16674 3a03e86 9a16674 3a03e86 9a16674 3a03e86 9a16674 3a03e86 55012e5 9a16674 55012e5 3a03e86 55012e5 1cca871 55012e5 d420fe2 1cca871 55012e5 1cca871 d420fe2 11d1836 55012e5 11d1836 d420fe2 3a03e86 d420fe2 1cca871 d420fe2 1cca871 d420fe2 1cca871 d420fe2 1cca871 afd5b3c d420fe2 3a03e86 d420fe2 3a03e86 d420fe2 3a03e86 d420fe2 3a03e86 d420fe2 1cca871 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | import spaces
import os
import gradio as gr
from transformers import pipeline
from fastapi import HTTPException
from pydantic import BaseModel, ConfigDict
from typing import List
# ββ Model ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ner = pipeline(
"ner",
model="Ramosh99/mixcode_pii_detector",
aggregation_strategy="average",
token=os.getenv("HF_TOKEN"),
)
# ββ Core inference (ZeroGPU β GPU used only during inference call) ββββββββββββββ
@spaces.GPU
def detect_pii(text: str):
"""Run NER and return structured PII results (used by REST API)."""
if not text.strip():
return {"text": text, "entities": []}
raw = ner(text)
entities = []
for item in raw:
label = item.get("entity_group", "")
if label.startswith(("B-", "I-")):
label = label[2:]
entities.append({
"entity": label,
"start": item["start"],
"end": item["end"],
"score": round(float(item["score"]), 4),
})
return {"text": text, "entities": entities}
def detect_pii_ui(text: str):
"""Return list-of-tuples for Gradio HighlightedText (used by UI)."""
res = detect_pii(text)
entities = sorted(res["entities"], key=lambda x: x["start"])
if not entities:
return [(text, None)]
result, last = [], 0
for ent in entities:
if ent["start"] > last:
result.append((text[last:ent["start"]], None))
result.append((text[ent["start"]:ent["end"]], ent["entity"]))
last = ent["end"]
if last < len(text):
result.append((text[last:], None))
return result
# ββ Gradio Interface βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
demo = gr.Interface(
fn=detect_pii_ui,
inputs=gr.Textbox(
label="Input Text",
placeholder="Enter Sinhala-English codemixed text...",
lines=4,
),
outputs=gr.HighlightedText(label="Detected PII Entities"),
title="Sinhala-English Codemix PII Detector",
description=(
"Detects PII entities (PERSON, ORG, PHONE, EMAIL, NIC, etc.) in "
"Sinhala-English code-mixed text.\n\n"
"**REST API:** `POST /api/detect` Β· `GET /api/health` Β· Docs: `/docs`"
),
examples=[
["Mage name Kamal, man wada karanne MAS Holdings eke, phone 071-1234567"],
["ΰΆΈΰΆΰ· email address kamal.perera@gmail.com, NIC number 199912345678"],
["I live in Colombo and my phone number is 0771234567"],
],
flagging_mode="never",
)
# ββ Launch Gradio β properly registers @spaces.GPU with ZeroGPU βββββββββββββββ
gr_app, _local_url, _share_url = demo.launch(
server_name="0.0.0.0",
server_port=7860,
prevent_thread_lock=True,
show_api=False,
)
# ββ REST API routes (added to Gradio's internal FastAPI app) βββββββββββββββββββ
class DetectRequest(BaseModel):
model_config = ConfigDict(
json_schema_extra={"example": {"text": "Mage name Kamal, phone 071-1234567"}}
)
text: str
class Entity(BaseModel):
entity: str
start: int
end: int
score: float
class DetectResponse(BaseModel):
text: str
entities: List[Entity]
redacted_text: str
@gr_app.get("/api/health")
def health_check():
return {"status": "ok", "model": "Ramosh99/mixcode_pii_detector"}
@gr_app.post("/api/detect", response_model=DetectResponse)
async def api_detect_pii(request: DetectRequest):
if not request.text.strip():
raise HTTPException(status_code=422, detail="Input text cannot be empty.")
result = detect_pii(request.text)
text = result["text"]
entities = sorted(result["entities"], key=lambda e: e["start"], reverse=True)
redacted = text
for ent in entities:
redacted = redacted[: ent["start"]] + f"[{ent['entity']}]" + redacted[ent["end"]:]
return {
"text": result["text"],
"entities": result["entities"],
"redacted_text": redacted,
}
# ββ Keep process alive βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
demo.block_thread()
|