iatoolkit 0.71.4__py3-none-any.whl → 0.91.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.
- iatoolkit/__init__.py +15 -5
- iatoolkit/base_company.py +4 -58
- iatoolkit/cli_commands.py +6 -7
- iatoolkit/common/exceptions.py +1 -0
- iatoolkit/common/routes.py +12 -28
- iatoolkit/common/util.py +7 -1
- iatoolkit/company_registry.py +50 -14
- iatoolkit/{iatoolkit.py → core.py} +54 -55
- iatoolkit/infra/{mail_app.py → brevo_mail_app.py} +15 -37
- iatoolkit/infra/llm_client.py +9 -5
- iatoolkit/locales/en.yaml +10 -2
- iatoolkit/locales/es.yaml +171 -162
- iatoolkit/repositories/database_manager.py +59 -14
- iatoolkit/repositories/llm_query_repo.py +34 -22
- iatoolkit/repositories/models.py +16 -18
- iatoolkit/repositories/profile_repo.py +5 -10
- iatoolkit/repositories/vs_repo.py +9 -4
- iatoolkit/services/auth_service.py +1 -1
- iatoolkit/services/branding_service.py +1 -1
- iatoolkit/services/company_context_service.py +19 -11
- iatoolkit/services/configuration_service.py +219 -46
- iatoolkit/services/dispatcher_service.py +31 -225
- iatoolkit/services/document_service.py +10 -1
- iatoolkit/services/embedding_service.py +9 -6
- iatoolkit/services/excel_service.py +50 -2
- iatoolkit/services/history_manager_service.py +189 -0
- iatoolkit/services/jwt_service.py +1 -1
- iatoolkit/services/language_service.py +8 -2
- iatoolkit/services/license_service.py +82 -0
- iatoolkit/services/mail_service.py +171 -25
- iatoolkit/services/profile_service.py +37 -32
- iatoolkit/services/{prompt_manager_service.py → prompt_service.py} +110 -1
- iatoolkit/services/query_service.py +192 -191
- iatoolkit/services/sql_service.py +63 -12
- iatoolkit/services/tool_service.py +231 -0
- iatoolkit/services/user_feedback_service.py +18 -6
- iatoolkit/services/user_session_context_service.py +18 -0
- iatoolkit/static/images/iatoolkit_core.png +0 -0
- iatoolkit/static/images/iatoolkit_logo.png +0 -0
- iatoolkit/static/js/chat_feedback_button.js +1 -1
- iatoolkit/static/js/chat_help_content.js +4 -4
- iatoolkit/static/js/chat_main.js +17 -5
- iatoolkit/static/js/chat_onboarding_button.js +1 -1
- iatoolkit/static/styles/chat_iatoolkit.css +1 -1
- iatoolkit/static/styles/chat_public.css +28 -0
- iatoolkit/static/styles/documents.css +598 -0
- iatoolkit/static/styles/landing_page.css +223 -7
- iatoolkit/system_prompts/__init__.py +0 -0
- iatoolkit/system_prompts/query_main.prompt +2 -1
- iatoolkit/system_prompts/sql_rules.prompt +47 -12
- iatoolkit/templates/_company_header.html +30 -5
- iatoolkit/templates/_login_widget.html +3 -3
- iatoolkit/templates/chat.html +1 -1
- iatoolkit/templates/forgot_password.html +3 -2
- iatoolkit/templates/onboarding_shell.html +1 -1
- iatoolkit/templates/signup.html +3 -0
- iatoolkit/views/base_login_view.py +1 -1
- iatoolkit/views/change_password_view.py +1 -1
- iatoolkit/views/forgot_password_view.py +9 -4
- iatoolkit/views/history_api_view.py +3 -3
- iatoolkit/views/home_view.py +4 -2
- iatoolkit/views/init_context_api_view.py +1 -1
- iatoolkit/views/llmquery_api_view.py +4 -3
- iatoolkit/views/{file_store_api_view.py → load_document_api_view.py} +1 -1
- iatoolkit/views/login_view.py +17 -5
- iatoolkit/views/logout_api_view.py +10 -2
- iatoolkit/views/prompt_api_view.py +1 -1
- iatoolkit/views/root_redirect_view.py +22 -0
- iatoolkit/views/signup_view.py +12 -4
- iatoolkit/views/static_page_view.py +27 -0
- iatoolkit/views/verify_user_view.py +1 -1
- iatoolkit-0.91.1.dist-info/METADATA +268 -0
- iatoolkit-0.91.1.dist-info/RECORD +125 -0
- iatoolkit-0.91.1.dist-info/licenses/LICENSE_COMMUNITY.md +15 -0
- iatoolkit/services/history_service.py +0 -37
- iatoolkit/templates/about.html +0 -13
- iatoolkit/templates/index.html +0 -145
- iatoolkit/templates/login_simulation.html +0 -45
- iatoolkit/views/external_login_view.py +0 -73
- iatoolkit/views/index_view.py +0 -14
- iatoolkit/views/login_simulation_view.py +0 -93
- iatoolkit-0.71.4.dist-info/METADATA +0 -276
- iatoolkit-0.71.4.dist-info/RECORD +0 -122
- {iatoolkit-0.71.4.dist-info → iatoolkit-0.91.1.dist-info}/WHEEL +0 -0
- {iatoolkit-0.71.4.dist-info → iatoolkit-0.91.1.dist-info}/licenses/LICENSE +0 -0
- {iatoolkit-0.71.4.dist-info → iatoolkit-0.91.1.dist-info}/top_level.txt +0 -0
iatoolkit/locales/es.yaml
CHANGED
|
@@ -1,163 +1,172 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
1
|
+
# locales/es.yaml
|
|
2
|
+
ui:
|
|
3
|
+
login_widget:
|
|
4
|
+
title: "Iniciar Sesión"
|
|
5
|
+
welcome_message: "Ingresa tus credenciales o registrate para acceder a la plataforma."
|
|
6
|
+
email_placeholder: "Correo electrónico"
|
|
7
|
+
login_button: "Acceder"
|
|
8
|
+
forgot_password_link: "¿Olvidaste tu contraseña?"
|
|
9
|
+
no_account_prompt: "¿No tienes una cuenta?"
|
|
10
|
+
signup_link: "Regístrate"
|
|
11
|
+
|
|
12
|
+
signup:
|
|
13
|
+
title: "Crear una Cuenta"
|
|
14
|
+
subtitle: "Comienza tu viaje con nosotros hoy."
|
|
15
|
+
first_name_label: "Nombre"
|
|
16
|
+
last_name_label: "Apellido"
|
|
17
|
+
email_label: "Correo Electrónico"
|
|
18
|
+
password_label: "Contraseña"
|
|
19
|
+
confirm_password_label: "Confirmar Contraseña"
|
|
20
|
+
signup_button: "Crear Cuenta"
|
|
21
|
+
already_have_account: "¿Ya tienes una cuenta?"
|
|
22
|
+
login_link: "Inicia Sesión"
|
|
23
|
+
disclaimer: "🔒 Valoramos tu privacidad. Tus datos se usarán exclusivamente para el funcionamiento de la plataforma."
|
|
24
|
+
|
|
25
|
+
forgot_password:
|
|
26
|
+
title: "Recuperar Contraseña"
|
|
27
|
+
subtitle: "Ingresa tu correo electrónico y te enviaremos un enlace para restablecer tu contraseña."
|
|
28
|
+
submit_button: "Enviar Enlace de Recuperación"
|
|
29
|
+
back_to_login: "Volver a Iniciar Sesión"
|
|
30
|
+
|
|
31
|
+
change_password:
|
|
32
|
+
title: "Crear Nueva Contraseña"
|
|
33
|
+
subtitle: "Estás cambiando la contraseña para <strong>{email}</strong>."
|
|
34
|
+
temp_code_label: "Código Temporal"
|
|
35
|
+
temp_code_placeholder: "Revisa tu correo electrónico"
|
|
36
|
+
new_password_label: "Nueva Contraseña"
|
|
37
|
+
password_instructions: "Debe contener al menos 8 caracteres, mayúscula, minúscula, número y un carácter especial."
|
|
38
|
+
confirm_password_label: "Confirmar Nueva Contraseña"
|
|
39
|
+
save_button: "Guardar Contraseña"
|
|
40
|
+
back_to_home: "Volver al inicio"
|
|
41
|
+
|
|
42
|
+
chat:
|
|
43
|
+
welcome_message: "¡Hola! ¿En qué te puedo ayudar hoy?"
|
|
44
|
+
input_placeholder: "Escribe aquí..."
|
|
45
|
+
prompts_available: "Prompts disponibles"
|
|
46
|
+
init_context: "Inicializando el contexto de la IA ..."
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
tooltips:
|
|
50
|
+
history: "Historial con mis consultas"
|
|
51
|
+
reload_context: "Forzar Recarga de Contexto"
|
|
52
|
+
feedback: "Tu feedback es muy importante"
|
|
53
|
+
usage_guide: "Guía de Uso"
|
|
54
|
+
onboarding: "Cómo preguntar mejor"
|
|
55
|
+
logout: "Cerrar sesión"
|
|
56
|
+
use_prompt_assistant: "Usar Asistente de Prompts"
|
|
57
|
+
attach_files: "Adjuntar archivos"
|
|
58
|
+
view_attached_files: "Ver archivos adjuntos"
|
|
59
|
+
modals:
|
|
60
|
+
files_title: "Archivos Cargados"
|
|
61
|
+
feedback_title: "Tu Opinión es Importante"
|
|
62
|
+
feedback_prompt: "¿Qué tan útil fue la respuesta del asistente?"
|
|
63
|
+
feedback_comment_label: "Tu comentario nos ayuda a mejorar:"
|
|
64
|
+
feedback_comment_placeholder: "Escribe aquí tu opinión, sugerencias o comentarios..."
|
|
65
|
+
history_title: "Historial de Consultas"
|
|
66
|
+
history_table_date: "Fecha"
|
|
67
|
+
history_table_query: "Consulta"
|
|
68
|
+
loading_history: "Cargando historial..."
|
|
69
|
+
no_history_found: "No se encontró historial de consultas."
|
|
70
|
+
help_title: "Guía de uso del Asistente IA"
|
|
71
|
+
|
|
72
|
+
buttons:
|
|
73
|
+
cancel: "Cerrar"
|
|
74
|
+
send: "Enviar"
|
|
75
|
+
stop: "Detener"
|
|
76
|
+
|
|
77
|
+
errors:
|
|
78
|
+
company_not_found: "La empresa {company_short_name} no existe."
|
|
79
|
+
timeout: "El tiempo de espera ha expirado."
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
auth:
|
|
83
|
+
invalid_password: "La contraseña proporcionada es incorrecta."
|
|
84
|
+
user_not_found: "No se encontró un usuario con ese correo."
|
|
85
|
+
invalid_or_expired_token: "Token inválido o expirado."
|
|
86
|
+
session_creation_failed: "No se pudo crear la sesión del usuario."
|
|
87
|
+
authentication_required: "Autenticación requerida. No se proporcionó cookie de sesión o clave de API."
|
|
88
|
+
invalid_api_key: "Clave de API inválida o inactiva."
|
|
89
|
+
no_user_identifier_api: "No se proporcionó user_identifier para la llamada a la API."
|
|
90
|
+
templates:
|
|
91
|
+
company_not_found: "Empresa no encontrada."
|
|
92
|
+
home_template_not_found: "La plantilla de la página de inicio para la empresa '{company_short_name}' no está configurada."
|
|
93
|
+
processing_error: "Error al procesar el template: {error}"
|
|
94
|
+
template_not_found: "No se encontro el template: '{template_name}'."
|
|
95
|
+
|
|
96
|
+
general:
|
|
97
|
+
unexpected_error: "Ha ocurrido un error inesperado: {error}."
|
|
98
|
+
unsupported_language: "El idioma seleccionado no es válido."
|
|
99
|
+
signup:
|
|
100
|
+
company_not_found: "La empresa {company_short_name} no existe."
|
|
101
|
+
incorrect_password_for_existing_user: "La contraseña para el usuario {email} es incorrecta."
|
|
102
|
+
user_already_registered: "El usuario con email '{email}' ya existe en esta empresa."
|
|
103
|
+
password_mismatch: "Las contraseñas no coinciden. Por favor, inténtalo de nuevo."
|
|
104
|
+
change_password:
|
|
105
|
+
token_expired: "El enlace de cambio de contraseña ha expirado. Por favor, solicita uno nuevo."
|
|
106
|
+
password_mismatch: "Las contraseñas no coinciden. Por favor, inténtalo nuevamente."
|
|
107
|
+
invalid_temp_code: "El código temporal no es válido. Por favor, verifica o solicita uno nuevo."
|
|
108
|
+
forgot_password:
|
|
109
|
+
user_not_registered: "El usuario con correo {email} no está registrado."
|
|
110
|
+
verification:
|
|
111
|
+
token_expired: "El enlace de verificación ha expirado. Por favor, contacta a soporte si necesitas uno nuevo."
|
|
112
|
+
user_not_found: "El usuario que intentas verificar no existe."
|
|
113
|
+
validation:
|
|
114
|
+
password_too_short: "La contraseña debe tener al menos 8 caracteres."
|
|
115
|
+
password_no_uppercase: "La contraseña debe tener al menos una letra mayúscula."
|
|
116
|
+
password_no_lowercase: "La contraseña debe tener al menos una letra minúscula."
|
|
117
|
+
password_no_digit: "La contraseña debe tener al menos un número."
|
|
118
|
+
password_no_special_char: "La contraseña debe tener al menos un carácter especial."
|
|
119
|
+
|
|
120
|
+
services:
|
|
121
|
+
no_text_file: "El archivo no es texto o la codificación no es UTF-8"
|
|
122
|
+
no_output_file: "falta el nombre del archivo de salida"
|
|
123
|
+
no_data_for_excel: "faltan los datos o no es una lista de diccionarios"
|
|
124
|
+
no_download_directory: "no esta configurado el directorio temporal para guardar excels"
|
|
125
|
+
cannot_create_excel: "no se pudo crear el archivo excel"
|
|
126
|
+
invalid_filename: "Nombre de archivo inválido"
|
|
127
|
+
file_not_exist : "Archivo no encontrado"
|
|
128
|
+
path_is_not_a_file : "La ruta no corresponde a un archivo"
|
|
129
|
+
file_validation_error : "Error validando archivo"
|
|
130
|
+
user_not_authorized: "Usuario no esta autorizado para esta empresa"
|
|
131
|
+
account_not_verified: "Tu cuenta no ha sido verificada. Por favor, revisa tu correo."
|
|
132
|
+
missing_response_id: "No se encontró 'previous_response_id' para '{company_short_name}/{user_identifier}'. Reinicia el contexto."
|
|
133
|
+
context_rebuild_failed: "No se pudo reconstruir el contexto de la empresa."
|
|
134
|
+
cannot_read_excel: "No se pudo leer el archivo excel"
|
|
135
|
+
cannot_read_csv: "No se pudo leer el archivo CSV"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
api_responses:
|
|
139
|
+
context_reloaded_success: "El contexto se ha recargado con éxito."
|
|
140
|
+
|
|
141
|
+
services:
|
|
142
|
+
mail_sent: "mail enviado exitosamente."
|
|
143
|
+
start_query: "Hola, cual es tu pregunta?"
|
|
144
|
+
mail_change_password: "se envio mail para cambio de clave"
|
|
145
|
+
|
|
146
|
+
flash_messages:
|
|
147
|
+
password_changed_success: "Tu contraseña ha sido restablecida exitosamente. Ahora puedes iniciar sesión."
|
|
148
|
+
signup_success: "Registro exitoso. Por favor, revisa tu correo para verificar tu cuenta."
|
|
149
|
+
signup_success_no_verification: "Registro exitoso."
|
|
150
|
+
user_associated_success: "Usuario existente asociado exitosamente a la nueva empresa."
|
|
151
|
+
account_verified_success: "Tu cuenta ha sido verificada exitosamente. ¡Bienvenido!"
|
|
152
|
+
forgot_password_success: "Si tu correo está registrado, recibirás un enlace para restablecer tu contraseña."
|
|
153
|
+
|
|
154
|
+
# Claves específicas para JavaScript
|
|
155
|
+
js_messages:
|
|
156
|
+
feedback_sent_success_title: "Feedback Enviado"
|
|
157
|
+
feedback_sent_success_body: "¡Gracias por tu comentario!"
|
|
158
|
+
feedback_sent_error: "No se pudo enviar el feedback, por favor intente nuevamente."
|
|
159
|
+
feedback_rating_error: "Por favor, califica al asistente con las estrellas."
|
|
160
|
+
feedback_comment_error: "Por favor, escribe tu comentario antes de enviar."
|
|
161
|
+
context_reloaded: "El contexto ha sido recargado."
|
|
162
|
+
error_loading_history: "Ocurrió un error al cargar el historial."
|
|
163
|
+
request_aborted: "La generación de la respuesta ha sido detenida."
|
|
164
|
+
processing_error: "Ocurrió un error al procesar la solicitud."
|
|
165
|
+
server_comm_error: "Error de comunicación con el servidor ({status}). Por favor, intente de nuevo más tarde."
|
|
166
|
+
network_error: "Ocurrió un error de red. Por favor, inténtalo de nuevo en unos momentos."
|
|
167
|
+
unknown_server_error: "Error desconocido del servidor."
|
|
168
|
+
loading: "Cargando..."
|
|
169
|
+
reload_init: "Iniciando recarga de contexto en segundo plano..."
|
|
170
|
+
no_history_found: "No existe historial de consultas."
|
|
171
|
+
example: "Ejemplo:"
|
|
163
172
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
6
|
# database_manager.py
|
|
7
|
-
from sqlalchemy import create_engine, event, inspect
|
|
7
|
+
from sqlalchemy import create_engine, event, inspect, text
|
|
8
8
|
from sqlalchemy.orm import sessionmaker, scoped_session
|
|
9
9
|
from sqlalchemy.engine.url import make_url
|
|
10
10
|
from iatoolkit.repositories.models import Base
|
|
@@ -14,14 +14,26 @@ from pgvector.psycopg2 import register_vector
|
|
|
14
14
|
|
|
15
15
|
class DatabaseManager:
|
|
16
16
|
@inject
|
|
17
|
-
def __init__(self,
|
|
17
|
+
def __init__(self,
|
|
18
|
+
database_url: str,
|
|
19
|
+
schema: str | None = None,
|
|
20
|
+
register_pgvector: bool = True):
|
|
18
21
|
"""
|
|
19
22
|
Inicializa el gestor de la base de datos.
|
|
20
23
|
:param database_url: URL de la base de datos.
|
|
24
|
+
:param schema: Esquema por defecto para la conexión (search_path).
|
|
21
25
|
:param echo: Si True, habilita logs de SQL.
|
|
22
26
|
"""
|
|
27
|
+
|
|
28
|
+
self.schema = schema
|
|
29
|
+
|
|
30
|
+
# FIX HEROKU: replace postgres:// by postgresql:// for compatibility with SQLAlchemy 1.4+
|
|
31
|
+
if database_url and database_url.startswith("postgres://"):
|
|
32
|
+
database_url = database_url.replace("postgres://", "postgresql://", 1)
|
|
33
|
+
|
|
23
34
|
self.url = make_url(database_url)
|
|
24
|
-
|
|
35
|
+
|
|
36
|
+
if database_url.startswith('sqlite'):
|
|
25
37
|
self._engine = create_engine(database_url, echo=False)
|
|
26
38
|
else:
|
|
27
39
|
self._engine = create_engine(
|
|
@@ -40,9 +52,30 @@ class DatabaseManager:
|
|
|
40
52
|
expire_on_commit=False)
|
|
41
53
|
self.scoped_session = scoped_session(self.SessionFactory)
|
|
42
54
|
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
|
|
55
|
+
# Register pgvector for each new connection
|
|
56
|
+
backend = self.url.get_backend_name()
|
|
57
|
+
if backend == 'postgresql' or backend == 'postgres':
|
|
58
|
+
if register_pgvector:
|
|
59
|
+
event.listen(self._engine, 'connect', self.on_connect)
|
|
60
|
+
|
|
61
|
+
# if there is a schema, configure the search_path for each connection
|
|
62
|
+
if self.schema:
|
|
63
|
+
event.listen(self._engine, 'checkout', self.set_search_path)
|
|
64
|
+
|
|
65
|
+
def set_search_path(self, dbapi_connection, connection_record, connection_proxy):
|
|
66
|
+
# Configure the search_path for this connection
|
|
67
|
+
cursor = dbapi_connection.cursor()
|
|
68
|
+
|
|
69
|
+
# The defined schema is first, and then public by default
|
|
70
|
+
try:
|
|
71
|
+
cursor.execute(f"SET search_path TO {self.schema}, public")
|
|
72
|
+
cursor.close()
|
|
73
|
+
|
|
74
|
+
# commit for persist the change in the session
|
|
75
|
+
dbapi_connection.commit()
|
|
76
|
+
except Exception:
|
|
77
|
+
# if failed, rollback to avoid invalidating the connection
|
|
78
|
+
dbapi_connection.rollback()
|
|
46
79
|
|
|
47
80
|
@staticmethod
|
|
48
81
|
def on_connect(dbapi_connection, connection_record):
|
|
@@ -62,6 +95,12 @@ class DatabaseManager:
|
|
|
62
95
|
return self._engine
|
|
63
96
|
|
|
64
97
|
def create_all(self):
|
|
98
|
+
# if there is a schema defined, make sure it exists before creating tables
|
|
99
|
+
backend = self.url.get_backend_name()
|
|
100
|
+
if self.schema and (backend == 'postgresql' or backend == 'postgres'):
|
|
101
|
+
with self._engine.begin() as conn:
|
|
102
|
+
conn.execute(text(f"CREATE SCHEMA IF NOT EXISTS {self.schema}"))
|
|
103
|
+
|
|
65
104
|
Base.metadata.create_all(self._engine)
|
|
66
105
|
|
|
67
106
|
def drop_all(self):
|
|
@@ -73,22 +112,24 @@ class DatabaseManager:
|
|
|
73
112
|
def get_all_table_names(self) -> list[str]:
|
|
74
113
|
# Returns a list of all table names in the database
|
|
75
114
|
inspector = inspect(self._engine)
|
|
76
|
-
return inspector.get_table_names()
|
|
115
|
+
return inspector.get_table_names(schema=self.schema)
|
|
77
116
|
|
|
78
117
|
def get_table_schema(self,
|
|
79
118
|
table_name: str,
|
|
80
|
-
|
|
119
|
+
db_schema: str,
|
|
120
|
+
schema_object_name: str | None = None,
|
|
81
121
|
exclude_columns: list[str] | None = None) -> str:
|
|
82
122
|
inspector = inspect(self._engine)
|
|
83
123
|
|
|
84
|
-
|
|
85
|
-
|
|
124
|
+
# search the table in the specified schema
|
|
125
|
+
if table_name not in inspector.get_table_names(schema=db_schema):
|
|
126
|
+
raise RuntimeError(f"Table '{table_name}' does not exist in database schema '{db_schema}'.")
|
|
86
127
|
|
|
87
128
|
if exclude_columns is None:
|
|
88
129
|
exclude_columns = []
|
|
89
130
|
|
|
90
|
-
# get all
|
|
91
|
-
columns = inspector.get_columns(table_name)
|
|
131
|
+
# get all the table columns
|
|
132
|
+
columns = inspector.get_columns(table_name, schema=db_schema)
|
|
92
133
|
|
|
93
134
|
# construct a json dictionary with the table definition
|
|
94
135
|
json_dict = {
|
|
@@ -96,8 +137,12 @@ class DatabaseManager:
|
|
|
96
137
|
"description": f"Definición de la tabla {table_name}.",
|
|
97
138
|
"fields": []
|
|
98
139
|
}
|
|
99
|
-
if
|
|
100
|
-
json_dict["description"] += f"Los detalles de cada campo están en el objeto **`{
|
|
140
|
+
if schema_object_name:
|
|
141
|
+
json_dict["description"] += f"Los detalles de cada campo están en el objeto **`{schema_object_name}`**."
|
|
142
|
+
|
|
143
|
+
if db_schema:
|
|
144
|
+
json_dict["schema"] = db_schema
|
|
145
|
+
json_dict["description"] += f"Pertenece al esquema **`{db_schema}`**."
|
|
101
146
|
|
|
102
147
|
# now add every column to the json dictionary
|
|
103
148
|
for col in columns:
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
#
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
|
-
from iatoolkit.repositories.models import LLMQuery,
|
|
6
|
+
from iatoolkit.repositories.models import LLMQuery, Tool, Company, Prompt, PromptCategory
|
|
7
7
|
from injector import inject
|
|
8
8
|
from iatoolkit.repositories.database_manager import DatabaseManager
|
|
9
9
|
from sqlalchemy import or_
|
|
@@ -13,38 +13,50 @@ class LLMQueryRepo:
|
|
|
13
13
|
def __init__(self, db_manager: DatabaseManager):
|
|
14
14
|
self.session = db_manager.get_session()
|
|
15
15
|
|
|
16
|
+
def commit(self):
|
|
17
|
+
self.session.commit()
|
|
18
|
+
|
|
19
|
+
def rollback(self):
|
|
20
|
+
self.session.rollback()
|
|
21
|
+
|
|
16
22
|
def add_query(self, query: LLMQuery):
|
|
17
23
|
self.session.add(query)
|
|
18
24
|
self.session.commit()
|
|
19
25
|
return query
|
|
20
26
|
|
|
21
27
|
|
|
22
|
-
def
|
|
28
|
+
def get_company_tools(self, company: Company) -> list[Tool]:
|
|
23
29
|
return (
|
|
24
|
-
self.session.query(
|
|
30
|
+
self.session.query(Tool)
|
|
25
31
|
.filter(
|
|
26
|
-
|
|
32
|
+
Tool.is_active.is_(True),
|
|
27
33
|
or_(
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
Tool.company_id == company.id,
|
|
35
|
+
Tool.system_function.is_(True)
|
|
30
36
|
)
|
|
31
37
|
)
|
|
32
38
|
.all()
|
|
33
39
|
)
|
|
34
40
|
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
def delete_system_tools(self):
|
|
42
|
+
self.session.query(Tool).filter_by(system_function=True).delete(synchronize_session=False)
|
|
43
|
+
|
|
44
|
+
def create_or_update_tool(self, new_tool: Tool):
|
|
45
|
+
tool = self.session.query(Tool).filter_by(company_id=new_tool.company_id,
|
|
46
|
+
name=new_tool.name).first()
|
|
47
|
+
if tool:
|
|
48
|
+
tool.description = new_tool.description
|
|
49
|
+
tool.parameters = new_tool.parameters
|
|
50
|
+
tool.system_function = new_tool.system_function
|
|
42
51
|
else:
|
|
43
|
-
self.session.add(
|
|
44
|
-
|
|
52
|
+
self.session.add(new_tool)
|
|
53
|
+
tool = new_tool
|
|
45
54
|
|
|
46
|
-
self.session.
|
|
47
|
-
return
|
|
55
|
+
self.session.flush()
|
|
56
|
+
return tool
|
|
57
|
+
|
|
58
|
+
def delete_tool(self, tool: Tool):
|
|
59
|
+
self.session.query(Tool).filter_by(id=tool.id).delete(synchronize_session=False)
|
|
48
60
|
|
|
49
61
|
def create_or_update_prompt(self, new_prompt: Prompt):
|
|
50
62
|
prompt = self.session.query(Prompt).filter_by(company_id=new_prompt.company_id,
|
|
@@ -53,7 +65,6 @@ class LLMQueryRepo:
|
|
|
53
65
|
prompt.category_id = new_prompt.category_id
|
|
54
66
|
prompt.description = new_prompt.description
|
|
55
67
|
prompt.order = new_prompt.order
|
|
56
|
-
prompt.active = new_prompt.active
|
|
57
68
|
prompt.is_system_prompt = new_prompt.is_system_prompt
|
|
58
69
|
prompt.filename = new_prompt.filename
|
|
59
70
|
prompt.custom_fields = new_prompt.custom_fields
|
|
@@ -61,7 +72,7 @@ class LLMQueryRepo:
|
|
|
61
72
|
self.session.add(new_prompt)
|
|
62
73
|
prompt = new_prompt
|
|
63
74
|
|
|
64
|
-
self.session.
|
|
75
|
+
self.session.flush()
|
|
65
76
|
return prompt
|
|
66
77
|
|
|
67
78
|
def create_or_update_prompt_category(self, new_category: PromptCategory):
|
|
@@ -73,7 +84,7 @@ class LLMQueryRepo:
|
|
|
73
84
|
self.session.add(new_category)
|
|
74
85
|
category = new_category
|
|
75
86
|
|
|
76
|
-
self.session.
|
|
87
|
+
self.session.flush()
|
|
77
88
|
return category
|
|
78
89
|
|
|
79
90
|
def get_history(self, company: Company, user_identifier: str) -> list[LLMQuery]:
|
|
@@ -84,8 +95,9 @@ class LLMQueryRepo:
|
|
|
84
95
|
def get_prompts(self, company: Company) -> list[Prompt]:
|
|
85
96
|
return self.session.query(Prompt).filter_by(company_id=company.id, is_system_prompt=False).all()
|
|
86
97
|
|
|
98
|
+
def get_prompt_by_name(self, company: Company, prompt_name: str):
|
|
99
|
+
return self.session.query(Prompt).filter_by(company_id=company.id, name=prompt_name).first()
|
|
100
|
+
|
|
87
101
|
def get_system_prompts(self) -> list[Prompt]:
|
|
88
102
|
return self.session.query(Prompt).filter_by(is_system_prompt=True, active=True).order_by(Prompt.order).all()
|
|
89
103
|
|
|
90
|
-
def get_prompt_by_name(self, company: Company, prompt_name: str):
|
|
91
|
-
return self.session.query(Prompt).filter_by(company_id=company.id, name=prompt_name).first()
|