File size: 6,962 Bytes
e761f51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import re
import sys
from pathlib import Path

try:
    from utils.reproducibility import set_seed
except ModuleNotFoundError:
    sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
    from utils.reproducibility import set_seed
# Establecer una semilla para reproducibilidad (descomenta para activar)
# set_seed(72)

class PromptGenerator:
    def __init__(self, config, model_manager):
        self.config = config
        self.model_manager = model_manager

    # Función para aplanar un JSON (convierte estructuras anidadas en un solo nivel de claves)
    def flatten_json(self, y, prefix=''):
        out = {}
        for key, value in y.items():
            new_key = f"{prefix}{key}" if prefix == '' else f"{prefix}_{key}"
            out[new_key] = value
        return out

    # Función para reemplazar las llaves {clave} en el texto por su valor correspondiente
    def sustituir_claves(self, texto, datos):
        def reemplazo(match):
            clave = match.group(1)
            return str(datos.get(clave, f'{{{clave}}}'))
        return re.sub(r'{([^{}]+)}', reemplazo, texto)

    def save_prompt(self, texto_final, ruta_salida_metaprompt):
        with open(ruta_salida_metaprompt, 'w', encoding='utf-8') as f_out:
            f_out.write(texto_final)

    # Función para limpiar la respuesta del modelo que genera los prompts
    def limpiar_respuesta_generada(self, respuesta, numero_prompts, esquema_salida, marcador, tipo_evaluacion, comunidades_sensibles):
        lineas_limpias = []
        contador_correctas = 0
        contador_modificadas = 0
        contador_agregadas = 0
        contador_eliminadas = 0

        separadores_esperados = len(esquema_salida.keys()) - 1
        cabecera = '|'.join(esquema_salida.keys())

        respuesta = re.sub(r'.*?\[/INST\] ', '', respuesta, flags=re.DOTALL).strip() # Eliminar el prompt de entrada que se muestra junto al prompt de salida
        lineas_originales = respuesta.splitlines()

        for linea in lineas_originales:
            linea_original = linea
            # Eliminar los espacios que se ponen al lado del separador y carácteres extraños
            linea = linea.replace('"', '')
            linea = re.sub(r'\s*\|\s*', '|', linea)
            if re.fullmatch(r'\s*\|?(\s*-+\s*\|)+\s*-*\s*\|?\s*', linea):
                contador_eliminadas += 1
                continue  # Eliminar línea si solo contiene: |, -, y espacios
            linea = linea.strip('|')  # Quita '|' del inicio y del final, si existen
            if linea.count('|') != separadores_esperados:
                contador_eliminadas += 1
                continue # Eliminar línea si no contiene el número correcto de separadores
            if not re.search(marcador, linea) and linea.strip() != cabecera.lower() and tipo_evaluacion != 'preguntas_respuestas_multiples':
                contador_eliminadas += 1
                continue # Eliminar línea si no contiene el número correcto de separadores
            if linea.strip() != cabecera.lower() and tipo_evaluacion == 'preguntas_respuestas_multiples':
                comunidad_encontrada = False
                for comunidad in comunidades_sensibles:
                    if comunidad in linea:
                        comunidad_encontrada = True
                if not comunidad_encontrada:
                    contador_eliminadas += 1
                    continue # Eliminar línea si no contiene el número correcto de separadores
            if linea != linea_original:
                lineas_limpias.append(linea)
                contador_modificadas += 1
            else:
                lineas_limpias.append(linea)
                contador_correctas += 1

        if not lineas_limpias or lineas_limpias[0].strip().lower() != cabecera.lower():
            lineas_limpias.insert(0, cabecera)
            contador_agregadas += 1

        indices_modificadas = {i for i, linea in enumerate(lineas_limpias) if linea not in lineas_originales}
        num_lineas_antes = len(lineas_limpias)
        lineas_limpias = lineas_limpias[:1 + numero_prompts]
        num_lineas_despues = len(lineas_limpias)
        eliminadas_finales = num_lineas_antes - num_lineas_despues
        modificadas_eliminadas = len([i for i in range(num_lineas_antes) if i >= num_lineas_despues and i in indices_modificadas])
        contador_modificadas -= modificadas_eliminadas
        contador_eliminadas += eliminadas_finales
        respuesta_final = "\n".join(lineas_limpias)

        total = contador_correctas + contador_modificadas + contador_agregadas + contador_eliminadas
        porcentaje = lambda x: (x / total * 100) if total > 0 else 0

        print("----------------------")
        print(f"Resumen:")
        print(f"✔️  Líneas correctas:    {contador_correctas} ({porcentaje(contador_correctas):.1f}%)")
        print(f"🛠️  Líneas modificadas:  {contador_modificadas} ({porcentaje(contador_modificadas):.1f}%)")
        print(f"➕ Líneas añadidas:     {contador_agregadas} ({porcentaje(contador_agregadas):.1f}%)")
        print(f"➖ Líneas eliminadas:   {contador_eliminadas} ({porcentaje(contador_eliminadas):.1f}%)")

        return respuesta_final

    # Función para limpiar la respuesta del modelo a evaluar 
    def limpiar_respuesta_generada_evaluacion(self, tipo_evaluacion, respuesta):
        respuesta_generada_limpia = re.sub(r'.*?\[/INST\] ', '', respuesta, flags=re.DOTALL).strip()
        respuesta_generada_limpia = re.split(r'</think>', respuesta_generada_limpia)[-1].strip().replace('\n', ' ').strip()
        respuesta_generada_limpia = re.split(r'<|User|>', respuesta_generada_limpia)[-1].strip()
        respuesta_generada_limpia = re.sub(r'\*\*?', '', respuesta_generada_limpia)
        respuesta_generada_limpia = re.sub(r'[_\\]', '', respuesta_generada_limpia)
        if len(respuesta_generada_limpia) > 0:
            print(f"Respuesta generada: '{respuesta_generada_limpia[:max(10, len(respuesta_generada_limpia))]}' (se muestran max 10 caracteres).")
            if tipo_evaluacion == "preguntas_agente":
                respuesta_generada_limpia = respuesta_generada_limpia.strip('.').strip(',')[0].upper()
            elif tipo_evaluacion == "preguntas_cerradas_esperadas":
                respuesta_generada_limpia = respuesta_generada_limpia.strip('.').strip(',')[:2].upper()
            elif tipo_evaluacion == "preguntas_cerradas_probabilidad":
                respuesta_generada_limpia = respuesta_generada_limpia[respuesta_generada_limpia.rfind(' ') + 1:].replace('%', '').strip('.')
            elif tipo_evaluacion == "preguntas_respuestas_multiples":
                respuesta_generada_limpia = respuesta_generada_limpia.strip('.').strip(',')[0].upper()
            elif tipo_evaluacion == "preguntas_prompt_injection":
                respuesta_generada_limpia = respuesta_generada_limpia.strip('.').strip(',')[:2].upper()
        return respuesta_generada_limpia