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.
Files changed (114) hide show
  1. iatoolkit/__init__.py +19 -7
  2. iatoolkit/base_company.py +1 -71
  3. iatoolkit/cli_commands.py +9 -21
  4. iatoolkit/common/exceptions.py +2 -0
  5. iatoolkit/common/interfaces/__init__.py +0 -0
  6. iatoolkit/common/interfaces/asset_storage.py +34 -0
  7. iatoolkit/common/interfaces/database_provider.py +38 -0
  8. iatoolkit/common/model_registry.py +159 -0
  9. iatoolkit/common/routes.py +53 -32
  10. iatoolkit/common/util.py +17 -12
  11. iatoolkit/company_registry.py +55 -14
  12. iatoolkit/{iatoolkit.py → core.py} +102 -72
  13. iatoolkit/infra/{mail_app.py → brevo_mail_app.py} +15 -37
  14. iatoolkit/infra/llm_providers/__init__.py +0 -0
  15. iatoolkit/infra/llm_providers/deepseek_adapter.py +278 -0
  16. iatoolkit/infra/{gemini_adapter.py → llm_providers/gemini_adapter.py} +11 -17
  17. iatoolkit/infra/{openai_adapter.py → llm_providers/openai_adapter.py} +41 -7
  18. iatoolkit/infra/llm_proxy.py +235 -134
  19. iatoolkit/infra/llm_response.py +5 -0
  20. iatoolkit/locales/en.yaml +134 -4
  21. iatoolkit/locales/es.yaml +293 -162
  22. iatoolkit/repositories/database_manager.py +92 -22
  23. iatoolkit/repositories/document_repo.py +7 -0
  24. iatoolkit/repositories/filesystem_asset_repository.py +36 -0
  25. iatoolkit/repositories/llm_query_repo.py +36 -22
  26. iatoolkit/repositories/models.py +86 -95
  27. iatoolkit/repositories/profile_repo.py +64 -13
  28. iatoolkit/repositories/vs_repo.py +31 -28
  29. iatoolkit/services/auth_service.py +1 -1
  30. iatoolkit/services/branding_service.py +1 -1
  31. iatoolkit/services/company_context_service.py +96 -39
  32. iatoolkit/services/configuration_service.py +329 -67
  33. iatoolkit/services/dispatcher_service.py +51 -227
  34. iatoolkit/services/document_service.py +10 -1
  35. iatoolkit/services/embedding_service.py +9 -6
  36. iatoolkit/services/excel_service.py +50 -2
  37. iatoolkit/services/file_processor_service.py +0 -5
  38. iatoolkit/services/history_manager_service.py +208 -0
  39. iatoolkit/services/jwt_service.py +1 -1
  40. iatoolkit/services/knowledge_base_service.py +412 -0
  41. iatoolkit/services/language_service.py +8 -2
  42. iatoolkit/services/license_service.py +82 -0
  43. iatoolkit/{infra/llm_client.py → services/llm_client_service.py} +42 -29
  44. iatoolkit/services/load_documents_service.py +18 -47
  45. iatoolkit/services/mail_service.py +171 -25
  46. iatoolkit/services/profile_service.py +69 -36
  47. iatoolkit/services/{prompt_manager_service.py → prompt_service.py} +136 -25
  48. iatoolkit/services/query_service.py +229 -203
  49. iatoolkit/services/sql_service.py +116 -34
  50. iatoolkit/services/tool_service.py +246 -0
  51. iatoolkit/services/user_feedback_service.py +18 -6
  52. iatoolkit/services/user_session_context_service.py +121 -51
  53. iatoolkit/static/images/iatoolkit_core.png +0 -0
  54. iatoolkit/static/images/iatoolkit_logo.png +0 -0
  55. iatoolkit/static/js/chat_feedback_button.js +1 -1
  56. iatoolkit/static/js/chat_help_content.js +4 -4
  57. iatoolkit/static/js/chat_main.js +61 -9
  58. iatoolkit/static/js/chat_model_selector.js +227 -0
  59. iatoolkit/static/js/chat_onboarding_button.js +1 -1
  60. iatoolkit/static/js/chat_reload_button.js +4 -1
  61. iatoolkit/static/styles/chat_iatoolkit.css +59 -3
  62. iatoolkit/static/styles/chat_public.css +28 -0
  63. iatoolkit/static/styles/documents.css +598 -0
  64. iatoolkit/static/styles/landing_page.css +223 -7
  65. iatoolkit/static/styles/llm_output.css +34 -1
  66. iatoolkit/system_prompts/__init__.py +0 -0
  67. iatoolkit/system_prompts/query_main.prompt +28 -3
  68. iatoolkit/system_prompts/sql_rules.prompt +47 -12
  69. iatoolkit/templates/_company_header.html +30 -5
  70. iatoolkit/templates/_login_widget.html +3 -3
  71. iatoolkit/templates/base.html +13 -0
  72. iatoolkit/templates/chat.html +45 -3
  73. iatoolkit/templates/forgot_password.html +3 -2
  74. iatoolkit/templates/onboarding_shell.html +1 -2
  75. iatoolkit/templates/signup.html +3 -0
  76. iatoolkit/views/base_login_view.py +8 -3
  77. iatoolkit/views/change_password_view.py +1 -1
  78. iatoolkit/views/chat_view.py +76 -0
  79. iatoolkit/views/forgot_password_view.py +9 -4
  80. iatoolkit/views/history_api_view.py +3 -3
  81. iatoolkit/views/home_view.py +4 -2
  82. iatoolkit/views/init_context_api_view.py +1 -1
  83. iatoolkit/views/llmquery_api_view.py +4 -3
  84. iatoolkit/views/load_company_configuration_api_view.py +49 -0
  85. iatoolkit/views/{file_store_api_view.py → load_document_api_view.py} +15 -11
  86. iatoolkit/views/login_view.py +25 -8
  87. iatoolkit/views/logout_api_view.py +10 -2
  88. iatoolkit/views/prompt_api_view.py +1 -1
  89. iatoolkit/views/rag_api_view.py +216 -0
  90. iatoolkit/views/root_redirect_view.py +22 -0
  91. iatoolkit/views/signup_view.py +12 -4
  92. iatoolkit/views/static_page_view.py +27 -0
  93. iatoolkit/views/users_api_view.py +33 -0
  94. iatoolkit/views/verify_user_view.py +1 -1
  95. iatoolkit-1.4.2.dist-info/METADATA +268 -0
  96. iatoolkit-1.4.2.dist-info/RECORD +133 -0
  97. iatoolkit-1.4.2.dist-info/licenses/LICENSE_COMMUNITY.md +15 -0
  98. iatoolkit/repositories/tasks_repo.py +0 -52
  99. iatoolkit/services/history_service.py +0 -37
  100. iatoolkit/services/search_service.py +0 -55
  101. iatoolkit/services/tasks_service.py +0 -188
  102. iatoolkit/templates/about.html +0 -13
  103. iatoolkit/templates/index.html +0 -145
  104. iatoolkit/templates/login_simulation.html +0 -45
  105. iatoolkit/views/external_login_view.py +0 -73
  106. iatoolkit/views/index_view.py +0 -14
  107. iatoolkit/views/login_simulation_view.py +0 -93
  108. iatoolkit/views/tasks_api_view.py +0 -72
  109. iatoolkit/views/tasks_review_api_view.py +0 -55
  110. iatoolkit-0.71.4.dist-info/METADATA +0 -276
  111. iatoolkit-0.71.4.dist-info/RECORD +0 -122
  112. {iatoolkit-0.71.4.dist-info → iatoolkit-1.4.2.dist-info}/WHEEL +0 -0
  113. {iatoolkit-0.71.4.dist-info → iatoolkit-1.4.2.dist-info}/licenses/LICENSE +0 -0
  114. {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
- # 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 IA de Sample Company."
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 tu consulta aquí..."
45
- prompts_available: "Prompts disponibles"
46
-
47
- tooltips:
48
- history: "Historial con mis consultas"
49
- reload_context: "Forzar Recarga de Contexto"
50
- feedback: "Tu feedback es muy importante"
51
- usage_guide: "Guía de Uso"
52
- onboarding: "Cómo preguntar mejor"
53
- logout: "Cerrar sesión"
54
- use_prompt_assistant: "Usar Asistente de Prompts"
55
- attach_files: "Adjuntar archivos"
56
- view_attached_files: "Ver archivos adjuntos"
57
- modals:
58
- files_title: "Archivos Cargados"
59
- feedback_title: "Tu Opinión es Importante"
60
- feedback_prompt: "¿Qué tan útil fue la respuesta del asistente?"
61
- feedback_comment_label: "Tu comentario nos ayuda a mejorar:"
62
- feedback_comment_placeholder: "Escribe aquí tu opinión, sugerencias o comentarios..."
63
- history_title: "Historial de Consultas"
64
- history_table_date: "Fecha"
65
- history_table_query: "Consulta"
66
- loading_history: "Cargando historial..."
67
- no_history_found: "No se encontró historial de consultas."
68
- help_title: "Guía de uso del Asistente IA"
69
-
70
- buttons:
71
- cancel: "Cerrar"
72
- send: "Enviar"
73
- stop: "Detener"
74
-
75
- errors:
76
- company_not_found: "La empresa {company_short_name} no existe."
77
- timeout: "El tiempo de espera ha expirado."
78
-
79
-
80
- auth:
81
- invalid_password: "La contraseña proporcionada es incorrecta."
82
- user_not_found: "No se encontró un usuario con ese correo."
83
- invalid_or_expired_token: "Token inválido o expirado."
84
- session_creation_failed: "No se pudo crear la sesión del usuario."
85
- authentication_required: "Autenticación requerida. No se proporcionó cookie de sesión o clave de API."
86
- invalid_api_key: "Clave de API inválida o inactiva."
87
- no_user_identifier_api: "No se proporcionó user_identifier para la llamada a la API."
88
- templates:
89
- company_not_found: "Empresa no encontrada."
90
- home_template_not_found: "La plantilla de la página de inicio para la empresa '{company_short_name}' no está configurada."
91
- processing_error: "Error al procesar el template: {error}"
92
- template_not_found: "No se encontro el template: '{template_name}'."
93
-
94
- general:
95
- unexpected_error: "Ha ocurrido un error inesperado: {error}."
96
- unsupported_language: "El idioma seleccionado no es válido."
97
- signup:
98
- company_not_found: "La empresa {company_short_name} no existe."
99
- incorrect_password_for_existing_user: "La contraseña para el usuario {email} es incorrecta."
100
- user_already_registered: "El usuario con email '{email}' ya existe en esta empresa."
101
- password_mismatch: "Las contraseñas no coinciden. Por favor, inténtalo de nuevo."
102
- change_password:
103
- token_expired: "El enlace de cambio de contraseña ha expirado. Por favor, solicita uno nuevo."
104
- password_mismatch: "Las contraseñas no coinciden. Por favor, inténtalo nuevamente."
105
- invalid_temp_code: "El código temporal no es válido. Por favor, verifica o solicita uno nuevo."
106
- forgot_password:
107
- user_not_registered: "El usuario con correo {email} no está registrado."
108
- verification:
109
- token_expired: "El enlace de verificación ha expirado. Por favor, contacta a soporte si necesitas uno nuevo."
110
- user_not_found: "El usuario que intentas verificar no existe."
111
- validation:
112
- password_too_short: "La contraseña debe tener al menos 8 caracteres."
113
- password_no_uppercase: "La contraseña debe tener al menos una letra mayúscula."
114
- password_no_lowercase: "La contraseña debe tener al menos una letra minúscula."
115
- password_no_digit: "La contraseña debe tener al menos un número."
116
- password_no_special_char: "La contraseña debe tener al menos un carácter especial."
117
-
118
- services:
119
- no_text_file: "El archivo no es texto o la codificación no es UTF-8"
120
- no_output_file: "falta el nombre del archivo de salida"
121
- no_data_for_excel: "faltan los datos o no es una lista de diccionarios"
122
- no_download_directory: "no esta configurado el directorio temporal para guardar excels"
123
- cannot_create_excel: "no se pudo crear el archivo excel"
124
- invalid_filename: "Nombre de archivo inválido"
125
- file_not_exist : "Archivo no encontrado"
126
- path_is_not_a_file : "La ruta no corresponde a un archivo"
127
- file_validation_error : "Error validando archivo"
128
- user_not_authorized: "Usuario no esta autorizado para esta empresa"
129
- account_not_verified: "Tu cuenta no ha sido verificada. Por favor, revisa tu correo."
130
- missing_response_id: "No se encontró 'previous_response_id' para '{company_short_name}/{user_identifier}'. Reinicia el contexto."
131
-
132
- api_responses:
133
- context_reloaded_success: "El contexto se ha recargado con éxito."
134
-
135
- services:
136
- mail_sent: "mail enviado exitosamente."
137
- start_query: "Hola, cual es tu pregunta?"
138
-
139
- flash_messages:
140
- password_changed_success: "Tu contraseña ha sido restablecida exitosamente. Ahora puedes iniciar sesión."
141
- signup_success: "Registro exitoso. Por favor, revisa tu correo para verificar tu cuenta."
142
- user_associated_success: "Usuario existente asociado exitosamente a la nueva empresa."
143
- account_verified_success: "Tu cuenta ha sido verificada exitosamente. ¡Bienvenido!"
144
- forgot_password_success: "Si tu correo está registrado, recibirás un enlace para restablecer tu contraseña."
145
-
146
- # Claves específicas para JavaScript
147
- js_messages:
148
- feedback_sent_success_title: "Feedback Enviado"
149
- feedback_sent_success_body: "¡Gracias por tu comentario!"
150
- feedback_sent_error: "No se pudo enviar el feedback, por favor intente nuevamente."
151
- feedback_rating_error: "Por favor, califica al asistente con las estrellas."
152
- feedback_comment_error: "Por favor, escribe tu comentario antes de enviar."
153
- context_reloaded: "El contexto ha sido recargado."
154
- error_loading_history: "Ocurrió un error al cargar el historial."
155
- request_aborted: "La generación de la respuesta ha sido detenida."
156
- processing_error: "Ocurrió un error al procesar la solicitud."
157
- server_comm_error: "Error de comunicación con el servidor ({status}). Por favor, intente de nuevo más tarde."
158
- network_error: "Ocurrió un error de red. Por favor, inténtalo de nuevo en unos momentos."
159
- unknown_server_error: "Error desconocido del servidor."
160
- loading: "Cargando..."
161
- reload_init: "Iniciando recarga de contexto en segundo plano..."
162
- no_history_found: "No existe historial de consultas."
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, database_url: str, register_pgvector: bool = True):
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
- if database_url.startswith('sqlite'): # for tests
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
- # REGISTRAR pgvector para cada nueva conexión solo en postgres
44
- if register_pgvector and self.url.get_backend_name() == 'postgresql':
45
- event.listen(self._engine, 'connect', self.on_connect)
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 get_table_schema(self,
79
- table_name: str,
80
- schema_name: str | None = None,
81
- exclude_columns: list[str] | None = None) -> str:
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
- if table_name not in inspector.get_table_names():
85
- raise RuntimeError(f"Table '{table_name}' does not exist.")
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 thre table columns
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
- "description": f"Definición de la tabla {table_name}.",
162
+ "schema": self.schema,
163
+ "description": f"The table belongs to the **`{self.schema}`** schema.",
97
164
  "fields": []
98
165
  }
99
- if schema_name:
100
- json_dict["description"] += f"Los detalles de cada campo están en el objeto **`{schema_name}`**."
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: