iatoolkit 0.59.1__py3-none-any.whl → 0.60.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.

Files changed (32) hide show
  1. iatoolkit/base_company.py +3 -1
  2. iatoolkit/common/routes.py +12 -21
  3. iatoolkit/iatoolkit.py +5 -5
  4. iatoolkit/repositories/models.py +1 -1
  5. iatoolkit/services/auth_service.py +32 -25
  6. iatoolkit/services/profile_service.py +1 -0
  7. iatoolkit/services/user_feedback_service.py +65 -26
  8. iatoolkit/static/js/{chat_feedback.js → chat_feedback_button.js} +5 -10
  9. iatoolkit/static/js/chat_main.js +0 -8
  10. iatoolkit/static/js/{chat_onboarding.js → chat_onboarding_button.js} +0 -1
  11. iatoolkit/static/js/{chat_context_reload.js → chat_reload_button.js} +2 -4
  12. iatoolkit/static/styles/chat_iatoolkit.css +44 -0
  13. iatoolkit/templates/chat.html +18 -10
  14. iatoolkit/templates/onboarding_shell.html +1 -1
  15. iatoolkit/views/change_password_view.py +2 -2
  16. iatoolkit/views/external_login_view.py +5 -11
  17. iatoolkit/views/file_store_api_view.py +7 -9
  18. iatoolkit/views/history_api_view.py +8 -8
  19. iatoolkit/views/init_context_api_view.py +2 -4
  20. iatoolkit/views/llmquery_api_view.py +1 -3
  21. iatoolkit/views/logout_api_view.py +45 -0
  22. iatoolkit/views/prompt_api_view.py +5 -5
  23. iatoolkit/views/{tasks_view.py → tasks_api_view.py} +10 -36
  24. iatoolkit/views/tasks_review_api_view.py +55 -0
  25. iatoolkit/views/user_feedback_api_view.py +6 -18
  26. {iatoolkit-0.59.1.dist-info → iatoolkit-0.60.0.dist-info}/METADATA +1 -1
  27. {iatoolkit-0.59.1.dist-info → iatoolkit-0.60.0.dist-info}/RECORD +30 -30
  28. iatoolkit/views/chat_token_request_view.py +0 -98
  29. iatoolkit/views/tasks_review_view.py +0 -83
  30. /iatoolkit/static/js/{chat_history.js → chat_history_button.js} +0 -0
  31. {iatoolkit-0.59.1.dist-info → iatoolkit-0.60.0.dist-info}/WHEEL +0 -0
  32. {iatoolkit-0.59.1.dist-info → iatoolkit-0.60.0.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@
6
6
  from flask import request, jsonify
7
7
  from flask.views import MethodView
8
8
  from iatoolkit.services.history_service import HistoryService
9
- from iatoolkit.services.profile_service import ProfileService
9
+ from iatoolkit.services.auth_service import AuthService
10
10
  from injector import inject
11
11
  import logging
12
12
 
@@ -19,18 +19,18 @@ class HistoryApiView(MethodView):
19
19
 
20
20
  @inject
21
21
  def __init__(self,
22
- profile_service: ProfileService,
22
+ auth_service: AuthService,
23
23
  history_service: HistoryService):
24
- self.profile_service = profile_service
24
+ self.auth_service = auth_service
25
25
  self.history_service = history_service
26
26
 
27
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")
28
+ # 1. Get the authenticated user's
29
+ auth_result = self.auth_service.verify()
30
+ if not auth_result.get("success"):
31
+ return jsonify(auth_result), auth_result.get("status_code")
31
32
 
32
- if not user_identifier:
33
- return jsonify({'error_message': 'Usuario no autenticado o sesión inválida'}), 401
33
+ user_identifier = auth_result.get('user_identifier')
34
34
 
35
35
  try:
36
36
  # 2. Call the history service with the unified identifier.
@@ -31,11 +31,9 @@ class InitContextApiView(MethodView):
31
31
  # 1. Authenticate the request. This handles both session and API Key.
32
32
  auth_result = self.auth_service.verify()
33
33
  if not auth_result.get("success"):
34
- return jsonify({"error": auth_result.get("error_message")}), auth_result.get("status_code", 401)
34
+ return jsonify(auth_result), auth_result.get("status_code")
35
35
 
36
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
37
 
40
38
  try:
41
39
  # 2. Execute the forced rebuild sequence using the unified identifier.
@@ -54,7 +52,7 @@ class InitContextApiView(MethodView):
54
52
  )
55
53
 
56
54
  # 3. Respond with JSON, as this is an API endpoint.
57
- return jsonify({'status': 'OK', 'message': 'El context se ha recargado con éxito.'}), 200
55
+ return jsonify({'status': 'OK', 'message': 'El contexto se ha recargado con éxito.'}), 200
58
56
 
59
57
  except Exception as e:
60
58
  logging.exception(f"Error durante la recarga de contexto {user_identifier}: {e}")
@@ -21,12 +21,10 @@ class LLMQueryApiView(MethodView):
21
21
  # 1. Authenticate the API request.
22
22
  auth_result = self.auth_service.verify()
23
23
  if not auth_result.get("success"):
24
- return jsonify({"error": auth_result.get("error_message")}), auth_result.get("status_code", 401)
24
+ return jsonify(auth_result), auth_result.get("status_code")
25
25
 
26
26
  # 2. Get the user identifier from the payload.
27
27
  user_identifier = auth_result.get('user_identifier')
28
- if not user_identifier:
29
- return jsonify({"error": "Payload must include 'user_identifier'"}), 400
30
28
 
31
29
  data = request.get_json()
32
30
  if not data:
@@ -0,0 +1,45 @@
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
8
+ from injector import inject
9
+ from iatoolkit.services.auth_service import AuthService
10
+ from iatoolkit.services.profile_service import ProfileService
11
+ from iatoolkit.common.session_manager import SessionManager
12
+
13
+
14
+ class LogoutApiView(MethodView):
15
+ @inject
16
+ def __init__(self,
17
+ profile_service: ProfileService,
18
+ auth_service: AuthService):
19
+ self.profile_service = profile_service
20
+ self.auth_service = auth_service
21
+
22
+ def get(self, company_short_name: str = None):
23
+ # 1. Get the authenticated user's
24
+ auth_result = self.auth_service.verify(anonymous=True)
25
+ if not auth_result.get("success"):
26
+ return jsonify(auth_result), auth_result.get("status_code", 401)
27
+
28
+ company = self.profile_service.get_company_by_short_name(company_short_name)
29
+ if not company:
30
+ return jsonify({"error": "Empresa no encontrada"}), 404
31
+
32
+ # get URL for redirection
33
+ url_for_redirect = company.parameters.get('external_urls', {}).get('logout_url')
34
+ if not url_for_redirect:
35
+ url_for_redirect = url_for('index', company_short_name=company_short_name)
36
+
37
+ # clear de session cookie
38
+ SessionManager.clear()
39
+
40
+ return {
41
+ 'status': 'success',
42
+ 'url': url_for_redirect,
43
+ }
44
+
45
+
@@ -14,16 +14,16 @@ import logging
14
14
  class PromptApiView(MethodView):
15
15
  @inject
16
16
  def __init__(self,
17
- iauthentication: AuthService,
17
+ auth_service: AuthService,
18
18
  prompt_service: PromptService ):
19
- self.iauthentication = iauthentication
19
+ self.auth_service = auth_service
20
20
  self.prompt_service = prompt_service
21
21
 
22
22
  def get(self, company_short_name):
23
23
  # get access credentials
24
- iaut = self.iauthentication.verify()
25
- if not iaut.get("success"):
26
- return jsonify(iaut), 401
24
+ auth_result = self.auth_service.verify(anonymous=True)
25
+ if not auth_result.get("success"):
26
+ return jsonify(auth_result), auth_result.get('status_code')
27
27
 
28
28
  try:
29
29
  response = self.prompt_service.get_user_prompts(company_short_name)
@@ -7,54 +7,28 @@ from flask.views import MethodView
7
7
  from flask import request, jsonify
8
8
  from iatoolkit.services.tasks_service import TaskService
9
9
  from iatoolkit.repositories.profile_repo import ProfileRepo
10
+ from iatoolkit.services.auth_service import AuthService
10
11
  from injector import inject
11
12
  from datetime import datetime
12
13
  import logging
13
14
  from typing import Optional
14
15
 
15
16
 
16
- class TaskView(MethodView):
17
+ class TaskApiView(MethodView):
17
18
  @inject
18
- def __init__(self, task_service: TaskService, profile_repo: ProfileRepo):
19
+ def __init__(self,
20
+ auth_service: AuthService,
21
+ task_service: TaskService,
22
+ profile_repo: ProfileRepo):
23
+ self.auth_service = auth_service
19
24
  self.task_service = task_service
20
25
  self.profile_repo = profile_repo
21
26
 
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
27
  def post(self):
53
28
  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]
29
+ auth_result = self.auth_service.verify(anonymous=True)
30
+ if not auth_result.get("success"):
31
+ return jsonify(auth_result), auth_result.get("status_code")
58
32
 
59
33
  req_data = request.get_json()
60
34
  files = request.files.getlist('files')
@@ -0,0 +1,55 @@
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 iatoolkit.services.auth_service import AuthService
11
+ from injector import inject
12
+ import logging
13
+ from typing import Optional
14
+
15
+
16
+ class TaskReviewApiView(MethodView):
17
+ @inject
18
+ def __init__(self,
19
+ auth_service: AuthService,
20
+ task_service: TaskService,
21
+ profile_repo: ProfileRepo):
22
+ self.auth_service = auth_service
23
+ self.task_service = task_service
24
+ self.profile_repo = profile_repo
25
+
26
+ def post(self, task_id: int):
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")
30
+
31
+ try:
32
+ req_data = request.get_json()
33
+ required_fields = ['review_user', 'approved']
34
+ for field in required_fields:
35
+ if field not in req_data:
36
+ return jsonify({"error": f"El campo {field} es requerido"}), 400
37
+
38
+ review_user = req_data.get('review_user', '')
39
+ approved = req_data.get('approved', False)
40
+ comment = req_data.get('comment', '')
41
+
42
+ new_task = self.task_service.review_task(
43
+ task_id=task_id,
44
+ review_user=review_user,
45
+ approved=approved,
46
+ comment=comment)
47
+
48
+ return jsonify({
49
+ "task_id": new_task.id,
50
+ "status": new_task.status.name
51
+ }), 200
52
+
53
+ except Exception as e:
54
+ logging.exception("Error al revisar la tarea: %s", str(e))
55
+ return jsonify({"error": str(e)}), 500
@@ -14,20 +14,18 @@ import logging
14
14
  class UserFeedbackApiView(MethodView):
15
15
  @inject
16
16
  def __init__(self,
17
- iauthentication: AuthService,
17
+ auth_service: AuthService,
18
18
  user_feedback_service: UserFeedbackService ):
19
- self.iauthentication = iauthentication
19
+ self.auth_service = auth_service
20
20
  self.user_feedback_service = user_feedback_service
21
21
 
22
22
  def post(self, company_short_name):
23
23
  # get access credentials
24
- iaut = self.iauthentication.verify()
25
- if not iaut.get("success"):
26
- return jsonify(iaut), 401
24
+ auth_result = self.auth_service.verify()
25
+ if not auth_result.get("success"):
26
+ return jsonify(auth_result), auth_result.get("status_code")
27
27
 
28
- user_identifier = iaut.get('user_identifier')
29
- if not user_identifier:
30
- return jsonify({"error": "Could not identify user from session or payload"}), 400
28
+ user_identifier = auth_result.get('user_identifier')
31
29
 
32
30
  data = request.get_json()
33
31
  if not data:
@@ -37,14 +35,6 @@ class UserFeedbackApiView(MethodView):
37
35
  if not message:
38
36
  return jsonify({"error_message": "Falta el mensaje de feedback"}), 400
39
37
 
40
- space = data.get("space")
41
- if not space:
42
- return jsonify({"error_message": "Falta el espacio de Google Chat"}), 400
43
-
44
- type = data.get("type")
45
- if not type:
46
- return jsonify({"error_message": "Falta el tipo de feedback"}), 400
47
-
48
38
  rating = data.get("rating")
49
39
  if not rating:
50
40
  return jsonify({"error_message": "Falta la calificación"}), 400
@@ -54,8 +44,6 @@ class UserFeedbackApiView(MethodView):
54
44
  company_short_name=company_short_name,
55
45
  message=message,
56
46
  user_identifier=user_identifier,
57
- space=space,
58
- type=type,
59
47
  rating=rating
60
48
  )
61
49
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 0.59.1
3
+ Version: 0.60.0
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
@@ -1,11 +1,11 @@
1
1
  iatoolkit/__init__.py,sha256=4PWjMJjktixtrxF6BY405qyA50Sv967kEP2x-oil6qk,1120
2
- iatoolkit/base_company.py,sha256=nfF-G0h63jy3Qh9kCnvx8Ozx76IjG2p7a34HpweWhOk,4608
2
+ iatoolkit/base_company.py,sha256=vU4ki-wB3PWIn3_Bvehfh0TfBH_XNC614tRBKNmEd84,4718
3
3
  iatoolkit/cli_commands.py,sha256=G5L9xQXZ0lVFXQWBaE_KEZHyfuiT6PL1nTQRoSdnBzc,2302
4
4
  iatoolkit/company_registry.py,sha256=tduqt3oV8iDX_IB1eA7KIgvIxE4edTcy-3qZIXh3Lzw,2549
5
- iatoolkit/iatoolkit.py,sha256=gaO37He8WhL0K0MItvOxxIq38PIaEtaumJ294lesqbA,17583
5
+ iatoolkit/iatoolkit.py,sha256=FCFi1qcaTEMh3ZKqBB3LJTkjxc-JCvcGf79LYfYukKM,17570
6
6
  iatoolkit/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  iatoolkit/common/exceptions.py,sha256=EXx40n5htp7UiOM6P1xfJ9U6NMcADqm62dlFaKz7ICU,1154
8
- iatoolkit/common/routes.py,sha256=en9LNxQ3oj7wPUA19okmauGVqdA1yIB_YjPo_-CV-UQ,6195
8
+ iatoolkit/common/routes.py,sha256=L8UTwbE-cNSYl5s09mMckO2J9on0N1F6p8ZXMFbjrDM,5738
9
9
  iatoolkit/common/session_manager.py,sha256=UeKfD15bcEA3P5e0WSURfotLqpsiIMp3AXxAMhtgHs0,471
10
10
  iatoolkit/common/util.py,sha256=w9dTd3csK0gKtFSp-a4t7XmCPZiYDhiON92uXRbTT8A,14624
11
11
  iatoolkit/infra/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCLkX7X4,102
@@ -29,12 +29,12 @@ iatoolkit/repositories/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCL
29
29
  iatoolkit/repositories/database_manager.py,sha256=QgV8hNnVv9RmeOvUdomdj_mfk0bf3Rl8Ti41a-5zIAY,3700
30
30
  iatoolkit/repositories/document_repo.py,sha256=Y7bF1kZB1HWJsAGjWdF7P2aVYeTYNufq9ngQXp7mDkY,1124
31
31
  iatoolkit/repositories/llm_query_repo.py,sha256=YT_t7cYGQk8rwzH_17-28aTzO-e2jUfa2rvXy8tugvA,3612
32
- iatoolkit/repositories/models.py,sha256=qM95kiKm_92JoPNXiwMT4HTjr5BjalnrDiatG3Rte-M,14300
32
+ iatoolkit/repositories/models.py,sha256=6KQpyCtp2l-ExfbeoPmoqc2V5qlTmSmEbzHYISZtu6g,14288
33
33
  iatoolkit/repositories/profile_repo.py,sha256=21am3GP7XCG0nq6i3pArQ7mfGsrRn8rdcWT98fsdwlU,4397
34
34
  iatoolkit/repositories/tasks_repo.py,sha256=icVO_r2oPagGnnBhwVFzznnvEEU2EAx-2dlWuWvoDC4,1745
35
35
  iatoolkit/repositories/vs_repo.py,sha256=UkpmQQiocgM5IwRBmmWhw3HHzHP6zK1nN3J3TcQgjhc,5300
36
36
  iatoolkit/services/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCLkX7X4,102
37
- iatoolkit/services/auth_service.py,sha256=1_YfoEflzV7UboOO9xIduwXuwb4I8XHC8mZLgYyDEyw,7171
37
+ iatoolkit/services/auth_service.py,sha256=PPaa_zB0L2i574O6VQzKvTmzHbmzlWK3vvMQl2TYjOA,7494
38
38
  iatoolkit/services/benchmark_service.py,sha256=CdbFYyS3FHFhNzWQEa9ZNjUlmON10DT1nKNbZQ1EUi8,5880
39
39
  iatoolkit/services/branding_service.py,sha256=gXj9Lj6EIFNIHT6wAHia5lr4_2a2sD-ExMbewno5YD8,7505
40
40
  iatoolkit/services/dispatcher_service.py,sha256=Qdn2x4cozpgpKg2448sUxkhO6tuplzb8xPWUxdTTFBE,12772
@@ -46,22 +46,22 @@ iatoolkit/services/jwt_service.py,sha256=W2kQVNQheQSLkNLS7RZ4jd3hmySBPLbHAS5hvBr
46
46
  iatoolkit/services/load_documents_service.py,sha256=ZpB0BZ3qX1fGJGBtZtMLbFdWWx0hkPoeCS3OqJKwCTs,7291
47
47
  iatoolkit/services/mail_service.py,sha256=2h-fcF3swZDya_o7IpgXkmuj3iEVHVCiHi7oVxU99sQ,2182
48
48
  iatoolkit/services/onboarding_service.py,sha256=cMO2Ho1-G3wAeVNl-j25LwCMJjRwj3yKHpYKnZUFLDE,2001
49
- iatoolkit/services/profile_service.py,sha256=TuWV5wVR7gUYylOPKcUR0KdxHAglRv0cXxO18kCgng0,20300
49
+ iatoolkit/services/profile_service.py,sha256=cOjGJvrH9f3DRoXrhs4vZEqG7dzwrl_GnOcwxyaSX18,20301
50
50
  iatoolkit/services/prompt_manager_service.py,sha256=U-XmSpkeXvv1KRN4dytdMxSYBMRSB7y-UHcb18mk0nA,8342
51
51
  iatoolkit/services/query_service.py,sha256=gtMEAQ7IRVrFAMq4h_Pc_lHlMlXFI6heLSuBYHenkfs,17502
52
52
  iatoolkit/services/search_service.py,sha256=i1xGWu7ORKIIDH0aAQBkF86dVVbLQ0Yrooz5TiZ6aGo,1823
53
53
  iatoolkit/services/sql_service.py,sha256=MIslAtpJWnTMgSD74nnqTvQj27p-lHiyRXc6OiA2C_c,2172
54
54
  iatoolkit/services/tasks_service.py,sha256=itREO5rDnUIgsqtyCOBKDtH30QL5v1egs4qPTiBK8xU,6865
55
- iatoolkit/services/user_feedback_service.py,sha256=ooy750qWmYOeJi-IJQofu8pLG4svGjGU_JKpKMURZkw,2353
55
+ iatoolkit/services/user_feedback_service.py,sha256=Bb6PVWcxzoQ3awev_k4MI0BQhkMh6iLPGC8_CK02t5g,4986
56
56
  iatoolkit/services/user_session_context_service.py,sha256=vYF_vWM37tPB_ZyPBJ6f6WTJVjT2j-4L8JfZbqbI93k,6775
57
57
  iatoolkit/static/images/fernando.jpeg,sha256=W68TYMuo5hZVpbP-evwH6Nu4xWFv2bc8pJzSKDoLTeQ,100612
58
- iatoolkit/static/js/chat_context_reload.js,sha256=DCOGEf7-t_YLw9wuqkP-kFpiFHt3UyaesFfePp9Cs_k,2512
59
- iatoolkit/static/js/chat_feedback.js,sha256=zlLEDQfEocGK7RKG2baqI-9fyQlqe6hVuAHOKTPmWek,4399
58
+ iatoolkit/static/js/chat_feedback_button.js,sha256=j3BIyxcgyDuDDBn3vxxe0DV4evQabj3xb6XF28OWwCs,4220
60
59
  iatoolkit/static/js/chat_filepond.js,sha256=mzXafm7a506EpM37KATTK3zvAswO1E0KSUY1vKbwuRc,3163
61
- iatoolkit/static/js/chat_history.js,sha256=4h6ldU7cDvgkW84fMKB8JReoxCX0NKSQAir_4CzAF9I,4382
62
- iatoolkit/static/js/chat_main.js,sha256=o1ZNkeaBgGG9d07e5BxTL9OI0aJ26z1bvca1lGoQ-Uo,18535
63
- iatoolkit/static/js/chat_onboarding.js,sha256=b6ofiFcPhuCaPmSFIvDQZqcMUVvbI7LpIsjZOZJUSAU,3185
64
- iatoolkit/static/styles/chat_iatoolkit.css,sha256=aA-PZ2TGl_k82JSVVBC2-CJT0NiZAuLOGoiaJhdeVUU,11416
60
+ iatoolkit/static/js/chat_history_button.js,sha256=4h6ldU7cDvgkW84fMKB8JReoxCX0NKSQAir_4CzAF9I,4382
61
+ iatoolkit/static/js/chat_main.js,sha256=GPPM3euqT_cGcrmsb7DGr0Aa6BpeQRTpuf2zW2VEvMM,18118
62
+ iatoolkit/static/js/chat_onboarding_button.js,sha256=vjvEloJ9PA9D7jOGca0QjS3J_7bU97R71L7kSyY3wOI,3153
63
+ iatoolkit/static/js/chat_reload_button.js,sha256=f8f_qRnZTNr_DwbcmafTHIuBLmiCoypYAKGQc472AOs,2393
64
+ iatoolkit/static/styles/chat_iatoolkit.css,sha256=w9O_4slQuls3R5UH30oUrgA5BetA5BVctzSPpelMN4Y,12934
65
65
  iatoolkit/static/styles/chat_info.css,sha256=17DbgoNYE21VYWfb5L9-QLCpD2R1idK4imKRLwXtJLY,1058
66
66
  iatoolkit/static/styles/chat_modal.css,sha256=mdfjrJtmUn3O9rKwIGjJc-oSNmJGnzUY1aAJqEfPh38,4301
67
67
  iatoolkit/static/styles/landing_page.css,sha256=5MHlXkmgZVv9uHE7rZTGYzZeynya3ONY4hp7e2uPXwk,4898
@@ -76,36 +76,36 @@ iatoolkit/templates/_navbar.html,sha256=o1PvZE5ueLmVpGUAmsjtu-vS_WPROTlJc2sTXl6A
76
76
  iatoolkit/templates/about.html,sha256=ciC08grUVz5qLzdzDDqDX31xirg5PrJIRYabWpV9oA8,294
77
77
  iatoolkit/templates/base.html,sha256=hHfBqZJsPcZGlb4BxAbHvpJFcSjckLIAVTYTmoyXrz0,2323
78
78
  iatoolkit/templates/change_password.html,sha256=G5a3hYLTpz_5Q_eZ4LNcYSqLeW-CuT4NCHD8bkhAd9k,3573
79
- iatoolkit/templates/chat.html,sha256=NdKj1cDqQWSLrjMjfne32IZcwBdg_DV9apm8BMeTQDI,11952
79
+ iatoolkit/templates/chat.html,sha256=kq9juQ1LhXxG-uERr8FdK80KtwI43DABmSS3z66qNfQ,12182
80
80
  iatoolkit/templates/chat_modals.html,sha256=NwwgPoOmVbjy4aO2eHsy1TUMXRiOfTOC5Jx_F2ehhcs,6947
81
81
  iatoolkit/templates/error.html,sha256=c3dxieMygsvdjQMiQu_sn6kqqag9zFtVu-z5FunX6so,580
82
82
  iatoolkit/templates/forgot_password.html,sha256=NRZqbNHJXSLNArF_KLbzuem-U57v07awS0ikI_DJbfM,2360
83
83
  iatoolkit/templates/header.html,sha256=179agI7rnYwP_rvJNXIiVde5E8Ec5649_XKq6eew2Hk,1263
84
84
  iatoolkit/templates/index.html,sha256=ZeDBmZGZ_GlxQ8p_NA2Fdf4syTU5kXrJBO6q2P77wKY,7907
85
85
  iatoolkit/templates/login_simulation.html,sha256=1svwCBPrJ3Gy6bD9WMuz25NBSdFgZt4j8_sC7HE6MFU,1270
86
- iatoolkit/templates/onboarding_shell.html,sha256=r1ivSR2ci8GrDSm1uaD-cf78rfO1bKT5gXa-v5aHLAk,4659
86
+ iatoolkit/templates/onboarding_shell.html,sha256=exSGckoPeE-ID9ym3B4TLh5hULpR7N1X6LeuSNmiUL0,4666
87
87
  iatoolkit/templates/signup.html,sha256=9ArDvcNQgHFR2dwxy-37AXzGUOeOsT7Nz5u0y6fAB3U,4385
88
88
  iatoolkit/templates/test.html,sha256=rwNtxC83tbCl5COZFXYvmRBxxmgFJtPNuVBd_nq9KWY,133
89
89
  iatoolkit/views/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCLkX7X4,102
90
90
  iatoolkit/views/base_login_view.py,sha256=qoMMrAezCJvzjcfNzIbd2vwHhksALIQfNK8f_9E8m0o,3241
91
- iatoolkit/views/change_password_view.py,sha256=tM0woZyKdhY4XYjS_YXg2sKq3RYkXGfcq_eVAKrNvNM,4498
92
- iatoolkit/views/chat_token_request_view.py,sha256=wf32_A2Sq8NHYWshCwL10Tovd1znLoD0jQjzutR3sVE,4408
93
- iatoolkit/views/external_login_view.py,sha256=U_1k8AB1u6hB7twsdtQr6yS0xcihDxksstg-IiMZB30,3087
94
- iatoolkit/views/file_store_api_view.py,sha256=Uz9f6sey3_F5K8zuyQz6SwYRKAalCjD1ekf-Mkl_Kfo,2326
91
+ iatoolkit/views/change_password_view.py,sha256=DRfyvD-zgX-zidoIrvYnWg6V0LOeeugCZTxgk7wNntM,4503
92
+ iatoolkit/views/external_login_view.py,sha256=d4gUQbxFsThGbbAUdtFn7AMgPJjeb7_8KFFoH3vspVA,2853
93
+ iatoolkit/views/file_store_api_view.py,sha256=UvtZWOG-rLQMLfs8igOIYoQ-tkkEg5baMjqCJdKxaRQ,2300
95
94
  iatoolkit/views/forgot_password_view.py,sha256=-qKJeeOBqJFdvDUk7rCNg1E1cDQnJQkozPpb0T0FgwA,3159
96
- iatoolkit/views/history_api_view.py,sha256=x-tZhB8UzqrD2n-WDIfmHK9iVhGZ9f0yncsGs9mxwt0,1953
95
+ iatoolkit/views/history_api_view.py,sha256=0YChbss0ae05KHzni2p3d4bGS2_yKAbjALk1OBeQk50,1867
97
96
  iatoolkit/views/index_view.py,sha256=P5aVdEWxsYOZGbzcXd6WFE733qZ7YXIoeqriUMAM6V8,1527
98
- iatoolkit/views/init_context_api_view.py,sha256=NbJdGD4BDTkmDhGO5jkrGihdkpdIvuqUsl_Qg7MDZvA,2878
99
- iatoolkit/views/llmquery_api_view.py,sha256=ihRtZygDLAamz8DIJ0UEN73MG_SHzjgvQIznXvCPyxg,1816
97
+ iatoolkit/views/init_context_api_view.py,sha256=YTjpT4xdtm1knUhelDj-VbV4EK6o_qGAgwwDhFmIOlg,2716
98
+ iatoolkit/views/llmquery_api_view.py,sha256=v_KxR6w-TrCVR2fMFHZCz3_v4o42CXb1Yvd-R1hSJHg,1662
100
99
  iatoolkit/views/login_simulation_view.py,sha256=0Qt-puRnltI2HZxlfdyJmOf26-hQp3xjknGV_jkwV7E,3484
101
100
  iatoolkit/views/login_view.py,sha256=ESJLKHGUKQw71STHK2AoxugQUJmxnPYlI13n7ZawLLg,5663
102
- iatoolkit/views/prompt_api_view.py,sha256=MP0r-MiswwKcbNc_5KY7aVbHkrR218I8XCiCX1D0yTA,1244
101
+ iatoolkit/views/logout_api_view.py,sha256=V1HkfUfTQr_IrHFqjmeA0-qN2qugk3bmtLRHtZd7VE8,1506
102
+ iatoolkit/views/prompt_api_view.py,sha256=S_4-qAD5knh8Esae1AczEYGdXy_AuU7LMOmnUPej4jQ,1294
103
103
  iatoolkit/views/signup_view.py,sha256=BCjhM2lMiDPwYrlW_eEwPl-ZLupblbFfsonWtq0E4vU,3922
104
- iatoolkit/views/tasks_review_view.py,sha256=keLsLCyOTTlcoIapnB_lbuSvLwrPVZVpBiFC_7ChbLg,3388
105
- iatoolkit/views/tasks_view.py,sha256=a3anTXrJTTvbQuc6PSpOzidLKQFL4hWa7PI2Cppcz8w,4110
106
- iatoolkit/views/user_feedback_api_view.py,sha256=59XB9uQLHI4Q6QA4_XhK787HzfXb-c6EY7k1Ccyr4hI,2424
104
+ iatoolkit/views/tasks_api_view.py,sha256=wGnuwuuL83ByQ1Yre6ytRVztA0OGQjGrwMjB1_G830U,2630
105
+ iatoolkit/views/tasks_review_api_view.py,sha256=wsCpzqyRyUdCXWAhyGlBe3eNZZ6A1DQG7TblN_GZNfM,1894
106
+ iatoolkit/views/user_feedback_api_view.py,sha256=-Ngex8SPf0mPvPNqwE_GUcRErLpOL49yJ43o5Y4Qhqo,1992
107
107
  iatoolkit/views/verify_user_view.py,sha256=7XLSaxvs8LjBr3cYOUDa9B8DqW_50IGlq0IvmOQcD0Y,2340
108
- iatoolkit-0.59.1.dist-info/METADATA,sha256=0XpfjEJK6gmLoXv1_MF0E2ttfqygFrIwy9pK5PngYZM,9301
109
- iatoolkit-0.59.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
110
- iatoolkit-0.59.1.dist-info/top_level.txt,sha256=V_w4QvDx0b1RXiy8zTCrD1Bp7AZkFe3_O0-9fMiwogg,10
111
- iatoolkit-0.59.1.dist-info/RECORD,,
108
+ iatoolkit-0.60.0.dist-info/METADATA,sha256=OHgf546tocA5arbQL9wnzreenlXGvfD52xko9xBi9kM,9301
109
+ iatoolkit-0.60.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
110
+ iatoolkit-0.60.0.dist-info/top_level.txt,sha256=V_w4QvDx0b1RXiy8zTCrD1Bp7AZkFe3_O0-9fMiwogg,10
111
+ iatoolkit-0.60.0.dist-info/RECORD,,
@@ -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,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