iatoolkit 0.64.0__tar.gz → 0.66.5__tar.gz

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.

Potentially problematic release.


This version of iatoolkit might be problematic. Click here for more details.

Files changed (133) hide show
  1. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/PKG-INFO +1 -1
  2. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/pyproject.toml +2 -2
  3. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/common/routes.py +6 -0
  4. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/common/session_manager.py +2 -0
  5. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/common/util.py +17 -0
  6. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/iatoolkit.py +35 -3
  7. iatoolkit-0.66.5/src/iatoolkit/locales/en.yaml +167 -0
  8. iatoolkit-0.66.5/src/iatoolkit/locales/es.yaml +163 -0
  9. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/repositories/database_manager.py +3 -3
  10. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/repositories/document_repo.py +1 -1
  11. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/repositories/models.py +2 -0
  12. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/auth_service.py +14 -9
  13. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/branding_service.py +1 -2
  14. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/dispatcher_service.py +8 -9
  15. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/document_service.py +5 -2
  16. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/excel_service.py +15 -11
  17. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/file_processor_service.py +4 -12
  18. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/help_content_service.py +1 -1
  19. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/history_service.py +8 -7
  20. iatoolkit-0.66.5/src/iatoolkit/services/i18n_service.py +104 -0
  21. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/jwt_service.py +7 -9
  22. iatoolkit-0.66.5/src/iatoolkit/services/language_service.py +77 -0
  23. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/load_documents_service.py +4 -4
  24. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/mail_service.py +9 -4
  25. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/profile_service.py +58 -38
  26. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/prompt_manager_service.py +20 -16
  27. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/query_service.py +15 -14
  28. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/sql_service.py +6 -2
  29. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/user_feedback_service.py +15 -13
  30. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/js/chat_feedback_button.js +9 -11
  31. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/js/chat_help_content.js +13 -13
  32. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/js/chat_history_button.js +27 -10
  33. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/js/chat_main.js +25 -23
  34. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/js/chat_reload_button.js +3 -3
  35. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/styles/chat_iatoolkit.css +181 -243
  36. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/styles/chat_modal.css +3 -41
  37. iatoolkit-0.66.5/src/iatoolkit/static/styles/chat_public.css +107 -0
  38. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/templates/_login_widget.html +9 -7
  39. iatoolkit-0.66.5/src/iatoolkit/templates/base.html +65 -0
  40. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/templates/change_password.html +15 -11
  41. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/templates/chat.html +35 -19
  42. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/templates/chat_modals.html +29 -15
  43. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/templates/error.html +4 -1
  44. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/templates/forgot_password.html +11 -7
  45. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/templates/index.html +3 -0
  46. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/templates/login_simulation.html +16 -5
  47. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/templates/signup.html +14 -14
  48. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/base_login_view.py +12 -1
  49. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/change_password_view.py +48 -33
  50. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/forgot_password_view.py +19 -21
  51. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/help_content_api_view.py +12 -8
  52. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/history_api_view.py +14 -9
  53. iatoolkit-0.66.5/src/iatoolkit/views/home_view.py +61 -0
  54. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/init_context_api_view.py +16 -11
  55. iatoolkit-0.66.5/src/iatoolkit/views/llmquery_api_view.py +57 -0
  56. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/login_simulation_view.py +14 -2
  57. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/login_view.py +45 -36
  58. iatoolkit-0.66.5/src/iatoolkit/views/logout_api_view.py +49 -0
  59. iatoolkit-0.66.5/src/iatoolkit/views/profile_api_view.py +46 -0
  60. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/prompt_api_view.py +6 -6
  61. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/signup_view.py +25 -27
  62. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/user_feedback_api_view.py +19 -18
  63. iatoolkit-0.66.5/src/iatoolkit/views/verify_user_view.py +62 -0
  64. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit.egg-info/PKG-INFO +1 -1
  65. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit.egg-info/SOURCES.txt +6 -3
  66. iatoolkit-0.64.0/src/iatoolkit/static/styles/chat_info.css +0 -53
  67. iatoolkit-0.64.0/src/iatoolkit/templates/base.html +0 -50
  68. iatoolkit-0.64.0/src/iatoolkit/templates/header.html +0 -31
  69. iatoolkit-0.64.0/src/iatoolkit/templates/test.html +0 -9
  70. iatoolkit-0.64.0/src/iatoolkit/views/home_view.py +0 -67
  71. iatoolkit-0.64.0/src/iatoolkit/views/llmquery_api_view.py +0 -45
  72. iatoolkit-0.64.0/src/iatoolkit/views/logout_api_view.py +0 -45
  73. iatoolkit-0.64.0/src/iatoolkit/views/verify_user_view.py +0 -60
  74. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/readme.md +0 -0
  75. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/requirements.txt +0 -0
  76. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/setup.cfg +0 -0
  77. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/__init__.py +0 -0
  78. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/base_company.py +0 -0
  79. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/cli_commands.py +0 -0
  80. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/common/__init__.py +0 -0
  81. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/common/exceptions.py +0 -0
  82. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/company_registry.py +0 -0
  83. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/__init__.py +0 -0
  84. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/call_service.py +0 -0
  85. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/connectors/__init__.py +0 -0
  86. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
  87. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
  88. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
  89. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
  90. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
  91. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
  92. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/gemini_adapter.py +0 -0
  93. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/google_chat_app.py +0 -0
  94. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/llm_client.py +0 -0
  95. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/llm_proxy.py +0 -0
  96. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/llm_response.py +0 -0
  97. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/mail_app.py +0 -0
  98. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/openai_adapter.py +0 -0
  99. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/infra/redis_session_manager.py +0 -0
  100. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/repositories/__init__.py +0 -0
  101. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
  102. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/repositories/profile_repo.py +0 -0
  103. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/repositories/tasks_repo.py +0 -0
  104. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/repositories/vs_repo.py +0 -0
  105. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/__init__.py +0 -0
  106. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/benchmark_service.py +0 -0
  107. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/onboarding_service.py +0 -0
  108. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/search_service.py +0 -0
  109. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/tasks_service.py +0 -0
  110. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/services/user_session_context_service.py +0 -0
  111. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/images/fernando.jpeg +0 -0
  112. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/js/chat_filepond.js +0 -0
  113. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
  114. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
  115. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
  116. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/styles/landing_page.css +0 -0
  117. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/styles/llm_output.css +0 -0
  118. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/static/styles/onboarding.css +0 -0
  119. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/system_prompts/format_styles.prompt +0 -0
  120. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/system_prompts/query_main.prompt +0 -0
  121. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/system_prompts/sql_rules.prompt +0 -0
  122. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/templates/_company_header.html +0 -0
  123. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/templates/about.html +0 -0
  124. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/templates/onboarding_shell.html +0 -0
  125. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/__init__.py +0 -0
  126. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/external_login_view.py +0 -0
  127. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/file_store_api_view.py +0 -0
  128. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/index_view.py +0 -0
  129. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/tasks_api_view.py +0 -0
  130. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit/views/tasks_review_api_view.py +0 -0
  131. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
  132. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit.egg-info/requires.txt +0 -0
  133. {iatoolkit-0.64.0 → iatoolkit-0.66.5}/src/iatoolkit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 0.64.0
3
+ Version: 0.66.5
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "iatoolkit"
7
- version = "0.64.0"
7
+ version = "0.66.5"
8
8
  requires-python = ">=3.12"
9
9
  description = "IAToolkit"
10
10
  readme = "readme.md"
@@ -26,4 +26,4 @@ exclude = ["tests*", "*/tests*"]
26
26
  dependencies = { file = ["requirements.txt"] }
27
27
 
28
28
  [tool.setuptools.package-data]
29
- iatoolkit = ["system_prompts/*.prompt", "templates/**/*", "static/**/*"]
29
+ iatoolkit = ["system_prompts/*.prompt", "templates/**/*", "static/**/*", "locales/*.yaml"]
@@ -25,6 +25,7 @@ def register_views(injector, app):
25
25
  from iatoolkit.views.prompt_api_view import PromptApiView
26
26
  from iatoolkit.views.history_api_view import HistoryApiView
27
27
  from iatoolkit.views.help_content_api_view import HelpContentApiView
28
+ from iatoolkit.views.profile_api_view import UserLanguageApiView # <-- Importa la nueva vista
28
29
 
29
30
  from iatoolkit.views.login_view import LoginView, FinalizeContextView
30
31
  from iatoolkit.views.external_login_view import ExternalLoginView, RedeemTokenApiView
@@ -55,6 +56,11 @@ def register_views(injector, app):
55
56
  view_func=FinalizeContextView.as_view('finalize_with_token')
56
57
  )
57
58
 
59
+ app.add_url_rule(
60
+ '/api/profile/language',
61
+ view_func=UserLanguageApiView.as_view('user_language_api')
62
+ )
63
+
58
64
  # logout
59
65
  app.add_url_rule('/<company_short_name>/api/logout',
60
66
  view_func=LogoutApiView.as_view('logout'))
@@ -3,6 +3,8 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
+ # This is the Flask connected session manager for IAToolkit
7
+
6
8
  from flask import session
7
9
 
8
10
  class SessionManager:
@@ -81,6 +81,23 @@ class Utility:
81
81
  f'No se pudo renderizar el template desde el string, error: {str(e)}') from e
82
82
 
83
83
 
84
+ def get_company_template(self, company_short_name: str, template_name: str) -> str:
85
+ # 1. get the path to the company specific template
86
+ template_path = os.path.join(os.getcwd(), f'companies/{company_short_name}/templates/{template_name}')
87
+ if not os.path.exists(template_path):
88
+ return None
89
+
90
+ # 2. read the file
91
+ try:
92
+ with open(template_path, 'r') as f:
93
+ template_string = f.read()
94
+
95
+ return template_string
96
+ except Exception as e:
97
+ logging.exception(e)
98
+ return None
99
+
100
+
84
101
  def serialize(self, obj):
85
102
  if isinstance(obj, datetime) or isinstance(obj, date):
86
103
  return obj.isoformat()
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
- from flask import Flask, url_for
6
+ from flask import Flask, url_for, get_flashed_messages
7
7
  from flask_session import Session
8
8
  from flask_injector import FlaskInjector
9
9
  from flask_bcrypt import Bcrypt
@@ -19,7 +19,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix
19
19
  from injector import Binder, Injector, singleton
20
20
  from importlib.metadata import version as _pkg_version, PackageNotFoundError
21
21
 
22
- IATOOLKIT_VERSION = "0.64.0"
22
+ IATOOLKIT_VERSION = "0.66.5"
23
23
 
24
24
  # global variable for the unique instance of IAToolkit
25
25
  _iatoolkit_instance: Optional['IAToolkit'] = None
@@ -96,6 +96,7 @@ class IAToolkit:
96
96
  self._setup_cors()
97
97
  self._setup_additional_services()
98
98
  self._setup_cli_commands()
99
+ self._setup_request_globals()
99
100
  self._setup_context_processors()
100
101
 
101
102
  # Step 8: define the download_dir for excel's
@@ -109,6 +110,22 @@ class IAToolkit:
109
110
  # get a value from the config dict or the environment variable
110
111
  return self.config.get(key, os.getenv(key, default))
111
112
 
113
+ def _setup_request_globals(self):
114
+ """
115
+ Configures functions to run before each request to set up
116
+ request-global variables, such as language.
117
+ """
118
+ injector = self._injector
119
+
120
+ @self.app.before_request
121
+ def set_request_language():
122
+ """
123
+ Determines and caches the language for the current request in g.lang.
124
+ """
125
+ from iatoolkit.services.language_service import LanguageService
126
+ language_service = injector.get(LanguageService)
127
+ language_service.get_current_language()
128
+
112
129
  def _setup_logging(self):
113
130
  # Lee el nivel de log desde una variable de entorno, con 'INFO' como valor por defecto.
114
131
  log_level_name = os.getenv('LOG_LEVEL', 'INFO').upper()
@@ -299,6 +316,8 @@ class IAToolkit:
299
316
  from iatoolkit.services.jwt_service import JWTService
300
317
  from iatoolkit.services.dispatcher_service import Dispatcher
301
318
  from iatoolkit.services.branding_service import BrandingService
319
+ from iatoolkit.services.i18n_service import I18nService
320
+ from iatoolkit.services.language_service import LanguageService
302
321
 
303
322
  binder.bind(QueryService, to=QueryService)
304
323
  binder.bind(TaskService, to=TaskService)
@@ -312,6 +331,8 @@ class IAToolkit:
312
331
  binder.bind(JWTService, to=JWTService)
313
332
  binder.bind(Dispatcher, to=Dispatcher)
314
333
  binder.bind(BrandingService, to=BrandingService)
334
+ binder.bind(I18nService, to=I18nService)
335
+ binder.bind(LanguageService, to=LanguageService)
315
336
 
316
337
  def _bind_infrastructure(self, binder: Binder):
317
338
  from iatoolkit.infra.llm_client import llmClient
@@ -366,8 +387,18 @@ class IAToolkit:
366
387
  def inject_globals():
367
388
  from iatoolkit.common.session_manager import SessionManager
368
389
  from iatoolkit.services.profile_service import ProfileService
390
+ from iatoolkit.services.i18n_service import I18nService
369
391
 
392
+ # Get services from the injector
370
393
  profile_service = self._injector.get(ProfileService)
394
+ i18n_service = self._injector.get(I18nService)
395
+
396
+ # The 't' function wrapper no longer needs to determine the language itself.
397
+ # It will be automatically handled by the refactored I18nService.
398
+ def translate_for_template(key: str, **kwargs):
399
+ return i18n_service.t(key, **kwargs)
400
+
401
+ # Get user profile if a session exists
371
402
  user_profile = profile_service.get_current_session_info().get('profile', {})
372
403
 
373
404
  return {
@@ -379,9 +410,10 @@ class IAToolkit:
379
410
  'user_is_local': user_profile.get('user_is_local'),
380
411
  'user_email': user_profile.get('user_email'),
381
412
  'iatoolkit_base_url': os.environ.get('IATOOLKIT_BASE_URL', ''),
413
+ 'flashed_messages': get_flashed_messages(with_categories=True),
414
+ 't': translate_for_template
382
415
  }
383
416
 
384
-
385
417
  def _get_default_static_folder(self) -> str:
386
418
  try:
387
419
  current_dir = os.path.dirname(os.path.abspath(__file__)) # .../src/iatoolkit
@@ -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_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_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_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_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
+
@@ -27,8 +27,8 @@ class DatabaseManager:
27
27
  self._engine = create_engine(
28
28
  database_url,
29
29
  echo=False,
30
- pool_size=2, # per worker
31
- max_overflow=3,
30
+ pool_size=10, # per worker
31
+ max_overflow=20,
32
32
  pool_timeout=30,
33
33
  pool_recycle=1800,
34
34
  pool_pre_ping=True,
@@ -77,7 +77,7 @@ class DatabaseManager:
77
77
  inspector = inspect(self._engine)
78
78
 
79
79
  if table_name not in inspector.get_table_names():
80
- raise RuntimeError(f"La tabla '{table_name}' no existe en la BD.")
80
+ raise RuntimeError(f"Table '{table_name}' does not exist.")
81
81
 
82
82
  if exclude_columns is None:
83
83
  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
 
@@ -53,6 +53,7 @@ class Company(Base):
53
53
  id = Column(Integer, primary_key=True)
54
54
  short_name = Column(String(20), nullable=False, unique=True, index=True)
55
55
  name = Column(String(256), nullable=False)
56
+ default_language = Column(String(5), nullable=False, default='es')
56
57
 
57
58
  # encrypted api-key
58
59
  openai_api_key = Column(String, nullable=True)
@@ -107,6 +108,7 @@ class User(Base):
107
108
  created_at = Column(DateTime, default=datetime.now)
108
109
  password = Column(String, nullable=False)
109
110
  verified = Column(Boolean, nullable=False, default=False)
111
+ preferred_language = Column(String(5), nullable=True)
110
112
  verification_url = Column(String, nullable=True)
111
113
  temp_code = Column(String, nullable=True)
112
114
 
@@ -7,6 +7,7 @@ from flask import request
7
7
  from injector import inject
8
8
  from iatoolkit.services.profile_service import ProfileService
9
9
  from iatoolkit.services.jwt_service import JWTService
10
+ from iatoolkit.services.i18n_service import I18nService
10
11
  from iatoolkit.repositories.database_manager import DatabaseManager
11
12
  from iatoolkit.repositories.models import AccessLog
12
13
  from flask import request
@@ -23,18 +24,20 @@ class AuthService:
23
24
  @inject
24
25
  def __init__(self, profile_service: ProfileService,
25
26
  jwt_service: JWTService,
26
- db_manager: DatabaseManager
27
+ db_manager: DatabaseManager,
28
+ i18n_service: I18nService
27
29
  ):
28
30
  self.profile_service = profile_service
29
31
  self.jwt_service = jwt_service
30
32
  self.db_manager = db_manager
33
+ self.i18n_service = i18n_service
31
34
 
32
35
  def login_local_user(self, company_short_name: str, email: str, password: str) -> dict:
33
36
  # try to autenticate a local user, register the event and return the result
34
37
  auth_response = self.profile_service.login(
35
38
  company_short_name=company_short_name,
36
39
  email=email,
37
- password=password
40
+ password=password,
38
41
  )
39
42
 
40
43
  if not auth_response.get('success'):
@@ -66,7 +69,7 @@ class AuthService:
66
69
  outcome='failure',
67
70
  reason_code='JWT_INVALID'
68
71
  )
69
- return {'success': False, 'error': 'Token inválido o expirado'}
72
+ return {'success': False, 'error': self.i18n_service.t('errors.auth.invalid_or_expired_token')}
70
73
 
71
74
  # 2. if token is valid, extract the user_identifier
72
75
  user_identifier = payload.get('user_identifier')
@@ -81,7 +84,7 @@ class AuthService:
81
84
  )
82
85
  return {'success': True, 'user_identifier': user_identifier}
83
86
  except Exception as e:
84
- logging.error(f"Error al crear la sesión desde token para {user_identifier}: {e}")
87
+ logging.error(f"error creeating session for Token of {user_identifier}: {e}")
85
88
  self.log_access(
86
89
  company_short_name=company_short_name,
87
90
  auth_type='redeem_token',
@@ -89,7 +92,7 @@ class AuthService:
89
92
  reason_code='SESSION_CREATION_FAILED',
90
93
  user_identifier=user_identifier
91
94
  )
92
- return {'success': False, 'error': 'No se pudo crear la sesión del usuario'}
95
+ return {'success': False, 'error': self.i18n_service.t('errors.auth.session_creation_failed')}
93
96
 
94
97
  def verify(self, anonymous: bool = False) -> dict:
95
98
  """
@@ -123,14 +126,15 @@ class AuthService:
123
126
  # --- Failure: No valid credentials found ---
124
127
  logging.info(f"Authentication required. No session cookie or API Key provided.")
125
128
  return {"success": False,
126
- "error_message": "Authentication required. No session cookie or API Key provided.",
129
+ "error_message": self.i18n_service.t('errors.auth.authentication_required'),
127
130
  "status_code": 401}
128
131
 
129
132
  # check if the api-key is valid and active
130
133
  api_key_entry = self.profile_service.get_active_api_key_entry(api_key)
131
134
  if not api_key_entry:
132
135
  logging.info(f"Invalid or inactive API Key {api_key}")
133
- return {"success": False, "error_message": "Invalid or inactive API Key",
136
+ return {"success": False,
137
+ "error_message": self.i18n_service.t('errors.auth.invalid_api_key'),
134
138
  "status_code": 402}
135
139
 
136
140
  # get the company from the api_key_entry
@@ -141,7 +145,8 @@ class AuthService:
141
145
  user_identifier = data.get('user_identifier', '')
142
146
  if not anonymous and not user_identifier:
143
147
  logging.info(f"No user_identifier provided for API call.")
144
- return {"success": False, "error_message": "No user_identifier provided for API call.",
148
+ return {"success": False,
149
+ "error_message": self.i18n_service.t('errors.auth.no_user_identifier_api'),
145
150
  "status_code": 403}
146
151
 
147
152
  return {
@@ -184,5 +189,5 @@ class AuthService:
184
189
  session.commit()
185
190
 
186
191
  except Exception as e:
187
- logging.error(f"Fallo al escribir en AccessLog: {e}", exc_info=False)
192
+ logging.error(f"error writting to AccessLog: {e}", exc_info=False)
188
193
  session.rollback()
@@ -9,9 +9,8 @@ from injector import inject
9
9
 
10
10
  class BrandingService:
11
11
  """
12
- Servicio centralizado que gestiona la configuración de branding.
12
+ Branding configuration for IAToolkit
13
13
  """
14
-
15
14
  @inject
16
15
  def __init__(self):
17
16
  """