iatoolkit 0.10.2__py3-none-any.whl → 0.11.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of iatoolkit might be problematic. Click here for more details.

@@ -16,39 +16,49 @@ class BrandingService:
16
16
  Define los estilos de branding por defecto para la aplicación.
17
17
  """
18
18
  self._default_branding = {
19
- # Colores del contenedor del encabezado
20
- "header_background_color": "#FFFFFF", # Fondo blanco por defecto
21
- "header_text_color": "#6C757D", # Color de texto 'muted' de Bootstrap
22
-
23
- # Estilos para el texto primario (ej. nombre de la compañía)
19
+ # --- Estilos del Encabezado Principal ---
20
+ "header_background_color": "#FFFFFF",
21
+ "header_text_color": "#6C757D",
24
22
  "primary_font_weight": "bold",
25
23
  "primary_font_size": "1rem",
24
+ "secondary_font_weight": "600",
25
+ "secondary_font_size": "0.875rem",
26
+ "tertiary_font_weight": "normal",
27
+ "tertiary_font_size": "0.75rem",
28
+ "tertiary_opacity": "0.8",
29
+
30
+ # Estilos Globales de la Marca ---
31
+ "brand_primary_color": "#0d6efd", # Azul de Bootstrap por defecto
32
+ "brand_secondary_color": "#6c757d", # Gris de Bootstrap por defecto
33
+ "brand_text_on_primary": "#FFFFFF", # Texto blanco sobre color primario
34
+ "brand_text_on_secondary": "#FFFFFF", # Texto blanco sobre color secundario
35
+
36
+ # Estilos para Alertas de Error ---
37
+ "brand_danger_color": "#dc3545", # Rojo principal para alertas
38
+ "brand_danger_bg": "#f8d7da", # Fondo rojo pálido
39
+ "brand_danger_text": "#842029", # Texto rojo oscuro
40
+ "brand_danger_border": "#f5c2c7", # Borde rojo intermedio
26
41
 
27
- # Estilos para el texto secundario (ej. ID de usuario)
28
- "secondary_font_weight": "600", # Semibold
29
- "secondary_font_size": "0.875rem" # Equivale a la clase 'small' de Bootstrap
42
+ # Estilos para Alertas Informativas ---
43
+ "brand_info_bg": "#cff4fc", # Fondo celeste pálido
44
+ "brand_info_text": "#055160", # Texto azul oscuro
45
+ "brand_info_border": "#b6effb",
30
46
 
47
+ # Color para el botón de Enviar ---
48
+ "send_button_color": "#212529" # Gris oscuro/casi negro por defecto
31
49
  }
32
50
 
33
51
  def get_company_branding(self, company: Company | None) -> dict:
34
52
  """
35
53
  Retorna los estilos de branding finales para una compañía,
36
54
  fusionando los valores por defecto con los personalizados.
37
-
38
- Args:
39
- company: El objeto Company, que puede contener un dict de branding.
40
-
41
- Returns:
42
- Un diccionario con todos los estilos de branding listos para usar en la plantilla.
43
55
  """
44
- # Empezamos con una copia de los valores por defecto
45
56
  final_branding_values = self._default_branding.copy()
46
57
 
47
- # Si la compañía existe y tiene branding personalizado, lo fusionamos.
48
58
  if company and company.branding:
49
59
  final_branding_values.update(company.branding)
50
60
 
51
- # Construimos las cadenas de estilo completas
61
+ # --- CONSTRUCCIÓN DE ESTILOS Y VARIABLES CSS ---
52
62
  header_style = (
53
63
  f"background-color: {final_branding_values['header_background_color']}; "
54
64
  f"color: {final_branding_values['header_text_color']};"
@@ -61,11 +71,39 @@ class BrandingService:
61
71
  f"font-weight: {final_branding_values['secondary_font_weight']}; "
62
72
  f"font-size: {final_branding_values['secondary_font_size']};"
63
73
  )
74
+ tertiary_text_style = (
75
+ f"font-weight: {final_branding_values['tertiary_font_weight']}; "
76
+ f"font-size: {final_branding_values['tertiary_font_size']}; "
77
+ f"opacity: {final_branding_values['tertiary_opacity']};"
78
+ )
79
+
80
+ # Generamos el bloque de variables CSS
81
+ css_variables = f"""
82
+ :root {{
83
+ --brand-primary-color: {final_branding_values['brand_primary_color']};
84
+ --brand-secondary-color: {final_branding_values['brand_secondary_color']};
85
+ --brand-text-on-primary: {final_branding_values['brand_text_on_primary']};
86
+ --brand-text-on-secondary: {final_branding_values['brand_text_on_secondary']};
87
+ --brand-modal-header-bg: {final_branding_values['header_background_color']};
88
+ --brand-modal-header-text: {final_branding_values['header_text_color']};
89
+ --brand-danger-color: {final_branding_values['brand_danger_color']};
90
+ --brand-danger-bg: {final_branding_values['brand_danger_bg']};
91
+ --brand-danger-text: {final_branding_values['brand_danger_text']};
92
+ --brand-danger-border: {final_branding_values['brand_danger_border']};
93
+ --brand-info-bg: {final_branding_values['brand_info_bg']};
94
+ --brand-info-text: {final_branding_values['brand_info_text']};
95
+ --brand-info-border: {final_branding_values['brand_info_border']};
96
+
97
+ }}
98
+ """
64
99
 
65
100
  return {
66
101
  "name": company.name if company else "IAToolkit",
67
102
  "header_style": header_style,
68
- "header_text_color": final_branding_values['header_text_color'],
69
103
  "primary_text_style": primary_text_style,
70
- "secondary_text_style": secondary_text_style
104
+ "secondary_text_style": secondary_text_style,
105
+ "tertiary_text_style": tertiary_text_style,
106
+ "header_text_color": final_branding_values['header_text_color'],
107
+ "css_variables": css_variables,
108
+ "send_button_color": final_branding_values['send_button_color']
71
109
  }
@@ -36,7 +36,7 @@ class HistoryService:
36
36
  history = self.llm_query_repo.get_history(company, user_identifier)
37
37
 
38
38
  if not history:
39
- return {'error': 'No se pudo obtener el historial'}
39
+ return {'message': 'Historial vacio actualmente', 'history': []}
40
40
 
41
41
  history_list = [query.to_dict() for query in history]
42
42
 
@@ -41,7 +41,7 @@ $(document).ready(function () {
41
41
  } catch (error) {
42
42
  console.error("Error al cargar historial:", error);
43
43
  const errorHtml = `
44
- <div class="alert alert-danger alert-dismissible show" role="alert">
44
+ <div class="alert alert-branded-danger alert-dismissible show" role="alert">
45
45
  <strong>Error al cargar el historial:</strong> ${error.message}
46
46
  <button type="button" class="close" data-dismiss="alert">
47
47
  <span>&times;</span>
@@ -92,6 +92,7 @@ $(document).ready(function () {
92
92
 
93
93
  // Copiar el texto al textarea del chat
94
94
  $('#question').val(queryText);
95
+ $('#send-button').removeClass('disabled');
95
96
 
96
97
  // Cerrar el modal
97
98
  $('#historyModal').modal('hide');
@@ -1,6 +1,6 @@
1
1
  // Global variables for request management
2
- let currentAbortController = null;
3
2
  let isRequestInProgress = false;
3
+ let abortController = null;
4
4
 
5
5
  let selectedPrompt = null; // Will hold a lightweight prompt object
6
6
 
@@ -8,6 +8,8 @@ $(document).ready(function () {
8
8
  // --- MAIN EVENT HANDLERS ---
9
9
  $('#send-button').on('click', handleChatMessage);
10
10
  $('#stop-button').on('click', abortCurrentRequest);
11
+ if (window.sendButtonColor)
12
+ $('#send-button i').css('color', window.sendButtonColor);
11
13
 
12
14
  // --- PROMPT ASSISTANT FUNCTIONALITY ---
13
15
  $('.input-area').on('click', '.dropdown-menu a.dropdown-item', function (event) {
@@ -118,75 +120,99 @@ function renderDynamicInputs(fields) {
118
120
  }
119
121
 
120
122
 
123
+
121
124
  /**
122
125
  * Main function to handle sending a chat message.
123
126
  */
124
127
  const handleChatMessage = async function () {
125
- if (isRequestInProgress || $('#send-button').hasClass('disabled')) return;
126
-
127
- const question = $('#question').val().trim();
128
- const promptName = selectedPrompt ? selectedPrompt.prompt : null;
128
+ if (isRequestInProgress || $('#send-button').hasClass('disabled')) {
129
+ return;
130
+ }
129
131
 
130
- let displayMessage = question;
131
- let isEditable = true;
132
- const clientData = {};
132
+ isRequestInProgress = true;
133
+ toggleSendStopButtons(true);
133
134
 
134
- if (selectedPrompt) {
135
- displayMessage = selectedPrompt.description;
136
- isEditable = false; // Prompts are not editable
135
+ try {
136
+ const question = $('#question').val().trim();
137
+ const promptName = selectedPrompt ? selectedPrompt.prompt : null;
137
138
 
138
- (selectedPrompt.custom_fields || []).forEach(field => {
139
- const value = $('#' + field.data_key + '-id').val().trim();
140
- if (value) {
141
- clientData[field.data_key] = value;
142
- }
143
- });
139
+ let displayMessage = question;
140
+ let isEditable = true;
141
+ const clientData = {};
144
142
 
145
- // Append the collected parameter values to the display message
146
- const paramsString = Object.values(clientData).join(', ');
147
- if (paramsString) {
148
- displayMessage += `: ${paramsString}`;
143
+ if (selectedPrompt) {
144
+ displayMessage = selectedPrompt.description;
145
+ isEditable = false;
146
+
147
+ (selectedPrompt.custom_fields || []).forEach(field => {
148
+ const value = $('#' + field.data_key + '-id').val().trim();
149
+ if (value) {
150
+ clientData[field.data_key] = value;
151
+ }
152
+ });
153
+
154
+ const paramsString = Object.values(clientData).join(', ');
155
+ if (paramsString) { displayMessage += `: ${paramsString}`; }
149
156
  }
150
- }
151
157
 
152
- // Si no hay pregunta libre Y no se ha seleccionado un prompt, no hacer nada.
153
- if (!displayMessage) return;
154
-
155
- displayUserMessage(displayMessage, isEditable, question);
156
- showSpinner();
157
- toggleSendStopButtons(true);
158
+ // Simplificado: Si no hay mensaje, el 'finally' se encargará de limpiar.
159
+ // Simplemente salimos de la función.
160
+ if (!displayMessage) {
161
+ return;
162
+ }
158
163
 
159
- resetAllInputs();
164
+ displayUserMessage(displayMessage, isEditable, question);
165
+ showSpinner();
166
+ resetAllInputs();
160
167
 
161
- const files = window.filePond.getFiles();
162
- const filesBase64 = await Promise.all(files.map(fileItem => toBase64(fileItem.file)));
168
+ const files = window.filePond.getFiles();
169
+ const filesBase64 = await Promise.all(files.map(fileItem => toBase64(fileItem.file)));
163
170
 
164
- // Prepare data payload
165
- const data = {
166
- question: question,
167
- prompt_name: promptName,
168
- client_data: clientData,
169
- files: filesBase64.map(f => ({ filename: f.name, content: f.base64 })),
170
- external_user_id: window.externalUserId
171
- };
171
+ const data = {
172
+ question: question,
173
+ prompt_name: promptName,
174
+ client_data: clientData,
175
+ files: filesBase64.map(f => ({ filename: f.name, content: f.base64 })),
176
+ external_user_id: window.externalUserId
177
+ };
172
178
 
173
- try {
174
179
  const responseData = await callLLMAPI("/llm_query", data, "POST");
175
180
  if (responseData && responseData.answer) {
176
181
  const answerSection = $('<div>').addClass('answer-section llm-output').append(responseData.answer);
177
182
  displayBotMessage(answerSection);
178
183
  }
179
184
  } catch (error) {
180
- console.error("Error in handleChatMessage:", error);
181
- // Implement error display logic as needed
185
+ if (error.name === 'AbortError') {
186
+ console.log('Petición abortada por el usuario.');
187
+
188
+ // Usando jQuery estándar para construir el elemento ---
189
+ const icon = $('<i>').addClass('bi bi-stop-circle me-2'); // Icono sin "fill" para un look más ligero
190
+ const textSpan = $('<span>').text('La generación de la respuesta ha sido detenida.');
191
+
192
+ const abortMessage = $('<div>')
193
+ .addClass('system-message')
194
+ .append(icon)
195
+ .append(textSpan);
196
+
197
+ displayBotMessage(abortMessage);
198
+ } else {
199
+ console.error("Error in handleChatMessage:", error);
200
+ const errorSection = $('<div>').addClass('error-section').append('<p>Ocurrió un error al procesar la solicitud.</p>');
201
+ displayBotMessage(errorSection);
202
+ }
182
203
  } finally {
204
+ // Este bloque se ejecuta siempre, garantizando que el estado se limpie.
205
+ isRequestInProgress = false;
183
206
  hideSpinner();
184
207
  toggleSendStopButtons(false);
185
208
  updateSendButtonState();
186
- window.filePond.removeFiles();
209
+ if (window.filePond) {
210
+ window.filePond.removeFiles();
211
+ }
187
212
  }
188
213
  };
189
214
 
215
+
190
216
  /**
191
217
  * Resets all inputs to their initial state.
192
218
  */
@@ -274,16 +300,15 @@ const callLLMAPI = async function(apiPath, data, method, timeoutMs = 500000) {
274
300
  headers['X-Chat-Token'] = window.sessionJWT;
275
301
  }
276
302
 
277
- const controller = new AbortController();
278
- currentAbortController = controller;
279
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
303
+ abortController = new AbortController();
304
+ const timeoutId = setTimeout(() => abortController.abort(), timeoutMs);
280
305
 
281
306
  try {
282
307
  const response = await fetch(url, {
283
308
  method: method,
284
309
  headers: headers,
285
310
  body: JSON.stringify(data),
286
- signal: controller.signal,
311
+ signal: abortController.signal, // Se usa el signal del controlador global
287
312
  credentials: 'include'
288
313
  });
289
314
  clearTimeout(timeoutId);
@@ -322,10 +347,11 @@ const displayUserMessage = function(message, isEditable, originalQuestion) {
322
347
  userMessage.append(messageText);
323
348
 
324
349
  if (isEditable) {
325
- const editIcon = $('<i>').addClass('bi bi-pencil-fill edit-icon').attr('title', 'Edit query').on('click', function () {
350
+ const editIcon = $('<i>').addClass('p-2 bi bi-pencil-fill edit-icon').attr('title', 'Edit query').on('click', function () {
326
351
  $('#question').val(originalQuestion).focus();
327
352
  autoResizeTextarea($('#question')[0]);
328
- updateSendButtonState();
353
+
354
+ $('#send-button').removeClass('disabled');
329
355
  });
330
356
  userMessage.append(editIcon);
331
357
  }
@@ -347,9 +373,8 @@ function displayBotMessage(section) {
347
373
  * Aborts the current in-progress API request.
348
374
  */
349
375
  const abortCurrentRequest = function () {
350
- if (currentAbortController && isRequestInProgress) {
351
- window.isManualAbort = true;
352
- currentAbortController.abort();
376
+ if (isRequestInProgress && abortController) {
377
+ abortController.abort();
353
378
  }
354
379
  };
355
380
 
@@ -1,3 +1,34 @@
1
+ /* Customización de variables de Bootstrap */
2
+ :root {
3
+ --bs-tooltip-bg: #495057; /* Gris carbón, más claro y profesional */
4
+ --bs-tooltip-opacity: 0.95; /* Ligeramente más opaco para mejor legibilidad */
5
+ }
6
+
7
+ .alert-branded-danger {
8
+ background-color: var(--brand-danger-bg);
9
+ color: var(--brand-danger-text);
10
+ border-color: var(--brand-danger-border);
11
+ }
12
+ /* Asegura que el texto fuerte y los enlaces dentro de la alerta también tomen el color correcto */
13
+ .alert-branded-danger strong,
14
+ .alert-branded-danger .alert-link {
15
+ color: inherit;
16
+ }
17
+
18
+
19
+ /* esta clase defines los atributos de cada bloque */
20
+ .chat-block {
21
+ padding: 1rem; /* Equivalente a p-3 de Bootstrap */
22
+ border: 1px solid #dee2e6; /* Borde estándar y sutil */
23
+ border-radius: 0.375rem; /* Borde redondeado estándar de Bootstrap */
24
+ box-shadow: 0 2px 8px rgba(0,0,0,0.06); /* Sombra suave y unificada */
25
+ }
26
+
27
+ /* Estilo para la sección del encabezado */
28
+ .company-section {
29
+ border-radius: 0.375rem; /* Mismo radio que .chat-block para consistencia */
30
+ }
31
+
1
32
  /* Spinner */
2
33
  .spinning {
3
34
  animation: spin 1s linear infinite;
@@ -17,7 +48,6 @@
17
48
  display: flex;
18
49
  flex-direction: column;
19
50
  background-color: #fff; /* Fondo blanco para el contenedor del chat */
20
- box-shadow: 0 0 10px rgba(0,0,0,0.1); /* Sombra sutil */
21
51
  }
22
52
  .answer-section, .error-section, .document-section {
23
53
  max-width: 100%;
@@ -139,9 +169,6 @@
139
169
  /* 1. La caja principal que envuelve toda el área de entrada */
140
170
  .input-area {
141
171
  background-color: #f8f9fa;
142
- padding: 10px;
143
- border-radius: 10px;
144
- box-shadow: 0 -2px 5px rgba(0,0,0,0.05);
145
172
  }
146
173
 
147
174
  /* 2. La barra "cápsula" que envuelve el texto y los iconos */
@@ -149,7 +176,7 @@
149
176
  background-color: #ffffff;
150
177
  border: 1px solid #dee2e6;
151
178
  border-radius: 1.5rem;
152
- padding: 0.25rem 0.5rem;
179
+ padding: 0.1rem 0.5rem;
153
180
  transition: all 0.2s ease-in-out;
154
181
  }
155
182
 
@@ -159,6 +186,30 @@
159
186
  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
160
187
  }
161
188
 
189
+ /* Estilo para mensajes sutiles del sistema (ej. abortado) */
190
+ .system-message {
191
+ color: var(--brand-secondary-color, #6c757d); /* Usa el color secundario o un gris por defecto */
192
+ font-style: italic;
193
+ font-size: 0.9rem;
194
+ display: flex;
195
+ align-items: center;
196
+ justify-content: start;
197
+ margin: 10px 0;
198
+ padding: 10px;
199
+ opacity: 0.8;
200
+ }
201
+
202
+
203
+ #prompt-assistant-collapse .card {
204
+ border-radius: 1.5rem; /* Mismo radio que el chat-input-bar */
205
+ border: 1px solid #dee2e6; /* Mismo borde que el chat-input-bar */
206
+ box-shadow: none; /* Eliminamos la sombra por defecto del card */
207
+ }
208
+
209
+ #prompt-assistant-collapse.show .card {
210
+ margin-bottom: 12px !important; /* Anula el mb-2 (8px) para añadir un poco más de espacio */
211
+ }
212
+
162
213
  /* 4. El textarea "invisible" en el centro */
163
214
  .chat-textarea {
164
215
  flex-grow: 1;
@@ -194,13 +245,12 @@
194
245
  color: #343a40;
195
246
  }
196
247
 
197
- /* 6. Anulación de color y tamaño para el botón de ENVIAR */
198
- .send-button-icon i {
199
- color: #0d6efd !important; /* Azul (importante para anular) */
200
- font-size: 1.7rem !important; /* Ligeramente más grande para mayor énfasis */
248
+ /* 6. Anulación específica para el botón de ENVIAR usando su ID (Máxima Prioridad) */
249
+ #send-button i {
250
+ font-size: 1.7rem; /* Ligeramente más grande */
201
251
  }
202
- .send-button-icon:hover i {
203
- color: #0b5ed7 !important; /* Azul más oscuro en hover */
252
+ #send-button:hover i {
253
+ filter: brightness(85%); /* Efecto hover genérico que funciona con cualquier color */
204
254
  }
205
255
 
206
256
  /* 7. Estilo para el botón de enviar cuando está deshabilitado */
@@ -383,3 +433,8 @@
383
433
  .star.hover-active {
384
434
  color: #ffc107;
385
435
  }
436
+
437
+ #send-button i {
438
+ color: var(--brand-send-button-color);
439
+ }
440
+
@@ -2,10 +2,51 @@
2
2
  /* Estilos generales para modales */
3
3
  /* ######################################################### */
4
4
 
5
- /* Separación del icono y el título del modal */
6
- .icon-spaced {
7
- margin-right: 10px;
5
+ /* Estilos del header del modal con branding */
6
+ .modal-header.branded {
7
+ background-color: var(--brand-modal-header-bg);
8
+ color: var(--brand-modal-header-text);
9
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
10
+ }
11
+ .modal-header.branded .btn-close {
12
+ filter: invert(1) grayscale(100%) brightness(200%); /* Hace el botón de cerrar visible en fondos oscuros */
13
+ }
14
+
15
+ /* Estilos para botones con branding */
16
+ .btn-branded-primary {
17
+ background-color: var(--brand-primary-color);
18
+ border-color: var(--brand-primary-color);
19
+ color: var(--brand-text-on-primary);
20
+ }
21
+ .btn-branded-primary:hover {
22
+ filter: brightness(90%);
23
+ color: var(--brand-text-on-primary);
24
+ }
25
+
26
+ .btn-branded-secondary {
27
+ background-color: var(--brand-secondary-color);
28
+ border-color: var(--brand-secondary-color);
29
+ color: var(--brand-text-on-secondary);
8
30
  }
31
+ .btn-branded-secondary:hover {
32
+ filter: brightness(90%);
33
+ color: var(--brand-text-on-secondary);
34
+ }
35
+
36
+ /* Estilos para alertas informativas personalizadas */
37
+ .alert-branded-info {
38
+ background-color: var(--brand-info-bg);
39
+ color: var(--brand-info-text);
40
+ border-color: var(--brand-info-border);
41
+ }
42
+ .alert-branded-info strong,
43
+ .alert-branded-info .alert-link {
44
+ color: inherit;
45
+ }
46
+ .alert-branded-info .bi { /* Asegura que los iconos también tomen el color */
47
+ color: inherit;
48
+ }
49
+
9
50
 
10
51
  /* Título del modal */
11
52
  .modal-title{
@@ -13,11 +54,6 @@
13
54
  font-weight: bold;
14
55
  }
15
56
 
16
- /* Texto del modal */
17
- .text-muted{
18
- font-size:16px;
19
- text-align: justify;
20
- }
21
57
 
22
58
  /* Estilos del header del modal*/
23
59
  .modal-header {
@@ -45,20 +81,17 @@
45
81
  /* Modal de historial */
46
82
  /* ######################################################### */
47
83
 
48
- /* Estilos cabeceras de tablas */
49
- .thead-dark th{
84
+ /* Encabezado de tabla con branding */
85
+ .thead-branded th {
86
+ background-color: var(--brand-primary-color);
87
+ color: var(--brand-text-on-primary);
50
88
  font-size: 16px;
51
89
  font-weight: bold;
52
90
  }
53
91
 
54
- /* Control de ancho de columnas en la tabla del historial */
55
- #history-content .table td:nth-child(1) {
56
- width: 5%; /* Columna del número */
57
- }
58
92
 
59
- #history-content .table td:nth-child(2) {
60
- width: 23%; /* Columna de la fecha */
61
- }
93
+ #history-content .table td:nth-child(1) { width: 5%; }
94
+ #history-content .table td:nth-child(2) { width: 23%; }
62
95
 
63
96
  #history-content .table td:nth-child(3) {
64
97
  width: auto; /* Columna de la consulta - ocupa el resto */
@@ -69,28 +102,6 @@
69
102
  /* ######################################################### */
70
103
 
71
104
  /* Contenedor de calificación del modal de feedback */
72
- .rating-container {
73
- text-align: center;
74
- margin: 10px 0 0 0;
75
- display: flex;
76
- flex-direction: column;
77
- justify-content: center;
78
- align-items: center;
79
- gap: 5px;
80
- width: 100%;
81
- }
82
-
83
- /* Contenedor de estrellas del modal de feedback */
84
- .rating-stars-container{
85
- text-align: center;
86
- margin: 2px 0 0 0;
87
- display: flex;
88
- flex-direction: row;
89
- justify-content: center;
90
- align-items: center;
91
- gap: 10px;
92
- width: 100%;
93
- }
94
105
 
95
106
  /* Estilos de las estrellas del modal de feedback */
96
107
  .star {
@@ -3,16 +3,26 @@
3
3
  {% block title %}IAToolkit{% endblock %}
4
4
 
5
5
  {% block content %}
6
+
7
+ <style>
8
+ {{ branding.css_variables | safe }}
9
+ </style>
10
+
6
11
  <!-- Sección de encabezado con el usuario conectado -->
7
- <div class="company-section d-flex justify-content-between align-items-center px-3 py-2"
12
+ <div id="company-section" class="company-section d-flex justify-content-between align-items-center px-3 py-2"
8
13
  style="{{ branding.header_style }}">
9
14
 
10
- <!-- Izquierda: Nombre de la Empresa -->
11
- <span style="{{ branding.primary_text_style }}">
12
- {{ branding.name }}
13
- </span>
15
+ <!-- Izquierda: Nombre de la Empresa y atribución "Powered by" -->
16
+ <div class="d-flex align-items-center">
17
+ <span style="{{ branding.primary_text_style }}">
18
+ {{ branding.name }}
19
+ </span>
20
+ <span class="ms-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Powered by IAToolkit">
21
+ <i class="bi bi-info-circle" style="color: {{ branding.header_text_color }}; opacity: 0.7; font-size: 0.9rem;"></i>
22
+ </span>
23
+ </div>
14
24
 
15
- <!-- Derecha: Grupo de información y acciones del usuario -->
25
+ <!-- Derecha: Grupo de información y acciones para el usuario -->
16
26
  <div class="d-flex align-items-center">
17
27
  <!-- 1. ID de Usuario -->
18
28
  <span style="{{ branding.secondary_text_style }}">
@@ -32,13 +42,7 @@
32
42
  <i class="bi bi-emoji-smile"></i>
33
43
  </a>
34
44
 
35
- <!-- 4. Powered by (oculto en un tooltip) -->
36
- <span class="ms-4" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Powered by IAToolkit">
37
- <i class="bi bi-info-circle" style="color: {{ branding.header_text_color }}; opacity: 0.7; font-size: 0.9rem;"></i>
38
- </span>
39
-
40
-
41
- <!-- 5. Icono de cerrar sesión (al final) -->
45
+ <!-- Icono de cerrar sesión (al final) -->
42
46
  {% if user.email %}
43
47
  <a href="{{ url_for('logout', company_short_name=company_short_name) }}"
44
48
  class="ms-3 action-icon-style" title="Cerrar sesión" style="color: {{ branding.header_text_color }} !important;">
@@ -50,7 +54,7 @@
50
54
  </div>
51
55
 
52
56
  <div id="chat-container"
53
- class="mt-2 p-3 border border-2 rounded flex-grow-1"
57
+ class="chat-block mt-2 flex-grow-1"
54
58
  style="overflow-y: auto;">
55
59
  <div id="chat-messages">
56
60
  <!-- Mensaje de bienvenida estático -->
@@ -65,7 +69,7 @@
65
69
  <input type="file" id="file-upload" class="filepond" data-max-files="5" multiple>
66
70
  </div>
67
71
 
68
- <div class="input-area mt-2 mb-2">
72
+ <div id="input-area" class="input-area chat-block mt-2 mb-2">
69
73
 
70
74
  <!-- 1. Contenido Colapsable del Asistente de prompts -->
71
75
  <div class="collapse" id="prompt-assistant-collapse">
@@ -110,7 +114,7 @@
110
114
  </div>
111
115
 
112
116
  <!-- 2. La Barra de Entrada Principal -->
113
- <div class="chat-input-bar d-flex align-items-center">
117
+ <div id="chat-input-bar" class="chat-input-bar d-flex align-items-center">
114
118
  <!-- Iconos de la izquierda -->
115
119
  <div class="d-flex align-items-center">
116
120
  <!-- BOTÓN PARA CONTROLAR EL COLLAPSE -->
@@ -160,6 +164,7 @@
160
164
  window.iatoolkit_base_url = "{{ iatoolkit_base_url }}";
161
165
  window.externalUserId = "{{ external_user_id }}";
162
166
  window.availablePrompts = {{ prompts.message | tojson }};
167
+ window.sendButtonColor = "{{ branding.send_button_color }}";
163
168
 
164
169
  {% if auth_method == 'jwt' and session_jwt %}
165
170
  // Store session JWT if it exists, defined in the same global scope
@@ -2,7 +2,7 @@
2
2
  <div class="modal fade" id="uploadedFilesModal" tabindex="-1" aria-labelledby="uploadedFilesModalLabel" aria-hidden="true">
3
3
  <div class="modal-dialog">
4
4
  <div class="modal-content">
5
- <div class="modal-header">
5
+ <div class="modal-header branded">
6
6
  <h5 class="modal-title" id="uploadedFilesModalLabel">Archivos Cargados</h5>
7
7
  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
8
8
  </div>
@@ -12,7 +12,7 @@
12
12
  </ul>
13
13
  </div>
14
14
  <div class="modal-footer">
15
- <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cerrar</button>
15
+ <button type="button" class="btn btn-branded-secondary" data-bs-dismiss="modal">Cerrar</button>
16
16
  </div>
17
17
  </div>
18
18
  </div>
@@ -22,7 +22,7 @@
22
22
  <div class="modal fade" id="feedbackModal" tabindex="-1" aria-labelledby="feedbackModalLabel" aria-hidden="true">
23
23
  <div class="modal-dialog modal-dialog-centered">
24
24
  <div class="modal-content">
25
- <div class="modal-header">
25
+ <div class="modal-header branded">
26
26
  <h5 class="modal-title" id="feedbackModalLabel">
27
27
  <i class="bi bi-chat-dots me-3"></i>Tu Opinión es Importante
28
28
  </h5>
@@ -52,10 +52,10 @@
52
52
  </div>
53
53
  </div>
54
54
  <div class="modal-footer">
55
- <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
55
+ <button type="button" class="btn btn-branded-secondary" data-bs-dismiss="modal">
56
56
  <i class="bi bi-x-circle me-1"></i>Cancelar
57
57
  </button>
58
- <button type="button" class="btn btn-primary" id="submit-feedback">
58
+ <button type="button" class="btn btn-branded-primary" id="submit-feedback">
59
59
  <i class="bi bi-send me-1"></i>Enviar
60
60
  </button>
61
61
  </div>
@@ -67,7 +67,7 @@
67
67
  <div class="modal fade" id="historyModal" tabindex="-1" aria-labelledby="historyModalLabel" aria-hidden="true">
68
68
  <div class="modal-dialog modal-lg modal-dialog-centered">
69
69
  <div class="modal-content">
70
- <div class="modal-header">
70
+ <div class="modal-header branded">
71
71
  <h5 class="modal-title" id="historyModalLabel">
72
72
  <i class="bi bi-clock-history me-3"></i>Historial de Consultas
73
73
  </h5>
@@ -85,13 +85,13 @@
85
85
  </div>
86
86
  <div id="history-content" style="display: none;">
87
87
  <!-- Texto explicativo -->
88
- <div class="alert alert-info mb-3" role="alert">
88
+ <div class="alert alert-branded-info mb-3" role="alert">
89
89
  <i class="bi bi-info-circle me-2"></i>
90
90
  <strong>Tip:</strong> Haz clic en cualquier pregunta del historial para copiarla automáticamente al área de texto.
91
91
  </div>
92
92
  <div class="table-responsive">
93
93
  <table class="table table-striped table-hover">
94
- <thead class="thead-dark">
94
+ <thead class="thead-branded">
95
95
  <tr>
96
96
  <th scope="col">#</th>
97
97
  <th scope="col">Fecha</th>
@@ -106,7 +106,7 @@
106
106
  </div>
107
107
  </div>
108
108
  <div class="modal-footer">
109
- <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
109
+ <button type="button" class="btn btn-branded-secondary" data-bs-dismiss="modal">
110
110
  <i class="bi bi-x-circle me-1"></i>Cerrar
111
111
  </button>
112
112
  </div>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 0.10.2
3
+ Version: 0.11.1
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
@@ -36,12 +36,12 @@ iatoolkit/repositories/tasks_repo.py,sha256=icVO_r2oPagGnnBhwVFzznnvEEU2EAx-2dlW
36
36
  iatoolkit/repositories/vs_repo.py,sha256=UkpmQQiocgM5IwRBmmWhw3HHzHP6zK1nN3J3TcQgjhc,5300
37
37
  iatoolkit/services/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCLkX7X4,102
38
38
  iatoolkit/services/benchmark_service.py,sha256=CdbFYyS3FHFhNzWQEa9ZNjUlmON10DT1nKNbZQ1EUi8,5880
39
- iatoolkit/services/branding_service.py,sha256=I6MgPe2T1JaeyKqysXSNQINGBrrpxuWoWd3ZZVEfvKs,2709
39
+ iatoolkit/services/branding_service.py,sha256=iYjlUy-5X-x2F6Yg9C70FTMjuItBaUYVHcrXgiKuDbA,4976
40
40
  iatoolkit/services/dispatcher_service.py,sha256=ykR1ye6McyCCuaBgwH6r3-PqcLAr4v4ApkPazMSBzbs,14040
41
41
  iatoolkit/services/document_service.py,sha256=nMXrNtbHQuc9pSaten0LvKY0kT8_WngBDmZJUP3jNPw,5936
42
42
  iatoolkit/services/excel_service.py,sha256=CJGhu7cQl9J6y_ZWSJ-M63Xm-RXR9Zs66oOR2NJErZQ,3868
43
43
  iatoolkit/services/file_processor_service.py,sha256=B1sUUhZNFf-rT4_1wrD38GKNoBFMp2g0dYrXYMCWe2E,4122
44
- iatoolkit/services/history_service.py,sha256=JwW958tYTrcdWSP5TRoDgDtBdGCvWIG4fnmHsrXLQ-8,1617
44
+ iatoolkit/services/history_service.py,sha256=ZlYfaSHOcCxxc6ICTnqflaGBlzctpJdNUwXpUPG5xWI,1630
45
45
  iatoolkit/services/jwt_service.py,sha256=YoZ9h7_o9xBko-arNQv4MbcwnxoSWVNj4VbZmMo_QGY,3908
46
46
  iatoolkit/services/load_documents_service.py,sha256=ZpB0BZ3qX1fGJGBtZtMLbFdWWx0hkPoeCS3OqJKwCTs,7291
47
47
  iatoolkit/services/mail_service.py,sha256=2h-fcF3swZDya_o7IpgXkmuj3iEVHVCiHi7oVxU99sQ,2182
@@ -64,11 +64,11 @@ iatoolkit/static/images/logo_umayor.png,sha256=FHr1wvI8uDn1YRbRcLSRLBOc0mYusHx9U
64
64
  iatoolkit/static/images/upload.png,sha256=zh5FiINURpaWZQF86bF_gALBX4W1c4aLp5wPQO9xGXI,296
65
65
  iatoolkit/static/js/chat_feedback.js,sha256=_izl49hFEUZYREcJoaPukpTs0YjDgJYUu-QfPt5Ll2s,4398
66
66
  iatoolkit/static/js/chat_filepond.js,sha256=mzXafm7a506EpM37KATTK3zvAswO1E0KSUY1vKbwuRc,3163
67
- iatoolkit/static/js/chat_history.js,sha256=_bmwmMl9DL37FWJgoSS74j3LSqckD93C6fktB9-PxZk,3901
68
- iatoolkit/static/js/chat_main.js,sha256=zVDERp3_WZBTeP3N-gCf0IVTWbKToUB6L_lUaEi56A8,15149
69
- iatoolkit/static/styles/chat_iatoolkit.css,sha256=hZBe2KRfcszlyR6UO6hDbfMqhpECKn_maXMjcR5ld_M,8400
67
+ iatoolkit/static/js/chat_history.js,sha256=G01rKSXOpLpIavycGPbfpfYg5vmPrLhkHYbCLhY3_zs,3964
68
+ iatoolkit/static/js/chat_main.js,sha256=BLKN5a3xP2uu0z6iF6qmgIpT1vClaZf3apcAVQBhU0g,16229
69
+ iatoolkit/static/styles/chat_iatoolkit.css,sha256=z1sYkvredKjU4Ag75ZB8CzsRGHi3t69vgxVW6rjSBMU,10073
70
70
  iatoolkit/static/styles/chat_info.css,sha256=17DbgoNYE21VYWfb5L9-QLCpD2R1idK4imKRLwXtJLY,1058
71
- iatoolkit/static/styles/chat_modal.css,sha256=67qwltu-QS-sU0d_fSeTIiXwcNjfAnQQtmsgStx-ezk,3047
71
+ iatoolkit/static/styles/chat_modal.css,sha256=pE7JY5D63Ds_d2FKdmxym4sevvg-2Mf7yo-gB7KA9vE,3730
72
72
  iatoolkit/static/styles/llm_output.css,sha256=AlxgRSOleeCk2dLAqFWVaQ-jwZiJjcpC5rHuUv3T6VU,2312
73
73
  iatoolkit/system_prompts/format_styles.prompt,sha256=MSMe1qvR3cF_0IbFshn8R0z6Wx6VCHQq1p37rpu5wwk,3576
74
74
  iatoolkit/system_prompts/query_main.prompt,sha256=w_9ybgWgiQH4V_RbAXqsvz0M7oOuiyhxcwf-D0CgfA4,3017
@@ -76,8 +76,8 @@ iatoolkit/system_prompts/sql_rules.prompt,sha256=y4nURVnb9AyFwt-lrbMNBHHtZlhk6kC
76
76
  iatoolkit/templates/about.html,sha256=ciC08grUVz5qLzdzDDqDX31xirg5PrJIRYabWpV9oA8,294
77
77
  iatoolkit/templates/base.html,sha256=LXXB8oPrcBFkf2pLfOSyAaSh66kHbs4SEujpFL3h9Nw,2163
78
78
  iatoolkit/templates/change_password.html,sha256=DFfQSFcZ2YJZNFis2IXfzEKStxTf4i9f4eQ_6GiyNs8,2342
79
- iatoolkit/templates/chat.html,sha256=Zuc7i_omW4OW0htl4t8sdYKK3NboNTsnIpcHV4wfoeU,9156
80
- iatoolkit/templates/chat_modals.html,sha256=9OtJHRMeBup8zojPnUqSl7Fd5StPdvOZMs2MiJPqVFY,5704
79
+ iatoolkit/templates/chat.html,sha256=xaQk5McHRO8VF2LCBbr4vT0d2fFNy2GW61DwCYht-_M,9371
80
+ iatoolkit/templates/chat_modals.html,sha256=3CQ430bwhebq6rAJ6Bk12PQDjt9YenqNXm5thC5WP2Y,5771
81
81
  iatoolkit/templates/error.html,sha256=BNF-7z8AYL5vF4ZMUFMrOBt8c85kCFrm9qSHn9EiHWg,540
82
82
  iatoolkit/templates/forgot_password.html,sha256=1lUbKg9CKnQdnySplceY_pibwYne1-mOlM38fqI1kW8,1563
83
83
  iatoolkit/templates/header.html,sha256=179agI7rnYwP_rvJNXIiVde5E8Ec5649_XKq6eew2Hk,1263
@@ -104,7 +104,7 @@ iatoolkit/views/tasks_review_view.py,sha256=keLsLCyOTTlcoIapnB_lbuSvLwrPVZVpBiFC
104
104
  iatoolkit/views/tasks_view.py,sha256=a3anTXrJTTvbQuc6PSpOzidLKQFL4hWa7PI2Cppcz8w,4110
105
105
  iatoolkit/views/user_feedback_view.py,sha256=G37zmP8P4LvZrSymNJ5iFXhLZg1A3BEwRfTpH1Iam5w,2652
106
106
  iatoolkit/views/verify_user_view.py,sha256=a3q4wHJ8mKAEmgbNTOcnX4rMikROjOR3mHvCr30qGGA,2351
107
- iatoolkit-0.10.2.dist-info/METADATA,sha256=sHrK0vERhSpig941JIOiEb6uXRs-lVRr2RxwpMbFZoY,9301
108
- iatoolkit-0.10.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
109
- iatoolkit-0.10.2.dist-info/top_level.txt,sha256=V_w4QvDx0b1RXiy8zTCrD1Bp7AZkFe3_O0-9fMiwogg,10
110
- iatoolkit-0.10.2.dist-info/RECORD,,
107
+ iatoolkit-0.11.1.dist-info/METADATA,sha256=5vmAxDm05jysqCbNZe1lJqpc3N--YhpxBizGrQ1-CJ8,9301
108
+ iatoolkit-0.11.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
109
+ iatoolkit-0.11.1.dist-info/top_level.txt,sha256=V_w4QvDx0b1RXiy8zTCrD1Bp7AZkFe3_O0-9fMiwogg,10
110
+ iatoolkit-0.11.1.dist-info/RECORD,,