import gradio as gr import PyPDF2 import os import vertexai from vertexai.generative_models import GenerativeModel, Part, SafetySetting import base64 """ Este código se encarga de: 1. Leer un archivo de credenciales JSON para configurar Google Cloud. 2. Inicializar Vertex AI en la región us-central1. 3. Extraer preguntas y respuestas de dos PDFs: uno del docente y otro del alumno. 4. Filtrar únicamente las preguntas realmente respondidas por el alumno. 5. Enviar ese contenido filtrado al modelo generativo (Gemini 1.5), con instrucciones para que NO mencione preguntas no respondidas. """ # Configuración del modelo y parámetros globales generation_config = { "max_output_tokens": 8192, "temperature": 0, "top_p": 0.75, } 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: """Extraer texto de todas las páginas de un PDF.""" 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_preguntas_respuestas(texto: str) -> dict: """Dado un texto con formato, retorna un dict {pregunta: respuesta}.""" # Buscamos líneas que inicien con "Pregunta" y "Respuesta" lineas = texto.split("\n") resultado = {} pregunta_actual = None for linea in lineas: linea_str = linea.strip() if linea_str.lower().startswith("pregunta"): pregunta_actual = linea_str resultado[pregunta_actual] = "" elif linea_str.lower().startswith("respuesta") and pregunta_actual: # No mezclamos en la misma línea "Pregunta X:" # sino que esperamos "Pregunta X" en una línea y "Respuesta X" en la siguiente # si el formateo es distinto, ajusta aquí. # Tomamos lo que está después de ':' partes = linea_str.split(":", 1) if len(partes) > 1: respuesta = partes[1].strip() resultado[pregunta_actual] = respuesta return resultado def revisar_examen(json_cred, pdf_docente, pdf_alumno): try: # Configurar credenciales configurar_credenciales(json_cred.name) # Inicializar Vertex AI vertexai.init(project="deploygpt", location="us-central1") # Extraer texto de ambos PDFs docente_texto = extraer_texto(pdf_docente.name) alumno_texto = extraer_texto(pdf_alumno.name) # Parsear preguntas y respuestas preguntas_docente = parsear_preguntas_respuestas(docente_texto) respuestas_alumno = parsear_preguntas_respuestas(alumno_texto) # Filtrar solo preguntas respondidas preguntas_filtradas = {} for pregunta_doc, resp_doc in preguntas_docente.items(): if pregunta_doc in respuestas_alumno: # El alumno respondió esta pregunta preguntas_filtradas[pregunta_doc] = { "respuesta_doc": resp_doc, "respuesta_alumno": respuestas_alumno[pregunta_doc] } if not preguntas_filtradas: return "El alumno no respondió ninguna de las preguntas del docente." # Construir un texto que contenga únicamente las preguntas respondidas # e instrucciones claras para no alucinar preguntas. # Vamos a pasarlo en 1 solo Part, para forzar a que la LLM no confunda. contenido_final = """Instrucciones: Solo hay estas preguntas respondidas por el alumno. No menciones preguntas que no estén en esta lista. Para cada pregunta, analiza la respuesta. Al final, da un resumen. """ for i, (p, data) in enumerate(preguntas_filtradas.items(), 1): contenido_final += f"\nPregunta {i}: {p}\n" \ f"Respuesta del alumno: {data['respuesta_alumno']}\n" \ f"Respuesta correcta (docente): {data['respuesta_doc']}\n" # Creamos un Part con el contenido filtrado part_filtrado = Part( mime_type="text/plain", text=contenido_final, ) # System instruction, for clarity textsi_1 = """Actúa como un asistente de docente experto en Bioquímica. No menciones preguntas que el alumno no respondió. Analiza únicamente las preguntas provistas en el texto. Calcula un porcentaje de precisión basado en las respuestas incluidas. """ model = GenerativeModel( "gemini-1.5-pro-001", system_instruction=[textsi_1] ) # Llamada al modelo con las partes. response = model.generate_content( [part_filtrado], generation_config=generation_config, safety_settings=safety_settings, stream=False, ) return response.text except Exception as e: return f"Error al procesar: {str(e)}" # Interfaz Gradio interface = gr.Interface( fn=revisar_examen, inputs=[ gr.File(label="Credenciales JSON"), gr.File(label="PDF Docente"), gr.File(label="PDF Alumno") ], outputs=gr.Textbox(label="Resultado"), title="Revisión de Exámenes", description="Sube tus credenciales, el PDF del docente y el del alumno para revisar las respuestas sin alucinaciones." ) interface.launch(debug=True)