iatoolkit 1.9.0__py3-none-any.whl → 1.15.3__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.
Files changed (38) hide show
  1. iatoolkit/__init__.py +1 -1
  2. iatoolkit/common/routes.py +1 -1
  3. iatoolkit/common/util.py +8 -123
  4. iatoolkit/core.py +1 -0
  5. iatoolkit/infra/connectors/file_connector.py +10 -2
  6. iatoolkit/infra/connectors/google_drive_connector.py +3 -0
  7. iatoolkit/infra/connectors/local_file_connector.py +3 -0
  8. iatoolkit/infra/connectors/s3_connector.py +24 -1
  9. iatoolkit/infra/llm_providers/deepseek_adapter.py +17 -1
  10. iatoolkit/infra/llm_providers/gemini_adapter.py +117 -18
  11. iatoolkit/infra/llm_providers/openai_adapter.py +175 -18
  12. iatoolkit/infra/llm_response.py +13 -0
  13. iatoolkit/locales/en.yaml +47 -2
  14. iatoolkit/locales/es.yaml +45 -1
  15. iatoolkit/repositories/llm_query_repo.py +44 -33
  16. iatoolkit/services/company_context_service.py +294 -133
  17. iatoolkit/services/dispatcher_service.py +1 -1
  18. iatoolkit/services/knowledge_base_service.py +26 -4
  19. iatoolkit/services/llm_client_service.py +58 -2
  20. iatoolkit/services/prompt_service.py +236 -330
  21. iatoolkit/services/query_service.py +37 -18
  22. iatoolkit/services/storage_service.py +92 -0
  23. iatoolkit/static/js/chat_filepond.js +188 -63
  24. iatoolkit/static/js/chat_main.js +105 -52
  25. iatoolkit/static/styles/chat_iatoolkit.css +96 -0
  26. iatoolkit/system_prompts/query_main.prompt +24 -41
  27. iatoolkit/templates/chat.html +15 -6
  28. iatoolkit/views/base_login_view.py +1 -1
  29. iatoolkit/views/categories_api_view.py +43 -3
  30. iatoolkit/views/chat_view.py +1 -1
  31. iatoolkit/views/login_view.py +1 -1
  32. iatoolkit/views/prompt_api_view.py +1 -1
  33. {iatoolkit-1.9.0.dist-info → iatoolkit-1.15.3.dist-info}/METADATA +1 -1
  34. {iatoolkit-1.9.0.dist-info → iatoolkit-1.15.3.dist-info}/RECORD +38 -37
  35. {iatoolkit-1.9.0.dist-info → iatoolkit-1.15.3.dist-info}/WHEEL +0 -0
  36. {iatoolkit-1.9.0.dist-info → iatoolkit-1.15.3.dist-info}/licenses/LICENSE +0 -0
  37. {iatoolkit-1.9.0.dist-info → iatoolkit-1.15.3.dist-info}/licenses/LICENSE_COMMUNITY.md +0 -0
  38. {iatoolkit-1.9.0.dist-info → iatoolkit-1.15.3.dist-info}/top_level.txt +0 -0
@@ -58,7 +58,6 @@ $(document).ready(function () {
58
58
 
59
59
  });
60
60
 
61
-
62
61
  /**
63
62
  * Main function to handle sending a chat message.
64
63
  */
@@ -93,16 +92,16 @@ const handleChatMessage = async function () {
93
92
  if (paramsString) { displayMessage += `: ${paramsString}`; }
94
93
  }
95
94
 
96
- // Simplificado: Si no hay mensaje, el 'finally' se encargará de limpiar.
97
95
  if (!displayMessage) {
98
96
  return;
99
97
  }
100
98
 
101
- displayUserMessage(displayMessage, isEditable, question);
99
+ const files = window.filePond.getFiles();
100
+
101
+ displayUserMessage(displayMessage, isEditable, question, files);
102
102
  showSpinner();
103
103
  resetAllInputs();
104
104
 
105
- const files = window.filePond.getFiles();
106
105
  const filesBase64 = await Promise.all(files.map(fileItem => toBase64(fileItem.file)));
107
106
 
108
107
  const data = {
@@ -112,59 +111,18 @@ const handleChatMessage = async function () {
112
111
  files: filesBase64.map(f => ({ filename: f.name, content: f.base64 })),
113
112
  user_identifier: window.user_identifier,
114
113
  model: (window.currentLlmModel || window.defaultLlmModel || '')
115
-
116
114
  };
117
115
 
118
116
  const responseData = await callToolkit("/api/llm_query", data, "POST");
119
- if (responseData && responseData.answer) {
120
- // CAMBIO: contenedor principal para la respuesta del bot
121
- const botMessageContainer = $('<div>').addClass('bot-message-container');
122
-
123
- // 1. Si hay reasoning_content, agregar el acordeón colapsable
124
- if (responseData.reasoning_content) {
125
- const uniqueId = 'reasoning-' + Date.now(); // ID único para el collapse
126
-
127
- const reasoningBlock = $(`
128
- <div class="reasoning-block">
129
- <button class="reasoning-toggle btn btn-sm btn-link text-decoration-none p-0"
130
- type="button"
131
- data-bs-toggle="collapse"
132
- data-bs-target="#${uniqueId}"
133
- aria-expanded="false"
134
- aria-controls="${uniqueId}">
135
- <i class="bi bi-lightbulb me-1"></i> ${t_js('show_reasoning')}
136
- </button>
137
-
138
- <div class="collapse mt-2" id="${uniqueId}">
139
- <div class="reasoning-card">
140
- ${responseData.reasoning_content}
141
- </div>
142
- </div>
143
- </div>
144
- `);
145
- botMessageContainer.append(reasoningBlock);
146
- }
147
-
148
- // 2. Agregar la respuesta final
149
- const answerSection = $('<div>').addClass('answer-section llm-output').append(responseData.answer);
150
- botMessageContainer.append(answerSection);
151
117
 
152
- // 3. Mostrar el contenedor completo
153
- displayBotMessage(botMessageContainer);
118
+ // Delegamos el procesamiento de la respuesta a la nueva función
119
+ processBotResponse(responseData);
154
120
 
155
- }
156
121
  } catch (error) {
157
122
  if (error.name === 'AbortError') {
158
-
159
- // Usando jQuery estándar para construir el elemento ---
160
- const icon = $('<i>').addClass('bi bi-stop-circle me-2'); // Icono sin "fill" para un look más ligero
123
+ const icon = $('<i>').addClass('bi bi-stop-circle me-2');
161
124
  const textSpan = $('<span>').text('La generación de la respuesta ha sido detenida.');
162
-
163
- const abortMessage = $('<div>')
164
- .addClass('system-message')
165
- .append(icon)
166
- .append(textSpan);
167
-
125
+ const abortMessage = $('<div>').addClass('system-message').append(icon).append(textSpan);
168
126
  displayBotMessage(abortMessage);
169
127
  } else {
170
128
  console.error("Error in handleChatMessage:", error);
@@ -172,17 +130,78 @@ const handleChatMessage = async function () {
172
130
  displayBotMessage(errorSection);
173
131
  }
174
132
  } finally {
175
- // Este bloque se ejecuta siempre, garantizando que el estado se limpie.
176
133
  isRequestInProgress = false;
177
134
  hideSpinner();
178
135
  toggleSendStopButtons(false);
179
136
  updateSendButtonState();
180
137
  if (window.filePond) {
181
- window.filePond.removeFiles();
138
+ window.filePond.removeFiles();
182
139
  }
183
140
  }
184
141
  };
185
142
 
143
+ /**
144
+ * Processes the response data from the LLM and displays it in the chat.
145
+ * Handles multimodal content: uses 'answer' for text (HTML) and 'content_parts' for images.
146
+ * @param {object} responseData - The JSON response from the server.
147
+ */
148
+ function processBotResponse(responseData) {
149
+ if (!responseData || (!responseData.answer && !responseData.content_parts)) {
150
+ return;
151
+ }
152
+
153
+ const botMessageContainer = $('<div>').addClass('bot-message-container');
154
+
155
+ // 1. Si hay reasoning_content, agregar el acordeón colapsable
156
+ if (responseData.reasoning_content) {
157
+ const uniqueId = 'reasoning-' + Date.now();
158
+ const reasoningBlock = $(`
159
+ <div class="reasoning-block">
160
+ <button class="reasoning-toggle btn btn-sm btn-link text-decoration-none p-0"
161
+ type="button" data-bs-toggle="collapse" data-bs-target="#${uniqueId}"
162
+ aria-expanded="false" aria-controls="${uniqueId}">
163
+ <i class="bi bi-lightbulb me-1"></i> ${t_js('show_reasoning')}
164
+ </button>
165
+ <div class="collapse mt-2" id="${uniqueId}">
166
+ <div class="reasoning-card">${responseData.reasoning_content}</div>
167
+ </div>
168
+ </div>
169
+ `);
170
+ botMessageContainer.append(reasoningBlock);
171
+ }
172
+
173
+ // 2. Agregar la respuesta final
174
+ const answerSection = $('<div>').addClass('answer-section llm-output');
175
+
176
+ // A. Texto: Usamos 'answer' porque contiene el HTML procesado y limpio del backend.
177
+ // Evitamos usar content_parts[type=text] porque contiene el JSON crudo del LLM.
178
+ if (responseData.answer) {
179
+ answerSection.append(responseData.answer);
180
+ }
181
+
182
+ // B. Imágenes: Iteramos content_parts buscando SOLO imágenes para adjuntarlas.
183
+ if (responseData.content_parts && responseData.content_parts.length > 0) {
184
+ responseData.content_parts.forEach(part => {
185
+ if (part.type === 'image' && part.source && part.source.url) {
186
+ const imgContainer = $('<div>').addClass('image-part my-3 text-center');
187
+ const img = $('<img>')
188
+ .attr('src', part.source.url)
189
+ .addClass('img-fluid rounded shadow-sm border')
190
+ .css({'max-height': '400px', 'cursor': 'pointer'})
191
+ .on('click', () => window.open(part.source.url, '_blank'));
192
+
193
+ imgContainer.append(img);
194
+ answerSection.append(imgContainer);
195
+ }
196
+ });
197
+ }
198
+
199
+ botMessageContainer.append(answerSection);
200
+
201
+ // 3. Mostrar el contenedor completo
202
+ displayBotMessage(botMessageContainer);
203
+ }
204
+
186
205
 
187
206
  /**
188
207
  * Resets all inputs to their initial state.
@@ -316,14 +335,48 @@ const callToolkit = async function(apiPath, data, method, timeoutMs = 500000) {
316
335
  * @param {string} message - The full message string to display.
317
336
  * @param {boolean} isEditable - Determines if the edit icon should be shown.
318
337
  * @param {string} originalQuestion - The original text to put back in the textarea for editing.
338
+ * @param {Array} [files] - Optional array of FilePond file items.
319
339
  */
320
- const displayUserMessage = function(message, isEditable, originalQuestion) {
340
+ const displayUserMessage = function(message, isEditable, originalQuestion, files = []) {
321
341
  const chatContainer = $('#chat-container');
322
342
  const userMessage = $('<div>').addClass('message shadow-sm');
323
343
  const messageText = $('<span>').text(message);
324
344
 
325
345
  userMessage.append(messageText);
326
346
 
347
+ // Renderizar previsualizaciones de archivos si existen
348
+ if (files && files.length > 0) {
349
+ const attachmentsContainer = $('<div>').addClass('mt-2 d-flex flex-wrap gap-2 ms-3');
350
+
351
+ files.forEach(fileItem => {
352
+ const file = fileItem.file;
353
+
354
+ if (file.type && file.type.startsWith('image/')) {
355
+ // Previsualización de imagen usando URL temporal
356
+ const imgUrl = URL.createObjectURL(file);
357
+ const img = $('<img>')
358
+ .attr('src', imgUrl)
359
+ .addClass('rounded border')
360
+ .css({
361
+ 'max-height': '80px',
362
+ 'max-width': '120px',
363
+ 'object-fit': 'cover',
364
+ 'cursor': 'pointer'
365
+ })
366
+ .on('click', () => window.open(imgUrl, '_blank')); // Click para ver en grande
367
+ attachmentsContainer.append(img);
368
+ } else {
369
+ // Icono genérico para documentos
370
+ const badge = $('<span>')
371
+ .addClass('badge bg-light text-dark border p-2')
372
+ .html(`<i class="bi bi-file-earmark-text me-1"></i> ${file.name}`);
373
+ attachmentsContainer.append(badge);
374
+ }
375
+ });
376
+
377
+ userMessage.append(attachmentsContainer);
378
+ }
379
+
327
380
  if (isEditable) {
328
381
  const editIcon = $('<i>').addClass('p-2 bi bi-pencil-fill edit-icon edit-pencil').attr('title', 'Edit query').on('click', function () {
329
382
  $('#question').val(originalQuestion)
@@ -303,6 +303,102 @@ li {
303
303
  color: #343a40;
304
304
  }
305
305
 
306
+ /* --- NUEVO: Dropzone y Lista de Archivos Inline --- */
307
+
308
+ /* Contenedor de la lista de archivos */
309
+ .file-list-inline {
310
+ margin-bottom: 0.5rem;
311
+ display: none; /* Se oculta si no hay archivos */
312
+ }
313
+
314
+ .file-list-item {
315
+ background-color: #fff;
316
+ border: 1px solid #dee2e6;
317
+ border-radius: 6px;
318
+ padding: 6px 10px;
319
+ margin-bottom: 5px;
320
+ display: flex;
321
+ align-items: center;
322
+ font-size: 0.9rem;
323
+ transition: background-color 0.2s;
324
+ }
325
+
326
+ .file-list-item:hover {
327
+ background-color: #f8f9fa;
328
+ }
329
+
330
+ .file-list-item i.file-icon {
331
+ font-size: 1.1rem;
332
+ margin-right: 8px;
333
+ color: var(--brand-primary-color);
334
+ }
335
+
336
+ .file-list-item .file-name {
337
+ flex-grow: 1;
338
+ white-space: nowrap;
339
+ overflow: hidden;
340
+ text-overflow: ellipsis;
341
+ margin-right: 10px;
342
+ color: #495057;
343
+ }
344
+
345
+ .file-list-item .file-remove {
346
+ cursor: pointer;
347
+ color: #dc3545; /* Rojo Bootstrap estándar */
348
+ opacity: 0.6;
349
+ transition: opacity 0.2s, transform 0.2s;
350
+ }
351
+
352
+ .file-list-item .file-remove:hover {
353
+ opacity: 1;
354
+ transform: scale(1.1);
355
+ }
356
+
357
+ /* Dropzone estática */
358
+ .chat-dropzone {
359
+ border: 2px dashed #ced4da;
360
+ border-radius: 0.5rem;
361
+ padding: 10px;
362
+ text-align: center;
363
+ background-color: rgba(255, 255, 255, 0.5);
364
+ color: #6c757d;
365
+ cursor: pointer;
366
+ transition: all 0.2s ease-in-out;
367
+ margin-top: 0.5rem;
368
+ font-size: 0.9rem;
369
+ }
370
+
371
+ .chat-dropzone:hover {
372
+ border-color: var(--brand-primary-color);
373
+ background-color: rgba(255, 255, 255, 0.8);
374
+ color: var(--brand-primary-color);
375
+ }
376
+
377
+ .chat-dropzone.drag-over {
378
+ border-color: var(--brand-primary-color);
379
+ background-color: #e9ecef;
380
+ transform: scale(1.01);
381
+ }
382
+
383
+ .chat-dropzone i {
384
+ font-size: 1.2rem;
385
+ vertical-align: middle;
386
+ margin-right: 5px;
387
+ }
388
+
389
+ /* Ajuste para el contador de archivos */
390
+ .dropzone-counter {
391
+ font-size: 0.8rem;
392
+ margin-left: 5px;
393
+ background: #e9ecef;
394
+ padding: 2px 6px;
395
+ border-radius: 10px;
396
+ color: #495057;
397
+ }
398
+
399
+ /* --- FIN NUEVO --- */
400
+
401
+
306
402
  /* Anulación específica para el botón de ENVIAR usando su ID (Máxima Prioridad) */
307
403
  #send-button i {
308
404
  font-size: 1.7rem; /* Ligeramente más grande */
@@ -1,4 +1,5 @@
1
- Eres un asistente que responde preguntas o ejecuta tareas según el contexto de la empresa.
1
+ Eres un asistente que responde preguntas o ejecuta tareas según el contexto
2
+ de la empresa.
2
3
 
3
4
  ### **Nombre de la empresa**
4
5
  ## Nombre: {{company}}, tambien se conoce como **{{ company_short_name }}**
@@ -12,9 +13,10 @@ Eres un asistente que responde preguntas o ejecuta tareas según el contexto de
12
13
  - Rol de usuario: {{ user_rol }}
13
14
 
14
15
 
15
- ## 🔧 Servicios de datos disponibles en {{ company.name }}
16
+ ## 🔧 Servicios de datos disponibles en {{ company_short_name }}
16
17
 
17
- A continuación se muestran los *function calls* (tools) que puedes usar para resolver tareas relacionadas con datos.
18
+ A continuación se muestran los *function calls* (tools) que tienes
19
+ disponibles para resolver consultas relacionadas con datos de la empresa.
18
20
  Cada servicio incluye su **nombre**, **propósito** y **parámetros esperados**.
19
21
 
20
22
  ### 📌 LISTA DE TOOLS DISPONIBLES
@@ -26,49 +28,30 @@ Cada servicio incluye su **nombre**, **propósito** y **parámetros esperados**.
26
28
 
27
29
  ---
28
30
 
29
- ### ⚠️ REGLAS IMPORTANTES PARA EL USO DE TOOLS
31
+ 1. **NO inventes información** si un tool existe para obtenerla.
32
+ 2. Si un usuario solicita datos específicos, **elige el tool cuyo propósito coincida exactamente** con la solicitud.
33
+ 3. **Si ningún tool aplica**, responde siguiendo el estilo de un asistente normal.
30
34
 
31
- 1. **Debes usar un tool cuando la tarea lo requiera.**
32
- Ejemplos:
33
- - Consultar bases de datos
34
- - Obtener información corporativa
35
- - Ejecutar SQL
36
- - Cargar documentos
37
-
38
- 2. **NO inventes información** si un tool existe para obtenerla.
39
- 3. Si un usuario solicita datos específicos, **elige el tool cuyo propósito coincida exactamente** con la solicitud.
40
- 4. **Si ningún tool aplica**, responde siguiendo el estilo de un asistente normal.
41
-
42
- ---
43
-
44
- Eres un asistente que responde preguntas sobre empresas y sus clientes.
45
35
 
46
36
  **Reglas obligatorias de contexto:**
47
37
  En caso que te hagan preguntas especificas sobre un cliente, debes asumir que la pregunta se
48
38
  refiere al **último cliente identificado** en la conversación.
49
39
 
50
- **IMPORTANTE:**
51
-
52
- ### **Instrucciones**
53
- 1. Devuelve siempre la respuesta en formato JSON.
54
- 2. Usa la información de contexto para responder la consulta del usuario de forma clara y concisa.
55
- Si el contexto o los adjuntos no te proveen información para la respuesta utiliza tu conocimiento propio.
56
- 3. Genera una única `"answer"` que integre la información de todos los servicios relevantes.
57
- 4. En `"aditional_data"`, a menos que se te indique lo contrario retorna un diccionario vacio: {}
58
- 5. El JSON de salida solo puede tener dos llaves: `"answer"` y `"aditional_data"`.
59
- 6. `"answer"` siempre debe contener un unico string con tu respuesta
60
- 7. Devuelve **únicamente** la respuesta en JSON válido, sin texto adicional.
61
- 7. **NO devuelvas el JSON como un string ni como texto escapado.**
62
- 9. **NO incluyas delimitadores como \`\`\`, \`\`\`json, ni comillas alrededor del objeto JSON.**
63
- 10. **NO escribas ninguna explicación ni texto fuera del JSON. Devuelve solo el objeto JSON.**
64
-
65
-
66
- ### **Ejemplo de salida esperada**
67
- {
68
- "answer": "El iPhone 15 Pro está disponible por 1099 USD y tu ticket de soporte está en proceso.",
69
- "aditional_data": {}
70
- }
71
-
72
-
40
+ ---
41
+ ### **Instrucciones de Formato de Salida**
42
+
43
+ 1. **Estructura Preferida (JSON):**
44
+ Para la parte **textual** de tu respuesta, utiliza preferentemente el siguiente formato JSON.
45
+ {
46
+ "answer": "Tu respuesta final al usuario. Usa Markdown para formato (negritas, listas, tablas).",
47
+ "aditional_data": {}
48
+ }
49
+
50
+ 2. **Generación de Imágenes y Contenido Multimodal:**
51
+ - Si el usuario te pide generar una imagen y tienes la capacidad nativa **HAZLO**.
52
+ - Genera la imagen como un bloque de contenido separado.
53
+ - **NO** intentes meter la imagen dentro del JSON.
54
+ - En el campo "answer" del JSON, describe o presenta la imagen que has generado.
55
+ - incluye la imagen como parte multimodal (output_image / file / base64) en la respuesta.
73
56
 
74
57
 
@@ -208,12 +208,7 @@
208
208
  title="{{ t('ui.tooltips.attach_files') }}">
209
209
  <i class="bi bi-plus-circle"></i>
210
210
  </a>
211
- <div id="view-files-button-container" style="display: none;">
212
- <a class="p-2" href="javascript:void(0);" id="view-files-button"
213
- title="{{ t('ui.tooltips.view_attached_files') }}">
214
- <i class="bi bi-file-earmark-text"></i>
215
- </a>
216
- </div>
211
+
217
212
  </div>
218
213
 
219
214
  <!-- Textarea Central -->
@@ -238,6 +233,20 @@
238
233
  </div>
239
234
  </div>
240
235
  </div>
236
+
237
+ <!-- NUEVO: Área de Adjuntos (Lista + Dropzone) -->
238
+ <div id="attachments-wrapper" class="px-1">
239
+ <!-- Lista de archivos cargados -->
240
+ <div id="inline-file-list" class="file-list-inline"></div>
241
+
242
+ <!-- Dropzone siempre visible -->
243
+ <div id="chat-dropzone" class="chat-dropzone">
244
+ <i class="bi bi-cloud-upload"></i>
245
+ <span>Arrastra archivos aquí o haz clic para adjuntar</span>
246
+ <span id="file-counter" class="dropzone-counter" style="display:none;">0/5</span>
247
+ </div>
248
+ </div>
249
+
241
250
  </div>
242
251
 
243
252
  <!-- Incluir los modales desde un archivo externo -->
@@ -77,7 +77,7 @@ class BaseLoginView(MethodView):
77
77
  # LLM configuration: default model and availables
78
78
  default_llm_model, available_llm_models = self.config_service.get_llm_configuration(company_short_name)
79
79
 
80
- prompts = self.prompt_service.get_user_prompts(company_short_name)
80
+ prompts = self.prompt_service.get_prompts(company_short_name)
81
81
 
82
82
  # Get the entire 'js_messages' block in the correct language.
83
83
  js_translations = self.i18n_service.get_translation_block('js_messages')
@@ -3,10 +3,11 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
- from flask import jsonify
6
+ from flask import jsonify, request
7
7
  from flask.views import MethodView
8
8
  from injector import inject
9
9
  from iatoolkit.services.auth_service import AuthService
10
+ from iatoolkit.services.prompt_service import PromptService
10
11
  from iatoolkit.services.profile_service import ProfileService
11
12
  from iatoolkit.services.configuration_service import ConfigurationService
12
13
  from iatoolkit.services.knowledge_base_service import KnowledgeBaseService
@@ -25,12 +26,15 @@ class CategoriesApiView(MethodView):
25
26
  profile_service: ProfileService,
26
27
  configuration_service: ConfigurationService,
27
28
  knowledge_base_service: KnowledgeBaseService,
28
- llm_query_repo: LLMQueryRepo):
29
+ llm_query_repo: LLMQueryRepo,
30
+ prompt_service: PromptService):
29
31
  self.auth_service = auth_service
30
32
  self.profile_service = profile_service
31
33
  self.knowledge_base_service = knowledge_base_service
32
34
  self.llm_query_repo = llm_query_repo
33
35
  self.configuration_service = configuration_service
36
+ self.prompt_service = prompt_service
37
+
34
38
 
35
39
  def get(self, company_short_name):
36
40
  try:
@@ -68,4 +72,40 @@ class CategoriesApiView(MethodView):
68
72
 
69
73
  except Exception as e:
70
74
  logging.exception(f"Error fetching categories for {company_short_name}: {e}")
71
- return jsonify({"status": "error", "message": str(e)}), 500
75
+ return jsonify({"status": "error", "message": str(e)}), 500
76
+
77
+ def post(self, company_short_name):
78
+ try:
79
+ # 1. Verify Authentication
80
+ auth_result = self.auth_service.verify()
81
+ if not auth_result.get("success"):
82
+ return jsonify(auth_result), 401
83
+
84
+ # 2. Get Company
85
+ company = self.profile_service.get_company_by_short_name(company_short_name)
86
+ if not company:
87
+ return jsonify({"error": "Company not found"}), 404
88
+
89
+ # 3. Parse Request
90
+ data = request.get_json() or {}
91
+
92
+ # 4. Sync Collection Types
93
+ # The service expects a list of names strings
94
+ if 'collection_types' in data:
95
+ self.knowledge_base_service.sync_collection_types(
96
+ company_short_name,
97
+ data.get('collection_types', [])
98
+ )
99
+
100
+ # 5. Sync Prompt Categories
101
+ if 'prompt_categories' in data:
102
+ self.prompt_service.sync_prompt_categories(
103
+ company_short_name,
104
+ data.get('prompt_categories', [])
105
+ )
106
+
107
+ return jsonify({"status": "success", "message": "Categories synchronized successfully"}), 200
108
+
109
+ except Exception as e:
110
+ logging.exception(f"Error syncing categories for {company_short_name}: {e}")
111
+ return jsonify({"status": "error", "message": str(e)}), 500
@@ -48,7 +48,7 @@ class ChatView(MethodView):
48
48
  branding_data = self.branding_service.get_company_branding(company_short_name)
49
49
  onboarding_cards = self.config_service.get_configuration(company_short_name, 'onboarding_cards')
50
50
  default_llm_model, available_llm_models = self.config_service.get_llm_configuration(company_short_name)
51
- prompts = self.prompt_service.get_user_prompts(company_short_name)
51
+ prompts = self.prompt_service.get_prompts(company_short_name)
52
52
  js_translations = self.i18n_service.get_translation_block('js_messages')
53
53
 
54
54
  return render_template(
@@ -142,7 +142,7 @@ class FinalizeContextView(MethodView):
142
142
  )
143
143
 
144
144
  # 3. render the chat page.
145
- prompts = self.prompt_service.get_user_prompts(company_short_name)
145
+ prompts = self.prompt_service.get_prompts(company_short_name)
146
146
  onboarding_cards = self.config_service.get_configuration(company_short_name, 'onboarding_cards')
147
147
 
148
148
  # Get the entire 'js_messages' block in the correct language.
@@ -58,7 +58,7 @@ class PromptApiView(MethodView):
58
58
  include_all = request.args.get('all', 'false').lower() == 'true'
59
59
 
60
60
  # return prompts based on filter
61
- return jsonify(self.prompt_service.get_user_prompts(company_short_name, include_all=include_all))
61
+ return jsonify(self.prompt_service.get_prompts(company_short_name, include_all=include_all))
62
62
 
63
63
  except Exception as e:
64
64
  logging.exception(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 1.9.0
3
+ Version: 1.15.3
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT