iatoolkit 0.71.4__py3-none-any.whl → 1.4.2__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 +19 -7
- iatoolkit/base_company.py +1 -71
- iatoolkit/cli_commands.py +9 -21
- iatoolkit/common/exceptions.py +2 -0
- iatoolkit/common/interfaces/__init__.py +0 -0
- iatoolkit/common/interfaces/asset_storage.py +34 -0
- iatoolkit/common/interfaces/database_provider.py +38 -0
- iatoolkit/common/model_registry.py +159 -0
- iatoolkit/common/routes.py +53 -32
- iatoolkit/common/util.py +17 -12
- iatoolkit/company_registry.py +55 -14
- iatoolkit/{iatoolkit.py → core.py} +102 -72
- iatoolkit/infra/{mail_app.py → brevo_mail_app.py} +15 -37
- iatoolkit/infra/llm_providers/__init__.py +0 -0
- iatoolkit/infra/llm_providers/deepseek_adapter.py +278 -0
- iatoolkit/infra/{gemini_adapter.py → llm_providers/gemini_adapter.py} +11 -17
- iatoolkit/infra/{openai_adapter.py → llm_providers/openai_adapter.py} +41 -7
- iatoolkit/infra/llm_proxy.py +235 -134
- iatoolkit/infra/llm_response.py +5 -0
- iatoolkit/locales/en.yaml +134 -4
- iatoolkit/locales/es.yaml +293 -162
- iatoolkit/repositories/database_manager.py +92 -22
- iatoolkit/repositories/document_repo.py +7 -0
- iatoolkit/repositories/filesystem_asset_repository.py +36 -0
- iatoolkit/repositories/llm_query_repo.py +36 -22
- iatoolkit/repositories/models.py +86 -95
- iatoolkit/repositories/profile_repo.py +64 -13
- iatoolkit/repositories/vs_repo.py +31 -28
- iatoolkit/services/auth_service.py +1 -1
- iatoolkit/services/branding_service.py +1 -1
- iatoolkit/services/company_context_service.py +96 -39
- iatoolkit/services/configuration_service.py +329 -67
- iatoolkit/services/dispatcher_service.py +51 -227
- iatoolkit/services/document_service.py +10 -1
- iatoolkit/services/embedding_service.py +9 -6
- iatoolkit/services/excel_service.py +50 -2
- iatoolkit/services/file_processor_service.py +0 -5
- iatoolkit/services/history_manager_service.py +208 -0
- iatoolkit/services/jwt_service.py +1 -1
- iatoolkit/services/knowledge_base_service.py +412 -0
- iatoolkit/services/language_service.py +8 -2
- iatoolkit/services/license_service.py +82 -0
- iatoolkit/{infra/llm_client.py → services/llm_client_service.py} +42 -29
- iatoolkit/services/load_documents_service.py +18 -47
- iatoolkit/services/mail_service.py +171 -25
- iatoolkit/services/profile_service.py +69 -36
- iatoolkit/services/{prompt_manager_service.py → prompt_service.py} +136 -25
- iatoolkit/services/query_service.py +229 -203
- iatoolkit/services/sql_service.py +116 -34
- iatoolkit/services/tool_service.py +246 -0
- iatoolkit/services/user_feedback_service.py +18 -6
- iatoolkit/services/user_session_context_service.py +121 -51
- 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 +61 -9
- iatoolkit/static/js/chat_model_selector.js +227 -0
- iatoolkit/static/js/chat_onboarding_button.js +1 -1
- iatoolkit/static/js/chat_reload_button.js +4 -1
- iatoolkit/static/styles/chat_iatoolkit.css +59 -3
- iatoolkit/static/styles/chat_public.css +28 -0
- iatoolkit/static/styles/documents.css +598 -0
- iatoolkit/static/styles/landing_page.css +223 -7
- iatoolkit/static/styles/llm_output.css +34 -1
- iatoolkit/system_prompts/__init__.py +0 -0
- iatoolkit/system_prompts/query_main.prompt +28 -3
- iatoolkit/system_prompts/sql_rules.prompt +47 -12
- iatoolkit/templates/_company_header.html +30 -5
- iatoolkit/templates/_login_widget.html +3 -3
- iatoolkit/templates/base.html +13 -0
- iatoolkit/templates/chat.html +45 -3
- iatoolkit/templates/forgot_password.html +3 -2
- iatoolkit/templates/onboarding_shell.html +1 -2
- iatoolkit/templates/signup.html +3 -0
- iatoolkit/views/base_login_view.py +8 -3
- iatoolkit/views/change_password_view.py +1 -1
- iatoolkit/views/chat_view.py +76 -0
- 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/load_company_configuration_api_view.py +49 -0
- iatoolkit/views/{file_store_api_view.py → load_document_api_view.py} +15 -11
- iatoolkit/views/login_view.py +25 -8
- iatoolkit/views/logout_api_view.py +10 -2
- iatoolkit/views/prompt_api_view.py +1 -1
- iatoolkit/views/rag_api_view.py +216 -0
- 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/users_api_view.py +33 -0
- iatoolkit/views/verify_user_view.py +1 -1
- iatoolkit-1.4.2.dist-info/METADATA +268 -0
- iatoolkit-1.4.2.dist-info/RECORD +133 -0
- iatoolkit-1.4.2.dist-info/licenses/LICENSE_COMMUNITY.md +15 -0
- iatoolkit/repositories/tasks_repo.py +0 -52
- iatoolkit/services/history_service.py +0 -37
- iatoolkit/services/search_service.py +0 -55
- iatoolkit/services/tasks_service.py +0 -188
- 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/views/tasks_api_view.py +0 -72
- iatoolkit/views/tasks_review_api_view.py +0 -55
- 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-1.4.2.dist-info}/WHEEL +0 -0
- {iatoolkit-0.71.4.dist-info → iatoolkit-1.4.2.dist-info}/licenses/LICENSE +0 -0
- {iatoolkit-0.71.4.dist-info → iatoolkit-1.4.2.dist-info}/top_level.txt +0 -0
iatoolkit/locales/es.yaml
CHANGED
|
@@ -1,163 +1,294 @@
|
|
|
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
|
+
admin:
|
|
49
|
+
workspace: "Recursos"
|
|
50
|
+
configuration: "Configuración"
|
|
51
|
+
company_config: "Configuración Empresa (company.yaml)"
|
|
52
|
+
knowledge: "Conocimiento"
|
|
53
|
+
knowledge_rag: "RAG (Vectorial)"
|
|
54
|
+
knowledge_static: "Contenido Estático"
|
|
55
|
+
prompts: "Prompts"
|
|
56
|
+
prompts_description: "Prompts de sistema"
|
|
57
|
+
schemas: "Schemas"
|
|
58
|
+
schemas_description: "Definiciones de entidades"
|
|
59
|
+
context: "Contexto"
|
|
60
|
+
context_description: "Contenido estatico (Markdown)"
|
|
61
|
+
administration: "Administración"
|
|
62
|
+
monitoring: "Monitoreo"
|
|
63
|
+
teams: "Usuarios"
|
|
64
|
+
billing: "Facturación"
|
|
65
|
+
company: "Empresa"
|
|
66
|
+
files: "Archivos"
|
|
67
|
+
select_file: "Seleccione un archivo a editar"
|
|
68
|
+
select_category: "Seleccione una categoría"
|
|
69
|
+
editing: "Editando"
|
|
70
|
+
no_file_selected: "Seleccione un archivo"
|
|
71
|
+
new: "Nuevo"
|
|
72
|
+
confirm: "Confirmar"
|
|
73
|
+
cancel: "Cancelar"
|
|
74
|
+
new_file: "Nuevo archivo"
|
|
75
|
+
delete_file: "Borrar archivo"
|
|
76
|
+
rename_file: "Renombrar archivo"
|
|
77
|
+
save_file: "Guardar"
|
|
78
|
+
credentials: "Email y password son requeridos."
|
|
79
|
+
saved_ok: "Guardado correctamente"
|
|
80
|
+
deleted_ok: "Eliminado correctamente"
|
|
81
|
+
company: "Empresa"
|
|
82
|
+
admin_page_title: "Acceso de administración"
|
|
83
|
+
user_manager: "Administración de usuarios"
|
|
84
|
+
load_configuration: "Guardar configuración"
|
|
85
|
+
goto_chat: "Ir al chat"
|
|
86
|
+
logout: "Cerrar sesión"
|
|
87
|
+
|
|
88
|
+
rag:
|
|
89
|
+
ingestion: "Ingesta"
|
|
90
|
+
ingestion_description: "Ingestar documentos en la base de conocimiento."
|
|
91
|
+
workbench: "Area de trabajo"
|
|
92
|
+
documents: "Documentos"
|
|
93
|
+
retrieval_lab: "Laboratorio de recuperación"
|
|
94
|
+
retrieval_description: "Probar la búsqueda semántica y la recuperación de contexto."
|
|
95
|
+
query_placeholder: "Introduce una pregunta para consultar la base de conocimiento..."
|
|
96
|
+
search_button: "Buscar"
|
|
97
|
+
filter: "Filtrar"
|
|
98
|
+
search_results_title: "Listo para probar"
|
|
99
|
+
search_results_description: "Los resultados aparecerán aquí"
|
|
100
|
+
filename: "Nombre de archivo"
|
|
101
|
+
filename_placeholder: "Contiene..."
|
|
102
|
+
user: "Usuario"
|
|
103
|
+
status: "Estado"
|
|
104
|
+
all_status: "Todos los estados"
|
|
105
|
+
status_active: "Activo"
|
|
106
|
+
status_pending: "Pendiente"
|
|
107
|
+
status_processing: "Procesando"
|
|
108
|
+
status_failed: "Fallido"
|
|
109
|
+
created_at: "Creado"
|
|
110
|
+
date_range: "Rango de fechas"
|
|
111
|
+
delete_confirmation: "¿Eliminar archivo?"
|
|
112
|
+
delete_message: "Esta acción no se puede deshacer. El archivo se eliminará permanentemente."
|
|
113
|
+
delete_button: "Eliminar"
|
|
114
|
+
delete_cancel: "Cancelar"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
tooltips:
|
|
118
|
+
history: "Historial con mis consultas"
|
|
119
|
+
reload_context: "Forzar Recarga de Contexto"
|
|
120
|
+
feedback: "Tu feedback es muy importante"
|
|
121
|
+
usage_guide: "Guía de Uso"
|
|
122
|
+
onboarding: "Cómo preguntar mejor"
|
|
123
|
+
preferences: "Preferencias"
|
|
124
|
+
logout: "Cerrar sesión"
|
|
125
|
+
use_prompt_assistant: "Usar Asistente de Prompts"
|
|
126
|
+
attach_files: "Adjuntar archivos"
|
|
127
|
+
view_attached_files: "Ver archivos adjuntos"
|
|
128
|
+
modals:
|
|
129
|
+
files_title: "Archivos Cargados"
|
|
130
|
+
feedback_title: "Tu Opinión es Importante"
|
|
131
|
+
feedback_prompt: "¿Qué tan útil fue la respuesta del asistente?"
|
|
132
|
+
feedback_comment_label: "Tu comentario nos ayuda a mejorar:"
|
|
133
|
+
feedback_comment_placeholder: "Escribe aquí tu opinión, sugerencias o comentarios..."
|
|
134
|
+
history_title: "Historial de Consultas"
|
|
135
|
+
history_table_date: "Fecha"
|
|
136
|
+
history_table_query: "Consulta"
|
|
137
|
+
loading_history: "Cargando historial..."
|
|
138
|
+
no_history_found: "No se encontró historial de consultas."
|
|
139
|
+
help_title: "Guía de uso del Asistente IA"
|
|
140
|
+
|
|
141
|
+
buttons:
|
|
142
|
+
cancel: "Cerrar"
|
|
143
|
+
send: "Enviar"
|
|
144
|
+
stop: "Detener"
|
|
145
|
+
|
|
146
|
+
errors:
|
|
147
|
+
company_not_found: "La empresa {company_short_name} no existe."
|
|
148
|
+
timeout: "El tiempo de espera ha expirado."
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
auth:
|
|
152
|
+
invalid_password: "La contraseña proporcionada es incorrecta."
|
|
153
|
+
user_not_found: "No se encontró un usuario con ese correo."
|
|
154
|
+
invalid_or_expired_token: "Token inválido o expirado."
|
|
155
|
+
session_creation_failed: "No se pudo crear la sesión del usuario."
|
|
156
|
+
authentication_required: "Autenticación requerida. No se proporcionó cookie de sesión o clave de API."
|
|
157
|
+
invalid_api_key: "Clave de API inválida o inactiva."
|
|
158
|
+
no_user_identifier_api: "No se proporcionó user_identifier para la llamada a la API."
|
|
159
|
+
no_company_permissions: "No tiene permisos para administrar esta empresa."
|
|
160
|
+
api_key_name_required: "el parametro api_key_name es requerido."
|
|
161
|
+
|
|
162
|
+
templates:
|
|
163
|
+
company_not_found: "Empresa no encontrada."
|
|
164
|
+
home_template_not_found: "La plantilla de la página de inicio para la empresa '{company_short_name}' no está configurada."
|
|
165
|
+
processing_error: "Error al procesar el template: {error}"
|
|
166
|
+
template_not_found: "No se encontro el template: '{template_name}'."
|
|
167
|
+
|
|
168
|
+
general:
|
|
169
|
+
unexpected_error: "Ha ocurrido un error inesperado: {error}."
|
|
170
|
+
unsupported_language: "El idioma seleccionado no es válido."
|
|
171
|
+
signup:
|
|
172
|
+
company_not_found: "La empresa {company_short_name} no existe."
|
|
173
|
+
incorrect_password_for_existing_user: "La contraseña para el usuario {email} es incorrecta."
|
|
174
|
+
user_already_registered: "El usuario con email '{email}' ya existe en esta empresa."
|
|
175
|
+
password_mismatch: "Las contraseñas no coinciden. Por favor, inténtalo de nuevo."
|
|
176
|
+
change_password:
|
|
177
|
+
token_expired: "El enlace de cambio de contraseña ha expirado. Por favor, solicita uno nuevo."
|
|
178
|
+
password_mismatch: "Las contraseñas no coinciden. Por favor, inténtalo nuevamente."
|
|
179
|
+
invalid_temp_code: "El código temporal no es válido. Por favor, verifica o solicita uno nuevo."
|
|
180
|
+
forgot_password:
|
|
181
|
+
user_not_registered: "El usuario con correo {email} no está registrado."
|
|
182
|
+
verification:
|
|
183
|
+
token_expired: "El enlace de verificación ha expirado. Por favor, contacta a soporte si necesitas uno nuevo."
|
|
184
|
+
user_not_found: "El usuario que intentas verificar no existe."
|
|
185
|
+
validation:
|
|
186
|
+
password_too_short: "La contraseña debe tener al menos 8 caracteres."
|
|
187
|
+
password_no_uppercase: "La contraseña debe tener al menos una letra mayúscula."
|
|
188
|
+
password_no_lowercase: "La contraseña debe tener al menos una letra minúscula."
|
|
189
|
+
password_no_digit: "La contraseña debe tener al menos un número."
|
|
190
|
+
password_no_special_char: "La contraseña debe tener al menos un carácter especial."
|
|
191
|
+
|
|
192
|
+
services:
|
|
193
|
+
no_text_file: "El archivo no es texto o la codificación no es UTF-8"
|
|
194
|
+
no_output_file: "falta el nombre del archivo de salida"
|
|
195
|
+
no_data_for_excel: "faltan los datos o no es una lista de diccionarios"
|
|
196
|
+
no_download_directory: "no esta configurado el directorio temporal para guardar excels"
|
|
197
|
+
cannot_create_excel: "no se pudo crear el archivo excel"
|
|
198
|
+
invalid_filename: "Nombre de archivo inválido"
|
|
199
|
+
file_not_exist : "Archivo no encontrado"
|
|
200
|
+
path_is_not_a_file : "La ruta no corresponde a un archivo"
|
|
201
|
+
file_validation_error : "Error validando archivo"
|
|
202
|
+
user_not_authorized: "Usuario no esta autorizado para esta empresa"
|
|
203
|
+
account_not_verified: "Tu cuenta no ha sido verificada. Por favor, revisa tu correo."
|
|
204
|
+
missing_response_id: "No se encontró 'previous_response_id' para '{company_short_name}/{user_identifier}'. Reinicia el contexto."
|
|
205
|
+
context_rebuild_failed: "No se pudo reconstruir el contexto de la empresa."
|
|
206
|
+
cannot_read_excel: "No se pudo leer el archivo excel"
|
|
207
|
+
cannot_read_csv: "No se pudo leer el archivo CSV"
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
api_responses:
|
|
211
|
+
context_reloaded_success: "El contexto se ha recargado con éxito."
|
|
212
|
+
|
|
213
|
+
services:
|
|
214
|
+
mail_sent: "mail enviado exitosamente."
|
|
215
|
+
start_query: "Hola, cual es tu pregunta?"
|
|
216
|
+
mail_change_password: "se envio mail para cambio de clave"
|
|
217
|
+
|
|
218
|
+
rag:
|
|
219
|
+
ingestion:
|
|
220
|
+
duplicate: "El documento '{filename}' ya existe para la empresa '{company_short_name}'. Omitiendo ingesta."
|
|
221
|
+
failed: "Error al ingerir el documento: {error}"
|
|
222
|
+
processing_failed: "El procesamiento falló: {error}"
|
|
223
|
+
empty_text: "El texto extraído está vacío."
|
|
224
|
+
search:
|
|
225
|
+
query_required: "La consulta (query) es obligatoria."
|
|
226
|
+
company_not_found: "Empresa '{company_short_name}' no encontrada."
|
|
227
|
+
management:
|
|
228
|
+
delete_success: "Documento eliminado."
|
|
229
|
+
not_found: "Documento no encontrado."
|
|
230
|
+
action_not_found: "Acción '{action}' no encontrada."
|
|
231
|
+
|
|
232
|
+
flash_messages:
|
|
233
|
+
password_changed_success: "Tu contraseña ha sido restablecida exitosamente. Ahora puedes iniciar sesión."
|
|
234
|
+
signup_success: "Registro exitoso. Por favor, revisa tu correo para verificar tu cuenta."
|
|
235
|
+
signup_success_no_verification: "Registro exitoso."
|
|
236
|
+
user_associated_success: "Usuario existente asociado exitosamente a la nueva empresa."
|
|
237
|
+
account_verified_success: "Tu cuenta ha sido verificada exitosamente. ¡Bienvenido!"
|
|
238
|
+
forgot_password_success: "Si tu correo está registrado, recibirás un enlace para restablecer tu contraseña."
|
|
239
|
+
|
|
240
|
+
# Claves específicas para JavaScript
|
|
241
|
+
js_messages:
|
|
242
|
+
feedback_sent_success_title: "Feedback Enviado"
|
|
243
|
+
feedback_sent_success_body: "¡Gracias por tu comentario!"
|
|
244
|
+
feedback_sent_error: "No se pudo enviar el feedback, por favor intente nuevamente."
|
|
245
|
+
feedback_rating_error: "Por favor, califica al asistente con las estrellas."
|
|
246
|
+
feedback_comment_error: "Por favor, escribe tu comentario antes de enviar."
|
|
247
|
+
context_reloaded: "El contexto ha sido recargado."
|
|
248
|
+
error_loading_history: "Ocurrió un error al cargar el historial."
|
|
249
|
+
request_aborted: "La generación de la respuesta ha sido detenida."
|
|
250
|
+
processing_error: "Ocurrió un error al procesar la solicitud."
|
|
251
|
+
server_comm_error: "Error de comunicación con el servidor ({status}). Por favor, intente de nuevo más tarde."
|
|
252
|
+
network_error: "Ocurrió un error de red. Por favor, inténtalo de nuevo en unos momentos."
|
|
253
|
+
unknown_server_error: "Error desconocido del servidor."
|
|
254
|
+
loading: "Cargando..."
|
|
255
|
+
reload_init: "Iniciando recarga de contexto en segundo plano..."
|
|
256
|
+
no_history_found: "No existe historial de consultas."
|
|
257
|
+
example: "Ejemplo:"
|
|
258
|
+
show_reasoning: "Ver razonamiento"
|
|
259
|
+
unsaved: "Modificado (no guardado)"
|
|
260
|
+
saved_ok: "Archivo guadado correctamente"
|
|
261
|
+
error_saving: "Error al guardar archivo"
|
|
262
|
+
select_file: "Seleccione un archivo de la lista"
|
|
263
|
+
no_file_selected: "No hay archivo seleccionado"
|
|
264
|
+
select_company: "Seleccione empresa"
|
|
265
|
+
file_created: "Archivo creado correctamente"
|
|
266
|
+
delete_ok: "Archivo eliminado correctamente"
|
|
267
|
+
delete_failed: "Error al eliminar archivo"
|
|
268
|
+
rename_ok: "Archivo renombrado correctamente"
|
|
269
|
+
not_saved: 'No puede guardar'
|
|
270
|
+
invalid_file_name: "Nombre de archivo no válido. Usa solo letras, números, guiones bajos, guiones y puntos."
|
|
271
|
+
config_loaded: "Configuración cargada correctamente."
|
|
272
|
+
config_load_error: "Error cargando confguración."
|
|
273
|
+
search_placeholder: "Buscar usuarios..."
|
|
274
|
+
showing: "Mostrando"
|
|
275
|
+
records: "Registros"
|
|
276
|
+
db_user: "Usuario"
|
|
277
|
+
db_role: "Rol"
|
|
278
|
+
db_verified: "Verificado"
|
|
279
|
+
db_collection: "Colección"
|
|
280
|
+
db_last_access: "ultimo acceso"
|
|
281
|
+
db_filename: "Nombre de archivo"
|
|
282
|
+
db_user: "Usuario"
|
|
283
|
+
db_status: "Estado"
|
|
284
|
+
db_created: "Creado"
|
|
285
|
+
editor_no_file_selected: "No hay archivo seleccionado"
|
|
286
|
+
error_loading: "Error cargando el contenido del archivo"
|
|
287
|
+
cant_load_company: "No puede cargarcompany.yaml"
|
|
288
|
+
config_saved: "Configuración guardada correctamente."
|
|
289
|
+
config_error: "Error guardando configuración"
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
163
294
|
|
|
@@ -4,24 +4,37 @@
|
|
|
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
|
|
11
11
|
from injector import inject
|
|
12
12
|
from pgvector.psycopg2 import register_vector
|
|
13
|
+
from iatoolkit.common.interfaces.database_provider import DatabaseProvider
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
class DatabaseManager:
|
|
16
|
+
class DatabaseManager(DatabaseProvider):
|
|
16
17
|
@inject
|
|
17
|
-
def __init__(self,
|
|
18
|
+
def __init__(self,
|
|
19
|
+
database_url: str,
|
|
20
|
+
schema: str = 'public',
|
|
21
|
+
register_pgvector: bool = True):
|
|
18
22
|
"""
|
|
19
23
|
Inicializa el gestor de la base de datos.
|
|
20
24
|
:param database_url: URL de la base de datos.
|
|
25
|
+
:param schema: Esquema por defecto para la conexión (search_path).
|
|
21
26
|
:param echo: Si True, habilita logs de SQL.
|
|
22
27
|
"""
|
|
28
|
+
|
|
29
|
+
self.schema = schema
|
|
30
|
+
|
|
31
|
+
# FIX HEROKU: replace postgres:// by postgresql:// for compatibility with SQLAlchemy 1.4+
|
|
32
|
+
if database_url and database_url.startswith("postgres://"):
|
|
33
|
+
database_url = database_url.replace("postgres://", "postgresql://", 1)
|
|
34
|
+
|
|
23
35
|
self.url = make_url(database_url)
|
|
24
|
-
|
|
36
|
+
|
|
37
|
+
if database_url.startswith('sqlite'):
|
|
25
38
|
self._engine = create_engine(database_url, echo=False)
|
|
26
39
|
else:
|
|
27
40
|
self._engine = create_engine(
|
|
@@ -40,9 +53,30 @@ class DatabaseManager:
|
|
|
40
53
|
expire_on_commit=False)
|
|
41
54
|
self.scoped_session = scoped_session(self.SessionFactory)
|
|
42
55
|
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
# Register pgvector for each new connection
|
|
57
|
+
backend = self.url.get_backend_name()
|
|
58
|
+
if backend == 'postgresql' or backend == 'postgres':
|
|
59
|
+
if register_pgvector:
|
|
60
|
+
event.listen(self._engine, 'connect', self.on_connect)
|
|
61
|
+
|
|
62
|
+
# if there is a schema, configure the search_path for each connection
|
|
63
|
+
if self.schema:
|
|
64
|
+
event.listen(self._engine, 'checkout', self.set_search_path)
|
|
65
|
+
|
|
66
|
+
def set_search_path(self, dbapi_connection, connection_record, connection_proxy):
|
|
67
|
+
# Configure the search_path for this connection
|
|
68
|
+
cursor = dbapi_connection.cursor()
|
|
69
|
+
|
|
70
|
+
# The defined schema is first, and then public by default
|
|
71
|
+
try:
|
|
72
|
+
cursor.execute(f"SET search_path TO {self.schema}, public")
|
|
73
|
+
cursor.close()
|
|
74
|
+
|
|
75
|
+
# commit for persist the change in the session
|
|
76
|
+
dbapi_connection.commit()
|
|
77
|
+
except Exception:
|
|
78
|
+
# if failed, rollback to avoid invalidating the connection
|
|
79
|
+
dbapi_connection.rollback()
|
|
46
80
|
|
|
47
81
|
@staticmethod
|
|
48
82
|
def on_connect(dbapi_connection, connection_record):
|
|
@@ -58,10 +92,13 @@ class DatabaseManager:
|
|
|
58
92
|
def get_connection(self):
|
|
59
93
|
return self._engine.connect()
|
|
60
94
|
|
|
61
|
-
def get_engine(self):
|
|
62
|
-
return self._engine
|
|
63
|
-
|
|
64
95
|
def create_all(self):
|
|
96
|
+
# if there is a schema defined, make sure it exists before creating tables
|
|
97
|
+
backend = self.url.get_backend_name()
|
|
98
|
+
if self.schema and (backend == 'postgresql' or backend == 'postgres'):
|
|
99
|
+
with self._engine.begin() as conn:
|
|
100
|
+
conn.execute(text(f"CREATE SCHEMA IF NOT EXISTS {self.schema}"))
|
|
101
|
+
|
|
65
102
|
Base.metadata.create_all(self._engine)
|
|
66
103
|
|
|
67
104
|
def drop_all(self):
|
|
@@ -70,34 +107,67 @@ class DatabaseManager:
|
|
|
70
107
|
def remove_session(self):
|
|
71
108
|
self.scoped_session.remove()
|
|
72
109
|
|
|
110
|
+
# -- execution methods ----
|
|
111
|
+
|
|
112
|
+
def execute_query(self, query: str, commit: bool = False) -> list[dict] | dict:
|
|
113
|
+
"""
|
|
114
|
+
Implementation for Direct SQLAlchemy connection.
|
|
115
|
+
"""
|
|
116
|
+
session = self.get_session()
|
|
117
|
+
if self.schema:
|
|
118
|
+
session.execute(text(f"SET search_path TO {self.schema}"))
|
|
119
|
+
|
|
120
|
+
result = session.execute(text(query))
|
|
121
|
+
if commit:
|
|
122
|
+
session.commit()
|
|
123
|
+
|
|
124
|
+
if result.returns_rows:
|
|
125
|
+
# Convert SQLAlchemy rows to list of dicts immediately
|
|
126
|
+
cols = result.keys()
|
|
127
|
+
return [dict(zip(cols, row)) for row in result.fetchall()]
|
|
128
|
+
|
|
129
|
+
return {'rowcount': result.rowcount}
|
|
130
|
+
|
|
131
|
+
def commit(self):
|
|
132
|
+
self.get_session().commit()
|
|
133
|
+
|
|
134
|
+
def rollback(self):
|
|
135
|
+
self.get_session().rollback()
|
|
136
|
+
|
|
137
|
+
# -- schema methods ----
|
|
73
138
|
def get_all_table_names(self) -> list[str]:
|
|
74
139
|
# Returns a list of all table names in the database
|
|
75
140
|
inspector = inspect(self._engine)
|
|
76
|
-
return inspector.get_table_names()
|
|
141
|
+
return inspector.get_table_names(schema=self.schema)
|
|
77
142
|
|
|
78
|
-
def
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
143
|
+
def get_table_description(self,
|
|
144
|
+
table_name: str,
|
|
145
|
+
schema_object_name: str | None = None,
|
|
146
|
+
exclude_columns: list[str] | None = None) -> str:
|
|
82
147
|
inspector = inspect(self._engine)
|
|
83
148
|
|
|
84
|
-
|
|
85
|
-
|
|
149
|
+
# search the table in the specified schema
|
|
150
|
+
if table_name not in inspector.get_table_names(schema=self.schema):
|
|
151
|
+
raise RuntimeError(f"Table '{table_name}' does not exist in database schema '{self.schema}'.")
|
|
86
152
|
|
|
87
153
|
if exclude_columns is None:
|
|
88
154
|
exclude_columns = []
|
|
89
155
|
|
|
90
|
-
# get all
|
|
91
|
-
columns = inspector.get_columns(table_name)
|
|
156
|
+
# get all the table columns
|
|
157
|
+
columns = inspector.get_columns(table_name, schema=self.schema)
|
|
92
158
|
|
|
93
159
|
# construct a json dictionary with the table definition
|
|
94
160
|
json_dict = {
|
|
95
161
|
"table": table_name,
|
|
96
|
-
"
|
|
162
|
+
"schema": self.schema,
|
|
163
|
+
"description": f"The table belongs to the **`{self.schema}`** schema.",
|
|
97
164
|
"fields": []
|
|
98
165
|
}
|
|
99
|
-
|
|
100
|
-
|
|
166
|
+
|
|
167
|
+
if schema_object_name:
|
|
168
|
+
json_dict["description"] += (
|
|
169
|
+
f"The meaning of each field in this table is detailed in the **`{schema_object_name}`** object."
|
|
170
|
+
)
|
|
101
171
|
|
|
102
172
|
# now add every column to the json dictionary
|
|
103
173
|
for col in columns:
|