iatoolkit 0.71.4__py3-none-any.whl → 1.4.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iatoolkit/__init__.py +19 -7
- iatoolkit/base_company.py +1 -71
- iatoolkit/cli_commands.py +9 -21
- iatoolkit/common/exceptions.py +2 -0
- iatoolkit/common/interfaces/__init__.py +0 -0
- iatoolkit/common/interfaces/asset_storage.py +34 -0
- iatoolkit/common/interfaces/database_provider.py +38 -0
- iatoolkit/common/model_registry.py +159 -0
- iatoolkit/common/routes.py +53 -32
- iatoolkit/common/util.py +17 -12
- iatoolkit/company_registry.py +55 -14
- iatoolkit/{iatoolkit.py → core.py} +102 -72
- iatoolkit/infra/{mail_app.py → brevo_mail_app.py} +15 -37
- iatoolkit/infra/llm_providers/__init__.py +0 -0
- iatoolkit/infra/llm_providers/deepseek_adapter.py +278 -0
- iatoolkit/infra/{gemini_adapter.py → llm_providers/gemini_adapter.py} +11 -17
- iatoolkit/infra/{openai_adapter.py → llm_providers/openai_adapter.py} +41 -7
- iatoolkit/infra/llm_proxy.py +235 -134
- iatoolkit/infra/llm_response.py +5 -0
- iatoolkit/locales/en.yaml +134 -4
- iatoolkit/locales/es.yaml +293 -162
- iatoolkit/repositories/database_manager.py +92 -22
- iatoolkit/repositories/document_repo.py +7 -0
- iatoolkit/repositories/filesystem_asset_repository.py +36 -0
- iatoolkit/repositories/llm_query_repo.py +36 -22
- iatoolkit/repositories/models.py +86 -95
- iatoolkit/repositories/profile_repo.py +64 -13
- iatoolkit/repositories/vs_repo.py +31 -28
- iatoolkit/services/auth_service.py +1 -1
- iatoolkit/services/branding_service.py +1 -1
- iatoolkit/services/company_context_service.py +96 -39
- iatoolkit/services/configuration_service.py +329 -67
- iatoolkit/services/dispatcher_service.py +51 -227
- iatoolkit/services/document_service.py +10 -1
- iatoolkit/services/embedding_service.py +9 -6
- iatoolkit/services/excel_service.py +50 -2
- iatoolkit/services/file_processor_service.py +0 -5
- iatoolkit/services/history_manager_service.py +208 -0
- iatoolkit/services/jwt_service.py +1 -1
- iatoolkit/services/knowledge_base_service.py +412 -0
- iatoolkit/services/language_service.py +8 -2
- iatoolkit/services/license_service.py +82 -0
- iatoolkit/{infra/llm_client.py → services/llm_client_service.py} +42 -29
- iatoolkit/services/load_documents_service.py +18 -47
- iatoolkit/services/mail_service.py +171 -25
- iatoolkit/services/profile_service.py +69 -36
- iatoolkit/services/{prompt_manager_service.py → prompt_service.py} +136 -25
- iatoolkit/services/query_service.py +229 -203
- iatoolkit/services/sql_service.py +116 -34
- iatoolkit/services/tool_service.py +246 -0
- iatoolkit/services/user_feedback_service.py +18 -6
- iatoolkit/services/user_session_context_service.py +121 -51
- iatoolkit/static/images/iatoolkit_core.png +0 -0
- iatoolkit/static/images/iatoolkit_logo.png +0 -0
- iatoolkit/static/js/chat_feedback_button.js +1 -1
- iatoolkit/static/js/chat_help_content.js +4 -4
- iatoolkit/static/js/chat_main.js +61 -9
- iatoolkit/static/js/chat_model_selector.js +227 -0
- iatoolkit/static/js/chat_onboarding_button.js +1 -1
- iatoolkit/static/js/chat_reload_button.js +4 -1
- iatoolkit/static/styles/chat_iatoolkit.css +59 -3
- iatoolkit/static/styles/chat_public.css +28 -0
- iatoolkit/static/styles/documents.css +598 -0
- iatoolkit/static/styles/landing_page.css +223 -7
- iatoolkit/static/styles/llm_output.css +34 -1
- iatoolkit/system_prompts/__init__.py +0 -0
- iatoolkit/system_prompts/query_main.prompt +28 -3
- iatoolkit/system_prompts/sql_rules.prompt +47 -12
- iatoolkit/templates/_company_header.html +30 -5
- iatoolkit/templates/_login_widget.html +3 -3
- iatoolkit/templates/base.html +13 -0
- iatoolkit/templates/chat.html +45 -3
- iatoolkit/templates/forgot_password.html +3 -2
- iatoolkit/templates/onboarding_shell.html +1 -2
- iatoolkit/templates/signup.html +3 -0
- iatoolkit/views/base_login_view.py +8 -3
- iatoolkit/views/change_password_view.py +1 -1
- iatoolkit/views/chat_view.py +76 -0
- iatoolkit/views/forgot_password_view.py +9 -4
- iatoolkit/views/history_api_view.py +3 -3
- iatoolkit/views/home_view.py +4 -2
- iatoolkit/views/init_context_api_view.py +1 -1
- iatoolkit/views/llmquery_api_view.py +4 -3
- iatoolkit/views/load_company_configuration_api_view.py +49 -0
- iatoolkit/views/{file_store_api_view.py → load_document_api_view.py} +15 -11
- iatoolkit/views/login_view.py +25 -8
- iatoolkit/views/logout_api_view.py +10 -2
- iatoolkit/views/prompt_api_view.py +1 -1
- iatoolkit/views/rag_api_view.py +216 -0
- iatoolkit/views/root_redirect_view.py +22 -0
- iatoolkit/views/signup_view.py +12 -4
- iatoolkit/views/static_page_view.py +27 -0
- iatoolkit/views/users_api_view.py +33 -0
- iatoolkit/views/verify_user_view.py +1 -1
- iatoolkit-1.4.2.dist-info/METADATA +268 -0
- iatoolkit-1.4.2.dist-info/RECORD +133 -0
- iatoolkit-1.4.2.dist-info/licenses/LICENSE_COMMUNITY.md +15 -0
- iatoolkit/repositories/tasks_repo.py +0 -52
- iatoolkit/services/history_service.py +0 -37
- iatoolkit/services/search_service.py +0 -55
- iatoolkit/services/tasks_service.py +0 -188
- iatoolkit/templates/about.html +0 -13
- iatoolkit/templates/index.html +0 -145
- iatoolkit/templates/login_simulation.html +0 -45
- iatoolkit/views/external_login_view.py +0 -73
- iatoolkit/views/index_view.py +0 -14
- iatoolkit/views/login_simulation_view.py +0 -93
- iatoolkit/views/tasks_api_view.py +0 -72
- iatoolkit/views/tasks_review_api_view.py +0 -55
- iatoolkit-0.71.4.dist-info/METADATA +0 -276
- iatoolkit-0.71.4.dist-info/RECORD +0 -122
- {iatoolkit-0.71.4.dist-info → iatoolkit-1.4.2.dist-info}/WHEEL +0 -0
- {iatoolkit-0.71.4.dist-info → iatoolkit-1.4.2.dist-info}/licenses/LICENSE +0 -0
- {iatoolkit-0.71.4.dist-info → iatoolkit-1.4.2.dist-info}/top_level.txt +0 -0
|
@@ -38,7 +38,6 @@
|
|
|
38
38
|
<div class="ob-stack">
|
|
39
39
|
<h1 id="ob-brand-header" class="ob-brand-header">
|
|
40
40
|
<span class="brand-name text-brand-primary">{{ branding.name | default('IAToolkit') }}</span>
|
|
41
|
-
<span class="brand-rest"> IA</span>
|
|
42
41
|
</h1>
|
|
43
42
|
|
|
44
43
|
<div id="card-container" class="ob-card">
|
|
@@ -60,7 +59,7 @@
|
|
|
60
59
|
|
|
61
60
|
<div id="loading-status" class="ob-loading-band">
|
|
62
61
|
<div class="spinner"></div>
|
|
63
|
-
<p>
|
|
62
|
+
<p>{{ t('ui.chat.init_context') }} </p>
|
|
64
63
|
</div>
|
|
65
64
|
</div>
|
|
66
65
|
</div>
|
iatoolkit/templates/signup.html
CHANGED
|
@@ -59,6 +59,9 @@
|
|
|
59
59
|
<input type="password" id="confirm_password" name="confirm_password" class="form-control" required>
|
|
60
60
|
</div>
|
|
61
61
|
|
|
62
|
+
<!-- language: html -->
|
|
63
|
+
<input type="hidden" name="lang" value="{{ lang or 'en' }}">
|
|
64
|
+
|
|
62
65
|
<!-- Botón actualizado con la clase de branding -->
|
|
63
66
|
<button type="submit" class="btn btn-branded-primary w-100 fw-bold py-2 mt-3">{{ t('ui.signup.signup_button') }}</button>
|
|
64
67
|
</form>
|
|
@@ -12,7 +12,7 @@ from iatoolkit.services.auth_service import AuthService
|
|
|
12
12
|
from iatoolkit.services.query_service import QueryService
|
|
13
13
|
from iatoolkit.services.branding_service import BrandingService
|
|
14
14
|
from iatoolkit.services.configuration_service import ConfigurationService
|
|
15
|
-
from iatoolkit.services.
|
|
15
|
+
from iatoolkit.services.prompt_service import PromptService
|
|
16
16
|
from iatoolkit.services.i18n_service import I18nService
|
|
17
17
|
from iatoolkit.common.util import Utility
|
|
18
18
|
from iatoolkit.services.jwt_service import JWTService
|
|
@@ -74,6 +74,9 @@ class BaseLoginView(MethodView):
|
|
|
74
74
|
)
|
|
75
75
|
else:
|
|
76
76
|
# --- FAST PATH: Render the chat page directly ---
|
|
77
|
+
# LLM configuration: default model and availables
|
|
78
|
+
default_llm_model, available_llm_models = self.config_service.get_llm_configuration(company_short_name)
|
|
79
|
+
|
|
77
80
|
prompts = self.prompt_service.get_user_prompts(company_short_name)
|
|
78
81
|
|
|
79
82
|
# Get the entire 'js_messages' block in the correct language.
|
|
@@ -87,5 +90,7 @@ class BaseLoginView(MethodView):
|
|
|
87
90
|
branding=branding_data,
|
|
88
91
|
onboarding_cards=onboarding_cards,
|
|
89
92
|
js_translations=js_translations,
|
|
90
|
-
redeem_token=redeem_token
|
|
91
|
-
|
|
93
|
+
redeem_token=redeem_token,
|
|
94
|
+
llm_default_model=default_llm_model,
|
|
95
|
+
llm_available_models = available_llm_models,
|
|
96
|
+
)
|
|
@@ -24,7 +24,7 @@ class ChangePasswordView(MethodView):
|
|
|
24
24
|
self.branding_service = branding_service
|
|
25
25
|
self.i18n_service = i18n_service
|
|
26
26
|
|
|
27
|
-
self.serializer = URLSafeTimedSerializer(os.getenv("
|
|
27
|
+
self.serializer = URLSafeTimedSerializer(os.getenv("IATOOLKIT_SECRET_KEY"))
|
|
28
28
|
self.bcrypt = Bcrypt()
|
|
29
29
|
|
|
30
30
|
def get(self, company_short_name: str, token: str):
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from flask import render_template, redirect, url_for, request
|
|
2
|
+
from flask.views import MethodView
|
|
3
|
+
from injector import inject
|
|
4
|
+
from iatoolkit.services.profile_service import ProfileService
|
|
5
|
+
from iatoolkit.services.branding_service import BrandingService
|
|
6
|
+
from iatoolkit.services.configuration_service import ConfigurationService
|
|
7
|
+
from iatoolkit.services.prompt_service import PromptService
|
|
8
|
+
from iatoolkit.services.i18n_service import I18nService
|
|
9
|
+
|
|
10
|
+
class ChatView(MethodView):
|
|
11
|
+
"""
|
|
12
|
+
Handles direct access to the chat interface.
|
|
13
|
+
Validates if the user has an active session for the company.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@inject
|
|
17
|
+
def __init__(self,
|
|
18
|
+
profile_service: ProfileService,
|
|
19
|
+
branding_service: BrandingService,
|
|
20
|
+
config_service: ConfigurationService,
|
|
21
|
+
prompt_service: PromptService,
|
|
22
|
+
i18n_service: I18nService):
|
|
23
|
+
self.profile_service = profile_service
|
|
24
|
+
self.branding_service = branding_service
|
|
25
|
+
self.config_service = config_service
|
|
26
|
+
self.prompt_service = prompt_service
|
|
27
|
+
self.i18n_service = i18n_service
|
|
28
|
+
|
|
29
|
+
def get(self, company_short_name: str):
|
|
30
|
+
# 1. Validate Company
|
|
31
|
+
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
32
|
+
if not company:
|
|
33
|
+
return render_template('error.html',
|
|
34
|
+
message=self.i18n_service.t('errors.templates.company_not_found')), 404
|
|
35
|
+
|
|
36
|
+
# 2. Check Session
|
|
37
|
+
session_info = self.profile_service.get_current_session_info()
|
|
38
|
+
user_identifier = session_info.get('user_identifier')
|
|
39
|
+
session_company = session_info.get('company_short_name')
|
|
40
|
+
|
|
41
|
+
# If no user or session belongs to another company -> Redirect to Home
|
|
42
|
+
if not user_identifier or session_company != company_short_name:
|
|
43
|
+
return redirect(url_for('home', company_short_name=company_short_name))
|
|
44
|
+
|
|
45
|
+
# 3. Prepare Context for Chat
|
|
46
|
+
# (This logic mirrors the FAST PATH in BaseLoginView)
|
|
47
|
+
try:
|
|
48
|
+
branding_data = self.branding_service.get_company_branding(company_short_name)
|
|
49
|
+
onboarding_cards = self.config_service.get_configuration(company_short_name, 'onboarding_cards')
|
|
50
|
+
default_llm_model, available_llm_models = self.config_service.get_llm_configuration(company_short_name)
|
|
51
|
+
prompts = self.prompt_service.get_user_prompts(company_short_name)
|
|
52
|
+
js_translations = self.i18n_service.get_translation_block('js_messages')
|
|
53
|
+
|
|
54
|
+
return render_template(
|
|
55
|
+
"chat.html",
|
|
56
|
+
company_short_name=company_short_name,
|
|
57
|
+
user_identifier=user_identifier,
|
|
58
|
+
prompts=prompts,
|
|
59
|
+
branding=branding_data,
|
|
60
|
+
onboarding_cards=onboarding_cards,
|
|
61
|
+
js_translations=js_translations,
|
|
62
|
+
llm_default_model=default_llm_model,
|
|
63
|
+
llm_available_models=available_llm_models,
|
|
64
|
+
# redeem_token is None for direct access
|
|
65
|
+
redeem_token=None
|
|
66
|
+
)
|
|
67
|
+
except Exception as e:
|
|
68
|
+
# Fallback error handling
|
|
69
|
+
message = self.i18n_service.t('errors.templates.processing_error', error=str(e))
|
|
70
|
+
branding_data = self.branding_service.get_company_branding(company_short_name)
|
|
71
|
+
return render_template(
|
|
72
|
+
"error.html",
|
|
73
|
+
company_short_name=company_short_name,
|
|
74
|
+
branding=branding_data,
|
|
75
|
+
message=message
|
|
76
|
+
), 500
|
|
@@ -21,7 +21,7 @@ class ForgotPasswordView(MethodView):
|
|
|
21
21
|
self.branding_service = branding_service
|
|
22
22
|
self.i18n_service = i18n_service
|
|
23
23
|
|
|
24
|
-
self.serializer = URLSafeTimedSerializer(os.getenv("
|
|
24
|
+
self.serializer = URLSafeTimedSerializer(os.getenv("IATOOLKIT_SECRET_KEY"))
|
|
25
25
|
|
|
26
26
|
def get(self, company_short_name: str):
|
|
27
27
|
# get company info
|
|
@@ -31,9 +31,11 @@ class ForgotPasswordView(MethodView):
|
|
|
31
31
|
message=self.i18n_service.t('errors.templates.company_not_found')), 404
|
|
32
32
|
|
|
33
33
|
branding_data = self.branding_service.get_company_branding(company_short_name)
|
|
34
|
+
current_lang = request.args.get("lang", "en")
|
|
34
35
|
return render_template('forgot_password.html',
|
|
35
36
|
company_short_name=company_short_name,
|
|
36
|
-
branding=branding_data
|
|
37
|
+
branding=branding_data,
|
|
38
|
+
lang=current_lang
|
|
37
39
|
)
|
|
38
40
|
|
|
39
41
|
def post(self, company_short_name: str):
|
|
@@ -53,7 +55,9 @@ class ForgotPasswordView(MethodView):
|
|
|
53
55
|
company_short_name=company_short_name,
|
|
54
56
|
token=token, _external=True)
|
|
55
57
|
|
|
56
|
-
response = self.profile_service.forgot_password(
|
|
58
|
+
response = self.profile_service.forgot_password(
|
|
59
|
+
company_short_name=company_short_name,
|
|
60
|
+
email=email, reset_url=reset_url)
|
|
57
61
|
if "error" in response:
|
|
58
62
|
flash(response["error"], 'error')
|
|
59
63
|
return render_template(
|
|
@@ -63,7 +67,8 @@ class ForgotPasswordView(MethodView):
|
|
|
63
67
|
form_data={"email": email}), 400
|
|
64
68
|
|
|
65
69
|
flash(self.i18n_service.t('flash_messages.forgot_password_success'), 'success')
|
|
66
|
-
|
|
70
|
+
lang = request.args.get("lang", "en")
|
|
71
|
+
return redirect(url_for('home', company_short_name=company_short_name, lang=lang))
|
|
67
72
|
|
|
68
73
|
except Exception as e:
|
|
69
74
|
flash(self.i18n_service.t('errors.general.unexpected_error'), 'error')
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
from flask import request, jsonify
|
|
7
7
|
from flask.views import MethodView
|
|
8
|
-
from iatoolkit.services.
|
|
8
|
+
from iatoolkit.services.history_manager_service import HistoryManagerService
|
|
9
9
|
from iatoolkit.services.auth_service import AuthService
|
|
10
10
|
from iatoolkit.services.i18n_service import I18nService
|
|
11
11
|
from injector import inject
|
|
@@ -21,7 +21,7 @@ class HistoryApiView(MethodView):
|
|
|
21
21
|
@inject
|
|
22
22
|
def __init__(self,
|
|
23
23
|
auth_service: AuthService,
|
|
24
|
-
history_service:
|
|
24
|
+
history_service: HistoryManagerService,
|
|
25
25
|
i18n_service: I18nService):
|
|
26
26
|
self.auth_service = auth_service
|
|
27
27
|
self.history_service = history_service
|
|
@@ -39,7 +39,7 @@ class HistoryApiView(MethodView):
|
|
|
39
39
|
|
|
40
40
|
# 2. Call the history service with the unified identifier.
|
|
41
41
|
# The service's signature should now only expect user_identifier.
|
|
42
|
-
response = self.history_service.
|
|
42
|
+
response = self.history_service.get_full_history(
|
|
43
43
|
company_short_name=company_short_name,
|
|
44
44
|
user_identifier=user_identifier
|
|
45
45
|
)
|
iatoolkit/views/home_view.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# iatoolkit/views/home_view.py
|
|
2
|
-
from flask import render_template, render_template_string
|
|
2
|
+
from flask import render_template, render_template_string, request
|
|
3
3
|
from flask.views import MethodView
|
|
4
4
|
from injector import inject
|
|
5
5
|
from iatoolkit.services.profile_service import ProfileService
|
|
@@ -33,7 +33,9 @@ class HomeView(MethodView):
|
|
|
33
33
|
message=self.i18n_service.t('errors.templates.company_not_found')), 404
|
|
34
34
|
|
|
35
35
|
branding_data = self.branding_service.get_company_branding(company_short_name)
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
template_name = self.util.get_template_by_language("home")
|
|
38
|
+
home_template = self.util.get_company_template(company_short_name, template_name)
|
|
37
39
|
|
|
38
40
|
# 2. Verificamos si el archivo de plantilla personalizado no existe.
|
|
39
41
|
if not home_template:
|
|
@@ -54,7 +54,7 @@ class InitContextApiView(MethodView):
|
|
|
54
54
|
response_message = {'status': 'OK', 'message': success_message}
|
|
55
55
|
|
|
56
56
|
# if received a response ID with the context, return it
|
|
57
|
-
if response.get('response_id'):
|
|
57
|
+
if response and response.get('response_id'):
|
|
58
58
|
response_message['response_id'] = response['response_id']
|
|
59
59
|
|
|
60
60
|
return jsonify(response_message), 200
|
|
@@ -41,18 +41,19 @@ class LLMQueryApiView(MethodView):
|
|
|
41
41
|
result = self.query_service.llm_query(
|
|
42
42
|
company_short_name=company_short_name,
|
|
43
43
|
user_identifier=user_identifier,
|
|
44
|
+
model=data.get('model', ''),
|
|
44
45
|
question=data.get('question', ''),
|
|
45
46
|
prompt_name=data.get('prompt_name'),
|
|
46
47
|
client_data=data.get('client_data', {}),
|
|
47
|
-
|
|
48
|
+
ignore_history=data.get('ignore_history', False),
|
|
48
49
|
files=data.get('files', [])
|
|
49
50
|
)
|
|
50
51
|
if 'error' in result:
|
|
51
|
-
return jsonify(result),
|
|
52
|
+
return jsonify(result), 409
|
|
52
53
|
|
|
53
54
|
return jsonify(result), 200
|
|
54
55
|
|
|
55
56
|
except Exception as e:
|
|
56
57
|
logging.exception(
|
|
57
58
|
f"Unexpected error: {e}")
|
|
58
|
-
return jsonify({"error_message": self.i18n_service.t('errors.general.unexpected_error'
|
|
59
|
+
return jsonify({"error": True, "error_message": self.i18n_service.t('errors.general.unexpected_error')}), 500
|
|
@@ -0,0 +1,49 @@
|
|
|
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, request, g
|
|
8
|
+
from injector import inject
|
|
9
|
+
from iatoolkit.services.auth_service import AuthService
|
|
10
|
+
from iatoolkit.services.profile_service import ProfileService
|
|
11
|
+
from iatoolkit.services.configuration_service import ConfigurationService
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
class LoadCompanyConfigurationApiView(MethodView):
|
|
15
|
+
@inject
|
|
16
|
+
def __init__(self,
|
|
17
|
+
configuration_service: ConfigurationService,
|
|
18
|
+
profile_service: ProfileService,
|
|
19
|
+
auth_service: AuthService):
|
|
20
|
+
self.configuration_service = configuration_service
|
|
21
|
+
self.profile_service = profile_service
|
|
22
|
+
self.auth_service = auth_service
|
|
23
|
+
|
|
24
|
+
def get(self, company_short_name: str = None):
|
|
25
|
+
try:
|
|
26
|
+
# 1. Get the authenticated user's
|
|
27
|
+
auth_result = self.auth_service.verify(anonymous=True)
|
|
28
|
+
if not auth_result.get("success"):
|
|
29
|
+
return jsonify(auth_result), auth_result.get("status_code", 401)
|
|
30
|
+
|
|
31
|
+
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
32
|
+
if not company:
|
|
33
|
+
return jsonify({"error": "company not found."}), 404
|
|
34
|
+
|
|
35
|
+
config, errors = self.configuration_service.load_configuration(company_short_name)
|
|
36
|
+
if config:
|
|
37
|
+
self.configuration_service.register_data_sources(company_short_name)
|
|
38
|
+
|
|
39
|
+
# this is fo avoid serialization issues
|
|
40
|
+
if 'company' in config:
|
|
41
|
+
config.pop('company')
|
|
42
|
+
|
|
43
|
+
status_code = 200 if not errors else 400
|
|
44
|
+
return {'config': config, 'errors': [errors]}, status_code
|
|
45
|
+
except Exception as e:
|
|
46
|
+
logging.exception(f"Unexpected error: {e}")
|
|
47
|
+
return {'status': 'error'}, 500
|
|
48
|
+
|
|
49
|
+
|
|
@@ -5,21 +5,22 @@
|
|
|
5
5
|
|
|
6
6
|
from flask.views import MethodView
|
|
7
7
|
from flask import request, jsonify
|
|
8
|
-
from iatoolkit.services.load_documents_service import LoadDocumentsService
|
|
9
|
-
from iatoolkit.services.auth_service import AuthService
|
|
10
|
-
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
11
8
|
from injector import inject
|
|
12
9
|
import base64
|
|
13
10
|
|
|
11
|
+
from iatoolkit.services.knowledge_base_service import KnowledgeBaseService
|
|
12
|
+
from iatoolkit.services.auth_service import AuthService
|
|
13
|
+
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
class LoadDocumentApiView(MethodView):
|
|
16
17
|
@inject
|
|
17
18
|
def __init__(self,
|
|
18
19
|
auth_service: AuthService,
|
|
19
|
-
|
|
20
|
-
profile_repo: ProfileRepo
|
|
20
|
+
knowledge_base_service: KnowledgeBaseService,
|
|
21
|
+
profile_repo: ProfileRepo):
|
|
21
22
|
self.auth_service = auth_service
|
|
22
|
-
self.
|
|
23
|
+
self.knowledge_base_service = knowledge_base_service
|
|
23
24
|
self.profile_repo = profile_repo
|
|
24
25
|
|
|
25
26
|
def post(self):
|
|
@@ -48,18 +49,21 @@ class FileStoreApiView(MethodView):
|
|
|
48
49
|
# get the file content from base64
|
|
49
50
|
content = base64.b64decode(base64_content)
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
# Use KnowledgeBaseService for ingestion
|
|
53
|
+
new_document = self.knowledge_base_service.ingest_document_sync(
|
|
54
|
+
company=company,
|
|
52
55
|
filename=filename,
|
|
53
56
|
content=content,
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
metadata=metadata
|
|
58
|
+
)
|
|
56
59
|
|
|
57
60
|
return jsonify({
|
|
58
61
|
"document_id": new_document.id,
|
|
62
|
+
"status": "active" # ingest_document_sync returns ACTIVE on success
|
|
59
63
|
}), 200
|
|
60
64
|
|
|
61
65
|
except Exception as e:
|
|
62
66
|
response = jsonify({"error": str(e)})
|
|
63
67
|
response.status_code = 500
|
|
64
68
|
|
|
65
|
-
return response
|
|
69
|
+
return response
|
iatoolkit/views/login_view.py
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
from flask.views import MethodView
|
|
7
7
|
from flask import (request, redirect, render_template, url_for,
|
|
8
|
-
render_template_string, flash)
|
|
8
|
+
render_template_string, flash, make_response)
|
|
9
9
|
from injector import inject
|
|
10
10
|
from iatoolkit.services.profile_service import ProfileService
|
|
11
11
|
from iatoolkit.services.jwt_service import JWTService
|
|
12
12
|
from iatoolkit.services.query_service import QueryService
|
|
13
|
-
from iatoolkit.services.
|
|
13
|
+
from iatoolkit.services.prompt_service import PromptService
|
|
14
14
|
from iatoolkit.services.branding_service import BrandingService
|
|
15
15
|
from iatoolkit.services.configuration_service import ConfigurationService
|
|
16
16
|
from iatoolkit.services.i18n_service import I18nService
|
|
@@ -32,6 +32,7 @@ class LoginView(BaseLoginView):
|
|
|
32
32
|
branding_data = self.branding_service.get_company_branding(company_short_name)
|
|
33
33
|
email = request.form.get('email')
|
|
34
34
|
password = request.form.get('password')
|
|
35
|
+
current_lang = request.form.get('lang') or request.args.get('lang') or 'en'
|
|
35
36
|
|
|
36
37
|
# 1. Authenticate internal user
|
|
37
38
|
auth_response = self.auth_service.login_local_user(
|
|
@@ -42,7 +43,14 @@ class LoginView(BaseLoginView):
|
|
|
42
43
|
|
|
43
44
|
if not auth_response['success']:
|
|
44
45
|
flash(auth_response["message"], 'error')
|
|
45
|
-
|
|
46
|
+
|
|
47
|
+
# Resolve the correct template name based on language (e.g., home_en.html or home_es.html)
|
|
48
|
+
template_name = self.utility.get_template_by_language("home")
|
|
49
|
+
home_template = self.utility.get_company_template(company_short_name, template_name)
|
|
50
|
+
|
|
51
|
+
if not home_template:
|
|
52
|
+
return render_template('error.html',
|
|
53
|
+
message=f'Home template ({template_name}) not found.'), 500
|
|
46
54
|
|
|
47
55
|
return render_template_string(
|
|
48
56
|
home_template,
|
|
@@ -57,7 +65,8 @@ class LoginView(BaseLoginView):
|
|
|
57
65
|
# 3. define URL to call when slow path is finished
|
|
58
66
|
target_url = url_for('finalize_no_token',
|
|
59
67
|
company_short_name=company_short_name,
|
|
60
|
-
_external=True
|
|
68
|
+
_external=True,
|
|
69
|
+
lang=current_lang)
|
|
61
70
|
|
|
62
71
|
# 2. Delegate the path decision to the centralized logic.
|
|
63
72
|
try:
|
|
@@ -97,6 +106,9 @@ class FinalizeContextView(MethodView):
|
|
|
97
106
|
|
|
98
107
|
def get(self, company_short_name: str, token: str = None):
|
|
99
108
|
try:
|
|
109
|
+
# get the languaje from the query string if it exists
|
|
110
|
+
current_lang = request.args.get('lang') or 'en'
|
|
111
|
+
|
|
100
112
|
session_info = self.profile_service.get_current_session_info()
|
|
101
113
|
if session_info:
|
|
102
114
|
# session exists, internal user
|
|
@@ -107,12 +119,12 @@ class FinalizeContextView(MethodView):
|
|
|
107
119
|
payload = self.jwt_service.validate_chat_jwt(token)
|
|
108
120
|
if not payload:
|
|
109
121
|
logging.warning("Fallo crítico: No se pudo leer el auth token.")
|
|
110
|
-
return redirect(url_for('home', company_short_name=company_short_name))
|
|
122
|
+
return redirect(url_for('home', company_short_name=company_short_name, lang=current_lang))
|
|
111
123
|
|
|
112
124
|
user_identifier = payload.get('user_identifier')
|
|
113
125
|
else:
|
|
114
126
|
logging.error("missing session information or auth token")
|
|
115
|
-
return redirect(url_for('home', company_short_name=company_short_name))
|
|
127
|
+
return redirect(url_for('home', company_short_name=company_short_name, lang=current_lang))
|
|
116
128
|
|
|
117
129
|
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
118
130
|
if not company:
|
|
@@ -121,6 +133,8 @@ class FinalizeContextView(MethodView):
|
|
|
121
133
|
message="Empresa no encontrada"), 404
|
|
122
134
|
branding_data = self.branding_service.get_company_branding(company_short_name)
|
|
123
135
|
|
|
136
|
+
default_llm_model, available_llm_models = self.config_service.get_llm_configuration(company_short_name)
|
|
137
|
+
|
|
124
138
|
# 2. Finalize the context rebuild (the heavy task).
|
|
125
139
|
self.query_service.set_context_for_llm(
|
|
126
140
|
company_short_name=company_short_name,
|
|
@@ -134,6 +148,8 @@ class FinalizeContextView(MethodView):
|
|
|
134
148
|
# Get the entire 'js_messages' block in the correct language.
|
|
135
149
|
js_translations = self.i18n_service.get_translation_block('js_messages')
|
|
136
150
|
|
|
151
|
+
# Importante: no envolver con make_response; dejar que Flask gestione
|
|
152
|
+
# tanto strings como tuplas (string, status) que pueda devolver render_template
|
|
137
153
|
return render_template(
|
|
138
154
|
"chat.html",
|
|
139
155
|
company_short_name=company_short_name,
|
|
@@ -142,7 +158,9 @@ class FinalizeContextView(MethodView):
|
|
|
142
158
|
prompts=prompts,
|
|
143
159
|
onboarding_cards=onboarding_cards,
|
|
144
160
|
js_translations=js_translations,
|
|
145
|
-
redeem_token=token
|
|
161
|
+
redeem_token=token,
|
|
162
|
+
llm_default_model=default_llm_model,
|
|
163
|
+
llm_available_models=available_llm_models,
|
|
146
164
|
)
|
|
147
165
|
|
|
148
166
|
except Exception as e:
|
|
@@ -150,4 +168,3 @@ class FinalizeContextView(MethodView):
|
|
|
150
168
|
company_short_name=company_short_name,
|
|
151
169
|
branding=branding_data,
|
|
152
170
|
message=f"An unexpected error occurred during context loading: {str(e)}"), 500
|
|
153
|
-
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
6
|
from flask.views import MethodView
|
|
7
|
-
from flask import redirect, url_for, jsonify
|
|
7
|
+
from flask import redirect, url_for, jsonify, request, g
|
|
8
8
|
from injector import inject
|
|
9
9
|
from iatoolkit.services.auth_service import AuthService
|
|
10
10
|
from iatoolkit.services.profile_service import ProfileService
|
|
@@ -33,7 +33,15 @@ class LogoutApiView(MethodView):
|
|
|
33
33
|
# get URL for redirection
|
|
34
34
|
url_for_redirect = company.parameters.get('external_urls', {}).get('logout_url')
|
|
35
35
|
if not url_for_redirect:
|
|
36
|
-
|
|
36
|
+
current_lang = (
|
|
37
|
+
request.args.get('lang')
|
|
38
|
+
or getattr(g, 'lang', None)
|
|
39
|
+
or 'en'
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
url_for_redirect = url_for('home',
|
|
43
|
+
company_short_name=company_short_name,
|
|
44
|
+
lang=current_lang)
|
|
37
45
|
|
|
38
46
|
# clear de session cookie
|
|
39
47
|
SessionManager.clear()
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
from flask import jsonify
|
|
7
7
|
from flask.views import MethodView
|
|
8
|
-
from iatoolkit.services.
|
|
8
|
+
from iatoolkit.services.prompt_service import PromptService
|
|
9
9
|
from iatoolkit.services.auth_service import AuthService
|
|
10
10
|
from injector import inject
|
|
11
11
|
import logging
|