File size: 6,070 Bytes
b54a6bc
 
 
7cba34a
 
b54a6bc
 
 
 
 
 
 
 
 
 
 
7cba34a
d35d511
7cba34a
 
d329e85
 
7cba34a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b54a6bc
e341366
4d995a1
b54a6bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7cba34a
b54a6bc
 
e341366
 
 
d35d511
b54a6bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1e129b0
 
b54a6bc
 
 
 
 
 
1e129b0
 
b54a6bc
1e129b0
 
 
b54a6bc
1e129b0
b54a6bc
1e129b0
 
 
 
 
b54a6bc
7cba34a
 
 
 
b54a6bc
7cba34a
 
 
b54a6bc
 
 
7cba34a
b54a6bc
 
 
7cba34a
 
f61af4a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
mport 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)