JeCabrera commited on
Commit
6748f9a
·
verified ·
1 Parent(s): cb531d4

Update session_state.py

Browse files
Files changed (1) hide show
  1. session_state.py +284 -220
session_state.py CHANGED
@@ -1,220 +1,284 @@
1
- import streamlit as st
2
- import time
3
- import joblib
4
- import google.generativeai as genai
5
-
6
- class SessionState:
7
- """
8
- Clase para gestionar el estado de la sesión de Streamlit de manera centralizada.
9
- Encapsula todas las operaciones relacionadas con st.session_state.
10
- """
11
-
12
- def __init__(self):
13
- # Inicializar valores por defecto si no existen
14
- if 'chat_id' not in st.session_state:
15
- st.session_state.chat_id = None
16
-
17
- if 'chat_title' not in st.session_state:
18
- st.session_state.chat_title = None
19
-
20
- if 'messages' not in st.session_state:
21
- st.session_state.messages = []
22
-
23
- if 'gemini_history' not in st.session_state:
24
- st.session_state.gemini_history = []
25
-
26
- if 'model' not in st.session_state:
27
- st.session_state.model = None
28
-
29
- if 'chat' not in st.session_state:
30
- st.session_state.chat = None
31
-
32
- if 'prompt' not in st.session_state:
33
- st.session_state.prompt = None
34
- self.avatar_analysis = AvatarAnalysis()
35
-
36
- # Getters y setters para cada propiedad
37
- @property
38
- def chat_id(self):
39
- return st.session_state.chat_id
40
-
41
- @chat_id.setter
42
- def chat_id(self, value):
43
- st.session_state.chat_id = value
44
-
45
- @property
46
- def chat_title(self):
47
- return st.session_state.chat_title
48
-
49
- @chat_title.setter
50
- def chat_title(self, value):
51
- st.session_state.chat_title = value
52
-
53
- @property
54
- def messages(self):
55
- return st.session_state.messages
56
-
57
- @messages.setter
58
- def messages(self, value):
59
- st.session_state.messages = value
60
-
61
- @property
62
- def gemini_history(self):
63
- return st.session_state.gemini_history
64
-
65
- @gemini_history.setter
66
- def gemini_history(self, value):
67
- st.session_state.gemini_history = value
68
-
69
- @property
70
- def model(self):
71
- return st.session_state.model
72
-
73
- @model.setter
74
- def model(self, value):
75
- st.session_state.model = value
76
-
77
- @property
78
- def chat(self):
79
- return st.session_state.chat
80
-
81
- @chat.setter
82
- def chat(self, value):
83
- st.session_state.chat = value
84
-
85
- @property
86
- def prompt(self):
87
- return st.session_state.prompt
88
-
89
- @prompt.setter
90
- def prompt(self, value):
91
- st.session_state.prompt = value
92
-
93
- # Métodos de utilidad
94
- def add_message(self, role, content, avatar=None):
95
- """Añade un mensaje al historial"""
96
- message = {
97
- 'role': role,
98
- 'content': content,
99
- }
100
- if avatar:
101
- message['avatar'] = avatar
102
- self.messages.append(message)
103
-
104
- def clear_prompt(self):
105
- """Limpia el prompt del estado de la sesión"""
106
- self.prompt = None
107
-
108
- def initialize_model(self, model_name='gemini-2.0-flash'):
109
- """Inicializa el modelo de IA"""
110
- self.model = genai.GenerativeModel(model_name)
111
-
112
- def initialize_chat(self, history=None):
113
- """Inicializa el chat con el modelo"""
114
- if history is None:
115
- history = self.gemini_history
116
-
117
- # Asegurar que el modelo está inicializado
118
- if self.model is None:
119
- self.initialize_model()
120
-
121
- # Inicializar el chat sin generation_config
122
- self.chat = self.model.start_chat(history=history)
123
-
124
- # Verificar que el chat se inicializó correctamente
125
- if self.chat is None:
126
- raise ValueError("Error al inicializar el chat")
127
-
128
- def send_message(self, prompt, stream=True):
129
- """Método unificado para enviar mensajes y mantener el streaming"""
130
- try:
131
- if self.chat is None:
132
- self.initialize_chat()
133
-
134
- return self.chat.send_message(
135
- prompt,
136
- stream=stream,
137
- generation_config={
138
- "temperature": 0.9
139
- }
140
- )
141
- except Exception as e:
142
- print(f"Error al enviar mensaje: {e}")
143
- # Reintentar una vez si hay error
144
- self.initialize_chat()
145
- return self.chat.send_message(
146
- prompt,
147
- stream=stream,
148
- generation_config={
149
- "temperature": 0.9
150
- }
151
- )
152
-
153
- def generate_chat_title(self, prompt, model_name='gemini-2.0-flash'):
154
- """Genera un título para el chat basado en el primer mensaje"""
155
- try:
156
- title_generator = genai.GenerativeModel(model_name)
157
- title_response = title_generator.generate_content(
158
- f"Genera un título corto (máximo 5 palabras) que describa de qué trata esta consulta, sin usar comillas ni puntuación: '{prompt}'")
159
- return title_response.text.strip()
160
- except Exception as e:
161
- print(f"Error al generar título: {e}")
162
- return None
163
-
164
- def save_chat_history(self, chat_id=None):
165
- """Guarda el historial del chat"""
166
- if chat_id is None:
167
- chat_id = self.chat_id
168
-
169
- joblib.dump(self.messages, f'data/{chat_id}-st_messages')
170
- joblib.dump(self.gemini_history, f'data/{chat_id}-gemini_messages')
171
-
172
- def load_chat_history(self, chat_id=None):
173
- """Carga el historial del chat"""
174
- if chat_id is None:
175
- chat_id = self.chat_id
176
-
177
- try:
178
- self.messages = joblib.load(f'data/{chat_id}-st_messages')
179
- self.gemini_history = joblib.load(f'data/{chat_id}-gemini_messages')
180
- return True
181
- except:
182
- self.messages = []
183
- self.gemini_history = []
184
- return False
185
-
186
- def has_messages(self):
187
- """Verifica si hay mensajes en el historial"""
188
- return len(self.messages) > 0
189
-
190
- def has_prompt(self):
191
- """Verifica si hay un prompt en el estado de la sesión"""
192
- return self.prompt is not None and self.prompt.strip() != ""
193
-
194
-
195
- class AvatarAnalysis:
196
- def __init__(self):
197
- self.basic_profile = {
198
- "who": None,
199
- "what": None,
200
- "age": None
201
- }
202
- self.main_pain = None
203
- self.main_desire = None
204
- self.obstacles = None
205
- self.motivations = None
206
-
207
- def update_profile(self, key, value):
208
- if key in self.basic_profile:
209
- self.basic_profile[key] = value
210
-
211
- def save_avatar_analysis(self):
212
- """Guarda el análisis del avatar en el historial"""
213
- analysis_data = {
214
- 'avatar_analysis': self.avatar_analysis.__dict__
215
- }
216
- # Guardar junto con el historial del chat
217
-
218
- def load_avatar_analysis(self):
219
- """Carga el análisis del avatar del historial"""
220
- # Cargar junto con el historial del chat
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import joblib
3
+ import os
4
+ from google import genai
5
+ from google.genai import types
6
+
7
+ DEFAULT_GEMINI_MODEL = 'gemini-3.1-flash-lite-preview'
8
+ DATA_DIR = 'data'
9
+ PAST_CHATS_LIST_PATH = f'{DATA_DIR}/past_chats_list'
10
+
11
+ class SessionState:
12
+ """
13
+ Clase para gestionar el estado de la sesión de Streamlit de manera centralizada.
14
+ Encapsula todas las operaciones relacionadas con st.session_state.
15
+ """
16
+
17
+ def __init__(self):
18
+ # Inicializar valores por defecto si no existen
19
+ if 'chat_id' not in st.session_state:
20
+ st.session_state.chat_id = None
21
+
22
+ if 'chat_title' not in st.session_state:
23
+ st.session_state.chat_title = None
24
+
25
+ if 'messages' not in st.session_state:
26
+ st.session_state.messages = []
27
+
28
+ if 'gemini_history' not in st.session_state:
29
+ st.session_state.gemini_history = []
30
+
31
+ if 'model' not in st.session_state:
32
+ st.session_state.model = None
33
+
34
+ if 'client' not in st.session_state:
35
+ st.session_state.client = None
36
+
37
+ if 'chat' not in st.session_state:
38
+ st.session_state.chat = None
39
+
40
+ if 'prompt' not in st.session_state:
41
+ st.session_state.prompt = None
42
+
43
+ if 'system_instruction' not in st.session_state:
44
+ st.session_state.system_instruction = None
45
+
46
+ # Getters y setters para cada propiedad
47
+ @property
48
+ def chat_id(self):
49
+ return st.session_state.chat_id
50
+
51
+ @chat_id.setter
52
+ def chat_id(self, value):
53
+ st.session_state.chat_id = value
54
+
55
+ @property
56
+ def chat_title(self):
57
+ return st.session_state.chat_title
58
+
59
+ @chat_title.setter
60
+ def chat_title(self, value):
61
+ st.session_state.chat_title = value
62
+
63
+ @property
64
+ def messages(self):
65
+ return st.session_state.messages
66
+
67
+ @messages.setter
68
+ def messages(self, value):
69
+ st.session_state.messages = value
70
+
71
+ @property
72
+ def gemini_history(self):
73
+ return st.session_state.gemini_history
74
+
75
+ @gemini_history.setter
76
+ def gemini_history(self, value):
77
+ st.session_state.gemini_history = value
78
+
79
+ @property
80
+ def model(self):
81
+ return st.session_state.model
82
+
83
+ @model.setter
84
+ def model(self, value):
85
+ st.session_state.model = value
86
+
87
+ @property
88
+ def client(self):
89
+ return st.session_state.client
90
+
91
+ @client.setter
92
+ def client(self, value):
93
+ st.session_state.client = value
94
+
95
+ @property
96
+ def chat(self):
97
+ return st.session_state.chat
98
+
99
+ @chat.setter
100
+ def chat(self, value):
101
+ st.session_state.chat = value
102
+
103
+ @property
104
+ def prompt(self):
105
+ return st.session_state.prompt
106
+
107
+ @prompt.setter
108
+ def prompt(self, value):
109
+ st.session_state.prompt = value
110
+
111
+ @property
112
+ def system_instruction(self):
113
+ return st.session_state.system_instruction
114
+
115
+ @system_instruction.setter
116
+ def system_instruction(self, value):
117
+ st.session_state.system_instruction = value
118
+
119
+ # Métodos de utilidad
120
+ def add_message(self, role, content, avatar=None):
121
+ """Añade un mensaje al historial"""
122
+ message = {
123
+ 'role': role,
124
+ 'content': content,
125
+ }
126
+ if avatar:
127
+ message['avatar'] = avatar
128
+ self.messages.append(message)
129
+
130
+ def clear_prompt(self):
131
+ """Limpia el prompt del estado de la sesión"""
132
+ self.prompt = None
133
+
134
+ def initialize_model(self, model_name=None, api_key=None):
135
+ """Inicializa el modelo de IA"""
136
+ if model_name is None:
137
+ model_name = DEFAULT_GEMINI_MODEL
138
+ if api_key is None:
139
+ api_key = os.environ.get('GOOGLE_API_KEY')
140
+ self.client = genai.Client(api_key=api_key)
141
+ self.model = model_name
142
+
143
+ def initialize_chat(self, history=None, system_instruction=None):
144
+ """Inicializa el chat con el modelo"""
145
+ if history is None:
146
+ history = self.gemini_history
147
+ if system_instruction is None:
148
+ system_instruction = self.system_instruction
149
+ else:
150
+ self.system_instruction = system_instruction
151
+
152
+ # Asegurar que el modelo está inicializado
153
+ if self.model is None or self.client is None:
154
+ self.initialize_model()
155
+
156
+ chat_kwargs = {'model': self.model}
157
+ if history:
158
+ chat_kwargs['history'] = history
159
+ if system_instruction:
160
+ chat_kwargs['config'] = types.GenerateContentConfig(
161
+ system_instruction=system_instruction
162
+ )
163
+
164
+ # Inicializar chat con el SDK moderno
165
+ self.chat = self.client.chats.create(**chat_kwargs)
166
+
167
+ # Verificar que el chat se inicializó correctamente
168
+ if self.chat is None:
169
+ raise ValueError("Error al inicializar el chat")
170
+
171
+ def send_message(self, prompt, stream=True):
172
+ """Método unificado para enviar mensajes y mantener el streaming"""
173
+ try:
174
+ if self.chat is None:
175
+ self.initialize_chat()
176
+
177
+ if stream:
178
+ return self.chat.send_message_stream(prompt)
179
+ return self.chat.send_message(prompt)
180
+ except Exception as e:
181
+ print(f"Error al enviar mensaje: {e}")
182
+ # Reintentar una vez si hay error
183
+ self.initialize_chat()
184
+ if stream:
185
+ return self.chat.send_message_stream(prompt)
186
+ return self.chat.send_message(prompt)
187
+
188
+ def generate_chat_title(self, prompt, model_name=None):
189
+ """Genera un título para el chat basado en el primer mensaje"""
190
+ try:
191
+ if model_name is None:
192
+ model_name = DEFAULT_GEMINI_MODEL
193
+ if self.client is None:
194
+ self.client = genai.Client(api_key=os.environ.get('GOOGLE_API_KEY'))
195
+ title_response = self.client.models.generate_content(
196
+ model=model_name,
197
+ contents=(
198
+ "Genera un título natural y humano en español (3 a 6 palabras) "
199
+ "que resuma esta consulta. No uses separadores tipo '|', no uses etiquetas, "
200
+ "no uses comillas y evita formato robótico. Devuelve solo el título final: "
201
+ f"'{prompt}'"
202
+ )
203
+ )
204
+ cleaned_title = " ".join(
205
+ title_response.text.strip().replace('"', '').replace('|', ' ').split()
206
+ )
207
+ return " ".join(cleaned_title.split()[:6])
208
+ except Exception as e:
209
+ print(f"Error al generar título: {e}")
210
+ return None
211
+
212
+ def save_chat_history(self, chat_id=None):
213
+ """Guarda el historial del chat"""
214
+ if chat_id is None:
215
+ chat_id = self.chat_id
216
+
217
+ serialized_history = self._serialize_gemini_history(self.gemini_history)
218
+ os.makedirs(DATA_DIR, exist_ok=True)
219
+ joblib.dump(self.messages, self._st_messages_path(chat_id))
220
+ joblib.dump(serialized_history, self._gemini_messages_path(chat_id))
221
+
222
+ def load_chat_history(self, chat_id=None):
223
+ """Carga el historial del chat"""
224
+ if chat_id is None:
225
+ chat_id = self.chat_id
226
+
227
+ try:
228
+ self.messages = joblib.load(self._st_messages_path(chat_id))
229
+ loaded_history = joblib.load(self._gemini_messages_path(chat_id))
230
+ self.gemini_history = self._deserialize_gemini_history(loaded_history)
231
+ return True
232
+ except (FileNotFoundError, EOFError):
233
+ self.messages = []
234
+ self.gemini_history = []
235
+ return False
236
+
237
+ def _st_messages_path(self, chat_id):
238
+ return f'{DATA_DIR}/{chat_id}-st_messages'
239
+
240
+ def _gemini_messages_path(self, chat_id):
241
+ return f'{DATA_DIR}/{chat_id}-gemini_messages'
242
+
243
+ def _serialize_gemini_history(self, history):
244
+ """Convierte tipos del SDK (Content/Part) a diccionarios serializables."""
245
+ serialized = []
246
+ for item in history or []:
247
+ if isinstance(item, dict):
248
+ serialized.append(item)
249
+ continue
250
+ if hasattr(item, "model_dump"):
251
+ serialized.append(item.model_dump(mode="python"))
252
+ continue
253
+ if hasattr(item, "to_dict"):
254
+ serialized.append(item.to_dict())
255
+ continue
256
+ serialized.append(item)
257
+ return serialized
258
+
259
+ def _deserialize_gemini_history(self, history):
260
+ """Reconstruye Content para rehidratar chat history en google-genai."""
261
+ deserialized = []
262
+ for item in history or []:
263
+ if isinstance(item, dict) and "role" in item and "parts" in item:
264
+ role = item.get("role")
265
+ parts_data = item.get("parts", [])
266
+ parts = []
267
+ for part in parts_data:
268
+ if isinstance(part, dict) and "text" in part:
269
+ parts.append(types.Part(text=part["text"]))
270
+ elif isinstance(part, str):
271
+ parts.append(types.Part(text=part))
272
+ if parts:
273
+ deserialized.append(types.Content(role=role, parts=parts))
274
+ continue
275
+ deserialized.append(item)
276
+ return deserialized
277
+
278
+ def has_messages(self):
279
+ """Verifica si hay mensajes en el historial"""
280
+ return len(self.messages) > 0
281
+
282
+ def has_prompt(self):
283
+ """Verifica si hay un prompt en el estado de la sesión"""
284
+ return self.prompt is not None and self.prompt.strip() != ""