iatoolkit 0.59.2__tar.gz → 0.60.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 (120) hide show
  1. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/PKG-INFO +1 -1
  2. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/pyproject.toml +1 -1
  3. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/common/routes.py +12 -21
  4. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/iatoolkit.py +5 -5
  5. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/auth_service.py +32 -25
  6. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/profile_service.py +1 -0
  7. iatoolkit-0.59.2/src/iatoolkit/static/js/chat_feedback.js → iatoolkit-0.60.1/src/iatoolkit/static/js/chat_feedback_button.js +4 -7
  8. iatoolkit-0.60.1/src/iatoolkit/static/js/chat_logout_button.js +36 -0
  9. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/static/js/chat_main.js +0 -8
  10. iatoolkit-0.59.2/src/iatoolkit/static/js/chat_onboarding.js → iatoolkit-0.60.1/src/iatoolkit/static/js/chat_onboarding_button.js +0 -1
  11. iatoolkit-0.59.2/src/iatoolkit/static/js/chat_context_reload.js → iatoolkit-0.60.1/src/iatoolkit/static/js/chat_reload_button.js +2 -4
  12. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/static/styles/chat_iatoolkit.css +44 -0
  13. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/chat.html +18 -10
  14. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/onboarding_shell.html +1 -1
  15. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/change_password_view.py +2 -2
  16. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/external_login_view.py +5 -11
  17. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/file_store_api_view.py +7 -9
  18. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/history_api_view.py +8 -8
  19. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/init_context_api_view.py +2 -4
  20. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/llmquery_api_view.py +1 -3
  21. iatoolkit-0.60.1/src/iatoolkit/views/logout_api_view.py +45 -0
  22. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/prompt_api_view.py +5 -5
  23. iatoolkit-0.59.2/src/iatoolkit/views/tasks_view.py → iatoolkit-0.60.1/src/iatoolkit/views/tasks_api_view.py +10 -36
  24. iatoolkit-0.60.1/src/iatoolkit/views/tasks_review_api_view.py +55 -0
  25. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/user_feedback_api_view.py +6 -8
  26. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit.egg-info/PKG-INFO +1 -1
  27. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit.egg-info/SOURCES.txt +8 -7
  28. iatoolkit-0.59.2/src/iatoolkit/views/chat_token_request_view.py +0 -98
  29. iatoolkit-0.59.2/src/iatoolkit/views/tasks_review_view.py +0 -83
  30. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/readme.md +0 -0
  31. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/requirements.txt +0 -0
  32. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/setup.cfg +0 -0
  33. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/__init__.py +0 -0
  34. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/base_company.py +0 -0
  35. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/cli_commands.py +0 -0
  36. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/common/__init__.py +0 -0
  37. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/common/exceptions.py +0 -0
  38. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/common/session_manager.py +0 -0
  39. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/common/util.py +0 -0
  40. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/company_registry.py +0 -0
  41. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/__init__.py +0 -0
  42. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/call_service.py +0 -0
  43. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/connectors/__init__.py +0 -0
  44. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
  45. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
  46. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
  47. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
  48. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
  49. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
  50. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/gemini_adapter.py +0 -0
  51. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/google_chat_app.py +0 -0
  52. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/llm_client.py +0 -0
  53. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/llm_proxy.py +0 -0
  54. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/llm_response.py +0 -0
  55. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/mail_app.py +0 -0
  56. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/openai_adapter.py +0 -0
  57. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/infra/redis_session_manager.py +0 -0
  58. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/repositories/__init__.py +0 -0
  59. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/repositories/database_manager.py +0 -0
  60. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/repositories/document_repo.py +0 -0
  61. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
  62. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/repositories/models.py +0 -0
  63. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/repositories/profile_repo.py +0 -0
  64. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/repositories/tasks_repo.py +0 -0
  65. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/repositories/vs_repo.py +0 -0
  66. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/__init__.py +0 -0
  67. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/benchmark_service.py +0 -0
  68. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/branding_service.py +0 -0
  69. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/dispatcher_service.py +0 -0
  70. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/document_service.py +0 -0
  71. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/excel_service.py +0 -0
  72. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/file_processor_service.py +0 -0
  73. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/history_service.py +0 -0
  74. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/jwt_service.py +0 -0
  75. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/load_documents_service.py +0 -0
  76. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/mail_service.py +0 -0
  77. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/onboarding_service.py +0 -0
  78. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/prompt_manager_service.py +0 -0
  79. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/query_service.py +0 -0
  80. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/search_service.py +0 -0
  81. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/sql_service.py +0 -0
  82. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/tasks_service.py +0 -0
  83. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/user_feedback_service.py +0 -0
  84. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/services/user_session_context_service.py +0 -0
  85. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/static/images/fernando.jpeg +0 -0
  86. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/static/js/chat_filepond.js +0 -0
  87. /iatoolkit-0.59.2/src/iatoolkit/static/js/chat_history.js → /iatoolkit-0.60.1/src/iatoolkit/static/js/chat_history_button.js +0 -0
  88. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/static/styles/chat_info.css +0 -0
  89. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/static/styles/chat_modal.css +0 -0
  90. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/static/styles/landing_page.css +0 -0
  91. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/static/styles/llm_output.css +0 -0
  92. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/static/styles/onboarding.css +0 -0
  93. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/system_prompts/format_styles.prompt +0 -0
  94. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/system_prompts/query_main.prompt +0 -0
  95. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/system_prompts/sql_rules.prompt +0 -0
  96. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/_branding_styles.html +0 -0
  97. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/_login_widget.html +0 -0
  98. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/_navbar.html +0 -0
  99. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/about.html +0 -0
  100. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/base.html +0 -0
  101. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/change_password.html +0 -0
  102. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/chat_modals.html +0 -0
  103. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/error.html +0 -0
  104. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/forgot_password.html +0 -0
  105. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/header.html +0 -0
  106. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/index.html +0 -0
  107. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/login_simulation.html +0 -0
  108. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/signup.html +0 -0
  109. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/templates/test.html +0 -0
  110. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/__init__.py +0 -0
  111. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/base_login_view.py +0 -0
  112. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/forgot_password_view.py +0 -0
  113. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/index_view.py +0 -0
  114. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/login_simulation_view.py +0 -0
  115. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/login_view.py +0 -0
  116. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/signup_view.py +0 -0
  117. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit/views/verify_user_view.py +0 -0
  118. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
  119. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/src/iatoolkit.egg-info/requires.txt +0 -0
  120. {iatoolkit-0.59.2 → iatoolkit-0.60.1}/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.59.2
3
+ Version: 0.60.1
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.59.2"
7
+ version = "0.60.1"
8
8
  requires-python = ">=3.12"
9
9
  description = "IAToolkit"
10
10
  readme = "readme.md"
@@ -3,17 +3,9 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
- from flask import render_template, redirect, flash, url_for,send_from_directory, current_app, abort
7
- from iatoolkit.common.session_manager import SessionManager
6
+ from flask import render_template, redirect, url_for,send_from_directory, current_app, abort
8
7
  from flask import jsonify
9
8
  from iatoolkit.views.history_api_view import HistoryApiView
10
- import os
11
-
12
-
13
- def logout(company_short_name: str):
14
- SessionManager.clear()
15
- flash("Has cerrado sesión correctamente", "info")
16
- return redirect(url_for('index', company_short_name=company_short_name))
17
9
 
18
10
 
19
11
  # this function register all the views
@@ -22,8 +14,8 @@ def register_views(injector, app):
22
14
  from iatoolkit.views.index_view import IndexView
23
15
  from iatoolkit.views.init_context_api_view import InitContextApiView
24
16
  from iatoolkit.views.llmquery_api_view import LLMQueryApiView
25
- from iatoolkit.views.tasks_view import TaskView
26
- from iatoolkit.views.tasks_review_view import TaskReviewView
17
+ from iatoolkit.views.tasks_api_view import TaskApiView
18
+ from iatoolkit.views.tasks_review_api_view import TaskReviewApiView
27
19
  from iatoolkit.views.login_simulation_view import LoginSimulationView
28
20
  from iatoolkit.views.signup_view import SignupView
29
21
  from iatoolkit.views.verify_user_view import VerifyAccountView
@@ -32,9 +24,10 @@ def register_views(injector, app):
32
24
  from iatoolkit.views.file_store_api_view import FileStoreApiView
33
25
  from iatoolkit.views.user_feedback_api_view import UserFeedbackApiView
34
26
  from iatoolkit.views.prompt_api_view import PromptApiView
35
- from iatoolkit.views.chat_token_request_view import ChatTokenRequestView
36
27
  from iatoolkit.views.login_view import LoginView, FinalizeContextView
37
28
  from iatoolkit.views.external_login_view import ExternalLoginView, RedeemTokenApiView
29
+ from iatoolkit.views.logout_api_view import LogoutApiView
30
+
38
31
 
39
32
  # iatoolkit home page
40
33
  app.add_url_rule('/<company_short_name>', view_func=IndexView.as_view('index'))
@@ -57,23 +50,21 @@ def register_views(injector, app):
57
50
  view_func=FinalizeContextView.as_view('finalize_with_token')
58
51
  )
59
52
 
53
+ # logout
54
+ app.add_url_rule('/<company_short_name>/api/logout',
55
+ view_func=LogoutApiView.as_view('logout'))
56
+
60
57
  # this endpoint is called by the JS for changing the token for a session
61
58
  app.add_url_rule('/<string:company_short_name>/api/redeem_token',
62
59
  view_func = RedeemTokenApiView.as_view('redeem_token'))
63
60
 
64
- # this endpoint is for requesting a chat token for external users
65
- app.add_url_rule('/auth/chat_token',
66
- view_func=ChatTokenRequestView.as_view('chat-token'))
67
-
68
- # init (reset) the company context (with api-key)
61
+ # init (reset) the company context
69
62
  app.add_url_rule('/<company_short_name>/api/init-context',
70
63
  view_func=InitContextApiView.as_view('init-context'),
71
64
  methods=['POST', 'OPTIONS'])
72
65
 
73
66
  # register new user, account verification and forgot password
74
67
  app.add_url_rule('/<company_short_name>/signup',view_func=SignupView.as_view('signup'))
75
- app.add_url_rule('/<company_short_name>/logout', 'logout', logout)
76
- app.add_url_rule('/logout', 'logout', logout)
77
68
  app.add_url_rule('/<company_short_name>/verify/<token>', view_func=VerifyAccountView.as_view('verify_account'))
78
69
  app.add_url_rule('/<company_short_name>/forgot-password', view_func=ForgotPasswordView.as_view('forgot_password'))
79
70
  app.add_url_rule('/<company_short_name>/change-password/<token>', view_func=ChangePasswordView.as_view('change_password'))
@@ -90,8 +81,8 @@ def register_views(injector, app):
90
81
  app.add_url_rule('/<company_short_name>/api/history', view_func=HistoryApiView.as_view('history'))
91
82
 
92
83
  # tasks management endpoints: create task, and review answer
93
- app.add_url_rule('/tasks', view_func=TaskView.as_view('tasks'))
94
- app.add_url_rule('/tasks/review/<int:task_id>', view_func=TaskReviewView.as_view('tasks-review'))
84
+ app.add_url_rule('/tasks', view_func=TaskApiView.as_view('tasks'))
85
+ app.add_url_rule('/tasks/review/<int:task_id>', view_func=TaskReviewApiView.as_view('tasks-review'))
95
86
 
96
87
  # this endpoint is for upload documents into the vector store (api-key)
97
88
  app.add_url_rule('/api/load', view_func=FileStoreApiView.as_view('load_api'))
@@ -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.59.2"
22
+ IATOOLKIT_VERSION = "0.60.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':
@@ -324,8 +324,8 @@ class IAToolkit:
324
324
  from iatoolkit.services.auth_service import AuthService
325
325
  from iatoolkit.common.util import Utility
326
326
 
327
- binder.bind(LLMProxy, to=LLMProxy, scope=singleton)
328
- binder.bind(llmClient, to=llmClient, scope=singleton)
327
+ binder.bind(LLMProxy, to=LLMProxy)
328
+ binder.bind(llmClient, to=llmClient)
329
329
  binder.bind(GoogleChatApp, to=GoogleChatApp)
330
330
  binder.bind(MailApp, to=MailApp)
331
331
  binder.bind(AuthService, to=AuthService)
@@ -91,9 +91,10 @@ class AuthService:
91
91
  )
92
92
  return {'success': False, 'error': 'No se pudo crear la sesión del usuario'}
93
93
 
94
- def verify(self) -> dict:
94
+ def verify(self, anonymous: bool = False) -> dict:
95
95
  """
96
96
  Verifies the current request and identifies the user.
97
+ If anonymous is True the non-presence of use_identifier is ignored
97
98
 
98
99
  Returns a dictionary with:
99
100
  - success: bool
@@ -118,31 +119,37 @@ class AuthService:
118
119
  if isinstance(auth, str) and auth.lower().startswith('bearer '):
119
120
  api_key = auth.split(' ', 1)[1].strip()
120
121
 
121
- if api_key:
122
- api_key_entry = self.profile_service.get_active_api_key_entry(api_key)
123
- if not api_key_entry:
124
- logging.info(f"Invalid or inactive API Key {api_key}")
125
- return {"success": False, "error_message": "Invalid or inactive API Key", "status_code": 401}
122
+ if not api_key:
123
+ # --- Failure: No valid credentials found ---
124
+ logging.info(f"Authentication required. No session cookie or API Key provided.")
125
+ return {"success": False,
126
+ "error_message": "Authentication required. No session cookie or API Key provided.",
127
+ "status_code": 401}
128
+
129
+ # check if the api-key is valid and active
130
+ api_key_entry = self.profile_service.get_active_api_key_entry(api_key)
131
+ if not api_key_entry:
132
+ logging.info(f"Invalid or inactive API Key {api_key}")
133
+ return {"success": False, "error_message": "Invalid or inactive API Key",
134
+ "status_code": 402}
135
+
136
+ # get the company from the api_key_entry
137
+ company = api_key_entry.company
138
+
139
+ # For API calls, the external_user_id must be provided in the request.
140
+ data = request.get_json(silent=True) or {}
141
+ user_identifier = data.get('user_identifier', '')
142
+ if not anonymous and not user_identifier:
143
+ logging.info(f"No user_identifier provided for API call.")
144
+ return {"success": False, "error_message": "No user_identifier provided for API call.",
145
+ "status_code": 403}
146
+
147
+ return {
148
+ "success": True,
149
+ "company_short_name": company.short_name,
150
+ "user_identifier": user_identifier
151
+ }
126
152
 
127
- # obtain the company from the api_key_entry
128
- company = api_key_entry.company
129
-
130
- # For API calls, the external_user_id must be provided in the request.
131
- user_identifier = ''
132
- if request.is_json:
133
- data = request.get_json() or {}
134
- user_identifier = data.get('user_identifier', '')
135
-
136
- return {
137
- "success": True,
138
- "company_short_name": company.short_name,
139
- "user_identifier": user_identifier
140
- }
141
-
142
- # --- Failure: No valid credentials found ---
143
- logging.info(f"Authentication required. No session cookie or API Key provided. session: {str(session_info)}")
144
- return {"success": False, "error_message": "Authentication required. No session cookie or API Key provided.",
145
- "status_code": 402}
146
153
 
147
154
  def log_access(self,
148
155
  company_short_name: str,
@@ -131,6 +131,7 @@ class ProfileService:
131
131
  "profile": profile
132
132
  }
133
133
 
134
+
134
135
  def get_profile_by_identifier(self, company_short_name: str, user_identifier: str) -> dict:
135
136
  """
136
137
  Fetches a user profile directly by their identifier, bypassing the Flask session.
@@ -42,14 +42,11 @@ $(document).ready(function () {
42
42
  submitButton.html('<i class="bi bi-send me-1 icon-spaced"></i>Enviando...');
43
43
 
44
44
  const response = await sendFeedback(feedbackText);
45
-
46
45
  $('#feedbackModal').modal('hide');
47
-
48
- if (response) {
49
- Swal.fire({ icon: 'success', title: 'Feedback enviado', text: 'Gracias por tu comentario.' });
50
- } else {
51
- Swal.fire({ icon: 'error', title: 'Error', text: 'No se pudo enviar el feedback, intente nuevamente.' });
52
- }
46
+ if (response)
47
+ toastr.success('¡Gracias por tu comentario!', 'Feedback Enviado');
48
+ else
49
+ toastr.error('No se pudo enviar el feedback, por favor intente nuevamente.', 'Error');
53
50
  });
54
51
 
55
52
  // Evento para abrir el modal de feedback
@@ -0,0 +1,36 @@
1
+ document.addEventListener('DOMContentLoaded', function() {
2
+ const logoutButton = document.getElementById('logout-button');
3
+ if (!logoutButton) {
4
+ console.warn('El botón de logout con id "logout-button" no fue encontrado.');
5
+ return;
6
+ }
7
+
8
+ if (window.toastr) {
9
+ toastr.options = { "positionClass": "toast-bottom-right", "preventDuplicates": true };
10
+ }
11
+
12
+ logoutButton.addEventListener('click', async function(event) {
13
+ event.preventDefault();
14
+
15
+ try {
16
+ const apiPath = '/api/logout';
17
+ const data = await callToolkit(apiPath, null, 'GET');
18
+
19
+ // Procesar la respuesta
20
+ if (data && data.status === 'success' && data.url) {
21
+ window.top.location.href = data.url;
22
+ } else {
23
+ // Si algo falla, callToolkit usualmente muestra un error.
24
+ // Mostramos un toast como fallback.
25
+ if (window.toastr) {
26
+ toastr.error('No se pudo procesar el cierre de sesión. Por favor, intente de nuevo.');
27
+ }
28
+ }
29
+ } catch (error) {
30
+ console.error('Error durante el logout:', error);
31
+ if (window.toastr) {
32
+ toastr.error('Ocurrió un error de red al intentar cerrar sesión.');
33
+ }
34
+ }
35
+ });
36
+ });
@@ -1,7 +1,6 @@
1
1
  // Global variables for request management
2
2
  let isRequestInProgress = false;
3
3
  let abortController = null;
4
-
5
4
  let selectedPrompt = null; // Will hold a lightweight prompt object
6
5
 
7
6
  $(document).ready(function () {
@@ -325,13 +324,6 @@ const callToolkit = async function(apiPath, data, method, timeoutMs = 500000) {
325
324
  clearTimeout(timeoutId);
326
325
 
327
326
  if (!response.ok) {
328
- if (response.status === 401) {
329
- const errorMessage = `Tu sesión ha expirado. `;
330
- const errorIcon = '<i class="bi bi-exclamation-triangle"></i>';
331
- const infrastructureError = $('<div>').addClass('error-section').html(errorIcon + `<p>${errorMessage}</p>`);
332
- displayBotMessage(infrastructureError);
333
- return null;
334
- }
335
327
  try {
336
328
  // Intentamos leer el error como JSON, que es el formato esperado de nuestra API.
337
329
  const errorData = await response.json();
@@ -1,4 +1,3 @@
1
- // static/js/chat_onboarding.js
2
1
  (function (global) {
3
2
  function qs(root, sel) { return (typeof sel === 'string') ? root.querySelector(sel) : sel; }
4
3
 
@@ -30,12 +30,10 @@ document.addEventListener('DOMContentLoaded', function() {
30
30
  // 4. Procesar la respuesta
31
31
  // callToolkit devuelve null si hubo un error que ya mostró en el chat.
32
32
  if (data) {
33
- if (data.status === 'OK') {
33
+ if (data.status === 'OK')
34
34
  toastr.success(data.message || 'Contexto recargado exitosamente.');
35
- } else {
36
- // El servidor respondió 200 OK pero con un mensaje de error en el cuerpo
35
+ else
37
36
  toastr.error(data.error_message || 'Ocurrió un error desconocido durante la recarga.');
38
- }
39
37
  } else {
40
38
  // Si data es null, callToolkit ya manejó el error (mostrando un mensaje en el chat).
41
39
  // Añadimos un toast para notificar al usuario que algo falló.
@@ -455,3 +455,47 @@
455
455
  #send-button i {
456
456
  color: var(--brand-send-button-color);
457
457
  }
458
+
459
+ /* Estilos personalizados para Toastr usando las variables de Branding */
460
+
461
+ /* --- Toast de Información (Usa el color primario de la marca) --- */
462
+ .toast-info {
463
+ /* ¡Importante! Usamos las variables CSS de tu BrandingService */
464
+ background-color: var(--brand-primary-color) !important;
465
+ color: var(--brand-text-on-primary) !important;
466
+ opacity: 0.95 !important;
467
+ }
468
+ .toast-info .toast-progress {
469
+ /* Usamos un color ligeramente más oscuro o una variante,
470
+ pero para simplificar, podemos empezar con el mismo */
471
+ background-color: rgba(0, 0, 0, 0.2) !important;
472
+ }
473
+
474
+ /* --- Toast de Éxito (Usa el verde por defecto o uno de marca si lo defines) --- */
475
+ .toast-success {
476
+ background-color: #198754 !important; /* Puedes crear una variable --brand-success-color si quieres */
477
+ color: #ffffff !important;
478
+ opacity: 0.95 !important;
479
+ }
480
+ .toast-success .toast-progress {
481
+ background-color: rgba(0, 0, 0, 0.2) !important;
482
+ }
483
+
484
+ /* --- Toast de Error (Usa el color de peligro de la marca) --- */
485
+ .toast-error {
486
+ background-color: var(--brand-danger-color) !important;
487
+ color: var(--brand-text-on-primary) !important; /* Asumimos texto blanco sobre el color de peligro */
488
+ opacity: 0.95 !important;
489
+ }
490
+ .toast-error .toast-progress {
491
+ background-color: rgba(0, 0, 0, 0.2) !important;
492
+ }
493
+
494
+ /* Opcional: Estilo para el botón de cierre para que contraste bien */
495
+ .toast-close-button {
496
+ color: var(--brand-text-on-primary) !important;
497
+ text-shadow: none !important;
498
+ }
499
+ .toast-close-button:hover {
500
+ opacity: 0.8;
501
+ }
@@ -40,7 +40,8 @@
40
40
  <div class="vr mx-3"></div>
41
41
 
42
42
  <!-- 3. Iconos de Acción -->
43
- <a href="javascript:void(0);" id="history-button"
43
+ <a href="javascript:void(0);"
44
+ id="history-button"
44
45
  class="action-icon-style" title="Historial con mis consultas" style="color: {{ branding.header_text_color }};">
45
46
  <i class="bi bi-clock-history"></i>
46
47
  </a>
@@ -51,19 +52,23 @@
51
52
  style="color: {{ branding.header_text_color }};">
52
53
  <i class="bi bi-arrow-clockwise"></i>
53
54
  </a>
54
- <a href="javascript:void(0);" id="send-feedback-button"
55
+ <a href="javascript:void(0);"
56
+ id="send-feedback-button"
55
57
  class="ms-3 action-icon-style" title="Tu feedback es muy importante" style="color: {{ branding.header_text_color }};">
56
58
  <i class="bi bi-emoji-smile"></i>
57
59
  </a>
58
- <a href="javascript:void(0);" id="onboarding-button"
60
+ <a href="javascript:void(0);"
61
+ id="onboarding-button"
59
62
  class="ms-3 action-icon-style" title="Ver onboarding"
60
63
  style="color: {{ branding.header_text_color }};">
61
64
  <i class="bi bi-lightbulb"></i>
62
65
  </a>
63
66
 
64
67
  <!-- Icono de cerrar sesión (al final) -->
65
- <a href="{{ url_for('logout', company_short_name=company_short_name, _external=True) }}"
66
- class="ms-3 action-icon-style" title="Cerrar sesión" style="color: {{ branding.header_text_color }} !important;">
68
+ <a href="javascript:void(0);"
69
+ id="logout-button"
70
+ class="ms-3 action-icon-style" title="Cerrar sesión"
71
+ style="color: {{ branding.header_text_color }}; !important;">
67
72
  <i class="bi bi-box-arrow-right"></i>
68
73
  </a>
69
74
  </div>
@@ -192,13 +197,16 @@
192
197
  </script>
193
198
 
194
199
  <!-- Carga de los scripts JS externos después de definir las variables globales -->
195
- <script src="{{ url_for('static', filename='js/chat_onboarding.js', _external=True) }}"></script>
200
+ <script src="{{ url_for('static', filename='js/chat_onboarding_button.js', _external=True) }}"></script>
196
201
  <script src="{{ url_for('static', filename='js/chat_filepond.js', _external=True) }}"></script>
197
- <script src="{{ url_for('static', filename='js/chat_history.js', _external=True) }}"></script>
198
- <script src="{{ url_for('static', filename='js/chat_feedback.js', _external=True) }}"></script>
199
- <script src="{{ url_for('static', filename='js/chat_context_reload.js', _external=True) }}"></script><script src="{{ url_for('static', filename='js/chat_main.js', _external=True) }}"></script>
202
+ <script src="{{ url_for('static', filename='js/chat_history_button.js', _external=True) }}"></script>
203
+ <script src="{{ url_for('static', filename='js/chat_feedback_button.js', _external=True) }}"></script>
204
+ <script src="{{ url_for('static', filename='js/chat_reload_button.js', _external=True) }}"></script>
205
+ <script src="{{ url_for('static', filename='js/chat_main.js', _external=True) }}"></script>
206
+ <script src="{{ url_for('static', filename='js/chat_logout_button.js', _external=True) }}"></script>
207
+ <script src="{{ url_for('static', filename='js/chat_main.js', _external=True) }}"></script>
200
208
 
201
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css">
209
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css">
202
210
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
203
211
  <script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
204
212
 
@@ -71,7 +71,7 @@
71
71
  {% block scripts %}
72
72
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
73
73
 
74
- <script src="{{ url_for('static', filename='js/chat_onboarding.js', _external=True) }}"></script>
74
+ <script src="{{ url_for('static', filename='js/chat_onboarding_button.js', _external=True) }}"></script>
75
75
  <script>
76
76
  (function() {
77
77
  const cardsData = {{ onboarding_cards | tojson }};
@@ -37,8 +37,8 @@ class ChangePasswordView(MethodView):
37
37
  email = self.serializer.loads(token, salt='password-reset', max_age=3600)
38
38
  except SignatureExpired as e:
39
39
  return render_template('forgot_password.html',
40
- branding=branding_data,
41
- alert_message="El enlace de cambio de contraseña ha expirado. Por favor, solicita uno nuevo.")
40
+ branding=branding_data,
41
+ alert_message="El enlace de cambio de contraseña ha expirado. Por favor, solicita uno nuevo.")
42
42
 
43
43
  return render_template('change_password.html',
44
44
  company_short_name=company_short_name,
@@ -15,22 +15,16 @@ class ExternalLoginView(BaseLoginView):
15
15
  Authenticates and then delegates the path decision (fast/slow) to the base class.
16
16
  """
17
17
  def post(self, company_short_name: str):
18
- data = request.get_json()
19
- if not data or 'user_identifier' not in data:
20
- return jsonify({"error": "Falta user_identifier"}), 400
18
+ # Authenticate the API call.
19
+ auth_result = self.auth_service.verify()
20
+ if not auth_result.get("success"):
21
+ return jsonify(auth_result), auth_result.get("status_code")
21
22
 
22
23
  company = self.profile_service.get_company_by_short_name(company_short_name)
23
24
  if not company:
24
25
  return jsonify({"error": "Empresa no encontrada"}), 404
25
26
 
26
- user_identifier = data.get('user_identifier')
27
- if not user_identifier:
28
- return jsonify({"error": "missing user_identifier"}), 404
29
-
30
- # 1. Authenticate the API call.
31
- auth_response = self.auth_service.verify()
32
- if not auth_response.get("success"):
33
- return jsonify(auth_response), 401
27
+ user_identifier = auth_result.get('user_identifier')
34
28
 
35
29
  # 2. Create the external user session.
36
30
  self.profile_service.create_external_user_profile_context(company, user_identifier)
@@ -15,24 +15,27 @@ import base64
15
15
  class FileStoreApiView(MethodView):
16
16
  @inject
17
17
  def __init__(self,
18
- iauthentication: AuthService,
18
+ auth_service: AuthService,
19
19
  doc_service: LoadDocumentsService,
20
20
  profile_repo: ProfileRepo,):
21
- self.iauthentication = iauthentication
21
+ self.auth_service = auth_service
22
22
  self.doc_service = doc_service
23
23
  self.profile_repo = profile_repo
24
24
 
25
25
  def post(self):
26
26
  try:
27
- req_data = request.get_json()
27
+ # 1. Authenticate the API request.
28
+ auth_result = self.auth_service.verify()
29
+ if not auth_result.get("success"):
30
+ return jsonify(auth_result), auth_result.get("status_code")
28
31
 
32
+ req_data = request.get_json()
29
33
  required_fields = ['company', 'filename', 'content']
30
34
  for field in required_fields:
31
35
  if field not in req_data:
32
36
  return jsonify({"error": f"El campo {field} es requerido"}), 400
33
37
 
34
38
  company_short_name = req_data.get('company', '')
35
- requested_name = req_data.get('username', 'external_user')
36
39
  filename = req_data.get('filename', False)
37
40
  base64_content = req_data.get('content', '')
38
41
  metadata = req_data.get('metadata', {})
@@ -42,11 +45,6 @@ class FileStoreApiView(MethodView):
42
45
  if not company:
43
46
  return jsonify({"error": f"La empresa {company_short_name} no existe"}), 400
44
47
 
45
- # get access credentials
46
- iaut = self.iauthentication.verify()
47
- if not iaut.get("success"):
48
- return jsonify(iaut), 401
49
-
50
48
  # get the file content from base64
51
49
  content = base64.b64decode(base64_content)
52
50
 
@@ -6,7 +6,7 @@
6
6
  from flask import request, jsonify
7
7
  from flask.views import MethodView
8
8
  from iatoolkit.services.history_service import HistoryService
9
- from iatoolkit.services.profile_service import ProfileService
9
+ from iatoolkit.services.auth_service import AuthService
10
10
  from injector import inject
11
11
  import logging
12
12
 
@@ -19,18 +19,18 @@ class HistoryApiView(MethodView):
19
19
 
20
20
  @inject
21
21
  def __init__(self,
22
- profile_service: ProfileService,
22
+ auth_service: AuthService,
23
23
  history_service: HistoryService):
24
- self.profile_service = profile_service
24
+ self.auth_service = auth_service
25
25
  self.history_service = history_service
26
26
 
27
27
  def post(self, company_short_name: str):
28
- # 1. Get the authenticated user's info from the unified session.
29
- session_info = self.profile_service.get_current_session_info()
30
- user_identifier = session_info.get("user_identifier")
28
+ # 1. Get the authenticated user's
29
+ auth_result = self.auth_service.verify()
30
+ if not auth_result.get("success"):
31
+ return jsonify(auth_result), auth_result.get("status_code")
31
32
 
32
- if not user_identifier:
33
- return jsonify({'error_message': 'Usuario no autenticado o sesión inválida'}), 401
33
+ user_identifier = auth_result.get('user_identifier')
34
34
 
35
35
  try:
36
36
  # 2. Call the history service with the unified identifier.
@@ -31,11 +31,9 @@ class InitContextApiView(MethodView):
31
31
  # 1. Authenticate the request. This handles both session and API Key.
32
32
  auth_result = self.auth_service.verify()
33
33
  if not auth_result.get("success"):
34
- return jsonify({"error": auth_result.get("error_message")}), auth_result.get("status_code", 401)
34
+ return jsonify(auth_result), auth_result.get("status_code")
35
35
 
36
36
  user_identifier = auth_result.get('user_identifier')
37
- if not user_identifier:
38
- return jsonify({"error": "Could not identify user from session or payload"}), 400
39
37
 
40
38
  try:
41
39
  # 2. Execute the forced rebuild sequence using the unified identifier.
@@ -54,7 +52,7 @@ class InitContextApiView(MethodView):
54
52
  )
55
53
 
56
54
  # 3. Respond with JSON, as this is an API endpoint.
57
- return jsonify({'status': 'OK', 'message': 'El context se ha recargado con éxito.'}), 200
55
+ return jsonify({'status': 'OK', 'message': 'El contexto se ha recargado con éxito.'}), 200
58
56
 
59
57
  except Exception as e:
60
58
  logging.exception(f"Error durante la recarga de contexto {user_identifier}: {e}")
@@ -21,12 +21,10 @@ class LLMQueryApiView(MethodView):
21
21
  # 1. Authenticate the API request.
22
22
  auth_result = self.auth_service.verify()
23
23
  if not auth_result.get("success"):
24
- return jsonify({"error": auth_result.get("error_message")}), auth_result.get("status_code", 401)
24
+ return jsonify(auth_result), auth_result.get("status_code")
25
25
 
26
26
  # 2. Get the user identifier from the payload.
27
27
  user_identifier = auth_result.get('user_identifier')
28
- if not user_identifier:
29
- return jsonify({"error": "Payload must include 'user_identifier'"}), 400
30
28
 
31
29
  data = request.get_json()
32
30
  if not data:
@@ -0,0 +1,45 @@
1
+ # Copyright (c) 2024 Fernando Libedinsky
2
+ # Product: IAToolkit
3
+ #
4
+ # IAToolkit is open source software.
5
+
6
+ from flask.views import MethodView
7
+ from flask import redirect, url_for, jsonify
8
+ from injector import inject
9
+ from iatoolkit.services.auth_service import AuthService
10
+ from iatoolkit.services.profile_service import ProfileService
11
+ from iatoolkit.common.session_manager import SessionManager
12
+
13
+
14
+ class LogoutApiView(MethodView):
15
+ @inject
16
+ def __init__(self,
17
+ profile_service: ProfileService,
18
+ auth_service: AuthService):
19
+ self.profile_service = profile_service
20
+ self.auth_service = auth_service
21
+
22
+ def get(self, company_short_name: str = None):
23
+ # 1. Get the authenticated user's
24
+ auth_result = self.auth_service.verify(anonymous=True)
25
+ if not auth_result.get("success"):
26
+ return jsonify(auth_result), auth_result.get("status_code", 401)
27
+
28
+ company = self.profile_service.get_company_by_short_name(company_short_name)
29
+ if not company:
30
+ return jsonify({"error": "Empresa no encontrada"}), 404
31
+
32
+ # get URL for redirection
33
+ url_for_redirect = company.parameters.get('external_urls', {}).get('logout_url')
34
+ if not url_for_redirect:
35
+ url_for_redirect = url_for('index', company_short_name=company_short_name)
36
+
37
+ # clear de session cookie
38
+ SessionManager.clear()
39
+
40
+ return {
41
+ 'status': 'success',
42
+ 'url': url_for_redirect,
43
+ }
44
+
45
+