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/templates/login.html
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
{% extends "base.html" %}
|
|
2
|
-
|
|
3
|
-
{% block title %}Inicio de Sesión{% endblock %}
|
|
4
|
-
|
|
5
|
-
{% block content %}
|
|
6
|
-
<div class="container d-flex justify-content-center align-items-center vh-100">
|
|
7
|
-
<div class="col-11 col-md-6 col-lg-4 border rounded p-3 shadow-sm">
|
|
8
|
-
<h4 class="text-muted fw-semibold text-start mb-3">Iniciar Sesión - {{ company.name }}
|
|
9
|
-
</h4>
|
|
10
|
-
<form action="{{ url_for('login', company_short_name=company_short_name) }}"
|
|
11
|
-
method="post">
|
|
12
|
-
<div class="mb-3">
|
|
13
|
-
<label for="email" class="form-label text-muted">Correo Electrónico</label>
|
|
14
|
-
<input type="email" id="email" name="email"
|
|
15
|
-
class="form-control" required
|
|
16
|
-
value="{{ form_data.email if form_data else '' }}">
|
|
17
|
-
</div>
|
|
18
|
-
<div class="mb-3">
|
|
19
|
-
<label for="password" class="form-label">Contraseña</label>
|
|
20
|
-
<input type="password" id="password" name="password"
|
|
21
|
-
class="form-control" required
|
|
22
|
-
value="{{ form_data.password if form_data else '' }}">
|
|
23
|
-
</div>
|
|
24
|
-
<button type="submit" class="btn btn-primary w-100">Iniciar Sesión</button>
|
|
25
|
-
|
|
26
|
-
<p class="text-muted text-start mt-3" style="text-align: justify;">
|
|
27
|
-
Ingresa tus credenciales para acceder a tu cuenta. Si olvidaste tu contraseña,
|
|
28
|
-
puedes recuperarla fácilmente a través del siguiente enlace.
|
|
29
|
-
</p>
|
|
30
|
-
<div class="text-center mt-3">
|
|
31
|
-
<a href="{{ url_for('signup', company_short_name=company_short_name) }}">
|
|
32
|
-
¿No tienes cuenta? Regístrate
|
|
33
|
-
</a>
|
|
34
|
-
</div>
|
|
35
|
-
<div class="text-center mt-3">
|
|
36
|
-
<a href="{{ url_for('forgot_password', company_short_name=company_short_name) }}">
|
|
37
|
-
¿Olvidaste tu contraseña?
|
|
38
|
-
</a>
|
|
39
|
-
</div>
|
|
40
|
-
</form>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
{% endblock %}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
# Product: IAToolkit
|
|
3
|
-
#
|
|
4
|
-
# IAToolkit is open source software.
|
|
5
|
-
|
|
6
|
-
from flask import request, jsonify, current_app
|
|
7
|
-
from flask.views import MethodView
|
|
8
|
-
from injector import inject
|
|
9
|
-
import logging
|
|
10
|
-
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
11
|
-
from iatoolkit.services.jwt_service import JWTService
|
|
12
|
-
from typing import Optional
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
# Necesitaremos JWT_EXPIRATION_SECONDS_CHAT de la configuración de la app
|
|
16
|
-
# Se podría inyectar o acceder globalmente.
|
|
17
|
-
|
|
18
|
-
class ChatTokenRequestView(MethodView):
|
|
19
|
-
@inject
|
|
20
|
-
def __init__(self, profile_repo: ProfileRepo, jwt_service: JWTService):
|
|
21
|
-
self.profile_repo = profile_repo
|
|
22
|
-
self.jwt_service = jwt_service
|
|
23
|
-
|
|
24
|
-
def _authenticate_requesting_company_via_api_key(self) -> tuple[
|
|
25
|
-
Optional[int], Optional[str], Optional[tuple[dict, int]]]:
|
|
26
|
-
"""
|
|
27
|
-
Autentica a la compañía que solicita el token JWT usando su API Key.
|
|
28
|
-
Retorna (company_id, company_short_name, None) en éxito.
|
|
29
|
-
Retorna (None, None, (error_json, status_code)) en fallo.
|
|
30
|
-
"""
|
|
31
|
-
api_key_header = request.headers.get('Authorization')
|
|
32
|
-
if not api_key_header or not api_key_header.startswith('Bearer '):
|
|
33
|
-
return None, None, ({"error": "API Key faltante o mal formada en el header Authorization"}, 401)
|
|
34
|
-
|
|
35
|
-
api_key_value = api_key_header.split('Bearer ')[1]
|
|
36
|
-
try:
|
|
37
|
-
api_key_entry = self.profile_repo.get_active_api_key_entry(api_key_value)
|
|
38
|
-
if not api_key_entry:
|
|
39
|
-
return None, None, ({"error": "API Key inválida o inactiva"}, 401)
|
|
40
|
-
|
|
41
|
-
# api_key_entry.company ya está cargado por joinedload en get_active_api_key_entry
|
|
42
|
-
if not api_key_entry.company: # Sanity check
|
|
43
|
-
logging.error(
|
|
44
|
-
f"ChatTokenRequest: API Key {api_key_value[:5]}... no tiene compañía asociada a pesar de ser válida.")
|
|
45
|
-
return None, None, ({"error": "Error interno del servidor al verificar API Key"}, 500)
|
|
46
|
-
|
|
47
|
-
return api_key_entry.company_id, api_key_entry.company.short_name, None
|
|
48
|
-
|
|
49
|
-
except Exception as e:
|
|
50
|
-
logging.exception(f"ChatTokenRequest: Error interno durante validación de API Key: {e}")
|
|
51
|
-
return None, None, ({"error": "Error interno del servidor al validar API Key"}, 500)
|
|
52
|
-
|
|
53
|
-
def post(self):
|
|
54
|
-
"""
|
|
55
|
-
Genera un JWT para una sesión de chat.
|
|
56
|
-
Autenticado por API Key de la empresa.
|
|
57
|
-
Requiere JSON body:
|
|
58
|
-
{"company_short_name": "target_company_name",
|
|
59
|
-
"external_user_id": "user_abc"
|
|
60
|
-
}
|
|
61
|
-
"""
|
|
62
|
-
# only requests with valid api-key are allowed
|
|
63
|
-
auth_company_id, auth_company_short_name, error = self._authenticate_requesting_company_via_api_key()
|
|
64
|
-
if error:
|
|
65
|
-
return jsonify(error[0]), error[1]
|
|
66
|
-
|
|
67
|
-
# get the json fields from the request body
|
|
68
|
-
data = request.get_json()
|
|
69
|
-
if not data:
|
|
70
|
-
return jsonify({"error": "Cuerpo de la solicitud JSON faltante"}), 400
|
|
71
|
-
|
|
72
|
-
target_company_short_name = data.get('company_short_name')
|
|
73
|
-
external_user_id = data.get('external_user_id')
|
|
74
|
-
|
|
75
|
-
if not target_company_short_name or not external_user_id:
|
|
76
|
-
return jsonify(
|
|
77
|
-
{"error": "Faltan 'company_short_name' o 'external_user_id' en el cuerpo de la solicitud"}), 401
|
|
78
|
-
|
|
79
|
-
# Verificar que la API Key usada pertenezca a la empresa para la cual se solicita el token
|
|
80
|
-
if auth_company_short_name != target_company_short_name:
|
|
81
|
-
return jsonify({
|
|
82
|
-
"error": f"API Key no autorizada para generar tokens para la compañía '{target_company_short_name}'"}), 403
|
|
83
|
-
|
|
84
|
-
jwt_expiration_seconds = current_app.config.get('JWT_EXPIRATION_SECONDS_CHAT', 3600)
|
|
85
|
-
|
|
86
|
-
# Aquí, auth_company_id es el ID de la compañía para la que se generará el token.
|
|
87
|
-
# auth_company_short_name es su nombre corto.
|
|
88
|
-
token = self.jwt_service.generate_chat_jwt(
|
|
89
|
-
company_id=auth_company_id,
|
|
90
|
-
company_short_name=auth_company_short_name, # Usamos el short_name autenticado
|
|
91
|
-
external_user_id=external_user_id,
|
|
92
|
-
expires_delta_seconds=jwt_expiration_seconds
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
if token:
|
|
96
|
-
return jsonify({"chat_jwt": token}), 200
|
|
97
|
-
else:
|
|
98
|
-
return jsonify({"error": "No se pudo generar el token de chat"}), 500
|
iatoolkit/views/chat_view.py
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
# Product: IAToolkit
|
|
3
|
-
#
|
|
4
|
-
# IAToolkit is open source software.
|
|
5
|
-
|
|
6
|
-
from flask import render_template, request, jsonify
|
|
7
|
-
from iatoolkit.services.profile_service import ProfileService
|
|
8
|
-
from flask.views import MethodView
|
|
9
|
-
from injector import inject
|
|
10
|
-
import os
|
|
11
|
-
from iatoolkit.common.auth import IAuthentication
|
|
12
|
-
from iatoolkit.services.prompt_manager_service import PromptService
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class ChatView(MethodView):
|
|
16
|
-
@inject
|
|
17
|
-
def __init__(self,
|
|
18
|
-
iauthentication: IAuthentication,
|
|
19
|
-
prompt_service: PromptService,
|
|
20
|
-
profile_service: ProfileService):
|
|
21
|
-
self.iauthentication = iauthentication
|
|
22
|
-
self.profile_service = profile_service
|
|
23
|
-
self.prompt_service = prompt_service
|
|
24
|
-
|
|
25
|
-
def get(self, company_short_name: str):
|
|
26
|
-
# get access credentials
|
|
27
|
-
iaut = self.iauthentication.verify(company_short_name)
|
|
28
|
-
if not iaut.get("success"):
|
|
29
|
-
return jsonify(iaut), 401
|
|
30
|
-
|
|
31
|
-
user_agent = request.user_agent
|
|
32
|
-
is_mobile = user_agent.platform in ["android", "iphone", "ipad"] or "mobile" in user_agent.string.lower()
|
|
33
|
-
alert_message = request.args.get('alert_message', None)
|
|
34
|
-
|
|
35
|
-
# 1. get company info
|
|
36
|
-
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
37
|
-
if not company:
|
|
38
|
-
return render_template('error.html', message="Empresa no encontrada"), 404
|
|
39
|
-
|
|
40
|
-
# 2. get the company prompts
|
|
41
|
-
prompts = self.prompt_service.get_user_prompts(company_short_name)
|
|
42
|
-
|
|
43
|
-
return render_template("chat.html",
|
|
44
|
-
company=company,
|
|
45
|
-
company_short_name=company_short_name,
|
|
46
|
-
is_mobile=is_mobile,
|
|
47
|
-
alert_message=alert_message,
|
|
48
|
-
alert_icon='success' if alert_message else None,
|
|
49
|
-
iatoolkit_base_url=os.getenv('IATOOLKIT_BASE_URL', 'http://localhost:5000'),
|
|
50
|
-
prompts=prompts
|
|
51
|
-
)
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
# Product: IAToolkit
|
|
3
|
-
#
|
|
4
|
-
# IAToolkit is open source software.
|
|
5
|
-
|
|
6
|
-
import logging
|
|
7
|
-
import os
|
|
8
|
-
|
|
9
|
-
from flask import current_app, jsonify, send_from_directory
|
|
10
|
-
from flask.views import MethodView
|
|
11
|
-
from injector import inject
|
|
12
|
-
|
|
13
|
-
from iatoolkit.common.auth import IAuthentication
|
|
14
|
-
from iatoolkit.services.excel_service import ExcelService
|
|
15
|
-
from iatoolkit.services.profile_service import ProfileService
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class DownloadFileView(MethodView):
|
|
19
|
-
@inject
|
|
20
|
-
def __init__(self, iauthentication: IAuthentication, profile_service: ProfileService, excel_service: ExcelService):
|
|
21
|
-
self.iauthentication = iauthentication
|
|
22
|
-
self.profile_service = profile_service
|
|
23
|
-
self.excel_service = excel_service
|
|
24
|
-
|
|
25
|
-
def get(self, company_short_name: str, external_user_id: str, filename: str):
|
|
26
|
-
if not external_user_id:
|
|
27
|
-
return jsonify({"error": "Falta external_user_id"}), 400
|
|
28
|
-
|
|
29
|
-
iauth = self.iauthentication.verify(
|
|
30
|
-
company_short_name,
|
|
31
|
-
body_external_user_id=external_user_id
|
|
32
|
-
)
|
|
33
|
-
if not iauth.get("success"):
|
|
34
|
-
return jsonify(iauth), 401
|
|
35
|
-
|
|
36
|
-
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
37
|
-
if not company:
|
|
38
|
-
return jsonify({"error": "Empresa no encontrada"}), 404
|
|
39
|
-
|
|
40
|
-
file_validation = self.excel_service.validate_file_access(filename)
|
|
41
|
-
if file_validation:
|
|
42
|
-
return file_validation
|
|
43
|
-
|
|
44
|
-
temp_dir = os.path.join(current_app.root_path, 'static', 'temp')
|
|
45
|
-
|
|
46
|
-
try:
|
|
47
|
-
response = send_from_directory(
|
|
48
|
-
temp_dir,
|
|
49
|
-
filename,
|
|
50
|
-
as_attachment=True,
|
|
51
|
-
mimetype='application/octet-stream'
|
|
52
|
-
)
|
|
53
|
-
logging.info(f"Archivo descargado via API: {filename}")
|
|
54
|
-
return response
|
|
55
|
-
except Exception as e:
|
|
56
|
-
logging.error(f"Error descargando archivo {filename}: {str(e)}")
|
|
57
|
-
return jsonify({"error": "Error descargando archivo"}), 500
|
|
58
|
-
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
# Product: IAToolkit
|
|
3
|
-
#
|
|
4
|
-
# IAToolkit is open source software.
|
|
5
|
-
|
|
6
|
-
import os
|
|
7
|
-
import logging
|
|
8
|
-
from flask import request, jsonify, render_template
|
|
9
|
-
from flask.views import MethodView
|
|
10
|
-
from injector import inject
|
|
11
|
-
from iatoolkit.common.auth import IAuthentication
|
|
12
|
-
from iatoolkit.services.profile_service import ProfileService
|
|
13
|
-
from iatoolkit.services.query_service import QueryService
|
|
14
|
-
from iatoolkit.services.prompt_manager_service import PromptService
|
|
15
|
-
from iatoolkit.services.jwt_service import JWTService
|
|
16
|
-
|
|
17
|
-
class ExternalChatLoginView(MethodView):
|
|
18
|
-
@inject
|
|
19
|
-
def __init__(self,
|
|
20
|
-
profile_service: ProfileService,
|
|
21
|
-
query_service: QueryService,
|
|
22
|
-
prompt_service: PromptService,
|
|
23
|
-
iauthentication: IAuthentication,
|
|
24
|
-
jwt_service: JWTService
|
|
25
|
-
):
|
|
26
|
-
self.profile_service = profile_service
|
|
27
|
-
self.query_service = query_service
|
|
28
|
-
self.prompt_service = prompt_service
|
|
29
|
-
self.iauthentication = iauthentication
|
|
30
|
-
self.jwt_service = jwt_service
|
|
31
|
-
|
|
32
|
-
def post(self, company_short_name: str):
|
|
33
|
-
data = request.get_json()
|
|
34
|
-
if not data or 'external_user_id' not in data:
|
|
35
|
-
return jsonify({"error": "Falta external_user_id"}), 400
|
|
36
|
-
|
|
37
|
-
external_user_id = data['external_user_id']
|
|
38
|
-
# 1. get access credentials
|
|
39
|
-
iaut = self.iauthentication.verify(
|
|
40
|
-
company_short_name,
|
|
41
|
-
body_external_user_id=external_user_id
|
|
42
|
-
)
|
|
43
|
-
if not iaut.get("success"):
|
|
44
|
-
return jsonify(iaut), 401
|
|
45
|
-
|
|
46
|
-
company = self.profile_service.get_company_by_short_name(company_short_name)
|
|
47
|
-
if not company:
|
|
48
|
-
return jsonify({"error": "Empresa no encontrada"}), 404
|
|
49
|
-
|
|
50
|
-
try:
|
|
51
|
-
# 1. generate a new JWT, our secure access token.
|
|
52
|
-
token = self.jwt_service.generate_chat_jwt(
|
|
53
|
-
company_id=company.id,
|
|
54
|
-
company_short_name=company.short_name,
|
|
55
|
-
external_user_id=external_user_id,
|
|
56
|
-
expires_delta_seconds=3600 * 8 # 8 horas
|
|
57
|
-
)
|
|
58
|
-
if not token:
|
|
59
|
-
raise Exception("No se pudo generar el token de sesión (JWT).")
|
|
60
|
-
|
|
61
|
-
# 2. init the company/user LLM context.
|
|
62
|
-
self.query_service.llm_init_context(
|
|
63
|
-
company_short_name=company_short_name,
|
|
64
|
-
external_user_id=external_user_id
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
# 3. get the prompt list from backend
|
|
68
|
-
prompts = self.prompt_service.get_user_prompts(company_short_name)
|
|
69
|
-
|
|
70
|
-
# 4. render the chat page with the company/user information.
|
|
71
|
-
user_agent = request.user_agent
|
|
72
|
-
is_mobile = user_agent.platform in ["android", "iphone", "ipad"] or "mobile" in user_agent.string.lower()
|
|
73
|
-
|
|
74
|
-
chat_html = render_template("chat.html",
|
|
75
|
-
company=company,
|
|
76
|
-
company_short_name=company_short_name,
|
|
77
|
-
external_user_id=external_user_id,
|
|
78
|
-
is_mobile=is_mobile,
|
|
79
|
-
auth_method='jwt', # login method is JWT
|
|
80
|
-
session_jwt=token, # pass the token to the front-end
|
|
81
|
-
iatoolkit_base_url=os.getenv('IATOOLKIT_BASE_URL'),
|
|
82
|
-
prompts=prompts,
|
|
83
|
-
external_login=True)
|
|
84
|
-
return chat_html, 200
|
|
85
|
-
|
|
86
|
-
except Exception as e:
|
|
87
|
-
logging.exception(f"Error al inicializar el chat para {company_short_name}/{external_user_id}: {e}")
|
|
88
|
-
return jsonify({"error": "Error interno al iniciar el chat"}), 500
|
iatoolkit/views/history_view.py
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
# Product: IAToolkit
|
|
3
|
-
#
|
|
4
|
-
# IAToolkit is open source software.
|
|
5
|
-
|
|
6
|
-
from flask import request, jsonify, render_template
|
|
7
|
-
from flask.views import MethodView
|
|
8
|
-
from iatoolkit.services.history_service import HistoryService
|
|
9
|
-
from iatoolkit.common.auth import IAuthentication
|
|
10
|
-
from injector import inject
|
|
11
|
-
import logging
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class HistoryView(MethodView):
|
|
15
|
-
@inject
|
|
16
|
-
def __init__(self,
|
|
17
|
-
iauthentication: IAuthentication,
|
|
18
|
-
history_service: HistoryService ):
|
|
19
|
-
self.iauthentication = iauthentication
|
|
20
|
-
self.history_service = history_service
|
|
21
|
-
|
|
22
|
-
def post(self, company_short_name):
|
|
23
|
-
try:
|
|
24
|
-
data = request.get_json()
|
|
25
|
-
except Exception:
|
|
26
|
-
return jsonify({"error_message": "Cuerpo de la solicitud JSON inválido o faltante"}), 400
|
|
27
|
-
|
|
28
|
-
if not data:
|
|
29
|
-
return jsonify({"error_message": "Cuerpo de la solicitud JSON inválido o faltante"}), 400
|
|
30
|
-
|
|
31
|
-
# get access credentials
|
|
32
|
-
iaut = self.iauthentication.verify(company_short_name, data.get("external_user_id"))
|
|
33
|
-
if not iaut.get("success"):
|
|
34
|
-
return jsonify(iaut), 401
|
|
35
|
-
|
|
36
|
-
external_user_id = data.get("external_user_id")
|
|
37
|
-
local_user_id = data.get("local_user_id", 0)
|
|
38
|
-
|
|
39
|
-
try:
|
|
40
|
-
response = self.history_service.get_history(
|
|
41
|
-
company_short_name=company_short_name,
|
|
42
|
-
external_user_id=external_user_id,
|
|
43
|
-
local_user_id=local_user_id
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
if "error" in response:
|
|
47
|
-
return {'error_message': response["error"]}, 402
|
|
48
|
-
|
|
49
|
-
return response, 200
|
|
50
|
-
except Exception as e:
|
|
51
|
-
logging.exception(
|
|
52
|
-
f"Error inesperado al obtener el historial de consultas para company {company_short_name}: {e}")
|
|
53
|
-
if local_user_id:
|
|
54
|
-
return render_template("error.html",
|
|
55
|
-
message="Ha ocurrido un error inesperado."), 500
|
|
56
|
-
else:
|
|
57
|
-
return jsonify({"error_message": str(e)}), 500
|
iatoolkit/views/llmquery_view.py
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
# Product: IAToolkit
|
|
3
|
-
#
|
|
4
|
-
# IAToolkit is open source software.
|
|
5
|
-
|
|
6
|
-
from flask import request, jsonify, render_template
|
|
7
|
-
from flask.views import MethodView
|
|
8
|
-
from iatoolkit.services.query_service import QueryService
|
|
9
|
-
from iatoolkit.common.auth import IAuthentication
|
|
10
|
-
from injector import inject
|
|
11
|
-
import logging
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class LLMQueryView(MethodView):
|
|
15
|
-
@inject
|
|
16
|
-
def __init__(self,
|
|
17
|
-
iauthentication: IAuthentication,
|
|
18
|
-
query_service: QueryService,
|
|
19
|
-
):
|
|
20
|
-
self.iauthentication = iauthentication
|
|
21
|
-
self.query_service = query_service
|
|
22
|
-
|
|
23
|
-
def post(self, company_short_name):
|
|
24
|
-
data = request.get_json()
|
|
25
|
-
if not data:
|
|
26
|
-
return jsonify({"error_message": "Cuerpo de la solicitud JSON inválido o faltante"}), 400
|
|
27
|
-
|
|
28
|
-
# get access credentials
|
|
29
|
-
iaut = self.iauthentication.verify(company_short_name, data.get("external_user_id"))
|
|
30
|
-
if not iaut.get("success"):
|
|
31
|
-
return jsonify(iaut), 401
|
|
32
|
-
|
|
33
|
-
company_id = iaut.get("company_id")
|
|
34
|
-
external_user_id = iaut.get("external_user_id")
|
|
35
|
-
local_user_id = iaut.get("local_user_id")
|
|
36
|
-
|
|
37
|
-
# now check the form
|
|
38
|
-
question = data.get("question")
|
|
39
|
-
files = data.get("files", [])
|
|
40
|
-
client_data = data.get("client_data", {})
|
|
41
|
-
prompt_name = data.get("prompt_name")
|
|
42
|
-
if not question and not prompt_name:
|
|
43
|
-
return jsonify({"error_message": "Falta la consulta o el prompt_name"}), 400
|
|
44
|
-
|
|
45
|
-
try:
|
|
46
|
-
response = self.query_service.llm_query(
|
|
47
|
-
company_short_name=company_short_name,
|
|
48
|
-
external_user_id=external_user_id,
|
|
49
|
-
local_user_id=local_user_id,
|
|
50
|
-
question=question,
|
|
51
|
-
prompt_name=prompt_name,
|
|
52
|
-
client_data=client_data,
|
|
53
|
-
files=files)
|
|
54
|
-
if "error" in response:
|
|
55
|
-
return {'error_message': response.get("error_message", '')}, 401
|
|
56
|
-
|
|
57
|
-
return response, 200
|
|
58
|
-
except Exception as e:
|
|
59
|
-
logging.exception(
|
|
60
|
-
f"Error inesperado al procesar llm_query para company {company_short_name}: {e}")
|
|
61
|
-
if local_user_id:
|
|
62
|
-
return render_template("error.html",
|
|
63
|
-
message="Ha ocurrido un error inesperado."), 500
|
|
64
|
-
else:
|
|
65
|
-
return jsonify({"error_message": str(e)}), 500
|
|
@@ -1,83 +0,0 @@
|
|
|
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 request, jsonify
|
|
8
|
-
from iatoolkit.services.tasks_service import TaskService
|
|
9
|
-
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
10
|
-
from injector import inject
|
|
11
|
-
import logging
|
|
12
|
-
from typing import Optional
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class TaskReviewView(MethodView):
|
|
16
|
-
@inject
|
|
17
|
-
def __init__(self, task_service: TaskService, profile_repo: ProfileRepo):
|
|
18
|
-
self.task_service = task_service
|
|
19
|
-
self.profile_repo = profile_repo
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def _authenticate_requesting_company_via_api_key(self) -> tuple[
|
|
23
|
-
Optional[int], Optional[str], Optional[tuple[dict, int]]]:
|
|
24
|
-
"""
|
|
25
|
-
Autentica a la compañía usando su API Key.
|
|
26
|
-
Retorna (company_id, company_short_name, None) en éxito.
|
|
27
|
-
Retorna (None, None, (error_json, status_code)) en fallo.
|
|
28
|
-
"""
|
|
29
|
-
api_key_header = request.headers.get('Authorization')
|
|
30
|
-
if not api_key_header or not api_key_header.startswith('Bearer '):
|
|
31
|
-
return None, None, ({"error": "API Key faltante o mal formada en el header Authorization"}, 401)
|
|
32
|
-
|
|
33
|
-
api_key_value = api_key_header.split('Bearer ')[1]
|
|
34
|
-
try:
|
|
35
|
-
api_key_entry = self.profile_repo.get_active_api_key_entry(api_key_value)
|
|
36
|
-
if not api_key_entry:
|
|
37
|
-
return None, None, ({"error": "API Key inválida o inactiva"}, 401)
|
|
38
|
-
|
|
39
|
-
# api_key_entry.company ya está cargado por joinedload en get_active_api_key_entry
|
|
40
|
-
if not api_key_entry.company: # Sanity check
|
|
41
|
-
logging.error(
|
|
42
|
-
f"ChatTokenRequest: API Key {api_key_value[:5]}... no tiene compañía asociada a pesar de ser válida.")
|
|
43
|
-
return None, None, ({"error": "Error interno del servidor al verificar API Key"}, 500)
|
|
44
|
-
|
|
45
|
-
return api_key_entry.company_id, api_key_entry.company.short_name, None
|
|
46
|
-
|
|
47
|
-
except Exception as e:
|
|
48
|
-
logging.exception(f"ChatTokenRequest: Error interno durante validación de API Key: {e}")
|
|
49
|
-
return None, None, ({"error": "Error interno del servidor al validar API Key"}, 500)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def post(self, task_id: int):
|
|
53
|
-
try:
|
|
54
|
-
# only requests with valid api-key are allowed
|
|
55
|
-
auth_company_id, auth_company_short_name, error = self._authenticate_requesting_company_via_api_key()
|
|
56
|
-
if error:
|
|
57
|
-
return jsonify(error[0]), error[1]
|
|
58
|
-
|
|
59
|
-
req_data = request.get_json()
|
|
60
|
-
|
|
61
|
-
required_fields = ['review_user', 'approved']
|
|
62
|
-
for field in required_fields:
|
|
63
|
-
if field not in req_data:
|
|
64
|
-
return jsonify({"error": f"El campo {field} es requerido"}), 400
|
|
65
|
-
|
|
66
|
-
review_user = req_data.get('review_user', '')
|
|
67
|
-
approved = req_data.get('approved', False)
|
|
68
|
-
comment = req_data.get('comment', '')
|
|
69
|
-
|
|
70
|
-
new_task = self.task_service.review_task(
|
|
71
|
-
task_id=task_id,
|
|
72
|
-
review_user=review_user,
|
|
73
|
-
approved=approved,
|
|
74
|
-
comment=comment)
|
|
75
|
-
|
|
76
|
-
return jsonify({
|
|
77
|
-
"task_id": new_task.id,
|
|
78
|
-
"status": new_task.status.name
|
|
79
|
-
}), 200
|
|
80
|
-
|
|
81
|
-
except Exception as e:
|
|
82
|
-
logging.exception("Error al revisar la tarea: %s", str(e))
|
|
83
|
-
return jsonify({"error": str(e)}), 500
|