"""PIRD REST API (Hugging Face Spaces, docker SDK). Serves the trained PIRD checkpoint (./pird_deploy) as a JSON API for external front-ends. CORS is open so the Next.js site (Vercel) can call it from the browser. """ import os from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from pird.detectors.pird import PIRDDetector MIN_WORDS = 20 MAX_CHARS = 20000 detector = PIRDDetector(os.environ.get("PIRD_CKPT", "pird_deploy")) app = FastAPI( title="PIRD API", description="Paraphrase-robust, calibrated AI-generated-text detection.", version="1.0.0", ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) class PredictRequest(BaseModel): text: str @app.get("/") def root(): return { "service": "PIRD — Paraphrase-Robust AI-Text Detector", "status": "ok", "usage": "POST /predict with JSON body {\"text\": \"...\"} (>= 20 words)", "disclaimer": ("Research demo. Predictions are probabilistic and not infallible; " "do not use as sole evidence of misconduct."), } @app.post("/predict") def predict(req: PredictRequest): text = (req.text or "").strip() if len(text) > MAX_CHARS: text = text[:MAX_CHARS] n_words = len(text.split()) if n_words < MIN_WORDS: raise HTTPException( status_code=422, detail=f"Please provide at least {MIN_WORDS} words for a reliable estimate " f"(got {n_words}).", ) p = float(detector.predict_proba([text])[0]) return { "p_ai": round(p, 4), "label": "ai" if p >= 0.5 else "human", "words": n_words, "calibrated": True, }