""" API HTTP do corretor de redacao ENEM. Endpoints: GET /health -> {"status": "ok"} POST /corrigir -> body {"texto": "..."}, retorna notas C1..C5 + total """ from __future__ import annotations from contextlib import asynccontextmanager from typing import Optional from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel, Field from corretor import corrigir, diagnosticar, load_models @asynccontextmanager async def lifespan(app: FastAPI): # Pre-carrega os 5 modelos no startup para a primeira request nao pagar o custo. load_models() yield app = FastAPI( title="Dissero - Corretor ENEM", description="API de correcao automatica de redacao ENEM (modelos kamel-usp).", version="0.1.0", lifespan=lifespan, ) # CORS restrito: Vite local + deploys do Vercel (production + previews). ALLOWED_ORIGINS = [ "http://localhost:5173", "http://localhost:4173", "https://dissero.vercel.app", "https://dissertaai.vercel.app", "https://dissero-git-main-tonetos-projects.vercel.app", ] app.add_middleware( CORSMiddleware, allow_origins=ALLOWED_ORIGINS, allow_origin_regex=r"^https://dissero-[a-z0-9-]+-tonetos-projects\.vercel\.app$", allow_credentials=True, allow_methods=["POST", "GET", "OPTIONS"], allow_headers=["*"], ) class RedacaoRequest(BaseModel): texto: str = Field(..., min_length=1, description="Texto da redacao em portugues.") class C5Boost(BaseModel): de: int para: int elementos: int class CorrecaoResponse(BaseModel): C1: int C2: int C3: int C4: int C5: int total: int # Metadados (transparencia da decisao) modelo_usado: Optional[str] = None jbcs_total: Optional[int] = None local_total: Optional[int] = None c5_boost: Optional[C5Boost] = None @app.get("/health") def health() -> dict: return {"status": "ok"} @app.post("/corrigir", response_model=CorrecaoResponse) def corrigir_endpoint(req: RedacaoRequest) -> dict: raw = corrigir(req.texto) # Mapeia underscores internos pra nomes publicos do schema out = { "C1": raw["C1"], "C2": raw["C2"], "C3": raw["C3"], "C4": raw["C4"], "C5": raw["C5"], "total": raw["total"], "modelo_usado": raw.get("_modelo_usado"), "jbcs_total": raw.get("_jbcs_total"), "local_total": raw.get("_local_total"), } boost = raw.get("_c5_heuristica_boost") if boost: out["c5_boost"] = { "de": boost["de"], "para": boost["para"], "elementos": boost["elementos"], } return out @app.post("/debug") def debug_endpoint(req: RedacaoRequest) -> dict: """Diagnostico completo: logits, softmax, e varias estrategias de decoding.""" return diagnosticar(req.texto)