"""Conclusiones narrativas generadas por LLM (opcional). Decisión de privacidad importante: al LLM se le envía SOLO el resumen estadístico agregado (medias, porcentajes, alertas) — jamás filas del dataset. Un dataset puede contener datos personales; sus estadísticas agregadas, normalmente no. """ from __future__ import annotations import json import os import requests GROQ_URL = "https://api.groq.com/openai/v1/chat/completions" GROQ_MODEL = "llama-3.3-70b-versatile" SYSTEM_PROMPT = """Eres un analista de datos senior. Te paso el resumen estadístico \ agregado de un dataset (no tienes acceso a las filas) y escribes conclusiones útiles \ en español, en Markdown, con esta estructura: ## Lectura general (2-3 frases: qué pinta tiene el dataset, tamaño, calidad global.) ## Lo más relevante (4-6 viñetas con los hallazgos que un analista destacaría: variables problemáticas, \ correlaciones interesantes, rarezas. Cita números concretos del resumen.) ## Próximos pasos sugeridos (3-5 viñetas accionables y ordenadas: qué limpiar primero, qué explorar después, \ qué precauciones tomar si se va a modelar.) Reglas: no inventes nada que no esté en el resumen; si algo no se puede saber sin \ ver los datos, dilo. Sé concreto y directo, sin relleno.""" def llm_available() -> bool: return bool(os.environ.get("GROQ_API_KEY")) def generate_insights(summary: dict) -> str: """Genera conclusiones a partir del resumen compacto. Lanza RuntimeError si falla.""" if not llm_available(): raise RuntimeError( "No hay GROQ_API_KEY configurada. Las conclusiones narrativas son " "opcionales: el resto del análisis no la necesita." ) payload = json.dumps(summary, ensure_ascii=False, default=str) response = requests.post( GROQ_URL, headers={"Authorization": f"Bearer {os.environ['GROQ_API_KEY']}"}, json={ "model": GROQ_MODEL, "temperature": 0.3, "messages": [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": payload[:20000]}, ], }, timeout=90, ) if response.status_code != 200: raise RuntimeError(f"Groq devolvió {response.status_code}: {response.text[:200]}") return response.json()["choices"][0]["message"]["content"]