iatoolkit 0.8.1__py3-none-any.whl → 0.63.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of iatoolkit might be problematic. Click here for more details.
- iatoolkit/__init__.py +8 -34
- iatoolkit/base_company.py +14 -3
- iatoolkit/common/routes.py +83 -52
- iatoolkit/common/session_manager.py +0 -1
- iatoolkit/common/util.py +0 -27
- iatoolkit/iatoolkit.py +61 -46
- iatoolkit/infra/llm_client.py +7 -8
- iatoolkit/infra/openai_adapter.py +1 -1
- iatoolkit/infra/redis_session_manager.py +48 -2
- iatoolkit/repositories/database_manager.py +17 -2
- iatoolkit/repositories/models.py +31 -6
- iatoolkit/repositories/profile_repo.py +7 -2
- iatoolkit/services/auth_service.py +188 -0
- iatoolkit/services/branding_service.py +147 -0
- iatoolkit/services/dispatcher_service.py +10 -40
- iatoolkit/services/excel_service.py +15 -15
- iatoolkit/services/history_service.py +3 -12
- iatoolkit/services/jwt_service.py +15 -24
- iatoolkit/services/onboarding_service.py +43 -0
- iatoolkit/services/profile_service.py +97 -44
- iatoolkit/services/query_service.py +124 -81
- iatoolkit/services/tasks_service.py +1 -1
- iatoolkit/services/user_feedback_service.py +67 -31
- iatoolkit/services/user_session_context_service.py +112 -54
- iatoolkit/static/images/fernando.jpeg +0 -0
- iatoolkit/static/js/{chat_feedback.js → chat_feedback_button.js} +6 -11
- iatoolkit/static/js/chat_history_button.js +126 -0
- iatoolkit/static/js/chat_logout_button.js +36 -0
- iatoolkit/static/js/chat_main.js +130 -220
- iatoolkit/static/js/chat_onboarding_button.js +97 -0
- iatoolkit/static/js/chat_prompt_manager.js +94 -0
- iatoolkit/static/js/chat_reload_button.js +52 -0
- iatoolkit/static/styles/chat_iatoolkit.css +329 -507
- iatoolkit/static/styles/chat_modal.css +95 -56
- iatoolkit/static/styles/landing_page.css +182 -0
- iatoolkit/static/styles/onboarding.css +169 -0
- iatoolkit/system_prompts/query_main.prompt +3 -12
- iatoolkit/templates/_company_header.html +20 -0
- iatoolkit/templates/_login_widget.html +40 -0
- iatoolkit/templates/base.html +8 -3
- iatoolkit/templates/change_password.html +54 -37
- iatoolkit/templates/chat.html +149 -66
- iatoolkit/templates/chat_modals.html +47 -18
- iatoolkit/templates/error.html +41 -8
- iatoolkit/templates/forgot_password.html +37 -24
- iatoolkit/templates/index.html +140 -0
- iatoolkit/templates/login_simulation.html +34 -0
- iatoolkit/templates/onboarding_shell.html +105 -0
- iatoolkit/templates/signup.html +64 -66
- iatoolkit/views/base_login_view.py +81 -0
- iatoolkit/views/change_password_view.py +23 -12
- iatoolkit/views/external_login_view.py +61 -28
- iatoolkit/views/{file_store_view.py → file_store_api_view.py} +9 -2
- iatoolkit/views/forgot_password_view.py +23 -13
- iatoolkit/views/history_api_view.py +52 -0
- iatoolkit/views/home_view.py +58 -25
- iatoolkit/views/index_view.py +14 -0
- iatoolkit/views/init_context_api_view.py +68 -0
- iatoolkit/views/llmquery_api_view.py +45 -0
- iatoolkit/views/login_simulation_view.py +81 -0
- iatoolkit/views/login_view.py +118 -34
- iatoolkit/views/logout_api_view.py +45 -0
- iatoolkit/views/{prompt_view.py → prompt_api_view.py} +7 -7
- iatoolkit/views/signup_view.py +38 -29
- iatoolkit/views/{tasks_view.py → tasks_api_view.py} +10 -36
- iatoolkit/views/tasks_review_api_view.py +55 -0
- iatoolkit/views/{user_feedback_view.py → user_feedback_api_view.py} +16 -31
- iatoolkit/views/verify_user_view.py +13 -8
- {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/METADATA +2 -2
- iatoolkit-0.63.4.dist-info/RECORD +113 -0
- {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/top_level.txt +0 -1
- iatoolkit/common/auth.py +0 -200
- iatoolkit/static/images/arrow_up.png +0 -0
- iatoolkit/static/images/diagrama_iatoolkit.jpg +0 -0
- iatoolkit/static/images/logo_clinica.png +0 -0
- iatoolkit/static/images/logo_iatoolkit.png +0 -0
- iatoolkit/static/images/logo_maxxa.png +0 -0
- iatoolkit/static/images/logo_notaria.png +0 -0
- iatoolkit/static/images/logo_tarjeta.png +0 -0
- iatoolkit/static/images/logo_umayor.png +0 -0
- iatoolkit/static/images/upload.png +0 -0
- iatoolkit/static/js/chat_history.js +0 -117
- iatoolkit/templates/home.html +0 -201
- iatoolkit/templates/login.html +0 -43
- iatoolkit/views/chat_token_request_view.py +0 -98
- iatoolkit/views/chat_view.py +0 -51
- iatoolkit/views/download_file_view.py +0 -58
- iatoolkit/views/external_chat_login_view.py +0 -88
- iatoolkit/views/history_view.py +0 -57
- iatoolkit/views/llmquery_view.py +0 -65
- iatoolkit/views/tasks_review_view.py +0 -83
- iatoolkit-0.8.1.dist-info/RECORD +0 -175
- tests/__init__.py +0 -5
- tests/common/__init__.py +0 -0
- tests/common/test_auth.py +0 -279
- tests/common/test_routes.py +0 -42
- tests/common/test_session_manager.py +0 -59
- tests/common/test_util.py +0 -444
- tests/companies/__init__.py +0 -5
- tests/conftest.py +0 -36
- tests/infra/__init__.py +0 -5
- tests/infra/connectors/__init__.py +0 -5
- tests/infra/connectors/test_google_drive_connector.py +0 -107
- tests/infra/connectors/test_local_file_connector.py +0 -85
- tests/infra/connectors/test_s3_connector.py +0 -95
- tests/infra/test_call_service.py +0 -92
- tests/infra/test_database_manager.py +0 -59
- tests/infra/test_gemini_adapter.py +0 -137
- tests/infra/test_google_chat_app.py +0 -68
- tests/infra/test_llm_client.py +0 -165
- tests/infra/test_llm_proxy.py +0 -122
- tests/infra/test_mail_app.py +0 -94
- tests/infra/test_openai_adapter.py +0 -105
- tests/infra/test_redis_session_manager_service.py +0 -117
- tests/repositories/__init__.py +0 -5
- tests/repositories/test_database_manager.py +0 -87
- tests/repositories/test_document_repo.py +0 -76
- tests/repositories/test_llm_query_repo.py +0 -340
- tests/repositories/test_models.py +0 -38
- tests/repositories/test_profile_repo.py +0 -142
- tests/repositories/test_tasks_repo.py +0 -76
- tests/repositories/test_vs_repo.py +0 -107
- tests/services/__init__.py +0 -5
- tests/services/test_dispatcher_service.py +0 -274
- tests/services/test_document_service.py +0 -181
- tests/services/test_excel_service.py +0 -208
- tests/services/test_file_processor_service.py +0 -121
- tests/services/test_history_service.py +0 -164
- tests/services/test_jwt_service.py +0 -255
- tests/services/test_load_documents_service.py +0 -112
- tests/services/test_mail_service.py +0 -70
- tests/services/test_profile_service.py +0 -379
- tests/services/test_prompt_manager_service.py +0 -190
- tests/services/test_query_service.py +0 -243
- tests/services/test_search_service.py +0 -39
- tests/services/test_sql_service.py +0 -160
- tests/services/test_tasks_service.py +0 -252
- tests/services/test_user_feedback_service.py +0 -389
- tests/services/test_user_session_context_service.py +0 -132
- tests/views/__init__.py +0 -5
- tests/views/test_change_password_view.py +0 -191
- tests/views/test_chat_token_request_view.py +0 -188
- tests/views/test_chat_view.py +0 -98
- tests/views/test_download_file_view.py +0 -149
- tests/views/test_external_chat_login_view.py +0 -120
- tests/views/test_external_login_view.py +0 -102
- tests/views/test_file_store_view.py +0 -128
- tests/views/test_forgot_password_view.py +0 -142
- tests/views/test_history_view.py +0 -336
- tests/views/test_home_view.py +0 -61
- tests/views/test_llm_query_view.py +0 -154
- tests/views/test_login_view.py +0 -114
- tests/views/test_prompt_view.py +0 -111
- tests/views/test_signup_view.py +0 -140
- tests/views/test_tasks_review_view.py +0 -104
- tests/views/test_tasks_view.py +0 -130
- tests/views/test_user_feedback_view.py +0 -214
- tests/views/test_verify_user_view.py +0 -110
- {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/WHEEL +0 -0
iatoolkit/__init__.py
CHANGED
|
@@ -4,37 +4,22 @@ IAToolkit Package
|
|
|
4
4
|
|
|
5
5
|
# Expose main classes and functions at the top level of the package
|
|
6
6
|
|
|
7
|
-
#
|
|
8
|
-
from .iatoolkit import IAToolkit, create_app
|
|
9
|
-
from .iatoolkit import current_iatoolkit
|
|
7
|
+
# main IAToolkit class
|
|
8
|
+
from .iatoolkit import IAToolkit, current_iatoolkit, create_app
|
|
10
9
|
|
|
11
|
-
#
|
|
10
|
+
# for registering the client companies
|
|
12
11
|
from .company_registry import register_company
|
|
13
|
-
|
|
14
|
-
# Assuming 'base_company.py' contains BaseCompany
|
|
15
12
|
from .base_company import BaseCompany
|
|
13
|
+
from iatoolkit.repositories.database_manager import DatabaseManager
|
|
16
14
|
|
|
17
15
|
# --- Services ---
|
|
18
|
-
|
|
16
|
+
from iatoolkit.services.query_service import QueryService
|
|
19
17
|
from iatoolkit.services.sql_service import SqlService
|
|
20
|
-
from iatoolkit.services.excel_service import ExcelService
|
|
21
|
-
from iatoolkit.services.dispatcher_service import Dispatcher
|
|
22
18
|
from iatoolkit.services.document_service import DocumentService
|
|
23
19
|
from iatoolkit.services.search_service import SearchService
|
|
24
20
|
from iatoolkit.services.load_documents_service import LoadDocumentsService
|
|
25
|
-
from iatoolkit.
|
|
26
|
-
from iatoolkit.repositories.llm_query_repo import LLMQueryRepo
|
|
27
|
-
|
|
28
|
-
from iatoolkit.services.query_service import QueryService
|
|
29
|
-
from iatoolkit.services.prompt_manager_service import PromptService
|
|
30
|
-
from iatoolkit.repositories.database_manager import DatabaseManager
|
|
31
|
-
|
|
21
|
+
from iatoolkit.services.excel_service import ExcelService
|
|
32
22
|
from iatoolkit.infra.call_service import CallServiceClient
|
|
33
|
-
from iatoolkit.common.util import Utility
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
from iatoolkit.repositories.models import Base, Company, Function, TaskType, Prompt, PromptCategory
|
|
37
|
-
|
|
38
23
|
|
|
39
24
|
__all__ = [
|
|
40
25
|
'IAToolkit',
|
|
@@ -42,23 +27,12 @@ __all__ = [
|
|
|
42
27
|
'current_iatoolkit',
|
|
43
28
|
'register_company',
|
|
44
29
|
'BaseCompany',
|
|
30
|
+
'DatabaseManager',
|
|
31
|
+
'QueryService',
|
|
45
32
|
'SqlService',
|
|
46
33
|
'ExcelService',
|
|
47
|
-
'Dispatcher',
|
|
48
34
|
'DocumentService',
|
|
49
35
|
'SearchService',
|
|
50
|
-
'QueryService',
|
|
51
36
|
'LoadDocumentsService',
|
|
52
|
-
'ProfileRepo',
|
|
53
|
-
'LLMQueryRepo',
|
|
54
|
-
'PromptService',
|
|
55
|
-
'DatabaseManager',
|
|
56
37
|
'CallServiceClient',
|
|
57
|
-
'Utility',
|
|
58
|
-
'Company',
|
|
59
|
-
'Function',
|
|
60
|
-
'TaskType',
|
|
61
|
-
'Base',
|
|
62
|
-
'Prompt',
|
|
63
|
-
'PromptCategory'
|
|
64
38
|
]
|
iatoolkit/base_company.py
CHANGED
|
@@ -26,8 +26,18 @@ class BaseCompany(ABC):
|
|
|
26
26
|
self.company = self.profile_repo.get_company_by_short_name(short_name)
|
|
27
27
|
return self.company
|
|
28
28
|
|
|
29
|
-
def _create_company(self,
|
|
30
|
-
|
|
29
|
+
def _create_company(self,
|
|
30
|
+
short_name: str,
|
|
31
|
+
name: str,
|
|
32
|
+
parameters: dict | None = None,
|
|
33
|
+
branding: dict | None = None,
|
|
34
|
+
onboarding_cards: dict | None = None,
|
|
35
|
+
) -> Company:
|
|
36
|
+
company_obj = Company(short_name=short_name,
|
|
37
|
+
name=name,
|
|
38
|
+
parameters=parameters,
|
|
39
|
+
branding=branding,
|
|
40
|
+
onboarding_cards=onboarding_cards)
|
|
31
41
|
self.company = self.profile_repo.create_company(company_obj)
|
|
32
42
|
return self.company
|
|
33
43
|
|
|
@@ -67,6 +77,7 @@ class BaseCompany(ABC):
|
|
|
67
77
|
**kwargs
|
|
68
78
|
)
|
|
69
79
|
|
|
80
|
+
|
|
70
81
|
@abstractmethod
|
|
71
82
|
# initialize all the database tables needed
|
|
72
83
|
def register_company(self):
|
|
@@ -79,7 +90,7 @@ class BaseCompany(ABC):
|
|
|
79
90
|
|
|
80
91
|
@abstractmethod
|
|
81
92
|
# get context specific for this company
|
|
82
|
-
def get_user_info(self,
|
|
93
|
+
def get_user_info(self, user_identifier: str) -> dict:
|
|
83
94
|
raise NotImplementedError("La subclase debe implementar el método get_user_info()")
|
|
84
95
|
|
|
85
96
|
@abstractmethod
|
iatoolkit/common/routes.py
CHANGED
|
@@ -3,80 +3,93 @@
|
|
|
3
3
|
#
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
|
-
from flask import render_template, redirect,
|
|
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
|
-
from iatoolkit.views.
|
|
10
|
-
import os
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def logout(company_short_name: str):
|
|
14
|
-
SessionManager.clear()
|
|
15
|
-
flash("Has cerrado sesión correctamente", "info")
|
|
16
|
-
if company_short_name:
|
|
17
|
-
return redirect(url_for('login', company_short_name=company_short_name))
|
|
18
|
-
else:
|
|
19
|
-
return redirect(url_for('home'))
|
|
8
|
+
from iatoolkit.views.history_api_view import HistoryApiView
|
|
20
9
|
|
|
21
10
|
|
|
22
11
|
# this function register all the views
|
|
23
12
|
def register_views(injector, app):
|
|
24
13
|
|
|
25
|
-
from iatoolkit.views.
|
|
26
|
-
from iatoolkit.views.
|
|
27
|
-
from iatoolkit.views.
|
|
28
|
-
from iatoolkit.views.
|
|
29
|
-
from iatoolkit.views.
|
|
30
|
-
from iatoolkit.views.
|
|
31
|
-
from iatoolkit.views.external_chat_login_view import ExternalChatLoginView
|
|
14
|
+
from iatoolkit.views.index_view import IndexView
|
|
15
|
+
from iatoolkit.views.init_context_api_view import InitContextApiView
|
|
16
|
+
from iatoolkit.views.llmquery_api_view import LLMQueryApiView
|
|
17
|
+
from iatoolkit.views.tasks_api_view import TaskApiView
|
|
18
|
+
from iatoolkit.views.tasks_review_api_view import TaskReviewApiView
|
|
19
|
+
from iatoolkit.views.login_simulation_view import LoginSimulationView
|
|
32
20
|
from iatoolkit.views.signup_view import SignupView
|
|
33
21
|
from iatoolkit.views.verify_user_view import VerifyAccountView
|
|
34
22
|
from iatoolkit.views.forgot_password_view import ForgotPasswordView
|
|
35
23
|
from iatoolkit.views.change_password_view import ChangePasswordView
|
|
36
|
-
from iatoolkit.views.
|
|
37
|
-
from iatoolkit.views.
|
|
38
|
-
from iatoolkit.views.
|
|
39
|
-
from iatoolkit.views.
|
|
40
|
-
from iatoolkit.views.external_login_view import ExternalLoginView
|
|
41
|
-
from iatoolkit.views.
|
|
42
|
-
|
|
43
|
-
app.add_url_rule('/', view_func=HomeView.as_view('home'))
|
|
24
|
+
from iatoolkit.views.file_store_api_view import FileStoreApiView
|
|
25
|
+
from iatoolkit.views.user_feedback_api_view import UserFeedbackApiView
|
|
26
|
+
from iatoolkit.views.prompt_api_view import PromptApiView
|
|
27
|
+
from iatoolkit.views.login_view import LoginView, FinalizeContextView
|
|
28
|
+
from iatoolkit.views.external_login_view import ExternalLoginView, RedeemTokenApiView
|
|
29
|
+
from iatoolkit.views.logout_api_view import LogoutApiView
|
|
30
|
+
from iatoolkit.views.home_view import HomeView
|
|
44
31
|
|
|
45
|
-
#
|
|
46
|
-
app.add_url_rule('
|
|
32
|
+
# iatoolkit home page
|
|
33
|
+
app.add_url_rule('/', view_func=IndexView.as_view('index'))
|
|
47
34
|
|
|
48
|
-
#
|
|
49
|
-
app.add_url_rule('/<company_short_name>/
|
|
50
|
-
app.add_url_rule('/<company_short_name>/external_login/<external_user_id>', view_func=ExternalLoginView.as_view('external_login'))
|
|
51
|
-
app.add_url_rule('/auth/chat_token', view_func=ChatTokenRequestView.as_view('chat-token'))
|
|
35
|
+
# company home view
|
|
36
|
+
app.add_url_rule('/<company_short_name>/home', view_func=HomeView.as_view('home'))
|
|
52
37
|
|
|
53
|
-
#
|
|
38
|
+
# login for the iatoolkit integrated frontend
|
|
54
39
|
app.add_url_rule('/<company_short_name>/login', view_func=LoginView.as_view('login'))
|
|
40
|
+
|
|
41
|
+
# this is the login for external users
|
|
42
|
+
app.add_url_rule('/<company_short_name>/external_login',
|
|
43
|
+
view_func=ExternalLoginView.as_view('external_login'))
|
|
44
|
+
|
|
45
|
+
# this endpoint is called when onboarding_shell finish the context load
|
|
46
|
+
app.add_url_rule(
|
|
47
|
+
'/<company_short_name>/finalize',
|
|
48
|
+
view_func=FinalizeContextView.as_view('finalize_no_token')
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
app.add_url_rule(
|
|
52
|
+
'/<company_short_name>/finalize/<token>',
|
|
53
|
+
view_func=FinalizeContextView.as_view('finalize_with_token')
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# logout
|
|
57
|
+
app.add_url_rule('/<company_short_name>/api/logout',
|
|
58
|
+
view_func=LogoutApiView.as_view('logout'))
|
|
59
|
+
|
|
60
|
+
# this endpoint is called by the JS for changing the token for a session
|
|
61
|
+
app.add_url_rule('/<string:company_short_name>/api/redeem_token',
|
|
62
|
+
view_func = RedeemTokenApiView.as_view('redeem_token'))
|
|
63
|
+
|
|
64
|
+
# init (reset) the company context
|
|
65
|
+
app.add_url_rule('/<company_short_name>/api/init-context',
|
|
66
|
+
view_func=InitContextApiView.as_view('init-context'),
|
|
67
|
+
methods=['POST', 'OPTIONS'])
|
|
68
|
+
|
|
69
|
+
# register new user, account verification and forgot password
|
|
55
70
|
app.add_url_rule('/<company_short_name>/signup',view_func=SignupView.as_view('signup'))
|
|
56
|
-
app.add_url_rule('/<company_short_name>/logout', 'logout', logout)
|
|
57
|
-
app.add_url_rule('/logout', 'logout', logout)
|
|
58
71
|
app.add_url_rule('/<company_short_name>/verify/<token>', view_func=VerifyAccountView.as_view('verify_account'))
|
|
59
72
|
app.add_url_rule('/<company_short_name>/forgot-password', view_func=ForgotPasswordView.as_view('forgot_password'))
|
|
60
73
|
app.add_url_rule('/<company_short_name>/change-password/<token>', view_func=ChangePasswordView.as_view('change_password'))
|
|
61
74
|
|
|
62
|
-
#
|
|
63
|
-
|
|
64
|
-
app.add_url_rule('/<company_short_name>/
|
|
65
|
-
app.add_url_rule('/<company_short_name>/prompts', view_func=PromptView.as_view('prompt'))
|
|
66
|
-
app.add_url_rule('/<company_short_name>/history', view_func=HistoryView.as_view('history'))
|
|
67
|
-
app.add_url_rule('/tasks', view_func=TaskView.as_view('tasks'))
|
|
68
|
-
app.add_url_rule('/tasks/review/<int:task_id>', view_func=TaskReviewView.as_view('tasks-review'))
|
|
69
|
-
app.add_url_rule('/load', view_func=FileStoreView.as_view('load'))
|
|
75
|
+
# main chat query, used by the JS in the browser (with credentials)
|
|
76
|
+
# can be used also for executing iatoolkit prompts
|
|
77
|
+
app.add_url_rule('/<company_short_name>/api/llm_query', view_func=LLMQueryApiView.as_view('llm_query_api'))
|
|
70
78
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
view_func=lambda: render_template('about.html'))
|
|
79
|
+
# open the promt directory
|
|
80
|
+
app.add_url_rule('/<company_short_name>/api/prompts', view_func=PromptApiView.as_view('prompt'))
|
|
74
81
|
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
# feedback and history
|
|
83
|
+
app.add_url_rule('/<company_short_name>/api/feedback', view_func=UserFeedbackApiView.as_view('feedback'))
|
|
84
|
+
app.add_url_rule('/<company_short_name>/api/history', view_func=HistoryApiView.as_view('history'))
|
|
85
|
+
|
|
86
|
+
# tasks management endpoints: create task, and review answer
|
|
87
|
+
app.add_url_rule('/tasks', view_func=TaskApiView.as_view('tasks'))
|
|
88
|
+
app.add_url_rule('/tasks/review/<int:task_id>', view_func=TaskReviewApiView.as_view('tasks-review'))
|
|
89
|
+
|
|
90
|
+
# this endpoint is for upload documents into the vector store (api-key)
|
|
91
|
+
app.add_url_rule('/api/load', view_func=FileStoreApiView.as_view('load_api'))
|
|
77
92
|
|
|
78
|
-
app.add_url_rule('/<company_short_name>/<external_user_id>/download-file/<path:filename>',
|
|
79
|
-
view_func=DownloadFileView.as_view('download-file'))
|
|
80
93
|
|
|
81
94
|
@app.route('/download/<path:filename>')
|
|
82
95
|
def download_file(filename):
|
|
@@ -99,3 +112,21 @@ def register_views(injector, app):
|
|
|
99
112
|
except FileNotFoundError:
|
|
100
113
|
abort(404)
|
|
101
114
|
|
|
115
|
+
# login testing
|
|
116
|
+
app.add_url_rule('/<company_short_name>/login_test',
|
|
117
|
+
view_func=LoginSimulationView.as_view('login_test'))
|
|
118
|
+
|
|
119
|
+
app.add_url_rule(
|
|
120
|
+
'/about', # URL de la ruta
|
|
121
|
+
view_func=lambda: render_template('about.html'))
|
|
122
|
+
|
|
123
|
+
app.add_url_rule('/version', 'version',
|
|
124
|
+
lambda: jsonify({"iatoolkit_version": current_app.config.get('VERSION', 'N/A')}))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# hacer que la raíz '/' vaya al home de iatoolkit
|
|
128
|
+
@app.route('/')
|
|
129
|
+
def root_redirect():
|
|
130
|
+
return redirect(url_for('index'))
|
|
131
|
+
|
|
132
|
+
|
iatoolkit/common/util.py
CHANGED
|
@@ -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)
|
iatoolkit/iatoolkit.py
CHANGED
|
@@ -15,10 +15,11 @@ import logging
|
|
|
15
15
|
import os
|
|
16
16
|
from typing import Optional, Dict, Any
|
|
17
17
|
from iatoolkit.repositories.database_manager import DatabaseManager
|
|
18
|
-
|
|
19
|
-
from injector import Binder,
|
|
18
|
+
from werkzeug.middleware.proxy_fix import ProxyFix
|
|
19
|
+
from injector import Binder, Injector, singleton
|
|
20
20
|
from importlib.metadata import version as _pkg_version, PackageNotFoundError
|
|
21
21
|
|
|
22
|
+
IATOOLKIT_VERSION = "0.63.4"
|
|
22
23
|
|
|
23
24
|
# global variable for the unique instance of IAToolkit
|
|
24
25
|
_iatoolkit_instance: Optional['IAToolkit'] = None
|
|
@@ -51,7 +52,7 @@ class IAToolkit:
|
|
|
51
52
|
self.app = None
|
|
52
53
|
self.db_manager = None
|
|
53
54
|
self._injector = None
|
|
54
|
-
self.version =
|
|
55
|
+
self.version = IATOOLKIT_VERSION # default version
|
|
55
56
|
|
|
56
57
|
@classmethod
|
|
57
58
|
def get_instance(cls) -> 'IAToolkit':
|
|
@@ -87,7 +88,7 @@ class IAToolkit:
|
|
|
87
88
|
# and other integrations, as views are handled manually.
|
|
88
89
|
FlaskInjector(app=self.app, injector=self._injector)
|
|
89
90
|
|
|
90
|
-
# Step 6: initialize dispatcher and registered
|
|
91
|
+
# Step 6: initialize dispatcher and registered companies
|
|
91
92
|
self._init_dispatcher_and_company_instances()
|
|
92
93
|
|
|
93
94
|
# Step 7: Finalize setup within the application context
|
|
@@ -100,11 +101,6 @@ class IAToolkit:
|
|
|
100
101
|
# Step 8: define the download_dir for excel's
|
|
101
102
|
self._setup_download_dir()
|
|
102
103
|
|
|
103
|
-
try:
|
|
104
|
-
self.version = _pkg_version("iatoolkit")
|
|
105
|
-
except PackageNotFoundError:
|
|
106
|
-
pass
|
|
107
|
-
|
|
108
104
|
logging.info(f"🎉 IAToolkit v{self.version} inicializado correctamente")
|
|
109
105
|
self._initialized = True
|
|
110
106
|
return self.app
|
|
@@ -114,8 +110,9 @@ class IAToolkit:
|
|
|
114
110
|
return self.config.get(key, os.getenv(key, default))
|
|
115
111
|
|
|
116
112
|
def _setup_logging(self):
|
|
117
|
-
|
|
118
|
-
|
|
113
|
+
# Lee el nivel de log desde una variable de entorno, con 'INFO' como valor por defecto.
|
|
114
|
+
log_level_name = os.getenv('LOG_LEVEL', 'INFO').upper()
|
|
115
|
+
log_level = getattr(logging, log_level_name, logging.INFO)
|
|
119
116
|
|
|
120
117
|
logging.basicConfig(
|
|
121
118
|
level=log_level,
|
|
@@ -144,11 +141,21 @@ class IAToolkit:
|
|
|
144
141
|
is_https = self._get_config_value('USE_HTTPS', 'false').lower() == 'true'
|
|
145
142
|
is_dev = self._get_config_value('FLASK_ENV') == 'development'
|
|
146
143
|
|
|
144
|
+
# get the iatoolkit domain
|
|
145
|
+
parsed_url = urlparse(os.getenv('IATOOLKIT_BASE_URL'))
|
|
146
|
+
domain = parsed_url.netloc
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
self.version = _pkg_version("iatoolkit")
|
|
150
|
+
except PackageNotFoundError:
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
|
|
147
154
|
self.app.config.update({
|
|
148
155
|
'VERSION': self.version,
|
|
149
156
|
'SECRET_KEY': self._get_config_value('FLASK_SECRET_KEY', 'iatoolkit-default-secret'),
|
|
150
|
-
'SESSION_COOKIE_SAMESITE': "None"
|
|
151
|
-
'SESSION_COOKIE_SECURE':
|
|
157
|
+
'SESSION_COOKIE_SAMESITE': "None",
|
|
158
|
+
'SESSION_COOKIE_SECURE': True,
|
|
152
159
|
'SESSION_PERMANENT': False,
|
|
153
160
|
'SESSION_USE_SIGNER': True,
|
|
154
161
|
'JWT_SECRET_KEY': self._get_config_value('JWT_SECRET_KEY', 'iatoolkit-jwt-secret'),
|
|
@@ -156,6 +163,12 @@ class IAToolkit:
|
|
|
156
163
|
'JWT_EXPIRATION_SECONDS_CHAT': int(self._get_config_value('JWT_EXPIRATION_SECONDS_CHAT', 3600))
|
|
157
164
|
})
|
|
158
165
|
|
|
166
|
+
if parsed_url.scheme == 'https':
|
|
167
|
+
self.app.config['PREFERRED_URL_SCHEME'] = 'https'
|
|
168
|
+
|
|
169
|
+
# 2. ProxyFix para no tener problemas con iframes y rutas
|
|
170
|
+
self.app.wsgi_app = ProxyFix(self.app.wsgi_app, x_proto=1)
|
|
171
|
+
|
|
159
172
|
# Configuración para tokenizers en desarrollo
|
|
160
173
|
if is_dev:
|
|
161
174
|
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
|
@@ -172,6 +185,15 @@ class IAToolkit:
|
|
|
172
185
|
self.db_manager.create_all()
|
|
173
186
|
logging.info("✅ Base de datos configurada correctamente")
|
|
174
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
|
+
|
|
175
197
|
def _setup_redis_sessions(self):
|
|
176
198
|
redis_url = self._get_config_value('REDIS_URL')
|
|
177
199
|
if not redis_url:
|
|
@@ -202,19 +224,19 @@ class IAToolkit:
|
|
|
202
224
|
|
|
203
225
|
def _setup_cors(self):
|
|
204
226
|
"""🌐 Configura CORS"""
|
|
205
|
-
|
|
227
|
+
from iatoolkit.company_registry import get_company_registry
|
|
228
|
+
|
|
229
|
+
# default CORS origin
|
|
206
230
|
default_origins = [
|
|
207
|
-
"http://localhost:5001",
|
|
208
|
-
"http://127.0.0.1:5001",
|
|
209
231
|
os.getenv('IATOOLKIT_BASE_URL')
|
|
210
232
|
]
|
|
211
233
|
|
|
212
|
-
#
|
|
234
|
+
# Iterate through the registered company names
|
|
213
235
|
extra_origins = []
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
|
218
240
|
|
|
219
241
|
all_origins = default_origins + extra_origins
|
|
220
242
|
|
|
@@ -229,7 +251,6 @@ class IAToolkit:
|
|
|
229
251
|
|
|
230
252
|
logging.info(f"✅ CORS configurado para: {all_origins}")
|
|
231
253
|
|
|
232
|
-
|
|
233
254
|
def _configure_core_dependencies(self, binder: Binder):
|
|
234
255
|
"""⚙️ Configures all system dependencies."""
|
|
235
256
|
try:
|
|
@@ -241,7 +262,6 @@ class IAToolkit:
|
|
|
241
262
|
self._bind_repositories(binder)
|
|
242
263
|
self._bind_services(binder)
|
|
243
264
|
self._bind_infrastructure(binder)
|
|
244
|
-
self._bind_views(binder)
|
|
245
265
|
|
|
246
266
|
logging.info("✅ Dependencias configuradas correctamente")
|
|
247
267
|
|
|
@@ -278,6 +298,7 @@ class IAToolkit:
|
|
|
278
298
|
from iatoolkit.services.profile_service import ProfileService
|
|
279
299
|
from iatoolkit.services.jwt_service import JWTService
|
|
280
300
|
from iatoolkit.services.dispatcher_service import Dispatcher
|
|
301
|
+
from iatoolkit.services.branding_service import BrandingService
|
|
281
302
|
|
|
282
303
|
binder.bind(QueryService, to=QueryService)
|
|
283
304
|
binder.bind(TaskService, to=TaskService)
|
|
@@ -290,38 +311,23 @@ class IAToolkit:
|
|
|
290
311
|
binder.bind(ProfileService, to=ProfileService)
|
|
291
312
|
binder.bind(JWTService, to=JWTService)
|
|
292
313
|
binder.bind(Dispatcher, to=Dispatcher)
|
|
314
|
+
binder.bind(BrandingService, to=BrandingService)
|
|
293
315
|
|
|
294
316
|
def _bind_infrastructure(self, binder: Binder):
|
|
295
317
|
from iatoolkit.infra.llm_client import llmClient
|
|
296
318
|
from iatoolkit.infra.llm_proxy import LLMProxy
|
|
297
319
|
from iatoolkit.infra.google_chat_app import GoogleChatApp
|
|
298
320
|
from iatoolkit.infra.mail_app import MailApp
|
|
299
|
-
from iatoolkit.
|
|
321
|
+
from iatoolkit.services.auth_service import AuthService
|
|
300
322
|
from iatoolkit.common.util import Utility
|
|
301
323
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
binder.bind(LLMProxy, to=LLMProxy, scope=singleton)
|
|
305
|
-
binder.bind(llmClient, to=llmClient, scope=singleton)
|
|
324
|
+
binder.bind(LLMProxy, to=LLMProxy)
|
|
325
|
+
binder.bind(llmClient, to=llmClient)
|
|
306
326
|
binder.bind(GoogleChatApp, to=GoogleChatApp)
|
|
307
327
|
binder.bind(MailApp, to=MailApp)
|
|
308
|
-
binder.bind(
|
|
328
|
+
binder.bind(AuthService, to=AuthService)
|
|
309
329
|
binder.bind(Utility, to=Utility)
|
|
310
330
|
|
|
311
|
-
def _bind_views(self, binder: Binder):
|
|
312
|
-
"""Vincula las vistas después de que el injector ha sido creado"""
|
|
313
|
-
from iatoolkit.views.llmquery_view import LLMQueryView
|
|
314
|
-
from iatoolkit.views.home_view import HomeView
|
|
315
|
-
from iatoolkit.views.chat_view import ChatView
|
|
316
|
-
from iatoolkit.views.change_password_view import ChangePasswordView
|
|
317
|
-
|
|
318
|
-
binder.bind(HomeView, to=HomeView)
|
|
319
|
-
binder.bind(ChatView, to=ChatView)
|
|
320
|
-
binder.bind(ChangePasswordView, to=ChangePasswordView)
|
|
321
|
-
binder.bind(LLMQueryView, to=LLMQueryView)
|
|
322
|
-
|
|
323
|
-
logging.info("✅ Views configuradas correctamente")
|
|
324
|
-
|
|
325
331
|
def _setup_additional_services(self):
|
|
326
332
|
Bcrypt(self.app)
|
|
327
333
|
|
|
@@ -359,14 +365,23 @@ class IAToolkit:
|
|
|
359
365
|
@self.app.context_processor
|
|
360
366
|
def inject_globals():
|
|
361
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
|
+
|
|
362
373
|
return {
|
|
363
374
|
'url_for': url_for,
|
|
364
375
|
'iatoolkit_version': self.version,
|
|
365
376
|
'app_name': 'IAToolkit',
|
|
366
|
-
'
|
|
367
|
-
'
|
|
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'),
|
|
381
|
+
'iatoolkit_base_url': os.environ.get('IATOOLKIT_BASE_URL', ''),
|
|
368
382
|
}
|
|
369
383
|
|
|
384
|
+
|
|
370
385
|
def _get_default_static_folder(self) -> str:
|
|
371
386
|
try:
|
|
372
387
|
current_dir = os.path.dirname(os.path.abspath(__file__)) # .../src/iatoolkit
|
|
@@ -409,7 +424,7 @@ class IAToolkit:
|
|
|
409
424
|
|
|
410
425
|
def _setup_download_dir(self):
|
|
411
426
|
# 1. set the default download directory
|
|
412
|
-
default_download_dir = os.path.join(os.getcwd(), 'downloads')
|
|
427
|
+
default_download_dir = os.path.join(os.getcwd(), 'iatoolkit-downloads')
|
|
413
428
|
|
|
414
429
|
# 3. if user specified one, use it
|
|
415
430
|
download_dir = self.app.config.get('IATOOLKIT_DOWNLOAD_DIR', default_download_dir)
|
iatoolkit/infra/llm_client.py
CHANGED
|
@@ -21,6 +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, utiliza el boton de recarga de contexto.'
|
|
24
25
|
|
|
25
26
|
class llmClient:
|
|
26
27
|
_llm_clients_cache = {} # class attribute, for the clients cache
|
|
@@ -116,7 +117,7 @@ class llmClient:
|
|
|
116
117
|
|
|
117
118
|
# in case of context error
|
|
118
119
|
if "context_length_exceeded" in str(e):
|
|
119
|
-
error_message =
|
|
120
|
+
error_message = CONTEXT_ERROR_MESSAGE
|
|
120
121
|
|
|
121
122
|
raise IAToolkitException(IAToolkitException.ErrorType.LLM_ERROR, error_message)
|
|
122
123
|
|
|
@@ -256,25 +257,23 @@ class llmClient:
|
|
|
256
257
|
|
|
257
258
|
# in case of context error
|
|
258
259
|
if "context_length_exceeded" in str(e):
|
|
259
|
-
error_message =
|
|
260
|
+
error_message = CONTEXT_ERROR_MESSAGE
|
|
260
261
|
elif "string_above_max_length" in str(e):
|
|
261
|
-
error_message = 'La respuesta es muy
|
|
262
|
+
error_message = 'La respuesta es muy extensa, trata de filtrar/restringuir tu consulta'
|
|
262
263
|
|
|
263
264
|
raise IAToolkitException(IAToolkitException.ErrorType.LLM_ERROR, error_message)
|
|
264
265
|
|
|
265
266
|
def set_company_context(self,
|
|
266
267
|
company: Company,
|
|
267
268
|
company_base_context: str,
|
|
268
|
-
model
|
|
269
|
+
model) -> str:
|
|
269
270
|
|
|
270
|
-
|
|
271
|
-
self.model = model
|
|
272
|
-
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...")
|
|
273
272
|
|
|
274
273
|
llm_proxy = self.llm_proxy_factory.create_for_company(company)
|
|
275
274
|
try:
|
|
276
275
|
response = llm_proxy.create_response(
|
|
277
|
-
model=
|
|
276
|
+
model=model,
|
|
278
277
|
input=[{
|
|
279
278
|
"role": "system",
|
|
280
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
|
|
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
|
|