DemoProfeIA / app.py
cesar's picture
Update app.py
00832a8 verified
raw
history blame
6.07 kB
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)