Spaces:
Sleeping
Sleeping
Download app.py from pngwn/github-issue-classifier-demo: direct link, hf CLI and curl.
- Browser
- Download file 2.38 kB
-
https://huggingface.co/spaces/pngwn/github-issue-classifier-demo/resolve/main/app.py
- Command line
-
hf download hf://spaces/pngwn/github-issue-classifier-demo/app.py
-
curl -L -o app.py https://huggingface.co/spaces/pngwn/github-issue-classifier-demo/resolve/main/app.py
2.38 kB
| """gradio.Server demo: 4-class GitHub issue classifier with a fully custom frontend. | |
| Backend: gradio.Server (FastAPI + Gradio's API engine — queue, gradio_client | |
| compatibility). One queued endpoint `classify` plus a custom `/` route serving | |
| the hand-written Vercel-style index.html. | |
| Models: pngwn/github-issue-classifier-setfit-minilm and | |
| pngwn/github-issue-classifier-distilbert, trained on pngwn/github-issues-4class. | |
| """ | |
| from pathlib import Path | |
| import numpy as np | |
| import torch | |
| from fastapi.responses import HTMLResponse | |
| from gradio import Server | |
| from setfit import SetFitModel | |
| from transformers import AutoModelForSequenceClassification, AutoTokenizer | |
| LABELS = ["bug", "feature", "question", "support"] | |
| SETFIT_ID = "pngwn/github-issue-classifier-setfit-minilm" | |
| DISTIL_ID = "pngwn/github-issue-classifier-distilbert" | |
| INDEX = Path(__file__).parent / "index.html" | |
| print("loading models (first start takes ~1 minute)...") | |
| setfit_model = SetFitModel.from_pretrained(SETFIT_ID) | |
| distil_tokenizer = AutoTokenizer.from_pretrained(DISTIL_ID) | |
| distil_model = AutoModelForSequenceClassification.from_pretrained(DISTIL_ID).eval() | |
| print("models loaded.") | |
| app = Server() | |
| def run_classify(text: str, model: str) -> dict: | |
| text = (text or "").strip() | |
| if not text: | |
| return {"labels": LABELS, "probs": [0.25, 0.25, 0.25, 0.25], "error": "empty_input"} | |
| if model == "distilbert": | |
| inputs = distil_tokenizer(text, truncation=True, max_length=256, return_tensors="pt") | |
| with torch.no_grad(): | |
| logits = distil_model(**inputs).logits | |
| probs = torch.softmax(logits, dim=-1)[0].numpy() | |
| else: | |
| probs = np.asarray(setfit_model.predict_proba([text])).ravel() | |
| probs = np.asarray(probs, dtype=float).ravel() | |
| assert probs.shape == (4,) | |
| return {"labels": LABELS, "probs": [round(float(p), 4) for p in probs]} | |
| def classify(text: str, model: str) -> dict: | |
| """Classify a GitHub issue as bug / feature / question / support. | |
| Args: | |
| text: issue title and body. | |
| model: "setfit" (22M) or "distilbert" (66M); anything else falls back to setfit. | |
| """ | |
| return run_classify(text, model if model in ("setfit", "distilbert") else "setfit") | |
| async def index() -> str: | |
| return INDEX.read_text() | |
| if __name__ == "__main__": | |
| app.launch() |