iatoolkit 0.11.0__py3-none-any.whl → 0.71.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 (122) hide show
  1. iatoolkit/__init__.py +2 -6
  2. iatoolkit/base_company.py +9 -29
  3. iatoolkit/cli_commands.py +1 -1
  4. iatoolkit/common/routes.py +96 -52
  5. iatoolkit/common/session_manager.py +2 -1
  6. iatoolkit/common/util.py +17 -27
  7. iatoolkit/company_registry.py +1 -2
  8. iatoolkit/iatoolkit.py +97 -53
  9. iatoolkit/infra/llm_client.py +15 -20
  10. iatoolkit/infra/llm_proxy.py +38 -10
  11. iatoolkit/infra/openai_adapter.py +1 -1
  12. iatoolkit/infra/redis_session_manager.py +48 -2
  13. iatoolkit/locales/en.yaml +167 -0
  14. iatoolkit/locales/es.yaml +163 -0
  15. iatoolkit/repositories/database_manager.py +23 -3
  16. iatoolkit/repositories/document_repo.py +1 -1
  17. iatoolkit/repositories/models.py +35 -10
  18. iatoolkit/repositories/profile_repo.py +3 -2
  19. iatoolkit/repositories/vs_repo.py +26 -20
  20. iatoolkit/services/auth_service.py +193 -0
  21. iatoolkit/services/branding_service.py +70 -25
  22. iatoolkit/services/company_context_service.py +155 -0
  23. iatoolkit/services/configuration_service.py +133 -0
  24. iatoolkit/services/dispatcher_service.py +80 -105
  25. iatoolkit/services/document_service.py +5 -2
  26. iatoolkit/services/embedding_service.py +146 -0
  27. iatoolkit/services/excel_service.py +30 -26
  28. iatoolkit/services/file_processor_service.py +4 -12
  29. iatoolkit/services/history_service.py +7 -16
  30. iatoolkit/services/i18n_service.py +104 -0
  31. iatoolkit/services/jwt_service.py +18 -29
  32. iatoolkit/services/language_service.py +83 -0
  33. iatoolkit/services/load_documents_service.py +100 -113
  34. iatoolkit/services/mail_service.py +9 -4
  35. iatoolkit/services/profile_service.py +152 -76
  36. iatoolkit/services/prompt_manager_service.py +20 -16
  37. iatoolkit/services/query_service.py +208 -96
  38. iatoolkit/services/search_service.py +11 -4
  39. iatoolkit/services/sql_service.py +57 -25
  40. iatoolkit/services/tasks_service.py +1 -1
  41. iatoolkit/services/user_feedback_service.py +72 -34
  42. iatoolkit/services/user_session_context_service.py +112 -54
  43. iatoolkit/static/images/fernando.jpeg +0 -0
  44. iatoolkit/static/js/chat_feedback_button.js +80 -0
  45. iatoolkit/static/js/chat_help_content.js +124 -0
  46. iatoolkit/static/js/chat_history_button.js +110 -0
  47. iatoolkit/static/js/chat_logout_button.js +36 -0
  48. iatoolkit/static/js/chat_main.js +135 -222
  49. iatoolkit/static/js/chat_onboarding_button.js +103 -0
  50. iatoolkit/static/js/chat_prompt_manager.js +94 -0
  51. iatoolkit/static/js/chat_reload_button.js +35 -0
  52. iatoolkit/static/styles/chat_iatoolkit.css +289 -210
  53. iatoolkit/static/styles/chat_modal.css +63 -77
  54. iatoolkit/static/styles/chat_public.css +107 -0
  55. iatoolkit/static/styles/landing_page.css +182 -0
  56. iatoolkit/static/styles/onboarding.css +176 -0
  57. iatoolkit/system_prompts/query_main.prompt +5 -22
  58. iatoolkit/templates/_company_header.html +20 -0
  59. iatoolkit/templates/_login_widget.html +42 -0
  60. iatoolkit/templates/base.html +40 -20
  61. iatoolkit/templates/change_password.html +57 -36
  62. iatoolkit/templates/chat.html +180 -86
  63. iatoolkit/templates/chat_modals.html +138 -68
  64. iatoolkit/templates/error.html +44 -8
  65. iatoolkit/templates/forgot_password.html +40 -23
  66. iatoolkit/templates/index.html +145 -0
  67. iatoolkit/templates/login_simulation.html +45 -0
  68. iatoolkit/templates/onboarding_shell.html +107 -0
  69. iatoolkit/templates/signup.html +63 -65
  70. iatoolkit/views/base_login_view.py +91 -0
  71. iatoolkit/views/change_password_view.py +56 -31
  72. iatoolkit/views/embedding_api_view.py +65 -0
  73. iatoolkit/views/external_login_view.py +61 -28
  74. iatoolkit/views/{file_store_view.py → file_store_api_view.py} +10 -3
  75. iatoolkit/views/forgot_password_view.py +27 -21
  76. iatoolkit/views/help_content_api_view.py +54 -0
  77. iatoolkit/views/history_api_view.py +56 -0
  78. iatoolkit/views/home_view.py +50 -23
  79. iatoolkit/views/index_view.py +14 -0
  80. iatoolkit/views/init_context_api_view.py +74 -0
  81. iatoolkit/views/llmquery_api_view.py +58 -0
  82. iatoolkit/views/login_simulation_view.py +93 -0
  83. iatoolkit/views/login_view.py +130 -37
  84. iatoolkit/views/logout_api_view.py +49 -0
  85. iatoolkit/views/profile_api_view.py +46 -0
  86. iatoolkit/views/{prompt_view.py → prompt_api_view.py} +10 -10
  87. iatoolkit/views/signup_view.py +41 -36
  88. iatoolkit/views/{tasks_view.py → tasks_api_view.py} +10 -36
  89. iatoolkit/views/tasks_review_api_view.py +55 -0
  90. iatoolkit/views/user_feedback_api_view.py +60 -0
  91. iatoolkit/views/verify_user_view.py +34 -29
  92. {iatoolkit-0.11.0.dist-info → iatoolkit-0.71.2.dist-info}/METADATA +41 -23
  93. iatoolkit-0.71.2.dist-info/RECORD +122 -0
  94. iatoolkit-0.71.2.dist-info/licenses/LICENSE +21 -0
  95. iatoolkit/common/auth.py +0 -200
  96. iatoolkit/static/images/arrow_up.png +0 -0
  97. iatoolkit/static/images/diagrama_iatoolkit.jpg +0 -0
  98. iatoolkit/static/images/logo_clinica.png +0 -0
  99. iatoolkit/static/images/logo_iatoolkit.png +0 -0
  100. iatoolkit/static/images/logo_maxxa.png +0 -0
  101. iatoolkit/static/images/logo_notaria.png +0 -0
  102. iatoolkit/static/images/logo_tarjeta.png +0 -0
  103. iatoolkit/static/images/logo_umayor.png +0 -0
  104. iatoolkit/static/images/upload.png +0 -0
  105. iatoolkit/static/js/chat_feedback.js +0 -115
  106. iatoolkit/static/js/chat_history.js +0 -117
  107. iatoolkit/static/styles/chat_info.css +0 -53
  108. iatoolkit/templates/header.html +0 -31
  109. iatoolkit/templates/home.html +0 -199
  110. iatoolkit/templates/login.html +0 -43
  111. iatoolkit/templates/test.html +0 -9
  112. iatoolkit/views/chat_token_request_view.py +0 -98
  113. iatoolkit/views/chat_view.py +0 -58
  114. iatoolkit/views/download_file_view.py +0 -58
  115. iatoolkit/views/external_chat_login_view.py +0 -95
  116. iatoolkit/views/history_view.py +0 -57
  117. iatoolkit/views/llmquery_view.py +0 -65
  118. iatoolkit/views/tasks_review_view.py +0 -83
  119. iatoolkit/views/user_feedback_view.py +0 -74
  120. iatoolkit-0.11.0.dist-info/RECORD +0 -110
  121. {iatoolkit-0.11.0.dist-info → iatoolkit-0.71.2.dist-info}/WHEEL +0 -0
  122. {iatoolkit-0.11.0.dist-info → iatoolkit-0.71.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,167 @@
1
+ # Language: English
2
+ ui:
3
+ login_widget:
4
+ title: "Sign In"
5
+ welcome_message: "Enter your credentials or register to access Sample Company’s AI platform."
6
+ email_placeholder: "Email address"
7
+ password_placeholder: "Password"
8
+ login_button: "Login"
9
+ forgot_password_link: "Forgot your password?"
10
+ no_account_prompt: "Don't have an account?"
11
+ signup_link: "Sign Up"
12
+
13
+ signup:
14
+ title: "Create an Account"
15
+ subtitle: "Start your journey with us today."
16
+ first_name_label: "First Name"
17
+ last_name_label: "Last Name"
18
+ email_label: "Email Address"
19
+ password_label: "Password"
20
+ confirm_password_label: "Confirm Password"
21
+ signup_button: "Create Account"
22
+ already_have_account: "Already have an account?"
23
+ login_link: "Log In"
24
+ disclaimer: "🔒We value your privacy. Your data will be used exclusively for the operation of the platform."
25
+
26
+ forgot_password:
27
+ title: "Recover Password"
28
+ subtitle: "Enter your email address and we will send you a link to reset your password."
29
+ submit_button: "Send Recovery Link"
30
+ back_to_login: "Back to Login"
31
+
32
+ change_password:
33
+ title: "Create New Password"
34
+ subtitle: "You are changing the password for <strong>{email}</strong>."
35
+ temp_code_label: "Temporary Code"
36
+ temp_code_placeholder: "Check your email"
37
+ new_password_label: "New Password"
38
+ password_instructions: "Must contain at least 8 characters, an uppercase letter, a lowercase letter, a number, and a special character."
39
+ confirm_password_label: "Confirm New Password"
40
+ save_button: "Save Password"
41
+ back_to_home: "Back to home"
42
+
43
+ chat:
44
+ welcome_message: "Hello! How can I help you today?"
45
+ input_placeholder: "Type your query here..."
46
+ prompts_available: "Available prompts"
47
+
48
+ tooltips:
49
+ history: "History of my queries"
50
+ reload_context: "Force Context Reload"
51
+ feedback: "Your feedback is very important"
52
+ usage_guide: "Usage Guide"
53
+ onboarding: "How to ask better questions"
54
+ logout: "Log out"
55
+ use_prompt_assistant: "Use Prompt Assistant"
56
+ attach_files: "Attach files"
57
+ view_attached_files: "View attached files"
58
+ send: "Send"
59
+ stop: "Stop"
60
+
61
+ modals:
62
+ files_title: "Uploaded Files"
63
+ feedback_title: "Your Opinion is Important"
64
+ feedback_prompt: "How useful was the assistant's response?"
65
+ feedback_comment_label: "Your feedback helps us improve:"
66
+ feedback_comment_placeholder: "Write your opinion, suggestions, or comments here..."
67
+ history_title: "Query History"
68
+ history_table_date: "Date"
69
+ history_table_query: "Query"
70
+ loading_history: "Loading history..."
71
+ no_history_found: "No query history found."
72
+ help_title: "AI Assistant User Guide"
73
+
74
+ buttons:
75
+ cancel: "Close"
76
+ send: "Send"
77
+ stop: "Stop"
78
+
79
+ errors:
80
+ company_not_found: "The company {company_short_name} does not exist."
81
+ timeout: "timeout expired."
82
+ auth:
83
+ invalid_password: "The provided password is incorrect."
84
+ user_not_found: "A user with that email address was not found."
85
+ invalid_or_expired_token: "Invalid or expired token."
86
+ session_creation_failed: "Could not create user session."
87
+ authentication_required: "Authentication required. No session cookie or API Key provided."
88
+ invalid_api_key: "Invalid or inactive API Key."
89
+ no_user_identifier_api: "No user_identifier provided for API call."
90
+ templates:
91
+ company_not_found: "Company not found."
92
+ home_template_not_found: "The home page template for the company '{company_short_name}' is not configured."
93
+ template_not_found: "Template not found: '{template_name}'."
94
+
95
+ processing_error: "An error occurred while processing the custom home page template: {error}"
96
+ general:
97
+ unexpected_error: "An unexpected error has occurred. Please contact support."
98
+ unsupported_language: "The selected language is not supported."
99
+ signup:
100
+ company_not_found: "The company {company_short_name} does not exist."
101
+ incorrect_password_for_existing_user: "The password for the user {email} is incorrect."
102
+ user_already_registered: "The user with email '{email}' already exists in this company."
103
+ password_mismatch: "The passwords do not match. Please try again."
104
+ change_password:
105
+ token_expired: "The password change link has expired. Please request a new one."
106
+ password_mismatch: "The passwords do not match. Please try again."
107
+ invalid_temp_code: "The temporary code is not valid. Please check it or request a new one."
108
+ forgot_password:
109
+ user_not_registered: "The user with email {email} is not registered."
110
+ verification:
111
+ token_expired: "The verification link has expired. Please contact support if you need a new one."
112
+ user_not_found: "The user you are trying to verify does not exist."
113
+ validation:
114
+ password_too_short: "Password must be at least 8 characters long."
115
+ password_no_uppercase: "Password must contain at least one uppercase letter."
116
+ password_no_lowercase: "Password must contain at least one lowercase letter."
117
+ password_no_digit: "Password must contain at least one number."
118
+ password_no_special_char: "Password must contain at least one special character."
119
+
120
+ services:
121
+ no_text_file: "The file is not text or the encoding is not UTF-8"
122
+ no_output_file: "Missing output file name"
123
+ no_data_for_excel: "Missing data or it is not a list of dictionaries"
124
+ no_download_directory: "Temporary directory for saving Excel files is not configured"
125
+ cannot_create_excel: "Could not create the Excel file"
126
+ invalid_filename: "Invalid filename"
127
+ file_not_exist: "File not found"
128
+ path_is_not_a_file: "The path does not correspond to a file"
129
+ file_validation_error: "Error validating file"
130
+ user_not_authorized: "user is not authorized for this company"
131
+ account_not_verified: "Your account has not been verified. Please check your email."
132
+ missing_response_id: "Can not found 'previous_response_id' for '{company_short_name}/{user_identifier}'. Reinit context"
133
+
134
+
135
+ api_responses:
136
+ context_reloaded_success: "The context has been successfully reloaded."
137
+
138
+ services:
139
+ mail_sent: "Email sent successfully."
140
+ start_query: "Hello, what can I help you with today?"
141
+
142
+
143
+ flash_messages:
144
+ password_changed_success: "Your password has been successfully reset. You can now log in."
145
+ login_required: "Please log in to continue."
146
+ signup_success: "Registration successful. Please check your email to verify your account."
147
+ user_associated_success: "Existing user successfully associated with the new company."
148
+ account_verified_success: "Your account has been successfully verified. Welcome!"
149
+ forgot_password_success: "If your email is registered, you will receive a link to reset your password."
150
+
151
+ # Keys specifically for JavaScript
152
+ js_messages:
153
+ feedback_sent_success_title: "Feedback Sent"
154
+ feedback_sent_success_body: "Thank you for your feedback!"
155
+ feedback_sent_error: "Could not send feedback, please try again."
156
+ feedback_rating_error: "Please rate the assistant using the stars."
157
+ feedback_comment_error: "Please write your comment before sending."
158
+ context_reloaded: "Context has been reloaded."
159
+ error_loading_history: "An error occurred while loading the history."
160
+ request_aborted: "The response generation has been stopped."
161
+ processing_error: "An error occurred while processing the request."
162
+ server_comm_error: "Server communication error ({status}). Please try again later."
163
+ network_error: "A network error occurred. Please try again in a few moments."
164
+ unknown_server_error: "Unknown server error."
165
+ loading: "Loading..."
166
+ reload_init: "init reloading context in background..."
167
+ no_history_found: "No query history found."
@@ -0,0 +1,163 @@
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."
163
+
@@ -21,8 +21,23 @@ class DatabaseManager:
21
21
  :param echo: Si True, habilita logs de SQL.
22
22
  """
23
23
  self.url = make_url(database_url)
24
- self._engine = create_engine(database_url, echo=False)
25
- self.SessionFactory = sessionmaker(bind=self._engine)
24
+ if database_url.startswith('sqlite'): # for tests
25
+ self._engine = create_engine(database_url, echo=False)
26
+ else:
27
+ self._engine = create_engine(
28
+ database_url,
29
+ echo=False,
30
+ pool_size=10, # per worker
31
+ max_overflow=20,
32
+ pool_timeout=30,
33
+ pool_recycle=1800,
34
+ pool_pre_ping=True,
35
+ future=True,
36
+ )
37
+ self.SessionFactory = sessionmaker(bind=self._engine,
38
+ autoflush=False,
39
+ autocommit=False,
40
+ expire_on_commit=False)
26
41
  self.scoped_session = scoped_session(self.SessionFactory)
27
42
 
28
43
  # REGISTRAR pgvector para cada nueva conexión solo en postgres
@@ -55,6 +70,11 @@ class DatabaseManager:
55
70
  def remove_session(self):
56
71
  self.scoped_session.remove()
57
72
 
73
+ def get_all_table_names(self) -> list[str]:
74
+ # Returns a list of all table names in the database
75
+ inspector = inspect(self._engine)
76
+ return inspector.get_table_names()
77
+
58
78
  def get_table_schema(self,
59
79
  table_name: str,
60
80
  schema_name: str | None = None,
@@ -62,7 +82,7 @@ class DatabaseManager:
62
82
  inspector = inspect(self._engine)
63
83
 
64
84
  if table_name not in inspector.get_table_names():
65
- raise RuntimeError(f"La tabla '{table_name}' no existe en la BD.")
85
+ raise RuntimeError(f"Table '{table_name}' does not exist.")
66
86
 
67
87
  if exclude_columns is None:
68
88
  exclude_columns = []
@@ -22,7 +22,7 @@ class DocumentRepo:
22
22
  def get(self, company_id, filename: str ) -> Document:
23
23
  if not company_id or not filename:
24
24
  raise IAToolkitException(IAToolkitException.ErrorType.PARAM_NOT_FILLED,
25
- 'Falta empresa o filename')
25
+ 'missing company_id or filename')
26
26
 
27
27
  return self.session.query(Document).filter_by(company_id=company_id, filename=filename).first()
28
28
 
@@ -3,9 +3,10 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
- from sqlalchemy import Column, Integer, String, DateTime, Enum, Text, JSON, Boolean, ForeignKey, Table
6
+ from sqlalchemy import Column, Integer, BigInteger, String, DateTime, Enum, Text, JSON, Boolean, ForeignKey, Table
7
7
  from sqlalchemy.orm import DeclarativeBase
8
8
  from sqlalchemy.orm import relationship, class_mapper, declarative_base
9
+ from sqlalchemy.sql import func
9
10
  from datetime import datetime
10
11
  from pgvector.sqlalchemy import Vector
11
12
  from enum import Enum as PyEnum
@@ -56,11 +57,8 @@ class Company(Base):
56
57
  # encrypted api-key
57
58
  openai_api_key = Column(String, nullable=True)
58
59
  gemini_api_key = Column(String, nullable=True)
59
-
60
- branding = Column(JSON, nullable=True)
61
- parameters = Column(JSON, nullable=True, default={})
60
+ parameters = Column(JSON, nullable=True)
62
61
  created_at = Column(DateTime, default=datetime.now)
63
- allow_jwt = Column(Boolean, default=True, nullable=True)
64
62
 
65
63
  documents = relationship("Document",
66
64
  back_populates="company",
@@ -105,6 +103,7 @@ class User(Base):
105
103
  created_at = Column(DateTime, default=datetime.now)
106
104
  password = Column(String, nullable=False)
107
105
  verified = Column(Boolean, nullable=False, default=False)
106
+ preferred_language = Column(String(5), nullable=True)
108
107
  verification_url = Column(String, nullable=True)
109
108
  temp_code = Column(String, nullable=True)
110
109
 
@@ -157,10 +156,10 @@ class Document(Base):
157
156
  company_id = Column(Integer, ForeignKey('iat_companies.id',
158
157
  ondelete='CASCADE'), nullable=False)
159
158
  filename = Column(String(256), nullable=False, index=True)
160
- content = Column(Text, nullable=False)
161
- content_b64 = Column(Text, nullable=False)
162
159
  meta = Column(JSON, nullable=True)
163
160
  created_at = Column(DateTime, default=datetime.now)
161
+ content = Column(Text, nullable=False)
162
+ content_b64 = Column(Text, nullable=False)
164
163
 
165
164
  company = relationship("Company", back_populates="documents")
166
165
 
@@ -203,7 +202,10 @@ class VSDoc(Base):
203
202
  document_id = Column(Integer, ForeignKey('iat_documents.id',
204
203
  ondelete='CASCADE'), nullable=False)
205
204
  text = Column(Text, nullable=False)
206
- embedding = Column(Vector(384), nullable=False) # Ajusta la dimensión si es necesario
205
+
206
+ # the size of this vector should be set depending on the embedding model used
207
+ # for OpenAI is 1536, and for huggingface is 384
208
+ embedding = Column(Vector(1536), nullable=False)
207
209
 
208
210
  company = relationship("Company", back_populates="vsdocs")
209
211
 
@@ -264,8 +266,7 @@ class UserFeedback(Base):
264
266
  id = Column(Integer, primary_key=True)
265
267
  company_id = Column(Integer, ForeignKey('iat_companies.id',
266
268
  ondelete='CASCADE'), nullable=False)
267
- local_user_id = Column(Integer, default=0, nullable=True)
268
- external_user_id = Column(String(128), default='', nullable=True)
269
+ user_identifier = Column(String(128), default='', nullable=True)
269
270
  message = Column(Text, nullable=False)
270
271
  rating = Column(Integer, nullable=False)
271
272
  created_at = Column(DateTime, default=datetime.now)
@@ -307,3 +308,27 @@ class Prompt(Base):
307
308
 
308
309
  company = relationship("Company", back_populates="prompts")
309
310
  category = relationship("PromptCategory", back_populates="prompts")
311
+
312
+ class AccessLog(Base):
313
+ # Modelo ORM para registrar cada intento de acceso a la plataforma.
314
+ __tablename__ = 'iat_access_log'
315
+
316
+ id = Column(BigInteger, primary_key=True)
317
+
318
+ timestamp = Column(DateTime(timezone=True), server_default=func.now(), nullable=False, index=True)
319
+ company_short_name = Column(String(100), nullable=False, index=True)
320
+ user_identifier = Column(String(255), index=True)
321
+
322
+ # Cómo y el Resultado
323
+ auth_type = Column(String(20), nullable=False) # 'local', 'external_api', 'redeem_token', etc.
324
+ outcome = Column(String(10), nullable=False) # 'success' o 'failure'
325
+ reason_code = Column(String(50)) # Causa de fallo, ej: 'INVALID_CREDENTIALS'
326
+
327
+ # Contexto de la Petición
328
+ source_ip = Column(String(45), nullable=False)
329
+ user_agent_hash = Column(String(16)) # Hash corto del User-Agent
330
+ request_path = Column(String(255), nullable=False)
331
+
332
+ def __repr__(self):
333
+ return (f"<AccessLog(id={self.id}, company='{self.company_short_name}', "
334
+ f"user='{self.user_identifier}', outcome='{self.outcome}')>")
@@ -72,9 +72,10 @@ class ProfileRepo:
72
72
  def create_company(self, new_company: Company):
73
73
  company = self.session.query(Company).filter_by(name=new_company.name).first()
74
74
  if company:
75
- company.parameters = new_company.parameters
76
- company.branding = new_company.branding
75
+ if company.parameters != new_company.parameters:
76
+ company.parameters = new_company.parameters
77
77
  else:
78
+ # Si la compañía no existe, la añade a la sesión.
78
79
  self.session.add(new_company)
79
80
  company = new_company
80
81
 
@@ -4,40 +4,38 @@
4
4
  # IAToolkit is open source software.
5
5
 
6
6
  from sqlalchemy import text
7
- from huggingface_hub import InferenceClient
8
7
  from injector import inject
9
8
  from iatoolkit.common.exceptions import IAToolkitException
10
9
  from iatoolkit.repositories.database_manager import DatabaseManager
11
- from iatoolkit.repositories.models import Document, VSDoc
12
- import os
10
+ from iatoolkit.services.embedding_service import EmbeddingService
11
+ from iatoolkit.repositories.models import Document, VSDoc, Company
13
12
  import logging
14
13
 
14
+
15
15
  class VSRepo:
16
16
  @inject
17
- def __init__(self, db_manager: DatabaseManager):
17
+ def __init__(self,
18
+ db_manager: DatabaseManager,
19
+ embedding_service: EmbeddingService):
18
20
  self.session = db_manager.get_session()
19
-
20
- # Inicializar el modelo de embeddings
21
- self.embedder = InferenceClient(
22
- model="sentence-transformers/all-MiniLM-L6-v2",
23
- token=os.getenv('HF_TOKEN'))
21
+ self.embedding_service = embedding_service
24
22
 
25
23
 
26
- def add_document(self, vs_chunk_list: list[VSDoc]):
24
+ def add_document(self, company_short_name, vs_chunk_list: list[VSDoc]):
27
25
  try:
28
26
  for doc in vs_chunk_list:
29
27
  # calculate the embedding for the text
30
- doc.embedding = self.embedder.feature_extraction(doc.text)
28
+ doc.embedding = self.embedding_service.embed_text(company_short_name, doc.text)
31
29
  self.session.add(doc)
32
30
  self.session.commit()
33
31
  except Exception as e:
34
- logging.error(f"Error insertando documentos en PostgreSQL: {str(e)}")
32
+ logging.error(f"Error inserting documents into PostgreSQL: {str(e)}")
35
33
  self.session.rollback()
36
34
  raise IAToolkitException(IAToolkitException.ErrorType.VECTOR_STORE_ERROR,
37
- f"Error insertando documentos en PostgreSQL: {str(e)}")
35
+ f"Error inserting documents into PostgreSQL: {str(e)}")
38
36
 
39
37
  def query(self,
40
- company_id: int,
38
+ company_short_name: str,
41
39
  query_text: str,
42
40
  n_results=5,
43
41
  metadata_filter=None
@@ -46,18 +44,25 @@ class VSRepo:
46
44
  search documents similar to the query for a company
47
45
 
48
46
  Args:
49
- company_id:
47
+ company_short_name: The company's unique short name.
50
48
  query_text: query text
51
49
  n_results: max number of results to return
52
- metadata_filter: (ej: {"document_type": "certificate"})
50
+ metadata_filter: (e.g., {"document_type": "certificate"})
53
51
 
54
52
  Returns:
55
53
  list of documents matching the query and filters
56
54
  """
57
- # Generate the embedding with the query text
58
- query_embedding = self.embedder.feature_extraction([query_text])[0]
55
+ # Generate the embedding with the query text for the specific company
56
+ query_embedding = self.embedding_service.embed_text(company_short_name, query_text)
59
57
 
58
+ sql_query, params = None, None
60
59
  try:
60
+ # Get company ID from its short name for the SQL query
61
+ company = self.session.query(Company).filter(Company.short_name == company_short_name).one_or_none()
62
+ if not company:
63
+ raise IAToolkitException(IAToolkitException.ErrorType.VECTOR_STORE_ERROR,
64
+ f"Company with short name '{company_short_name}' not found.")
65
+
61
66
  # build the SQL query
62
67
  sql_query_parts = ["""
63
68
  SELECT iat_documents.id, \
@@ -73,11 +78,12 @@ class VSRepo:
73
78
 
74
79
  # query parameters
75
80
  params = {
76
- "company_id": company_id,
81
+ "company_id": company.id,
77
82
  "query_embedding": query_embedding,
78
83
  "n_results": n_results
79
84
  }
80
85
 
86
+
81
87
  # add metadata filter, if exists
82
88
  if metadata_filter and isinstance(metadata_filter, dict):
83
89
  for key, value in metadata_filter.items():
@@ -108,7 +114,7 @@ class VSRepo:
108
114
  meta_data = row[4] if len(row) > 4 and row[4] is not None else {}
109
115
  doc = Document(
110
116
  id=row[0],
111
- company_id=company_id,
117
+ company_id=company.id,
112
118
  filename=row[1],
113
119
  content=row[2],
114
120
  content_b64=row[3],