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.

Files changed (159) hide show
  1. iatoolkit/__init__.py +8 -34
  2. iatoolkit/base_company.py +14 -3
  3. iatoolkit/common/routes.py +83 -52
  4. iatoolkit/common/session_manager.py +0 -1
  5. iatoolkit/common/util.py +0 -27
  6. iatoolkit/iatoolkit.py +61 -46
  7. iatoolkit/infra/llm_client.py +7 -8
  8. iatoolkit/infra/openai_adapter.py +1 -1
  9. iatoolkit/infra/redis_session_manager.py +48 -2
  10. iatoolkit/repositories/database_manager.py +17 -2
  11. iatoolkit/repositories/models.py +31 -6
  12. iatoolkit/repositories/profile_repo.py +7 -2
  13. iatoolkit/services/auth_service.py +188 -0
  14. iatoolkit/services/branding_service.py +147 -0
  15. iatoolkit/services/dispatcher_service.py +10 -40
  16. iatoolkit/services/excel_service.py +15 -15
  17. iatoolkit/services/history_service.py +3 -12
  18. iatoolkit/services/jwt_service.py +15 -24
  19. iatoolkit/services/onboarding_service.py +43 -0
  20. iatoolkit/services/profile_service.py +97 -44
  21. iatoolkit/services/query_service.py +124 -81
  22. iatoolkit/services/tasks_service.py +1 -1
  23. iatoolkit/services/user_feedback_service.py +67 -31
  24. iatoolkit/services/user_session_context_service.py +112 -54
  25. iatoolkit/static/images/fernando.jpeg +0 -0
  26. iatoolkit/static/js/{chat_feedback.js → chat_feedback_button.js} +6 -11
  27. iatoolkit/static/js/chat_history_button.js +126 -0
  28. iatoolkit/static/js/chat_logout_button.js +36 -0
  29. iatoolkit/static/js/chat_main.js +130 -220
  30. iatoolkit/static/js/chat_onboarding_button.js +97 -0
  31. iatoolkit/static/js/chat_prompt_manager.js +94 -0
  32. iatoolkit/static/js/chat_reload_button.js +52 -0
  33. iatoolkit/static/styles/chat_iatoolkit.css +329 -507
  34. iatoolkit/static/styles/chat_modal.css +95 -56
  35. iatoolkit/static/styles/landing_page.css +182 -0
  36. iatoolkit/static/styles/onboarding.css +169 -0
  37. iatoolkit/system_prompts/query_main.prompt +3 -12
  38. iatoolkit/templates/_company_header.html +20 -0
  39. iatoolkit/templates/_login_widget.html +40 -0
  40. iatoolkit/templates/base.html +8 -3
  41. iatoolkit/templates/change_password.html +54 -37
  42. iatoolkit/templates/chat.html +149 -66
  43. iatoolkit/templates/chat_modals.html +47 -18
  44. iatoolkit/templates/error.html +41 -8
  45. iatoolkit/templates/forgot_password.html +37 -24
  46. iatoolkit/templates/index.html +140 -0
  47. iatoolkit/templates/login_simulation.html +34 -0
  48. iatoolkit/templates/onboarding_shell.html +105 -0
  49. iatoolkit/templates/signup.html +64 -66
  50. iatoolkit/views/base_login_view.py +81 -0
  51. iatoolkit/views/change_password_view.py +23 -12
  52. iatoolkit/views/external_login_view.py +61 -28
  53. iatoolkit/views/{file_store_view.py → file_store_api_view.py} +9 -2
  54. iatoolkit/views/forgot_password_view.py +23 -13
  55. iatoolkit/views/history_api_view.py +52 -0
  56. iatoolkit/views/home_view.py +58 -25
  57. iatoolkit/views/index_view.py +14 -0
  58. iatoolkit/views/init_context_api_view.py +68 -0
  59. iatoolkit/views/llmquery_api_view.py +45 -0
  60. iatoolkit/views/login_simulation_view.py +81 -0
  61. iatoolkit/views/login_view.py +118 -34
  62. iatoolkit/views/logout_api_view.py +45 -0
  63. iatoolkit/views/{prompt_view.py → prompt_api_view.py} +7 -7
  64. iatoolkit/views/signup_view.py +38 -29
  65. iatoolkit/views/{tasks_view.py → tasks_api_view.py} +10 -36
  66. iatoolkit/views/tasks_review_api_view.py +55 -0
  67. iatoolkit/views/{user_feedback_view.py → user_feedback_api_view.py} +16 -31
  68. iatoolkit/views/verify_user_view.py +13 -8
  69. {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/METADATA +2 -2
  70. iatoolkit-0.63.4.dist-info/RECORD +113 -0
  71. {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/top_level.txt +0 -1
  72. iatoolkit/common/auth.py +0 -200
  73. iatoolkit/static/images/arrow_up.png +0 -0
  74. iatoolkit/static/images/diagrama_iatoolkit.jpg +0 -0
  75. iatoolkit/static/images/logo_clinica.png +0 -0
  76. iatoolkit/static/images/logo_iatoolkit.png +0 -0
  77. iatoolkit/static/images/logo_maxxa.png +0 -0
  78. iatoolkit/static/images/logo_notaria.png +0 -0
  79. iatoolkit/static/images/logo_tarjeta.png +0 -0
  80. iatoolkit/static/images/logo_umayor.png +0 -0
  81. iatoolkit/static/images/upload.png +0 -0
  82. iatoolkit/static/js/chat_history.js +0 -117
  83. iatoolkit/templates/home.html +0 -201
  84. iatoolkit/templates/login.html +0 -43
  85. iatoolkit/views/chat_token_request_view.py +0 -98
  86. iatoolkit/views/chat_view.py +0 -51
  87. iatoolkit/views/download_file_view.py +0 -58
  88. iatoolkit/views/external_chat_login_view.py +0 -88
  89. iatoolkit/views/history_view.py +0 -57
  90. iatoolkit/views/llmquery_view.py +0 -65
  91. iatoolkit/views/tasks_review_view.py +0 -83
  92. iatoolkit-0.8.1.dist-info/RECORD +0 -175
  93. tests/__init__.py +0 -5
  94. tests/common/__init__.py +0 -0
  95. tests/common/test_auth.py +0 -279
  96. tests/common/test_routes.py +0 -42
  97. tests/common/test_session_manager.py +0 -59
  98. tests/common/test_util.py +0 -444
  99. tests/companies/__init__.py +0 -5
  100. tests/conftest.py +0 -36
  101. tests/infra/__init__.py +0 -5
  102. tests/infra/connectors/__init__.py +0 -5
  103. tests/infra/connectors/test_google_drive_connector.py +0 -107
  104. tests/infra/connectors/test_local_file_connector.py +0 -85
  105. tests/infra/connectors/test_s3_connector.py +0 -95
  106. tests/infra/test_call_service.py +0 -92
  107. tests/infra/test_database_manager.py +0 -59
  108. tests/infra/test_gemini_adapter.py +0 -137
  109. tests/infra/test_google_chat_app.py +0 -68
  110. tests/infra/test_llm_client.py +0 -165
  111. tests/infra/test_llm_proxy.py +0 -122
  112. tests/infra/test_mail_app.py +0 -94
  113. tests/infra/test_openai_adapter.py +0 -105
  114. tests/infra/test_redis_session_manager_service.py +0 -117
  115. tests/repositories/__init__.py +0 -5
  116. tests/repositories/test_database_manager.py +0 -87
  117. tests/repositories/test_document_repo.py +0 -76
  118. tests/repositories/test_llm_query_repo.py +0 -340
  119. tests/repositories/test_models.py +0 -38
  120. tests/repositories/test_profile_repo.py +0 -142
  121. tests/repositories/test_tasks_repo.py +0 -76
  122. tests/repositories/test_vs_repo.py +0 -107
  123. tests/services/__init__.py +0 -5
  124. tests/services/test_dispatcher_service.py +0 -274
  125. tests/services/test_document_service.py +0 -181
  126. tests/services/test_excel_service.py +0 -208
  127. tests/services/test_file_processor_service.py +0 -121
  128. tests/services/test_history_service.py +0 -164
  129. tests/services/test_jwt_service.py +0 -255
  130. tests/services/test_load_documents_service.py +0 -112
  131. tests/services/test_mail_service.py +0 -70
  132. tests/services/test_profile_service.py +0 -379
  133. tests/services/test_prompt_manager_service.py +0 -190
  134. tests/services/test_query_service.py +0 -243
  135. tests/services/test_search_service.py +0 -39
  136. tests/services/test_sql_service.py +0 -160
  137. tests/services/test_tasks_service.py +0 -252
  138. tests/services/test_user_feedback_service.py +0 -389
  139. tests/services/test_user_session_context_service.py +0 -132
  140. tests/views/__init__.py +0 -5
  141. tests/views/test_change_password_view.py +0 -191
  142. tests/views/test_chat_token_request_view.py +0 -188
  143. tests/views/test_chat_view.py +0 -98
  144. tests/views/test_download_file_view.py +0 -149
  145. tests/views/test_external_chat_login_view.py +0 -120
  146. tests/views/test_external_login_view.py +0 -102
  147. tests/views/test_file_store_view.py +0 -128
  148. tests/views/test_forgot_password_view.py +0 -142
  149. tests/views/test_history_view.py +0 -336
  150. tests/views/test_home_view.py +0 -61
  151. tests/views/test_llm_query_view.py +0 -154
  152. tests/views/test_login_view.py +0 -114
  153. tests/views/test_prompt_view.py +0 -111
  154. tests/views/test_signup_view.py +0 -140
  155. tests/views/test_tasks_review_view.py +0 -104
  156. tests/views/test_tasks_view.py +0 -130
  157. tests/views/test_user_feedback_view.py +0 -214
  158. tests/views/test_verify_user_view.py +0 -110
  159. {iatoolkit-0.8.1.dist-info → iatoolkit-0.63.4.dist-info}/WHEEL +0 -0
@@ -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
@@ -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
@@ -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
@@ -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