File size: 10,524 Bytes
2c2f50d
 
 
 
 
 
 
 
 
 
2108de0
21a807d
2108de0
e0ecd33
 
 
 
e4f7e04
 
 
53a4ac5
e4f7e04
 
 
53a4ac5
 
 
e4f7e04
2108de0
088ccd5
53a4ac5
 
2108de0
53a4ac5
 
c52e98f
2108de0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a83f5b8
c52e98f
2108de0
 
e4f7e04
2108de0
a83f5b8
e4f7e04
2108de0
53a4ac5
 
2108de0
e4f7e04
2108de0
 
 
 
6f6776f
2108de0
6f6776f
 
 
 
 
 
6440885
 
bde368b
2108de0
 
 
 
 
21a807d
 
2108de0
 
 
 
 
01fb88d
bde368b
2108de0
53a4ac5
2108de0
 
 
e4f7e04
2108de0
 
 
 
bde368b
01fb88d
2108de0
53a4ac5
 
2108de0
 
 
 
 
01fb88d
53a4ac5
 
01fb88d
2108de0
 
 
 
 
 
 
 
 
 
 
 
 
01fb88d
bde368b
2108de0
53a4ac5
2108de0
 
 
 
21a807d
53a4ac5
2108de0
5d02f0a
e0ecd33
 
 
 
2108de0
 
 
 
 
 
 
e0ecd33
 
 
 
 
 
2108de0
 
 
 
 
e0ecd33
53a4ac5
6f6776f
bde368b
 
53a4ac5
bde368b
e4f7e04
 
6f6776f
01fb88d
6f6776f
53a4ac5
 
01fb88d
bde368b
 
53a4ac5
bde368b
01fb88d
bde368b
53a4ac5
 
 
bde368b
e0ecd33
53a4ac5
2108de0
 
21a807d
6f6776f
 
 
5d02f0a
6f6776f
bde368b
2108de0
e4f7e04
 
2108de0
 
 
e4f7e04
2108de0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e4f7e04
2108de0
e4f7e04
2108de0
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# ====================    Interprete de manchas de Test de Rorschach   =====================================
#
#    (o\ | /o)
#     \.\_/. /
# .--._/oo\_.--.
# \ VVVV/ \VVVV /
#  \____/ \____/
# __________________________________________________________________________________________________________
# DIANA MILENA SOLER Psicologa Est Medicina U. Juan N Corpas JAIRO ALEXANDER ERASO MD U Nacional de Colombia
# 

import streamlit as st
import google.generativeai as genai # Import Gemini
from docx import Document
import tempfile
import os
import re
import logging
import datetime

# Configurar logging para monitorear las llamadas al API
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler()
    ]
)
logger = logging.getLogger("rorschach_gemini_app")

# Configuración mínima de Streamlit
st.set_page_config(
    page_title="Interpretación Rorschach con Gemini",
    page_icon="🧠",
)

# --- CONFIGURACIÓN Gemini ---
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
if not GEMINI_API_KEY:
    st.error("GEMINI_API_KEY no encontrada. Por favor configúrala en tus variables de entorno.")
    logger.error("GEMINI_API_KEY no encontrada.")
    st.stop()

try:
    genai.configure(api_key=GEMINI_API_KEY)
    logger.info("✅ Configuración de Gemini API realizada.")
except Exception as e:
    error_msg = f"❌ Error al configurar Gemini API: {str(e)}"
    logger.error(error_msg)
    st.error(error_msg)
    st.stop()

@st.cache_resource
def get_gemini_model():
    logger.info("🔄 Cargando modelo Gemini...")
    # Puedes cambiar a "gemini-pro" si lo prefieres, gemini-1.5-flash es más rápido y económico
    return genai.GenerativeModel("gemini-2.5-flash-lite")

# Inicializar modelo Gemini
model = None
try:
    model = get_gemini_model()
    logger.info("✅ Modelo Gemini cargado correctamente (gemini-2.5-flash-lite)")
except Exception as e:
    error_msg = f"❌ Error al inicializar modelo Gemini: {str(e)}"
    logger.error(error_msg)
    st.error(error_msg)
    # st.stop() # Decidir si detener la app si el modelo no carga

# Formatear prompt para interpretación Rorschach con Gemini
def format_prompt_gemini(message):
    system_prompt = """Eres un experto en interpretación del Test de Rorschach.
    Analiza las respuestas del usuario a las láminas y proporciona una interpretación
    psicológica detallada basada en el método de Exner.

    Considera:
    - Localización de las respuestas
    - Determinantes (forma, color, movimiento)
    - Contenido de las respuestas
    - Originalidad/popularidad
    - Funcionamiento cognitivo, afectivo e interpersonal
    Al final describe una conclusion creativa en terminos sencillos de la personalidad del usuario
    con recomendaciones generales.
    """

    # Gemini prefiere una concatenación más directa o el uso de 'parts' para roles.
    # Para una única llamada de generación de texto, concatenar es simple.
    prompt = f"{system_prompt}\n\nInterpretación Rorschach para las siguientes respuestas del usuario:\n{message}"
    logger.info(f"Prompt para Gemini generado con {len(prompt)} caracteres")
    return prompt

# Generar respuesta desde Gemini
def generate_with_gemini(user_input_message):
    if not model:
        logger.error("❌ No hay modelo Gemini disponible para generar respuesta")
        return "Error de conexión con el modelo Gemini. Por favor, inténtelo de nuevo más tarde."

    try:
        logger.info(f"🔄 Iniciando llamada al API de Gemini - {datetime.datetime.now()}")

        generation_config = genai.types.GenerationConfig(
            temperature=0.7, # Gemini usa un rango similar, 0.9 es bastante creativo
            max_output_tokens=1024, # Aumentado un poco para asegurar respuestas completas
            top_p=0.95,
            # top_k es otro parámetro que podrías usar en Gemini
            # repetition_penalty no es un parámetro directo en GenerationConfig
            # do_sample es implícito si temperature > 0
            # seed no es un parámetro directo de GenerationConfig para `generate_content`
        )

        formatted_prompt = format_prompt_gemini(user_input_message)
        start_time = datetime.datetime.now()

        # Llamada a Gemini
        response = model.generate_content(
            formatted_prompt,
            generation_config=generation_config
        )

        end_time = datetime.datetime.now()
        duration = (end_time - start_time).total_seconds()

        logger.info(f"✅ Respuesta de Gemini generada en {duration:.2f} segundos")

        # Acceder al texto de la respuesta
        if response.parts:
            output_text = response.text
        else:
            # Esto podría ocurrir si la respuesta fue bloqueada por filtros de seguridad
            logger.warning(f"⚠️ Respuesta de Gemini vacía o bloqueada. Razón: {response.prompt_feedback}")
            output_text = (
                "No se pudo generar una respuesta. Esto podría deberse a filtros de seguridad "
                f"o a un problema con la solicitud. Razón del feedback: {response.prompt_feedback}"
            )
        return output_text

    except Exception as e:
        error_msg = f"❌ Error en la generación con Gemini: {str(e)}"
        logger.error(error_msg)
        # Verificar si el error es por API key inválida (aunque ya se verifica al inicio)
        if "API_KEY_INVALID" in str(e):
             st.error("La clave API de Gemini es inválida o ha expirado. Verifica tu configuración.")
        return f"Lo siento, ocurrió un error durante la interpretación con Gemini. Detalles: {str(e)}"

# Reemplazo de variables en documento Word
from docx.enum.text import WD_ALIGN_PARAGRAPH

def replace_variables_word(doc, variables):
    for paragraph in doc.paragraphs:
        for key, value in variables.items():
            if f'<{key}>' in paragraph.text:
                # Usar run para preservar formato si es posible, pero simple replace es más robusto para placeholder
                # Si el placeholder está solo, paragraph.text es suficiente
                # Si está entre otro texto, se necesita más cuidado con runs
                new_text = paragraph.text.replace(f'<{key}>', str(value)) # Asegurar que value sea string
                if paragraph.text != new_text: # Solo si hubo cambio
                    paragraph.text = new_text
                    paragraph.alignment = WD_ALIGN_PARAGRAPH.LEFT # Justificación
    for table in doc.tables:
        for row in table.rows:
            for cell in row.cells:
                for paragraph in cell.paragraphs:
                    for key, value in variables.items():
                        if f'<{key}>' in paragraph.text:
                            new_text = paragraph.text.replace(f'<{key}>', str(value))
                            if paragraph.text != new_text:
                                paragraph.text = new_text
                                paragraph.alignment = WD_ALIGN_PARAGRAPH.LEFT


# Generar documento Word con interpretación
def generate_word_document(interpretation):
    try:
        template_path = os.path.join('PLANTILLAS', 'PLANTILLA_INTERPRETACION.docx')

        if not os.path.exists(template_path):
            logger.warning(f"⚠️ No se encontró la plantilla en {template_path}")
            st.warning(f"No se encontró la plantilla en {template_path}")
            return None

        doc = Document(template_path)
        variables = {'INTERPRETACION': interpretation}
        replace_variables_word(doc, variables)

        with tempfile.NamedTemporaryFile(delete=False, suffix='.docx') as tmp:
            doc.save(tmp.name)
            logger.info(f"✅ Documento Word generado correctamente: {tmp.name}")
            return tmp.name

    except Exception as e:
        error_msg = f"❌ Error al generar el documento: {str(e)}"
        logger.error(error_msg)
        st.error(error_msg)
        return None

# Interfaz Streamlit
st.title("Interpretación del Test de Rorschach (con Gemini)")
logger.info("🚀 Aplicación (Gemini) iniciada")

entrada_usuario = st.text_area(
    "Ingrese respuestas del Test de Rorschach:",
    height=150,
    placeholder="Ejemplo: Lámina I: Veo un murciélago sobre un noche estrellada..."
)

if st.button("Enviar Interpretación", type="primary"):
    if not entrada_usuario:
        st.warning("Por favor ingrese texto para interpretar")
    elif not model: # Verificar si el modelo se cargó
        st.error("El modelo de IA no está disponible. Por favor, revise la configuración y los logs.")
        logger.error("Intento de generar interpretación sin modelo cargado.")
    else:
        logger.info(f"🔄 Procesando entrada de {len(entrada_usuario)} caracteres para Gemini")
        with st.spinner("Generando interpretación con Gemini..."):
            bot_response = generate_with_gemini(entrada_usuario) # Llamada a la nueva función
            st.subheader("Interpretación (Gemini)")
            st.markdown(bot_response) # Usar markdown para mejor formato si Gemini lo usa

            if "No se pudo generar una respuesta" not in bot_response and "ocurrió un error" not in bot_response :
                document_path = generate_word_document(bot_response)
                if document_path:
                    with open(document_path, "rb") as file:
                        file_data = file.read()
                    st.download_button(
                        label="Descargar Interpretación",
                        data=file_data,
                        file_name="Interpretacion_Rorschach_Gemini.docx",
                        mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
                    )
                    logger.info("📄 Botón de descarga (Gemini) mostrado al usuario")
                    # Clean up the temporary file
                    try:
                        os.remove(document_path)
                        logger.info(f"🧹 Archivo temporal {document_path} eliminado.")
                    except OSError as e_rm:
                        logger.error(f"⚠️ Error al eliminar archivo temporal {document_path}: {e_rm}")

                else:
                    logger.error("❌ No se pudo generar el documento Word (Gemini)")
            else:
                logger.warning("⚠️ No se generó documento Word debido a error en la interpretación.")

# --- END OF FILE manchas_gemini.py ---