iatoolkit 1.7.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.
- iatoolkit/__init__.py +1 -1
- iatoolkit/common/routes.py +16 -3
- iatoolkit/common/util.py +8 -123
- iatoolkit/core.py +1 -0
- iatoolkit/infra/connectors/file_connector.py +10 -2
- iatoolkit/infra/connectors/google_drive_connector.py +3 -0
- iatoolkit/infra/connectors/local_file_connector.py +3 -0
- iatoolkit/infra/connectors/s3_connector.py +24 -1
- iatoolkit/infra/llm_providers/deepseek_adapter.py +17 -1
- iatoolkit/infra/llm_providers/gemini_adapter.py +117 -18
- iatoolkit/infra/llm_providers/openai_adapter.py +175 -18
- iatoolkit/infra/llm_response.py +13 -0
- iatoolkit/locales/en.yaml +82 -4
- iatoolkit/locales/es.yaml +79 -4
- iatoolkit/repositories/llm_query_repo.py +51 -18
- iatoolkit/repositories/models.py +16 -7
- iatoolkit/services/company_context_service.py +294 -133
- iatoolkit/services/configuration_service.py +140 -121
- iatoolkit/services/dispatcher_service.py +1 -4
- iatoolkit/services/knowledge_base_service.py +26 -4
- iatoolkit/services/llm_client_service.py +58 -2
- iatoolkit/services/prompt_service.py +251 -164
- iatoolkit/services/query_service.py +37 -18
- iatoolkit/services/storage_service.py +92 -0
- iatoolkit/static/js/chat_filepond.js +188 -63
- iatoolkit/static/js/chat_main.js +105 -52
- iatoolkit/static/styles/chat_iatoolkit.css +96 -0
- iatoolkit/system_prompts/query_main.prompt +24 -41
- iatoolkit/templates/chat.html +15 -6
- iatoolkit/views/base_login_view.py +1 -1
- iatoolkit/views/categories_api_view.py +111 -0
- iatoolkit/views/chat_view.py +1 -1
- iatoolkit/views/configuration_api_view.py +1 -1
- iatoolkit/views/login_view.py +1 -1
- iatoolkit/views/prompt_api_view.py +88 -7
- {iatoolkit-1.7.0.dist-info → iatoolkit-1.15.3.dist-info}/METADATA +1 -1
- {iatoolkit-1.7.0.dist-info → iatoolkit-1.15.3.dist-info}/RECORD +41 -39
- {iatoolkit-1.7.0.dist-info → iatoolkit-1.15.3.dist-info}/WHEEL +0 -0
- {iatoolkit-1.7.0.dist-info → iatoolkit-1.15.3.dist-info}/licenses/LICENSE +0 -0
- {iatoolkit-1.7.0.dist-info → iatoolkit-1.15.3.dist-info}/licenses/LICENSE_COMMUNITY.md +0 -0
- {iatoolkit-1.7.0.dist-info → iatoolkit-1.15.3.dist-info}/top_level.txt +0 -0
|
@@ -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
|
|
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 {{
|
|
16
|
+
## 🔧 Servicios de datos disponibles en {{ company_short_name }}
|
|
16
17
|
|
|
17
|
-
A continuación se muestran los *function calls* (tools) que
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
1.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
|
iatoolkit/templates/chat.html
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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')
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from flask import jsonify, request
|
|
7
|
+
from flask.views import MethodView
|
|
8
|
+
from injector import inject
|
|
9
|
+
from iatoolkit.services.auth_service import AuthService
|
|
10
|
+
from iatoolkit.services.prompt_service import PromptService
|
|
11
|
+
from iatoolkit.services.profile_service import ProfileService
|
|
12
|
+
from iatoolkit.services.configuration_service import ConfigurationService
|
|
13
|
+
from iatoolkit.services.knowledge_base_service import KnowledgeBaseService
|
|
14
|
+
from iatoolkit.repositories.llm_query_repo import LLMQueryRepo
|
|
15
|
+
from iatoolkit.repositories.models import PromptType, PromptCategory
|
|
16
|
+
import logging
|
|
17
|
+
|
|
18
|
+
class CategoriesApiView(MethodView):
|
|
19
|
+
"""
|
|
20
|
+
Endpoint to retrieve all available categories and types in the system.
|
|
21
|
+
Useful for populating dropdowns in the frontend.
|
|
22
|
+
"""
|
|
23
|
+
@inject
|
|
24
|
+
def __init__(self,
|
|
25
|
+
auth_service: AuthService,
|
|
26
|
+
profile_service: ProfileService,
|
|
27
|
+
configuration_service: ConfigurationService,
|
|
28
|
+
knowledge_base_service: KnowledgeBaseService,
|
|
29
|
+
llm_query_repo: LLMQueryRepo,
|
|
30
|
+
prompt_service: PromptService):
|
|
31
|
+
self.auth_service = auth_service
|
|
32
|
+
self.profile_service = profile_service
|
|
33
|
+
self.knowledge_base_service = knowledge_base_service
|
|
34
|
+
self.llm_query_repo = llm_query_repo
|
|
35
|
+
self.configuration_service = configuration_service
|
|
36
|
+
self.prompt_service = prompt_service
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get(self, company_short_name):
|
|
40
|
+
try:
|
|
41
|
+
# 1. Verify Authentication
|
|
42
|
+
auth_result = self.auth_service.verify()
|
|
43
|
+
if not auth_result.get("success"):
|
|
44
|
+
return jsonify(auth_result), 401
|
|
45
|
+
|
|
46
|
+
# 2. Get Company
|
|
47
|
+
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
48
|
+
if not company:
|
|
49
|
+
return jsonify({"error": "Company not found"}), 404
|
|
50
|
+
|
|
51
|
+
# 3. Gather Categories
|
|
52
|
+
response_data = {
|
|
53
|
+
"prompt_types": [t.value for t in PromptType],
|
|
54
|
+
"prompt_categories": [],
|
|
55
|
+
"collection_types": [],
|
|
56
|
+
# Future categories can be added here (e.g., tool_types, user_roles)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# A. Prompt Categories (from DB)
|
|
60
|
+
prompt_cats = self.llm_query_repo.get_all_categories(company_id=company.id)
|
|
61
|
+
response_data["prompt_categories"] = [c.name for c in prompt_cats]
|
|
62
|
+
|
|
63
|
+
# B. Collection Types (from KnowledgeBaseService)
|
|
64
|
+
response_data["collection_types"] = self.knowledge_base_service.get_collection_names(company_short_name)
|
|
65
|
+
|
|
66
|
+
# C. LLM Models (from ConfigurationService)
|
|
67
|
+
_, llm_models = self.configuration_service.get_llm_configuration(company_short_name)
|
|
68
|
+
# Extract only IDs
|
|
69
|
+
response_data["llm_models"] = [m['id'] for m in llm_models if 'id' in m]
|
|
70
|
+
|
|
71
|
+
return jsonify(response_data)
|
|
72
|
+
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logging.exception(f"Error fetching categories for {company_short_name}: {e}")
|
|
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
|
iatoolkit/views/chat_view.py
CHANGED
|
@@ -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.
|
|
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(
|
|
@@ -62,7 +62,7 @@ class ConfigurationApiView(MethodView):
|
|
|
62
62
|
Body: { "key": "llm.model", "value": "gpt-4" }
|
|
63
63
|
"""
|
|
64
64
|
try:
|
|
65
|
-
auth_result = self.auth_service.verify(
|
|
65
|
+
auth_result = self.auth_service.verify()
|
|
66
66
|
if not auth_result.get("success"):
|
|
67
67
|
return jsonify(auth_result), 401
|
|
68
68
|
|
iatoolkit/views/login_view.py
CHANGED
|
@@ -142,7 +142,7 @@ class FinalizeContextView(MethodView):
|
|
|
142
142
|
)
|
|
143
143
|
|
|
144
144
|
# 3. render the chat page.
|
|
145
|
-
prompts = self.prompt_service.
|
|
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.
|
|
@@ -3,9 +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 iatoolkit.services.prompt_service import PromptService
|
|
9
|
+
from iatoolkit.services.profile_service import ProfileService
|
|
10
|
+
from iatoolkit.repositories.llm_query_repo import LLMQueryRepo
|
|
9
11
|
from iatoolkit.services.auth_service import AuthService
|
|
10
12
|
from injector import inject
|
|
11
13
|
import logging
|
|
@@ -15,23 +17,102 @@ class PromptApiView(MethodView):
|
|
|
15
17
|
@inject
|
|
16
18
|
def __init__(self,
|
|
17
19
|
auth_service: AuthService,
|
|
18
|
-
prompt_service: PromptService
|
|
20
|
+
prompt_service: PromptService,
|
|
21
|
+
profile_service: ProfileService,
|
|
22
|
+
llm_query_repo: LLMQueryRepo):
|
|
19
23
|
self.auth_service = auth_service
|
|
20
24
|
self.prompt_service = prompt_service
|
|
25
|
+
self.profile_service = profile_service
|
|
26
|
+
self.llm_query_repo = llm_query_repo
|
|
21
27
|
|
|
22
|
-
def get(self, company_short_name):
|
|
28
|
+
def get(self, company_short_name, prompt_name=None):
|
|
29
|
+
"""
|
|
30
|
+
GET /: Lista el árbol de prompts (Categorías > Prompts).
|
|
31
|
+
GET /<name>: Devuelve detalle completo: metadata + contenido texto.
|
|
32
|
+
"""
|
|
23
33
|
try:
|
|
24
34
|
# get access credentials
|
|
25
35
|
auth_result = self.auth_service.verify(anonymous=True)
|
|
26
36
|
if not auth_result.get("success"):
|
|
27
37
|
return jsonify(auth_result), auth_result.get('status_code')
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
if
|
|
31
|
-
|
|
39
|
+
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
40
|
+
if not company:
|
|
41
|
+
return jsonify({"error": "Company not found"}), 404
|
|
42
|
+
|
|
43
|
+
if prompt_name:
|
|
44
|
+
# get the prompt object from database
|
|
45
|
+
prompt_obj = self.llm_query_repo.get_prompt_by_name(company, prompt_name)
|
|
46
|
+
if not prompt_obj:
|
|
47
|
+
return jsonify({"error": "Prompt not found"}), 404
|
|
48
|
+
|
|
49
|
+
# get the prompt content
|
|
50
|
+
content = self.prompt_service.get_prompt_content(company, prompt_name)
|
|
51
|
+
|
|
52
|
+
return jsonify({
|
|
53
|
+
"meta": prompt_obj.to_dict(),
|
|
54
|
+
"content": content
|
|
55
|
+
})
|
|
56
|
+
else:
|
|
57
|
+
# Check for query param to include all prompts (admin view)
|
|
58
|
+
include_all = request.args.get('all', 'false').lower() == 'true'
|
|
59
|
+
|
|
60
|
+
# return prompts based on filter
|
|
61
|
+
return jsonify(self.prompt_service.get_prompts(company_short_name, include_all=include_all))
|
|
32
62
|
|
|
33
|
-
return response, 200
|
|
34
63
|
except Exception as e:
|
|
35
64
|
logging.exception(
|
|
36
65
|
f"unexpected error getting company prompts: {e}")
|
|
37
66
|
return jsonify({"error_message": str(e)}), 500
|
|
67
|
+
|
|
68
|
+
def put(self, company_short_name, prompt_name):
|
|
69
|
+
try:
|
|
70
|
+
auth_result = self.auth_service.verify()
|
|
71
|
+
if not auth_result.get("success"):
|
|
72
|
+
return jsonify(auth_result), 401
|
|
73
|
+
|
|
74
|
+
data = request.get_json()
|
|
75
|
+
|
|
76
|
+
# The service handles file magic and YAML sync
|
|
77
|
+
self.prompt_service.save_prompt(company_short_name, prompt_name, data)
|
|
78
|
+
|
|
79
|
+
return jsonify({"status": "success"})
|
|
80
|
+
except Exception as e:
|
|
81
|
+
logging.exception(f"Error saving prompt {prompt_name}: {e}")
|
|
82
|
+
return jsonify({"status": "error", "message": str(e)}), 500
|
|
83
|
+
|
|
84
|
+
def post(self, company_short_name, prompt_name=None):
|
|
85
|
+
"""Creates a new prompt."""
|
|
86
|
+
try:
|
|
87
|
+
auth_result = self.auth_service.verify()
|
|
88
|
+
if not auth_result.get("success"):
|
|
89
|
+
return jsonify(auth_result), 401
|
|
90
|
+
|
|
91
|
+
data = request.get_json()
|
|
92
|
+
# If prompt_name is not in URL, check body
|
|
93
|
+
target_name = prompt_name if prompt_name else data.get('name')
|
|
94
|
+
|
|
95
|
+
if not target_name:
|
|
96
|
+
return jsonify({"status": "error", "message": "Prompt name is required"}), 400
|
|
97
|
+
|
|
98
|
+
# Reuse save_prompt logic which handles create/update
|
|
99
|
+
self.prompt_service.save_prompt(company_short_name, target_name, data)
|
|
100
|
+
|
|
101
|
+
return jsonify({"status": "success"})
|
|
102
|
+
except Exception as e:
|
|
103
|
+
logging.exception(f"Error creating prompt: {e}")
|
|
104
|
+
return jsonify({"status": "error", "message": str(e)}), 500
|
|
105
|
+
|
|
106
|
+
def delete(self, company_short_name, prompt_name):
|
|
107
|
+
"""Deletes a prompt."""
|
|
108
|
+
try:
|
|
109
|
+
auth_result = self.auth_service.verify()
|
|
110
|
+
if not auth_result.get("success"):
|
|
111
|
+
return jsonify(auth_result), 401
|
|
112
|
+
|
|
113
|
+
self.prompt_service.delete_prompt(company_short_name, prompt_name)
|
|
114
|
+
|
|
115
|
+
return jsonify({"status": "success"})
|
|
116
|
+
except Exception as e:
|
|
117
|
+
logging.exception(f"Error deleting prompt {prompt_name}: {e}")
|
|
118
|
+
return jsonify({"status": "error", "message": str(e)}), 500
|