iatoolkit 0.22.0__py3-none-any.whl → 0.50.0__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/common/routes.py +32 -32
- iatoolkit/common/session_manager.py +0 -1
- iatoolkit/common/util.py +0 -21
- iatoolkit/iatoolkit.py +5 -18
- iatoolkit/infra/llm_client.py +3 -5
- iatoolkit/infra/redis_session_manager.py +48 -2
- iatoolkit/repositories/models.py +1 -2
- iatoolkit/services/auth_service.py +74 -0
- iatoolkit/services/dispatcher_service.py +12 -21
- iatoolkit/services/excel_service.py +15 -15
- iatoolkit/services/history_service.py +2 -11
- iatoolkit/services/profile_service.py +83 -25
- iatoolkit/services/query_service.py +132 -82
- iatoolkit/services/tasks_service.py +1 -1
- iatoolkit/services/user_feedback_service.py +3 -6
- iatoolkit/services/user_session_context_service.py +112 -54
- iatoolkit/static/js/chat_feedback.js +1 -1
- iatoolkit/static/js/chat_history.js +1 -5
- iatoolkit/static/js/chat_main.js +1 -1
- iatoolkit/static/styles/landing_page.css +62 -2
- iatoolkit/system_prompts/query_main.prompt +3 -12
- iatoolkit/templates/_login_widget.html +7 -9
- iatoolkit/templates/chat.html +78 -4
- iatoolkit/templates/error.html +1 -1
- iatoolkit/templates/index.html +38 -9
- iatoolkit/templates/{home.html → login_test.html} +12 -52
- iatoolkit/views/external_login_view.py +50 -111
- iatoolkit/views/{file_store_view.py → file_store_api_view.py} +4 -4
- iatoolkit/views/history_api_view.py +52 -0
- iatoolkit/views/init_context_api_view.py +62 -0
- iatoolkit/views/llmquery_api_view.py +50 -0
- iatoolkit/views/llmquery_web_view.py +38 -0
- iatoolkit/views/{home_view.py → login_test_view.py} +2 -5
- iatoolkit/views/login_view.py +79 -56
- iatoolkit/views/{prompt_view.py → prompt_api_view.py} +4 -4
- iatoolkit/views/{user_feedback_view.py → user_feedback_api_view.py} +16 -19
- {iatoolkit-0.22.0.dist-info → iatoolkit-0.50.0.dist-info}/METADATA +2 -2
- {iatoolkit-0.22.0.dist-info → iatoolkit-0.50.0.dist-info}/RECORD +40 -41
- iatoolkit/common/auth.py +0 -200
- iatoolkit/templates/login.html +0 -43
- iatoolkit/views/download_file_view.py +0 -58
- iatoolkit/views/history_view.py +0 -57
- iatoolkit/views/init_context_view.py +0 -35
- iatoolkit/views/llmquery_view.py +0 -65
- {iatoolkit-0.22.0.dist-info → iatoolkit-0.50.0.dist-info}/WHEEL +0 -0
- {iatoolkit-0.22.0.dist-info → iatoolkit-0.50.0.dist-info}/top_level.txt +0 -0
|
@@ -7,60 +7,20 @@
|
|
|
7
7
|
<!-- Contenido principal con espaciado adicional respecto al header -->
|
|
8
8
|
<div class="row flex-fill mt-5 flex-wrap">
|
|
9
9
|
|
|
10
|
-
{% if not user or user_company != company_short_name %}
|
|
11
|
-
<!-- Sección de login (se coloca primero en el HTML para mobile) -->
|
|
12
|
-
<div class="col-12 col-lg-5 offset-lg-1">
|
|
13
|
-
<div class="border rounded p-4 shadow-sm bg-light">
|
|
14
|
-
<h4 class="text-muted fw-semibold text-start mb-3">login integrado (IAToolkit)</h4>
|
|
15
|
-
<form id="login-form"
|
|
16
|
-
action="{{ url_for('initiate_login', company_short_name=company_short_name, external_login=True) }}"
|
|
17
|
-
method="post">
|
|
18
|
-
<div class="mb-3">
|
|
19
|
-
<label for="company_short_name" class="form-label d-block text-muted">Empresa</label>
|
|
20
|
-
<select id="company_short_name" name="company_short_name" class="form-select" required>
|
|
21
|
-
<option value="" disabled selected>Selecciona una empresa</option>
|
|
22
|
-
{% for company in companies %}
|
|
23
|
-
<option value="{{ company.short_name }}"
|
|
24
|
-
{% if company.short_name == company_short_name %}selected{% endif %}>
|
|
25
|
-
{{ company.short_name }}
|
|
26
|
-
</option>
|
|
27
|
-
{% endfor %}
|
|
28
|
-
</select>
|
|
29
|
-
</div>
|
|
30
10
|
|
|
31
|
-
|
|
32
|
-
<label for="email" class="form-label d-block text-muted">Correo Electrónico</label>
|
|
33
|
-
<input type="email" id="email" name="email" class="form-control" required>
|
|
34
|
-
</div>
|
|
35
|
-
<div class="mb-3">
|
|
36
|
-
<label for="password" class="form-label d-block text-muted">Contraseña</label>
|
|
37
|
-
<input type="password" id="password" name="password" class="form-control" required>
|
|
38
|
-
</div>
|
|
39
|
-
<button type="submit" class="btn btn-primary w-100">
|
|
40
|
-
Iniciar Sesión</button>
|
|
41
|
-
</form>
|
|
42
|
-
|
|
43
|
-
<div class="text-center mt-3">
|
|
44
|
-
<a href="{% if company_short_name %}{{ url_for('signup', company_short_name=company_short_name) }}{% else %}#{% endif %}"
|
|
45
|
-
id="signup-link"
|
|
46
|
-
class="btn btn-outline-primary w-100">Registrarse</a>
|
|
47
|
-
</div>
|
|
48
|
-
<div class="text-center mt-3">
|
|
49
|
-
<a href="{{ url_for('forgot_password', company_short_name=company_short_name) }}" class="text-decoration-none text-muted fw-semibold">
|
|
50
|
-
¿Olvidaste tu contraseña?
|
|
51
|
-
</a>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
{% endif %}
|
|
56
|
-
|
|
57
|
-
<!-- Sección de JWT -->
|
|
11
|
+
<!-- login desde sistema externo -->
|
|
58
12
|
<div class="col-12 col-lg-5 offset-lg-1">
|
|
59
13
|
<div class="border rounded p-4 shadow-sm bg-light">
|
|
60
|
-
<
|
|
14
|
+
<h3 class="text-muted fw-semibold text-start mb-3">login externo (api-key)</h3>
|
|
15
|
+
<div class="text-center mb-4">
|
|
16
|
+
<p class="text-muted widget-intro-text">
|
|
17
|
+
Este formulario permite testear el acceso a IAToolkit desde un portal externo utilizando una api-key.
|
|
18
|
+
El external user ID es el nombre del usuario ya autentificado en algún portal interno de la empresa.
|
|
19
|
+
</p>
|
|
20
|
+
</div>
|
|
61
21
|
<form id="jwt-form" method="post">
|
|
62
22
|
<div class="mb-3">
|
|
63
|
-
<label for="company_name" class="form-label d-block
|
|
23
|
+
<label for="company_name" class="form-label d-block">Empresa</label>
|
|
64
24
|
<select id="company_name" name="company_short_name" class="form-select" required>
|
|
65
25
|
<option value="" disabled selected>Selecciona una empresa</option>
|
|
66
26
|
{% for company in companies %}
|
|
@@ -73,7 +33,7 @@
|
|
|
73
33
|
</div>
|
|
74
34
|
|
|
75
35
|
<div class="mb-3">
|
|
76
|
-
<label for="external_user_id" class="form-label d-block
|
|
36
|
+
<label for="external_user_id" class="form-label d-block">External user ID</label>
|
|
77
37
|
<input type="text" id="external_user_id" name="external_user_id" class="form-control" required>
|
|
78
38
|
</div>
|
|
79
39
|
|
|
@@ -81,7 +41,7 @@
|
|
|
81
41
|
id="initiateJwtChatButton"
|
|
82
42
|
class="ml-5 btn btn-primary">
|
|
83
43
|
<span class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
|
|
84
|
-
Iniciar Sesión
|
|
44
|
+
Iniciar Sesión
|
|
85
45
|
</button>
|
|
86
46
|
</form>
|
|
87
47
|
</div>
|
|
@@ -112,7 +72,7 @@
|
|
|
112
72
|
|
|
113
73
|
// Actualizar action del formulario "Iniciar Sesión"
|
|
114
74
|
if (selectedCompany && selectedCompany.trim() !== '') {
|
|
115
|
-
const loginAction = '/' + selectedCompany + '/
|
|
75
|
+
const loginAction = '/' + selectedCompany + '/chat';
|
|
116
76
|
$('#login-form').attr('action', loginAction); // Actualizamos la URL del form
|
|
117
77
|
} else {
|
|
118
78
|
$('#login-form').attr('action', '#'); // URL genérica si no hay selección
|
|
@@ -5,150 +5,89 @@
|
|
|
5
5
|
|
|
6
6
|
import os
|
|
7
7
|
import logging
|
|
8
|
-
from flask import request, jsonify, render_template, url_for
|
|
8
|
+
from flask import request, jsonify, render_template, url_for
|
|
9
9
|
from flask.views import MethodView
|
|
10
10
|
from injector import inject
|
|
11
|
-
from iatoolkit.
|
|
11
|
+
from iatoolkit.services.auth_service import AuthService
|
|
12
12
|
from iatoolkit.services.profile_service import ProfileService
|
|
13
13
|
from iatoolkit.services.query_service import QueryService
|
|
14
14
|
from iatoolkit.services.prompt_manager_service import PromptService
|
|
15
|
-
from iatoolkit.services.jwt_service import JWTService
|
|
16
15
|
from iatoolkit.services.branding_service import BrandingService
|
|
17
16
|
from iatoolkit.services.onboarding_service import OnboardingService
|
|
18
|
-
from iatoolkit.services.jwt_service import JWTService
|
|
19
17
|
|
|
20
18
|
|
|
21
19
|
class InitiateExternalChatView(MethodView):
|
|
22
20
|
@inject
|
|
23
21
|
def __init__(self,
|
|
24
|
-
iauthentication:
|
|
22
|
+
iauthentication: AuthService,
|
|
25
23
|
branding_service: BrandingService,
|
|
26
24
|
profile_service: ProfileService,
|
|
27
25
|
onboarding_service: OnboardingService,
|
|
28
|
-
|
|
26
|
+
query_service: QueryService,
|
|
27
|
+
prompt_service: PromptService
|
|
29
28
|
):
|
|
30
29
|
self.iauthentication = iauthentication
|
|
31
30
|
self.branding_service = branding_service
|
|
32
31
|
self.profile_service = profile_service
|
|
33
32
|
self.onboarding_service = onboarding_service
|
|
34
|
-
self.
|
|
33
|
+
self.query_service = query_service
|
|
34
|
+
self.prompt_service = prompt_service
|
|
35
35
|
|
|
36
36
|
def post(self, company_short_name: str):
|
|
37
37
|
data = request.get_json()
|
|
38
38
|
if not data or 'external_user_id' not in data:
|
|
39
39
|
return jsonify({"error": "Falta external_user_id"}), 400
|
|
40
40
|
|
|
41
|
-
external_user_id = data['external_user_id']
|
|
42
|
-
|
|
43
41
|
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
44
42
|
if not company:
|
|
45
43
|
return jsonify({"error": "Empresa no encontrada"}), 404
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
iaut = self.iauthentication.verify(
|
|
49
|
-
company_short_name,
|
|
50
|
-
body_external_user_id=external_user_id
|
|
51
|
-
)
|
|
52
|
-
if not iaut.get("success"):
|
|
53
|
-
return jsonify(iaut), 401
|
|
54
|
-
|
|
55
|
-
# 2. Generate a short-lived initiation token.
|
|
56
|
-
initiation_token = self.jwt_service.generate_chat_jwt(
|
|
57
|
-
company_id=company.id,
|
|
58
|
-
company_short_name=company.short_name,
|
|
59
|
-
external_user_id=external_user_id,
|
|
60
|
-
expires_delta_seconds=180
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
# 2. Get branding and onboarding data for the shell page
|
|
64
|
-
branding_data = self.branding_service.get_company_branding(company)
|
|
65
|
-
onboarding_cards = self.onboarding_service.get_onboarding_cards(company)
|
|
66
|
-
|
|
67
|
-
# 4. Generate the URL for the iframe's SRC, now with the secure token.
|
|
68
|
-
target_url = url_for('external_login',
|
|
69
|
-
company_short_name=company_short_name,
|
|
70
|
-
init_token=initiation_token,
|
|
71
|
-
_external=True)
|
|
72
|
-
|
|
73
|
-
# 5. Render the shell.
|
|
74
|
-
return render_template("onboarding_shell.html",
|
|
75
|
-
iframe_src_url=target_url,
|
|
76
|
-
branding=branding_data,
|
|
77
|
-
onboarding_cards=onboarding_cards
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
class ExternalChatLoginView(MethodView):
|
|
81
|
-
@inject
|
|
82
|
-
def __init__(self,
|
|
83
|
-
profile_service: ProfileService,
|
|
84
|
-
query_service: QueryService,
|
|
85
|
-
prompt_service: PromptService,
|
|
86
|
-
iauthentication: IAuthentication,
|
|
87
|
-
jwt_service: JWTService,
|
|
88
|
-
branding_service: BrandingService
|
|
89
|
-
):
|
|
90
|
-
self.profile_service = profile_service
|
|
91
|
-
self.query_service = query_service
|
|
92
|
-
self.prompt_service = prompt_service
|
|
93
|
-
self.iauthentication = iauthentication
|
|
94
|
-
self.jwt_service = jwt_service
|
|
95
|
-
self.branding_service = branding_service
|
|
96
|
-
|
|
97
|
-
def get(self, company_short_name: str):
|
|
98
|
-
# 1. Validate the initiation token from the URL
|
|
99
|
-
init_token = request.args.get('init_token')
|
|
100
|
-
if not init_token:
|
|
101
|
-
return "Falta el token de iniciación.", 401
|
|
102
|
-
|
|
103
|
-
# Reutilizamos el validador de JWT, ya que el token tiene la misma estructura
|
|
104
|
-
payload = self.jwt_service.validate_chat_jwt(init_token, company_short_name)
|
|
105
|
-
if not payload:
|
|
106
|
-
return "Token de iniciación inválido o expirado.", 401
|
|
107
|
-
|
|
108
|
-
# 2. Extract user ID securely from the validated token
|
|
109
|
-
external_user_id = payload.get('external_user_id')
|
|
45
|
+
external_user_id = data['external_user_id']
|
|
110
46
|
if not external_user_id:
|
|
111
|
-
return "
|
|
112
|
-
|
|
113
|
-
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
114
|
-
if not company:
|
|
115
|
-
logging.error(f'Company {company_short_name} not found')
|
|
116
|
-
return jsonify({"error": "Empresa no encontrada"}), 404
|
|
47
|
+
return jsonify({"error": "missing external_user_id"}), 404
|
|
117
48
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
company_short_name=company.short_name,
|
|
123
|
-
external_user_id=external_user_id,
|
|
124
|
-
expires_delta_seconds=3600 * 8 # 8 horas
|
|
125
|
-
)
|
|
126
|
-
if not token:
|
|
127
|
-
raise Exception("No se pudo generar el token de sesión (JWT).")
|
|
49
|
+
# 1. Authenticate the API call.
|
|
50
|
+
iaut = self.iauthentication.verify()
|
|
51
|
+
if not iaut.get("success"):
|
|
52
|
+
return jsonify(iaut), 401
|
|
128
53
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
company_short_name=company_short_name,
|
|
132
|
-
external_user_id=external_user_id
|
|
133
|
-
)
|
|
54
|
+
# 2. Delegate session creation to ProfileService.
|
|
55
|
+
self.profile_service.create_external_user_session(company, external_user_id)
|
|
134
56
|
|
|
135
|
-
|
|
136
|
-
|
|
57
|
+
# 3. prepare and decide the path
|
|
58
|
+
prep_result = self.query_service.prepare_context(
|
|
59
|
+
company_short_name=company_short_name, user_identifier=external_user_id
|
|
60
|
+
)
|
|
137
61
|
|
|
138
|
-
|
|
62
|
+
if prep_result.get('rebuild_needed'):
|
|
139
63
|
branding_data = self.branding_service.get_company_branding(company)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
64
|
+
onboarding_cards = self.onboarding_service.get_onboarding_cards(company)
|
|
65
|
+
target_url = url_for('login', company_short_name=company_short_name,
|
|
66
|
+
_external=True)
|
|
67
|
+
|
|
68
|
+
return render_template(
|
|
69
|
+
"onboarding_shell.html",
|
|
70
|
+
iframe_src_url=target_url,
|
|
71
|
+
branding=branding_data,
|
|
72
|
+
onboarding_cards=onboarding_cards
|
|
73
|
+
)
|
|
74
|
+
else:
|
|
75
|
+
# fast path, the context is already on the cache, render the chat directly
|
|
76
|
+
try:
|
|
77
|
+
session_info = self.profile_service.get_current_session_info()
|
|
78
|
+
user_profile = session_info.get('profile', {})
|
|
79
|
+
|
|
80
|
+
prompts = self.prompt_service.get_user_prompts(company_short_name)
|
|
81
|
+
branding_data = self.branding_service.get_company_branding(company)
|
|
82
|
+
|
|
83
|
+
return render_template("chat.html",
|
|
84
|
+
company_short_name=company_short_name,
|
|
85
|
+
user_is_local=user_profile.get('user_is_local'),
|
|
86
|
+
user_email=user_profile.get('user_email'),
|
|
87
|
+
branding=branding_data,
|
|
88
|
+
prompts=prompts,
|
|
89
|
+
iatoolkit_base_url=os.getenv('IATOOLKIT_BASE_URL'),
|
|
90
|
+
), 200
|
|
91
|
+
except Exception as e:
|
|
92
|
+
logging.exception(f"Error en el camino rápido para {company_short_name}/{external_user_id}: {e}")
|
|
93
|
+
return jsonify({"error": f"Error interno al iniciar el chat. {str(e)}"}), 500
|
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
from flask.views import MethodView
|
|
7
7
|
from flask import request, jsonify
|
|
8
8
|
from iatoolkit.services.load_documents_service import LoadDocumentsService
|
|
9
|
-
from iatoolkit.
|
|
9
|
+
from iatoolkit.services.auth_service import AuthService
|
|
10
10
|
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
11
11
|
from injector import inject
|
|
12
12
|
import base64
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
class
|
|
15
|
+
class FileStoreApiView(MethodView):
|
|
16
16
|
@inject
|
|
17
17
|
def __init__(self,
|
|
18
|
-
iauthentication:
|
|
18
|
+
iauthentication: AuthService,
|
|
19
19
|
doc_service: LoadDocumentsService,
|
|
20
20
|
profile_repo: ProfileRepo,):
|
|
21
21
|
self.iauthentication = iauthentication
|
|
@@ -43,7 +43,7 @@ class FileStoreView(MethodView):
|
|
|
43
43
|
return jsonify({"error": f"La empresa {company_short_name} no existe"}), 400
|
|
44
44
|
|
|
45
45
|
# get access credentials
|
|
46
|
-
iaut = self.iauthentication.verify(
|
|
46
|
+
iaut = self.iauthentication.verify()
|
|
47
47
|
if not iaut.get("success"):
|
|
48
48
|
return jsonify(iaut), 401
|
|
49
49
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from flask import request, jsonify
|
|
7
|
+
from flask.views import MethodView
|
|
8
|
+
from iatoolkit.services.history_service import HistoryService
|
|
9
|
+
from iatoolkit.services.profile_service import ProfileService
|
|
10
|
+
from injector import inject
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HistoryApiView(MethodView):
|
|
15
|
+
"""
|
|
16
|
+
Handles requests from the web UI to fetch a user's query history.
|
|
17
|
+
Authentication is based on the active Flask session.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
@inject
|
|
21
|
+
def __init__(self,
|
|
22
|
+
profile_service: ProfileService,
|
|
23
|
+
history_service: HistoryService):
|
|
24
|
+
self.profile_service = profile_service
|
|
25
|
+
self.history_service = history_service
|
|
26
|
+
|
|
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")
|
|
31
|
+
|
|
32
|
+
if not user_identifier:
|
|
33
|
+
return jsonify({'error_message': 'Usuario no autenticado o sesión inválida'}), 401
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
# 2. Call the history service with the unified identifier.
|
|
37
|
+
# The service's signature should now only expect user_identifier.
|
|
38
|
+
response = self.history_service.get_history(
|
|
39
|
+
company_short_name=company_short_name,
|
|
40
|
+
user_identifier=user_identifier
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if "error" in response:
|
|
44
|
+
# Handle errors reported by the service itself.
|
|
45
|
+
return jsonify({'error_message': response["error"]}), 400
|
|
46
|
+
|
|
47
|
+
return jsonify(response), 200
|
|
48
|
+
|
|
49
|
+
except Exception as e:
|
|
50
|
+
logging.exception(
|
|
51
|
+
f"Unexpected error fetching history for {company_short_name}/{user_identifier}: {e}")
|
|
52
|
+
return jsonify({"error_message": "Ha ocurrido un error inesperado en el servidor."}), 500
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from flask.views import MethodView
|
|
2
|
+
from injector import inject
|
|
3
|
+
from iatoolkit.services.query_service import QueryService
|
|
4
|
+
from iatoolkit.services.profile_service import ProfileService
|
|
5
|
+
from iatoolkit.services.auth_service import AuthService
|
|
6
|
+
from flask import jsonify, request
|
|
7
|
+
import logging
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class InitContextApiView(MethodView):
|
|
11
|
+
"""
|
|
12
|
+
API endpoint to force a full context rebuild for a user.
|
|
13
|
+
Handles both web users (via session) and API users (via API Key).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@inject
|
|
17
|
+
def __init__(self,
|
|
18
|
+
auth_service: AuthService,
|
|
19
|
+
query_service: QueryService,
|
|
20
|
+
profile_service: ProfileService):
|
|
21
|
+
self.auth_service = auth_service
|
|
22
|
+
self.query_service = query_service
|
|
23
|
+
self.profile_service = profile_service
|
|
24
|
+
|
|
25
|
+
def post(self, company_short_name: str):
|
|
26
|
+
"""
|
|
27
|
+
Cleans and rebuilds the context. The user is identified either by
|
|
28
|
+
an active web session or by the external_user_id in the JSON payload
|
|
29
|
+
for API calls.
|
|
30
|
+
"""
|
|
31
|
+
# 1. Authenticate the request. This handles both session and API Key.
|
|
32
|
+
auth_result = self.auth_service.verify()
|
|
33
|
+
if not auth_result.get("success"):
|
|
34
|
+
return jsonify({"error": auth_result.get("error_message")}), auth_result.get("status_code", 401)
|
|
35
|
+
|
|
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
|
+
|
|
40
|
+
try:
|
|
41
|
+
# 2. Execute the forced rebuild sequence using the unified identifier.
|
|
42
|
+
self.query_service.session_context.clear_all_context(company_short_name, user_identifier)
|
|
43
|
+
logging.info(f"Context for {company_short_name}/{user_identifier} has been cleared.")
|
|
44
|
+
|
|
45
|
+
self.query_service.prepare_context(
|
|
46
|
+
company_short_name=company_short_name,
|
|
47
|
+
user_identifier=user_identifier
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
self.query_service.finalize_context_rebuild(
|
|
51
|
+
company_short_name=company_short_name,
|
|
52
|
+
user_identifier=user_identifier
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
logging.info(f"Context for {company_short_name}/{user_identifier} rebuilt successfully.")
|
|
56
|
+
|
|
57
|
+
# 3. Respond with JSON, as this is an API endpoint.
|
|
58
|
+
return jsonify({'status': 'OK', 'message': 'Context has been reloaded successfully.'}), 200
|
|
59
|
+
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logging.exception(f"Error forcing context rebuild for {user_identifier}: {e}")
|
|
62
|
+
return jsonify({"error_message": str(e)}), 500
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from flask.views import MethodView
|
|
2
|
+
from flask import request, jsonify
|
|
3
|
+
from injector import inject
|
|
4
|
+
from iatoolkit.services.query_service import QueryService
|
|
5
|
+
from iatoolkit.services.auth_service import AuthService
|
|
6
|
+
from iatoolkit.services.profile_service import ProfileService
|
|
7
|
+
import logging
|
|
8
|
+
|
|
9
|
+
class LLMQueryApiView(MethodView):
|
|
10
|
+
"""
|
|
11
|
+
API-only endpoint for submitting queries. Authenticates via API Key.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
@inject
|
|
15
|
+
def __init__(self, auth_service: AuthService, query_service: QueryService, profile_service: ProfileService):
|
|
16
|
+
self.auth_service = auth_service
|
|
17
|
+
self.query_service = query_service
|
|
18
|
+
self.profile_service = profile_service
|
|
19
|
+
|
|
20
|
+
def post(self, company_short_name: str):
|
|
21
|
+
# 1. Authenticate the API request.
|
|
22
|
+
auth_result = self.auth_service.verify()
|
|
23
|
+
if not auth_result.get("success"):
|
|
24
|
+
return jsonify({"error": auth_result.get("error_message")}), auth_result.get("status_code", 401)
|
|
25
|
+
|
|
26
|
+
# 2. Get the user identifier from the payload.
|
|
27
|
+
user_identifier = auth_result.get('user_identifier')
|
|
28
|
+
if not user_identifier:
|
|
29
|
+
return jsonify({"error": "Payload must include 'user_identifier'"}), 400
|
|
30
|
+
|
|
31
|
+
data = request.get_json()
|
|
32
|
+
if not data:
|
|
33
|
+
return jsonify({"error": "Invalid JSON body"}), 400
|
|
34
|
+
|
|
35
|
+
# 3. Ensure session state exists for this API user (lazy creation).
|
|
36
|
+
profile = self.profile_service.get_profile_by_identifier(company_short_name, user_identifier)
|
|
37
|
+
if not profile:
|
|
38
|
+
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
39
|
+
self.profile_service.create_external_user_session(company, user_identifier)
|
|
40
|
+
|
|
41
|
+
# 4. Call the unified query service method.
|
|
42
|
+
result = self.query_service.llm_query(
|
|
43
|
+
company_short_name=company_short_name,
|
|
44
|
+
user_identifier=user_identifier,
|
|
45
|
+
question=data.get('question', ''),
|
|
46
|
+
prompt_name=data.get('prompt_name'),
|
|
47
|
+
client_data=data.get('client_data', {}),
|
|
48
|
+
files=data.get('files', [])
|
|
49
|
+
)
|
|
50
|
+
return jsonify(result)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from flask.views import MethodView
|
|
2
|
+
from flask import request, jsonify
|
|
3
|
+
from injector import inject
|
|
4
|
+
from iatoolkit.services.query_service import QueryService
|
|
5
|
+
from iatoolkit.services.auth_service import AuthService # Use AuthService for session check
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LLMQueryWebView(MethodView): # Renamed for clarity
|
|
9
|
+
"""
|
|
10
|
+
Web-only endpoint for submitting queries from the chat UI.
|
|
11
|
+
Authenticates via Flask session cookie.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
@inject
|
|
15
|
+
def __init__(self, auth_service: AuthService, query_service: QueryService):
|
|
16
|
+
self.auth_service = auth_service
|
|
17
|
+
self.query_service = query_service
|
|
18
|
+
|
|
19
|
+
def post(self, company_short_name: str):
|
|
20
|
+
# 1. Authenticate the web session request.
|
|
21
|
+
auth_result = self.auth_service.verify()
|
|
22
|
+
if not auth_result.get("success"):
|
|
23
|
+
return jsonify({"error": auth_result.get("error_message")}), auth_result.get("status_code", 401)
|
|
24
|
+
|
|
25
|
+
# 2. Get the guaranteed user_identifier from the auth result.
|
|
26
|
+
user_identifier = auth_result['user_identifier']
|
|
27
|
+
data = request.get_json() or {}
|
|
28
|
+
|
|
29
|
+
# 3. Call the unified query service method.
|
|
30
|
+
result = self.query_service.llm_query(
|
|
31
|
+
company_short_name=company_short_name,
|
|
32
|
+
user_identifier=user_identifier,
|
|
33
|
+
question=data.get('question', ''),
|
|
34
|
+
prompt_name=data.get('prompt_name'),
|
|
35
|
+
client_data=data.get('client_data', {}),
|
|
36
|
+
files=data.get('files', [])
|
|
37
|
+
)
|
|
38
|
+
return jsonify(result)
|
|
@@ -10,24 +10,21 @@ from iatoolkit.services.profile_service import ProfileService
|
|
|
10
10
|
import os
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class
|
|
13
|
+
class LoginTest(MethodView):
|
|
14
14
|
@inject
|
|
15
15
|
def __init__(self,
|
|
16
16
|
profile_service: ProfileService):
|
|
17
17
|
self.profile_service = profile_service
|
|
18
18
|
|
|
19
19
|
def get(self):
|
|
20
|
-
user_agent = request.user_agent
|
|
21
|
-
is_mobile = user_agent.platform in ["android", "iphone", "ipad"] or "mobile" in user_agent.string.lower()
|
|
22
20
|
alert_message = request.args.get('alert_message', None)
|
|
23
21
|
companies = self.profile_service.get_companies()
|
|
24
22
|
|
|
25
23
|
# Esta API_KEY para el login
|
|
26
24
|
api_key_for_login = os.getenv("IATOOLKIT_API_KEY", "tu_api_key_por_defecto_o_error")
|
|
27
25
|
|
|
28
|
-
return render_template('
|
|
26
|
+
return render_template('login_test.html',
|
|
29
27
|
companies=companies,
|
|
30
|
-
is_mobile=is_mobile,
|
|
31
28
|
alert_message=alert_message,
|
|
32
29
|
alert_icon='success' if alert_message else None,
|
|
33
30
|
api_key=api_key_for_login
|