import gradio as gr import PyPDF2 import os import json import vertexai from vertexai.generative_models import GenerativeModel, Part, SafetySetting # Configuración global generation_config = { "max_output_tokens": 4096, "temperature": 0, "top_p": 0.8, } safety_settings = [ SafetySetting( category=SafetySetting.HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold=SafetySetting.HarmBlockThreshold.OFF ), SafetySetting( category=SafetySetting.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold=SafetySetting.HarmBlockThreshold.OFF ), SafetySetting( category=SafetySetting.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold=SafetySetting.HarmBlockThreshold.OFF ), SafetySetting( category=SafetySetting.HarmCategory.HARM_CATEGORY_HARASSMENT, threshold=SafetySetting.HarmBlockThreshold.OFF ), ] def configurar_credenciales(json_path: str): os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = json_path def extraer_texto(pdf_path: str) -> str: texto_total = "" with open(pdf_path, "rb") as f: lector = PyPDF2.PdfReader(f) for page in lector.pages: texto_total += page.extract_text() or "" return texto_total def parsear_con_llm(texto_pdf: str, model: GenerativeModel) -> dict: """ Usa el LLM para extraer preguntas y respuestas: - Considera que 'Explicá' o 'Explica' o 'Explique' es una pregunta, aunque no diga 'Pregunta:'. - Reconoce 'RESPUESTA', 'RESPUESTAS', con o sin dos puntos, como inicio de la respuesta. """ prompt = f""" Eres un parser de texto. Te entrego el contenido de un PDF con una pregunta o varias, y su(s) respuesta(s). Usa estas reglas para interpretarlo: 1. Si ves 'Explicá', 'Explica', 'Explique', 'Teniendo en cuenta que...' o algo similar, asúmelo como una pregunta. Si no hay 'Pregunta:' literal, aun así consideralo pregunta. 2. Las respuestas podrían estar marcadas como 'RESPUESTAS', 'RESPUESTA', 'RESPUESTAS:', etc. 3. Devuelve un JSON con la estructura: {{ "Pregunta 1": "Texto de la respuesta" }} Si hay más de una pregunta, enumerarlas como 'Pregunta 2', etc. 4. Si no hay nada reconocible, devuelve {{}}. Texto PDF: {texto_pdf} Devuelve solo el JSON, sin explicaciones ni texto extra. """ part_text = Part.from_text(prompt) response = model.generate_content( [part_text], generation_config=generation_config, safety_settings=safety_settings, stream=False ) try: data = json.loads(response.text.strip()) if isinstance(data, dict): return data else: return {} except: return {} def comparar_preguntas_respuestas(dict_docente: dict, dict_alumno: dict) -> str: retroalimentacion = [] for pregunta, resp_correcta in dict_docente.items(): resp_alumno = dict_alumno.get(pregunta, None) if resp_alumno is None: retroalimentacion.append( f"**{pregunta}**\nNo fue asignada al alumno.\n" ) else: retroalimentacion.append( f"**{pregunta}**\n" f"Respuesta del alumno: {resp_alumno}\n" f"Respuesta correcta: {resp_correcta}\n" ) return "\n".join(retroalimentacion) def revisar_examen(json_cred, pdf_docente, pdf_alumno): try: configurar_credenciales(json_cred.name) vertexai.init(project="deploygpt", location="us-central1") texto_docente = extraer_texto(pdf_docente.name) texto_alumno = extraer_texto(pdf_alumno.name) # 1) Instanciar el modelo model = GenerativeModel( "gemini-1.5-pro-001", system_instruction=["Eres un parser estricto."] ) # 2) Convertir PDF Docente y Alumno en dict {Pregunta X: Respuesta X} dict_docente = parsear_con_llm(texto_docente, model) dict_alumno = parsear_con_llm(texto_alumno, model) # 3) Comparar y generar feedback feedback = comparar_preguntas_respuestas(dict_docente, dict_alumno) if len(feedback.strip()) < 5: return "No se encontraron preguntas o respuestas válidas." # 4) Generar un resumen summary_prompt = f""" Eres un profesor experto de bioquímica. Te muestro la comparación de preguntas y respuestas: {feedback} Por favor, genera un breve resumen del desempeño del alumno sin inventar preguntas adicionales. """ summary_part = Part.from_text(summary_prompt) summary_resp = model.generate_content( [summary_part], generation_config=generation_config, safety_settings=safety_settings, stream=False ) return f"{feedback}\n\n**Resumen**\n{summary_resp.text.strip()}" except Exception as e: return f"Error al procesar: {str(e)}" import gradio as gr interface = gr.Interface( fn=revisar_examen, inputs=[ gr.File(label="Credenciales JSON"), gr.File(label="PDF Docente"), gr.File(label="PDF Alumno") ], outputs=gr.Markdown(), title="Revisión de Exámenes con LLM (Permisivo)", description=("Sube credenciales, el PDF del docente y del alumno; " "se emplea un LLM para encontrar 'Explicá' y 'RESPUESTAS' etc. " "y evitar alucinaciones.") ) interface.launch(debug=True)