iatoolkit 0.22.1__tar.gz → 0.64.1__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 (156) hide show
  1. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/PKG-INFO +2 -2
  2. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/pyproject.toml +3 -3
  3. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/base_company.py +4 -2
  4. iatoolkit-0.64.1/src/iatoolkit/common/routes.py +135 -0
  5. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/common/session_manager.py +0 -1
  6. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/common/util.py +0 -27
  7. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/iatoolkit.py +37 -38
  8. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/llm_client.py +4 -6
  9. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/openai_adapter.py +1 -1
  10. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/redis_session_manager.py +48 -2
  11. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/repositories/database_manager.py +17 -2
  12. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/repositories/models.py +28 -4
  13. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/repositories/profile_repo.py +7 -3
  14. iatoolkit-0.64.1/src/iatoolkit/services/auth_service.py +188 -0
  15. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/branding_service.py +28 -22
  16. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/dispatcher_service.py +10 -41
  17. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/excel_service.py +15 -15
  18. iatoolkit-0.64.1/src/iatoolkit/services/help_content_service.py +30 -0
  19. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/history_service.py +2 -11
  20. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/jwt_service.py +15 -24
  21. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/profile_service.py +88 -27
  22. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/query_service.py +124 -81
  23. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/tasks_service.py +1 -1
  24. iatoolkit-0.64.1/src/iatoolkit/services/user_feedback_service.py +103 -0
  25. iatoolkit-0.64.1/src/iatoolkit/services/user_session_context_service.py +143 -0
  26. iatoolkit-0.64.1/src/iatoolkit/static/images/fernando.jpeg +0 -0
  27. iatoolkit-0.64.1/src/iatoolkit/static/js/chat_feedback_button.js +82 -0
  28. iatoolkit-0.64.1/src/iatoolkit/static/js/chat_help_content.js +124 -0
  29. iatoolkit-0.64.1/src/iatoolkit/static/js/chat_history_button.js +93 -0
  30. iatoolkit-0.64.1/src/iatoolkit/static/js/chat_logout_button.js +36 -0
  31. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/static/js/chat_main.js +40 -168
  32. iatoolkit-0.64.1/src/iatoolkit/static/js/chat_onboarding_button.js +97 -0
  33. iatoolkit-0.64.1/src/iatoolkit/static/js/chat_prompt_manager.js +94 -0
  34. iatoolkit-0.64.1/src/iatoolkit/static/js/chat_reload_button.js +35 -0
  35. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/static/styles/chat_iatoolkit.css +258 -141
  36. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/static/styles/chat_modal.css +95 -91
  37. iatoolkit-0.64.1/src/iatoolkit/static/styles/landing_page.css +182 -0
  38. iatoolkit-0.64.1/src/iatoolkit/static/styles/onboarding.css +169 -0
  39. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/system_prompts/query_main.prompt +3 -12
  40. iatoolkit-0.64.1/src/iatoolkit/templates/_company_header.html +20 -0
  41. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/templates/_login_widget.html +10 -13
  42. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/templates/base.html +3 -0
  43. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/templates/change_password.html +12 -14
  44. iatoolkit-0.64.1/src/iatoolkit/templates/chat.html +271 -0
  45. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/templates/chat_modals.html +116 -58
  46. iatoolkit-0.64.1/src/iatoolkit/templates/error.html +48 -0
  47. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/templates/forgot_password.html +10 -12
  48. iatoolkit-0.64.1/src/iatoolkit/templates/index.html +142 -0
  49. iatoolkit-0.64.1/src/iatoolkit/templates/login_simulation.html +34 -0
  50. iatoolkit-0.64.1/src/iatoolkit/templates/onboarding_shell.html +104 -0
  51. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/templates/signup.html +12 -10
  52. iatoolkit-0.64.1/src/iatoolkit/views/base_login_view.py +81 -0
  53. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/views/change_password_view.py +8 -6
  54. iatoolkit-0.64.1/src/iatoolkit/views/external_login_view.py +73 -0
  55. iatoolkit-0.22.1/src/iatoolkit/views/file_store_view.py → iatoolkit-0.64.1/src/iatoolkit/views/file_store_api_view.py +9 -11
  56. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/views/forgot_password_view.py +10 -6
  57. iatoolkit-0.64.1/src/iatoolkit/views/help_content_api_view.py +50 -0
  58. iatoolkit-0.64.1/src/iatoolkit/views/history_api_view.py +51 -0
  59. iatoolkit-0.64.1/src/iatoolkit/views/home_view.py +67 -0
  60. iatoolkit-0.64.1/src/iatoolkit/views/index_view.py +14 -0
  61. iatoolkit-0.64.1/src/iatoolkit/views/init_context_api_view.py +68 -0
  62. iatoolkit-0.64.1/src/iatoolkit/views/llmquery_api_view.py +45 -0
  63. iatoolkit-0.64.1/src/iatoolkit/views/login_simulation_view.py +81 -0
  64. iatoolkit-0.64.1/src/iatoolkit/views/login_view.py +144 -0
  65. iatoolkit-0.64.1/src/iatoolkit/views/logout_api_view.py +45 -0
  66. iatoolkit-0.22.1/src/iatoolkit/views/prompt_view.py → iatoolkit-0.64.1/src/iatoolkit/views/prompt_api_view.py +7 -7
  67. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/views/signup_view.py +10 -6
  68. iatoolkit-0.22.1/src/iatoolkit/views/tasks_view.py → iatoolkit-0.64.1/src/iatoolkit/views/tasks_api_view.py +10 -36
  69. iatoolkit-0.64.1/src/iatoolkit/views/tasks_review_api_view.py +55 -0
  70. iatoolkit-0.22.1/src/iatoolkit/views/user_feedback_view.py → iatoolkit-0.64.1/src/iatoolkit/views/user_feedback_api_view.py +16 -31
  71. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/views/verify_user_view.py +8 -3
  72. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit.egg-info/PKG-INFO +2 -2
  73. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit.egg-info/SOURCES.txt +25 -26
  74. iatoolkit-0.22.1/src/iatoolkit/common/auth.py +0 -200
  75. iatoolkit-0.22.1/src/iatoolkit/common/routes.py +0 -130
  76. iatoolkit-0.22.1/src/iatoolkit/services/user_feedback_service.py +0 -67
  77. iatoolkit-0.22.1/src/iatoolkit/services/user_session_context_service.py +0 -85
  78. iatoolkit-0.22.1/src/iatoolkit/static/images/arrow_up.png +0 -0
  79. iatoolkit-0.22.1/src/iatoolkit/static/images/diagrama_iatoolkit.jpg +0 -0
  80. iatoolkit-0.22.1/src/iatoolkit/static/images/logo_clinica.png +0 -0
  81. iatoolkit-0.22.1/src/iatoolkit/static/images/logo_iatoolkit.png +0 -0
  82. iatoolkit-0.22.1/src/iatoolkit/static/images/logo_maxxa.png +0 -0
  83. iatoolkit-0.22.1/src/iatoolkit/static/images/logo_notaria.png +0 -0
  84. iatoolkit-0.22.1/src/iatoolkit/static/images/logo_tarjeta.png +0 -0
  85. iatoolkit-0.22.1/src/iatoolkit/static/images/logo_umayor.png +0 -0
  86. iatoolkit-0.22.1/src/iatoolkit/static/images/upload.png +0 -0
  87. iatoolkit-0.22.1/src/iatoolkit/static/js/chat_feedback.js +0 -115
  88. iatoolkit-0.22.1/src/iatoolkit/static/js/chat_history.js +0 -131
  89. iatoolkit-0.22.1/src/iatoolkit/static/styles/landing_page.css +0 -168
  90. iatoolkit-0.22.1/src/iatoolkit/templates/_branding_styles.html +0 -53
  91. iatoolkit-0.22.1/src/iatoolkit/templates/_navbar.html +0 -9
  92. iatoolkit-0.22.1/src/iatoolkit/templates/chat.html +0 -204
  93. iatoolkit-0.22.1/src/iatoolkit/templates/error.html +0 -15
  94. iatoolkit-0.22.1/src/iatoolkit/templates/home.html +0 -190
  95. iatoolkit-0.22.1/src/iatoolkit/templates/index.html +0 -134
  96. iatoolkit-0.22.1/src/iatoolkit/templates/login.html +0 -43
  97. iatoolkit-0.22.1/src/iatoolkit/templates/onboarding_shell.html +0 -167
  98. iatoolkit-0.22.1/src/iatoolkit/views/chat_token_request_view.py +0 -98
  99. iatoolkit-0.22.1/src/iatoolkit/views/download_file_view.py +0 -58
  100. iatoolkit-0.22.1/src/iatoolkit/views/external_login_view.py +0 -154
  101. iatoolkit-0.22.1/src/iatoolkit/views/history_view.py +0 -57
  102. iatoolkit-0.22.1/src/iatoolkit/views/home_view.py +0 -34
  103. iatoolkit-0.22.1/src/iatoolkit/views/index_view.py +0 -43
  104. iatoolkit-0.22.1/src/iatoolkit/views/init_context_view.py +0 -35
  105. iatoolkit-0.22.1/src/iatoolkit/views/llmquery_view.py +0 -65
  106. iatoolkit-0.22.1/src/iatoolkit/views/login_view.py +0 -136
  107. iatoolkit-0.22.1/src/iatoolkit/views/tasks_review_view.py +0 -83
  108. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/readme.md +0 -0
  109. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/requirements.txt +0 -0
  110. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/setup.cfg +0 -0
  111. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/__init__.py +0 -0
  112. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/cli_commands.py +0 -0
  113. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/common/__init__.py +0 -0
  114. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/common/exceptions.py +0 -0
  115. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/company_registry.py +0 -0
  116. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/__init__.py +0 -0
  117. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/call_service.py +0 -0
  118. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/connectors/__init__.py +0 -0
  119. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
  120. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
  121. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
  122. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
  123. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
  124. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
  125. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/gemini_adapter.py +0 -0
  126. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/google_chat_app.py +0 -0
  127. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/llm_proxy.py +0 -0
  128. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/llm_response.py +0 -0
  129. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/infra/mail_app.py +0 -0
  130. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/repositories/__init__.py +0 -0
  131. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/repositories/document_repo.py +0 -0
  132. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
  133. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/repositories/tasks_repo.py +0 -0
  134. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/repositories/vs_repo.py +0 -0
  135. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/__init__.py +0 -0
  136. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/benchmark_service.py +0 -0
  137. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/document_service.py +0 -0
  138. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/file_processor_service.py +0 -0
  139. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/load_documents_service.py +0 -0
  140. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/mail_service.py +0 -0
  141. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/onboarding_service.py +0 -0
  142. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/prompt_manager_service.py +0 -0
  143. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/search_service.py +0 -0
  144. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/services/sql_service.py +0 -0
  145. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/static/js/chat_filepond.js +0 -0
  146. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/static/styles/chat_info.css +0 -0
  147. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/static/styles/llm_output.css +0 -0
  148. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/system_prompts/format_styles.prompt +0 -0
  149. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/system_prompts/sql_rules.prompt +0 -0
  150. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/templates/about.html +0 -0
  151. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/templates/header.html +0 -0
  152. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/templates/test.html +0 -0
  153. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit/views/__init__.py +0 -0
  154. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
  155. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit.egg-info/requires.txt +0 -0
  156. {iatoolkit-0.22.1 → iatoolkit-0.64.1}/src/iatoolkit.egg-info/top_level.txt +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 0.22.1
3
+ Version: 0.64.1
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
7
- Requires-Python: >=3.11
7
+ Requires-Python: >=3.12
8
8
  Description-Content-Type: text/markdown
9
9
  Requires-Dist: aiohappyeyeballs==2.4.4
10
10
  Requires-Dist: aiohttp==3.11.9
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "iatoolkit"
7
- version = "0.22.1"
8
- requires-python = ">=3.11"
7
+ version = "0.64.1"
8
+ requires-python = ">=3.12"
9
9
  description = "IAToolkit"
10
10
  readme = "readme.md"
11
11
  license = "MIT"
@@ -19,7 +19,7 @@ package-dir = {"" = "src"}
19
19
 
20
20
  [tool.setuptools.packages.find]
21
21
  where = ["src"]
22
- exclude = ["tests*"]
22
+ exclude = ["tests*", "*/tests*"]
23
23
 
24
24
 
25
25
  [tool.setuptools.dynamic]
@@ -29,11 +29,13 @@ class BaseCompany(ABC):
29
29
  def _create_company(self,
30
30
  short_name: str,
31
31
  name: str,
32
+ parameters: dict | None = None,
32
33
  branding: dict | None = None,
33
- onboarding_cards: dict | None = None
34
+ onboarding_cards: dict | None = None,
34
35
  ) -> Company:
35
36
  company_obj = Company(short_name=short_name,
36
37
  name=name,
38
+ parameters=parameters,
37
39
  branding=branding,
38
40
  onboarding_cards=onboarding_cards)
39
41
  self.company = self.profile_repo.create_company(company_obj)
@@ -88,7 +90,7 @@ class BaseCompany(ABC):
88
90
 
89
91
  @abstractmethod
90
92
  # get context specific for this company
91
- def get_user_info(self, user_identifier: str) -> str:
93
+ def get_user_info(self, user_identifier: str) -> dict:
92
94
  raise NotImplementedError("La subclase debe implementar el método get_user_info()")
93
95
 
94
96
  @abstractmethod
@@ -0,0 +1,135 @@
1
+ # Copyright (c) 2024 Fernando Libedinsky
2
+ # Product: IAToolkit
3
+ #
4
+ # IAToolkit is open source software.
5
+
6
+ from flask import render_template, redirect, url_for,send_from_directory, current_app, abort
7
+ from flask import jsonify
8
+
9
+
10
+ # this function register all the views
11
+ def register_views(injector, app):
12
+
13
+ from iatoolkit.views.index_view import IndexView
14
+ from iatoolkit.views.init_context_api_view import InitContextApiView
15
+ from iatoolkit.views.llmquery_api_view import LLMQueryApiView
16
+ from iatoolkit.views.tasks_api_view import TaskApiView
17
+ from iatoolkit.views.tasks_review_api_view import TaskReviewApiView
18
+ from iatoolkit.views.login_simulation_view import LoginSimulationView
19
+ from iatoolkit.views.signup_view import SignupView
20
+ from iatoolkit.views.verify_user_view import VerifyAccountView
21
+ from iatoolkit.views.forgot_password_view import ForgotPasswordView
22
+ from iatoolkit.views.change_password_view import ChangePasswordView
23
+ from iatoolkit.views.file_store_api_view import FileStoreApiView
24
+ from iatoolkit.views.user_feedback_api_view import UserFeedbackApiView
25
+ from iatoolkit.views.prompt_api_view import PromptApiView
26
+ from iatoolkit.views.history_api_view import HistoryApiView
27
+ from iatoolkit.views.help_content_api_view import HelpContentApiView
28
+
29
+ from iatoolkit.views.login_view import LoginView, FinalizeContextView
30
+ from iatoolkit.views.external_login_view import ExternalLoginView, RedeemTokenApiView
31
+ from iatoolkit.views.logout_api_view import LogoutApiView
32
+ from iatoolkit.views.home_view import HomeView
33
+
34
+ # iatoolkit home page
35
+ app.add_url_rule('/', view_func=IndexView.as_view('index'))
36
+
37
+ # company home view
38
+ app.add_url_rule('/<company_short_name>/home', view_func=HomeView.as_view('home'))
39
+
40
+ # login for the iatoolkit integrated frontend
41
+ app.add_url_rule('/<company_short_name>/login', view_func=LoginView.as_view('login'))
42
+
43
+ # this is the login for external users
44
+ app.add_url_rule('/<company_short_name>/external_login',
45
+ view_func=ExternalLoginView.as_view('external_login'))
46
+
47
+ # this endpoint is called when onboarding_shell finish the context load
48
+ app.add_url_rule(
49
+ '/<company_short_name>/finalize',
50
+ view_func=FinalizeContextView.as_view('finalize_no_token')
51
+ )
52
+
53
+ app.add_url_rule(
54
+ '/<company_short_name>/finalize/<token>',
55
+ view_func=FinalizeContextView.as_view('finalize_with_token')
56
+ )
57
+
58
+ # logout
59
+ app.add_url_rule('/<company_short_name>/api/logout',
60
+ view_func=LogoutApiView.as_view('logout'))
61
+
62
+ # this endpoint is called by the JS for changing the token for a session
63
+ app.add_url_rule('/<string:company_short_name>/api/redeem_token',
64
+ view_func = RedeemTokenApiView.as_view('redeem_token'))
65
+
66
+ # init (reset) the company context
67
+ app.add_url_rule('/<company_short_name>/api/init-context',
68
+ view_func=InitContextApiView.as_view('init-context'),
69
+ methods=['POST', 'OPTIONS'])
70
+
71
+ # register new user, account verification and forgot password
72
+ app.add_url_rule('/<company_short_name>/signup',view_func=SignupView.as_view('signup'))
73
+ app.add_url_rule('/<company_short_name>/verify/<token>', view_func=VerifyAccountView.as_view('verify_account'))
74
+ app.add_url_rule('/<company_short_name>/forgot-password', view_func=ForgotPasswordView.as_view('forgot_password'))
75
+ app.add_url_rule('/<company_short_name>/change-password/<token>', view_func=ChangePasswordView.as_view('change_password'))
76
+
77
+ # main chat query, used by the JS in the browser (with credentials)
78
+ # can be used also for executing iatoolkit prompts
79
+ app.add_url_rule('/<company_short_name>/api/llm_query', view_func=LLMQueryApiView.as_view('llm_query_api'))
80
+
81
+ # open the promt directory
82
+ app.add_url_rule('/<company_short_name>/api/prompts', view_func=PromptApiView.as_view('prompt'))
83
+
84
+ # toolbar buttons
85
+ app.add_url_rule('/<company_short_name>/api/feedback', view_func=UserFeedbackApiView.as_view('feedback'))
86
+ app.add_url_rule('/<company_short_name>/api/history', view_func=HistoryApiView.as_view('history'))
87
+ app.add_url_rule('/<company_short_name>/api/help-content', view_func=HelpContentApiView.as_view('help-content'))
88
+
89
+ # tasks management endpoints: create task, and review answer
90
+ app.add_url_rule('/tasks', view_func=TaskApiView.as_view('tasks'))
91
+ app.add_url_rule('/tasks/review/<int:task_id>', view_func=TaskReviewApiView.as_view('tasks-review'))
92
+
93
+ # this endpoint is for upload documents into the vector store (api-key)
94
+ app.add_url_rule('/api/load', view_func=FileStoreApiView.as_view('load_api'))
95
+
96
+
97
+ @app.route('/download/<path:filename>')
98
+ def download_file(filename):
99
+ """
100
+ Esta vista sirve un archivo previamente generado desde el directorio
101
+ configurado en IATOOLKIT_DOWNLOAD_DIR.
102
+ """
103
+ # Valida que la configuración exista
104
+ if 'IATOOLKIT_DOWNLOAD_DIR' not in current_app.config:
105
+ abort(500, "Error de configuración: IATOOLKIT_DOWNLOAD_DIR no está definido.")
106
+
107
+ download_dir = current_app.config['IATOOLKIT_DOWNLOAD_DIR']
108
+
109
+ try:
110
+ return send_from_directory(
111
+ download_dir,
112
+ filename,
113
+ as_attachment=True # Fuerza la descarga en lugar de la visualización
114
+ )
115
+ except FileNotFoundError:
116
+ abort(404)
117
+
118
+ # login testing
119
+ app.add_url_rule('/<company_short_name>/login_test',
120
+ view_func=LoginSimulationView.as_view('login_test'))
121
+
122
+ app.add_url_rule(
123
+ '/about', # URL de la ruta
124
+ view_func=lambda: render_template('about.html'))
125
+
126
+ app.add_url_rule('/version', 'version',
127
+ lambda: jsonify({"iatoolkit_version": current_app.config.get('VERSION', 'N/A')}))
128
+
129
+
130
+ # hacer que la raíz '/' vaya al home de iatoolkit
131
+ @app.route('/')
132
+ def root_redirect():
133
+ return redirect(url_for('index'))
134
+
135
+
@@ -5,7 +5,6 @@
5
5
 
6
6
  from flask import session
7
7
 
8
-
9
8
  class SessionManager:
10
9
  @staticmethod
11
10
  def set(key, value):
@@ -9,7 +9,6 @@ from iatoolkit.common.exceptions import IAToolkitException
9
9
  from injector import inject
10
10
  import os
11
11
  from jinja2 import Environment, FileSystemLoader
12
- from iatoolkit.common.session_manager import SessionManager
13
12
  from datetime import datetime, date
14
13
  from decimal import Decimal
15
14
  import yaml
@@ -22,30 +21,8 @@ class Utility:
22
21
  def __init__(self):
23
22
  self.encryption_key = os.getenv('FERNET_KEY')
24
23
 
25
- @staticmethod
26
- def resolve_user_identifier(external_user_id: str = None, local_user_id: int = 0) -> tuple[str, bool]:
27
- """
28
- Resuelve un identificador único de usuario desde external_user_id o local_user_id.
29
-
30
- Lógica:
31
- - Si external_user_id existe y no está vacío: usar external_user_id
32
- - Si no, y local_user_id > 0: obtener email de la sesión actual y retornarlo como ID
33
- - Si ninguno: retornar string vacío
34
-
35
- """
36
- if external_user_id and external_user_id.strip():
37
- return external_user_id.strip(), False
38
- elif local_user_id and local_user_id > 0:
39
- # get the user information from the session
40
- user_data = SessionManager.get('user')
41
- if user_data:
42
- return user_data.get('email', ''), True
43
-
44
- return "", False
45
-
46
24
  def render_prompt_from_template(self,
47
25
  template_pathname: str,
48
- query: str = None,
49
26
  client_data: dict = {},
50
27
  **kwargs) -> str:
51
28
 
@@ -58,8 +35,6 @@ class Utility:
58
35
  env = Environment(loader=FileSystemLoader(template_dir))
59
36
  template = env.get_template(template_file)
60
37
 
61
- kwargs["query"] = query
62
-
63
38
  # add all the keys in client_data to kwargs
64
39
  kwargs.update(client_data)
65
40
 
@@ -74,7 +49,6 @@ class Utility:
74
49
  def render_prompt_from_string(self,
75
50
  template_string: str,
76
51
  searchpath: str | list[str] = None,
77
- query: str = None,
78
52
  client_data: dict = {},
79
53
  **kwargs) -> str:
80
54
  """
@@ -97,7 +71,6 @@ class Utility:
97
71
  env = Environment(loader=loader)
98
72
  template = env.from_string(template_string)
99
73
 
100
- kwargs["query"] = query
101
74
  kwargs.update(client_data)
102
75
 
103
76
  prompt = template.render(**kwargs)
@@ -16,10 +16,10 @@ import os
16
16
  from typing import Optional, Dict, Any
17
17
  from iatoolkit.repositories.database_manager import DatabaseManager
18
18
  from werkzeug.middleware.proxy_fix import ProxyFix
19
- from injector import Binder, singleton, Injector
19
+ from injector import Binder, Injector, singleton
20
20
  from importlib.metadata import version as _pkg_version, PackageNotFoundError
21
21
 
22
- IATOOLKIT_VERSION = "0.21.0"
22
+ IATOOLKIT_VERSION = "0.64.1"
23
23
 
24
24
  # global variable for the unique instance of IAToolkit
25
25
  _iatoolkit_instance: Optional['IAToolkit'] = None
@@ -52,7 +52,7 @@ class IAToolkit:
52
52
  self.app = None
53
53
  self.db_manager = None
54
54
  self._injector = None
55
- self.version = IATOOLKIT_VERSION
55
+ self.version = IATOOLKIT_VERSION # default version
56
56
 
57
57
  @classmethod
58
58
  def get_instance(cls) -> 'IAToolkit':
@@ -88,7 +88,7 @@ class IAToolkit:
88
88
  # and other integrations, as views are handled manually.
89
89
  FlaskInjector(app=self.app, injector=self._injector)
90
90
 
91
- # Step 6: initialize dispatcher and registered compaies
91
+ # Step 6: initialize dispatcher and registered companies
92
92
  self._init_dispatcher_and_company_instances()
93
93
 
94
94
  # Step 7: Finalize setup within the application context
@@ -101,8 +101,6 @@ class IAToolkit:
101
101
  # Step 8: define the download_dir for excel's
102
102
  self._setup_download_dir()
103
103
 
104
-
105
-
106
104
  logging.info(f"🎉 IAToolkit v{self.version} inicializado correctamente")
107
105
  self._initialized = True
108
106
  return self.app
@@ -155,10 +153,9 @@ class IAToolkit:
155
153
 
156
154
  self.app.config.update({
157
155
  'VERSION': self.version,
158
- 'SERVER_NAME': domain,
159
156
  'SECRET_KEY': self._get_config_value('FLASK_SECRET_KEY', 'iatoolkit-default-secret'),
160
- 'SESSION_COOKIE_SAMESITE': "None" if is_https else "Lax",
161
- 'SESSION_COOKIE_SECURE': is_https,
157
+ 'SESSION_COOKIE_SAMESITE': "None",
158
+ 'SESSION_COOKIE_SECURE': True,
162
159
  'SESSION_PERMANENT': False,
163
160
  'SESSION_USE_SIGNER': True,
164
161
  'JWT_SECRET_KEY': self._get_config_value('JWT_SECRET_KEY', 'iatoolkit-jwt-secret'),
@@ -188,6 +185,15 @@ class IAToolkit:
188
185
  self.db_manager.create_all()
189
186
  logging.info("✅ Base de datos configurada correctamente")
190
187
 
188
+ @self.app.teardown_appcontext
189
+ def remove_session(exception=None):
190
+ """
191
+ Flask calls this after each request.
192
+ It ensures the SQLAlchemy session is properly closed
193
+ and the DB connection is returned to the pool.
194
+ """
195
+ self.db_manager.scoped_session.remove()
196
+
191
197
  def _setup_redis_sessions(self):
192
198
  redis_url = self._get_config_value('REDIS_URL')
193
199
  if not redis_url:
@@ -218,19 +224,19 @@ class IAToolkit:
218
224
 
219
225
  def _setup_cors(self):
220
226
  """🌐 Configura CORS"""
221
- # Origins por defecto para desarrollo
227
+ from iatoolkit.company_registry import get_company_registry
228
+
229
+ # default CORS origin
222
230
  default_origins = [
223
- "http://localhost:5001",
224
- "http://127.0.0.1:5001",
225
231
  os.getenv('IATOOLKIT_BASE_URL')
226
232
  ]
227
233
 
228
- # Obtener origins adicionales desde configuración/env
234
+ # Iterate through the registered company names
229
235
  extra_origins = []
230
- for i in range(1, 11): # Soporte para CORS_ORIGIN_1 a CORS_ORIGIN_10
231
- origin = self._get_config_value(f'CORS_ORIGIN_{i}')
232
- if origin:
233
- extra_origins.append(origin)
236
+ all_company_instances = get_company_registry().get_all_company_instances()
237
+ for company_name, company_instance in all_company_instances.items():
238
+ cors_origin = company_instance.company.parameters.get('cors_origin', [])
239
+ extra_origins += cors_origin
234
240
 
235
241
  all_origins = default_origins + extra_origins
236
242
 
@@ -245,7 +251,6 @@ class IAToolkit:
245
251
 
246
252
  logging.info(f"✅ CORS configurado para: {all_origins}")
247
253
 
248
-
249
254
  def _configure_core_dependencies(self, binder: Binder):
250
255
  """⚙️ Configures all system dependencies."""
251
256
  try:
@@ -257,7 +262,6 @@ class IAToolkit:
257
262
  self._bind_repositories(binder)
258
263
  self._bind_services(binder)
259
264
  self._bind_infrastructure(binder)
260
- self._bind_views(binder)
261
265
 
262
266
  logging.info("✅ Dependencias configuradas correctamente")
263
267
 
@@ -309,34 +313,21 @@ class IAToolkit:
309
313
  binder.bind(Dispatcher, to=Dispatcher)
310
314
  binder.bind(BrandingService, to=BrandingService)
311
315
 
312
-
313
316
  def _bind_infrastructure(self, binder: Binder):
314
317
  from iatoolkit.infra.llm_client import llmClient
315
318
  from iatoolkit.infra.llm_proxy import LLMProxy
316
319
  from iatoolkit.infra.google_chat_app import GoogleChatApp
317
320
  from iatoolkit.infra.mail_app import MailApp
318
- from iatoolkit.common.auth import IAuthentication
321
+ from iatoolkit.services.auth_service import AuthService
319
322
  from iatoolkit.common.util import Utility
320
323
 
321
-
322
-
323
- binder.bind(LLMProxy, to=LLMProxy, scope=singleton)
324
- binder.bind(llmClient, to=llmClient, scope=singleton)
324
+ binder.bind(LLMProxy, to=LLMProxy)
325
+ binder.bind(llmClient, to=llmClient)
325
326
  binder.bind(GoogleChatApp, to=GoogleChatApp)
326
327
  binder.bind(MailApp, to=MailApp)
327
- binder.bind(IAuthentication, to=IAuthentication)
328
+ binder.bind(AuthService, to=AuthService)
328
329
  binder.bind(Utility, to=Utility)
329
330
 
330
- def _bind_views(self, binder: Binder):
331
- """Vincula las vistas después de que el injector ha sido creado"""
332
- from iatoolkit.views.llmquery_view import LLMQueryView
333
- from iatoolkit.views.home_view import HomeView
334
-
335
- binder.bind(HomeView, to=HomeView)
336
- binder.bind(LLMQueryView, to=LLMQueryView)
337
-
338
- logging.info("✅ Views configuradas correctamente")
339
-
340
331
  def _setup_additional_services(self):
341
332
  Bcrypt(self.app)
342
333
 
@@ -374,15 +365,23 @@ class IAToolkit:
374
365
  @self.app.context_processor
375
366
  def inject_globals():
376
367
  from iatoolkit.common.session_manager import SessionManager
368
+ from iatoolkit.services.profile_service import ProfileService
369
+
370
+ profile_service = self._injector.get(ProfileService)
371
+ user_profile = profile_service.get_current_session_info().get('profile', {})
372
+
377
373
  return {
378
374
  'url_for': url_for,
379
375
  'iatoolkit_version': self.version,
380
376
  'app_name': 'IAToolkit',
381
- 'user': SessionManager.get('user'),
382
- 'user_company': SessionManager.get('company_short_name'),
377
+ 'user_identifier': SessionManager.get('user_identifier'),
378
+ 'company_short_name': SessionManager.get('company_short_name'),
379
+ 'user_is_local': user_profile.get('user_is_local'),
380
+ 'user_email': user_profile.get('user_email'),
383
381
  'iatoolkit_base_url': os.environ.get('IATOOLKIT_BASE_URL', ''),
384
382
  }
385
383
 
384
+
386
385
  def _get_default_static_folder(self) -> str:
387
386
  try:
388
387
  current_dir = os.path.dirname(os.path.abspath(__file__)) # .../src/iatoolkit
@@ -21,7 +21,7 @@ import tiktoken
21
21
  from typing import Dict, Optional, List
22
22
  from iatoolkit.services.dispatcher_service import Dispatcher
23
23
 
24
- CONTEXT_ERROR_MESSAGE = 'Tu consulta supera el límite de contexto, recarga la página para limpiarlo (esto demora un poco ...)'
24
+ CONTEXT_ERROR_MESSAGE = 'Tu consulta supera el límite de contexto, utiliza el boton de recarga de contexto.'
25
25
 
26
26
  class llmClient:
27
27
  _llm_clients_cache = {} # class attribute, for the clients cache
@@ -266,16 +266,14 @@ class llmClient:
266
266
  def set_company_context(self,
267
267
  company: Company,
268
268
  company_base_context: str,
269
- model: str = None) -> str:
269
+ model) -> str:
270
270
 
271
- if model:
272
- self.model = model
273
- logging.info(f"initializing model '{self.model}' with company context: {self.count_tokens(company_base_context)} tokens...")
271
+ logging.info(f"initializing model '{model}' with company context: {self.count_tokens(company_base_context)} tokens...")
274
272
 
275
273
  llm_proxy = self.llm_proxy_factory.create_for_company(company)
276
274
  try:
277
275
  response = llm_proxy.create_response(
278
- model=self.model,
276
+ model=model,
279
277
  input=[{
280
278
  "role": "system",
281
279
  "content": company_base_context
@@ -55,7 +55,7 @@ class OpenAIAdapter:
55
55
 
56
56
  # En caso de error de contexto
57
57
  if "context_length_exceeded" in str(e):
58
- error_message = 'Tu consulta supera el limite de contexto, sale e ingresa de nuevo a IAToolkit'
58
+ error_message = 'Tu consulta supera el limite de contexto. Reinicia el contexto con el boton de la barra superior.'
59
59
 
60
60
  raise IAToolkitException(IAToolkitException.ErrorType.LLM_ERROR, error_message)
61
61
 
@@ -37,9 +37,14 @@ class RedisSessionManager:
37
37
  return cls._client
38
38
 
39
39
  @classmethod
40
- def set(cls, key: str, value: str, ex: int = None):
40
+ def set(cls, key: str, value: str, **kwargs):
41
+ """
42
+ Método set flexible que pasa argumentos opcionales (como ex, nx)
43
+ directamente al cliente de redis.
44
+ """
41
45
  client = cls._get_client()
42
- result = client.set(key, value, ex=ex)
46
+ # Pasa todos los argumentos de palabra clave adicionales al cliente real
47
+ result = client.set(key, value, **kwargs)
43
48
  return result
44
49
 
45
50
  @classmethod
@@ -49,12 +54,53 @@ class RedisSessionManager:
49
54
  result = value if value is not None else default
50
55
  return result
51
56
 
57
+ @classmethod
58
+ def hset(cls, key: str, field: str, value: str):
59
+ """
60
+ Establece un campo en un Hash de Redis.
61
+ """
62
+ client = cls._get_client()
63
+ return client.hset(key, field, value)
64
+
65
+ @classmethod
66
+ def hget(cls, key: str, field: str):
67
+ """
68
+ Obtiene el valor de un campo de un Hash de Redis.
69
+ Devuelve None si la clave o el campo no existen.
70
+ """
71
+ client = cls._get_client()
72
+ return client.hget(key, field)
73
+
74
+ @classmethod
75
+ def hdel(cls, key: str, *fields):
76
+ """
77
+ Elimina uno o más campos de un Hash de Redis.
78
+ """
79
+ client = cls._get_client()
80
+ return client.hdel(key, *fields)
81
+
82
+ @classmethod
83
+ def pipeline(cls):
84
+ """
85
+ Inicia una transacción (pipeline) de Redis.
86
+ """
87
+ client = cls._get_client()
88
+ return client.pipeline()
89
+
90
+
52
91
  @classmethod
53
92
  def remove(cls, key: str):
54
93
  client = cls._get_client()
55
94
  result = client.delete(key)
56
95
  return result
57
96
 
97
+ @classmethod
98
+ def exists(cls, key: str) -> bool:
99
+ """Verifica si una clave existe en Redis."""
100
+ client = cls._get_client()
101
+ # El comando EXISTS de Redis devuelve un entero (0 o 1). Lo convertimos a booleano.
102
+ return bool(client.exists(key))
103
+
58
104
  @classmethod
59
105
  def set_json(cls, key: str, value: dict, ex: int = None):
60
106
  json_str = json.dumps(value)
@@ -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=2, # per worker
31
+ max_overflow=3,
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
@@ -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
@@ -59,7 +60,7 @@ class Company(Base):
59
60
 
60
61
  branding = Column(JSON, nullable=True)
61
62
  onboarding_cards = Column(JSON, nullable=True)
62
- parameters = Column(JSON, nullable=True, default={})
63
+ parameters = Column(JSON, nullable=True)
63
64
  created_at = Column(DateTime, default=datetime.now)
64
65
  allow_jwt = Column(Boolean, default=True, nullable=True)
65
66
 
@@ -265,8 +266,7 @@ class UserFeedback(Base):
265
266
  id = Column(Integer, primary_key=True)
266
267
  company_id = Column(Integer, ForeignKey('iat_companies.id',
267
268
  ondelete='CASCADE'), nullable=False)
268
- local_user_id = Column(Integer, default=0, nullable=True)
269
- external_user_id = Column(String(128), default='', nullable=True)
269
+ user_identifier = Column(String(128), default='', nullable=True)
270
270
  message = Column(Text, nullable=False)
271
271
  rating = Column(Integer, nullable=False)
272
272
  created_at = Column(DateTime, default=datetime.now)
@@ -308,3 +308,27 @@ class Prompt(Base):
308
308
 
309
309
  company = relationship("Company", back_populates="prompts")
310
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,10 +72,14 @@ 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
77
- company.onboarding_cards = new_company.onboarding_cards
75
+ if company.parameters != new_company.parameters:
76
+ company.parameters = new_company.parameters
77
+ if company.branding != new_company.branding:
78
+ company.branding = new_company.branding
79
+ if company.onboarding_cards != new_company.onboarding_cards:
80
+ company.onboarding_cards = new_company.onboarding_cards
78
81
  else:
82
+ # Si la compañía no existe, la añade a la sesión.
79
83
  self.session.add(new_company)
80
84
  company = new_company
81
85