Spaces:
Runtime error
Runtime error
| import os | |
| import json | |
| import logging | |
| import io | |
| from typing import Dict, Any | |
| from fastapi import FastAPI, UploadFile, File, HTTPException, Depends | |
| from fastapi.responses import FileResponse, JSONResponse | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel, Field, field_validator | |
| from groq import Groq | |
| import pdfplumber | |
| # ==================== CONFIGURACIÓN ==================== | |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') | |
| logger = logging.getLogger(__name__) | |
| app = FastAPI(title="PragmaLens API", version="1.1.0") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # ==================== DEPENDENCIAS ==================== | |
| def get_groq_client(): | |
| api_key = os.getenv("GROQ_API_KEY") | |
| if not api_key: | |
| raise HTTPException(status_code=500, detail="GROQ_API_KEY no configurada") | |
| return Groq(api_key=api_key) | |
| # ==================== MODELOS ==================== | |
| class TextInput(BaseModel): | |
| text: str = Field(..., min_length=1) | |
| def text_not_empty(cls, v: str) -> str: | |
| if not v.strip(): | |
| raise ValueError('El texto no puede estar vacío') | |
| return v.strip() | |
| # ==================== LÓGICA DE ANÁLISIS ==================== | |
| def run_audit(text: str, client: Groq) -> Dict[str, str]: | |
| # El prompt instruye a la IA para detectar el idioma automáticamente y responder en el mismo | |
| system_prompt = ( | |
| "You are an expert polyglot discourse analyst. " | |
| "1. Automatically detect the language of the input text. " | |
| "2. Analyze for: Grice's Maxims, Pragmatic markers, Ambiguity, and Logical fallacies. " | |
| "3. Respond in the same language as the input text. " | |
| "Return ONLY a raw JSON object with these exact keys: " | |
| "'grice_maxims', 'pragmatics', 'ambiguity', 'fallacies'. " | |
| "Each value must be a single continuous string." | |
| ) | |
| try: | |
| response = client.chat.completions.create( | |
| model="llama-3.3-70b-versatile", | |
| messages=[ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": text[:4000]} | |
| ], | |
| response_format={"type": "json_object"}, | |
| temperature=0.1 | |
| ) | |
| return json.loads(response.choices[0].message.content) | |
| except Exception as e: | |
| logger.error(f"Error en auditoría: {e}") | |
| return { | |
| "grice_maxims": "Error processing", | |
| "pragmatics": "Error processing", | |
| "ambiguity": "Error processing", | |
| "fallacies": "Error processing" | |
| } | |
| # ==================== ENDPOINTS ==================== | |
| async def root(): | |
| return FileResponse("index.html") | |
| async def analyze(payload: TextInput, client: Groq = Depends(get_groq_client)): | |
| return run_audit(payload.text, client) | |
| async def analyze_pdf(file: UploadFile = File(...), client: Groq = Depends(get_groq_client)): | |
| if not file.filename.lower().endswith('.pdf'): | |
| raise HTTPException(status_code=400, detail="Solo se aceptan archivos PDF") | |
| content = await file.read() | |
| with pdfplumber.open(io.BytesIO(content)) as pdf: | |
| # Extraemos hasta 10 páginas para optimizar tokens | |
| text = "\n".join([p.extract_text() or "" for p in pdf.pages[:10]]) | |
| if not text.strip(): | |
| raise HTTPException(status_code=400, detail="No se pudo extraer texto del PDF") | |
| return run_audit(text, client) |