iatoolkit 0.55.1__py3-none-any.whl → 0.55.3__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.

@@ -111,8 +111,8 @@ def register_views(injector, app):
111
111
  except FileNotFoundError:
112
112
  abort(404)
113
113
 
114
- # login testing (old home page)
115
- app.add_url_rule('/login_test/<company_short_name>/<external_user_id>',
114
+ # login testing
115
+ app.add_url_rule('/<company_short_name>/login_test',
116
116
  view_func=LoginSimulationView.as_view('login_test'))
117
117
 
118
118
  app.add_url_rule(
iatoolkit/iatoolkit.py CHANGED
@@ -19,7 +19,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix
19
19
  from injector import Binder, singleton, Injector
20
20
  from importlib.metadata import version as _pkg_version, PackageNotFoundError
21
21
 
22
- IATOOLKIT_VERSION = "0.55.1"
22
+ IATOOLKIT_VERSION = "0.55.3"
23
23
 
24
24
  # global variable for the unique instance of IAToolkit
25
25
  _iatoolkit_instance: Optional['IAToolkit'] = None
@@ -188,6 +188,15 @@ class IAToolkit:
188
188
  self.db_manager.create_all()
189
189
  logging.info("✅ Base de datos configurada correctamente")
190
190
 
191
+ @self.app.teardown_appcontext
192
+ def remove_session(exception=None):
193
+ """
194
+ Flask calls this after each request.
195
+ It ensures the SQLAlchemy session is properly closed
196
+ and the DB connection is returned to the pool.
197
+ """
198
+ self.db_manager.scoped_session.remove()
199
+
191
200
  def _setup_redis_sessions(self):
192
201
  redis_url = self._get_config_value('REDIS_URL')
193
202
  if not redis_url:
@@ -21,8 +21,23 @@ class DatabaseManager:
21
21
  :param echo: Si True, habilita logs de SQL.
22
22
  """
23
23
  self.url = make_url(database_url)
24
- self._engine = create_engine(database_url, echo=False)
25
- self.SessionFactory = sessionmaker(bind=self._engine)
24
+ if database_url.startswith('sqlite'): # for tests
25
+ self._engine = create_engine(database_url, echo=False)
26
+ else:
27
+ self._engine = create_engine(
28
+ database_url,
29
+ echo=False,
30
+ pool_size=2, # per worker
31
+ max_overflow=3,
32
+ pool_timeout=30,
33
+ pool_recycle=1800,
34
+ pool_pre_ping=True,
35
+ future=True,
36
+ )
37
+ self.SessionFactory = sessionmaker(bind=self._engine,
38
+ autoflush=False,
39
+ autocommit=False,
40
+ expire_on_commit=False)
26
41
  self.scoped_session = scoped_session(self.SessionFactory)
27
42
 
28
43
  # REGISTRAR pgvector para cada nueva conexión solo en postgres
@@ -74,20 +74,20 @@ class ProfileService:
74
74
  except Exception as e:
75
75
  return {'success': False, "message": str(e)}
76
76
 
77
- def create_external_user_session(self, company: Company, external_user_id: str):
77
+ def create_external_user_session(self, company: Company, user_identifier: str):
78
78
  """
79
79
  Public method for views to create a web session for an external user.
80
80
  """
81
81
  # 1. Fetch the profile from the external system via Dispatcher.
82
82
  user_profile = self.dispatcher.get_user_info(
83
83
  company_name=company.short_name,
84
- user_identifier=external_user_id
84
+ user_identifier=user_identifier
85
85
  )
86
86
 
87
87
  # 2. Call the session creation helper with external_user_id as user_identifier
88
88
  self.create_web_session(
89
89
  company=company,
90
- user_identifier=external_user_id,
90
+ user_identifier=user_identifier,
91
91
  user_profile=user_profile)
92
92
 
93
93
  def create_web_session(self, company: Company, user_identifier: str, user_profile: dict):
@@ -114,10 +114,6 @@ class QueryService:
114
114
  self._has_valid_cached_context(company_short_name, user_identifier))
115
115
 
116
116
  if rebuild_is_needed:
117
- logging.info(
118
- f"Se necesita reconstrucción de contexto para {company_short_name}/{user_identifier}. Preparando...")
119
-
120
-
121
117
  # Guardar el contexto preparado y su versión para que `finalize_context_rebuild` los use.
122
118
  self.session_context.save_prepared_context(company_short_name, user_identifier, final_system_context,
123
119
  current_version)
Binary file
@@ -1,6 +1,6 @@
1
1
  {% extends "base.html" %}
2
2
 
3
- {% block title %}IAToolkit - Acelerador de IA {% endblock %}
3
+ {% block title %}IAToolkit{% endblock %}
4
4
 
5
5
  {% block content %}
6
6
 
@@ -119,32 +119,31 @@
119
119
 
120
120
  <!-- 3.5 Sobre el Autor -->
121
121
  <section class="author-section py-5">
122
- <div class="container">
123
- <div class="row align-items-center g-4">
124
- <div class="col-md-1 d-none d-md-block text-center">
125
- <!-- Opcional: avatar (si no tienes imagen, deja el ícono) -->
126
- <div class="author-avatar mx-auto">
127
- <i class="bi bi-person-circle"></i>
128
- </div>
129
- </div>
130
- <div class="col-md-11">
131
- <div class="author-card p-4">
132
- <div class="d-flex flex-wrap align-items-center">
133
- <h5 class="mb-0 me-3" style="color: var(--brand-primary-color);">Sobre el autor</h5>
134
- <a href="https://www.linkedin.com/in/fernandolibedinsky" target="_blank" rel="noopener" class="author-linkedin ms-auto">
135
- <i class="bi bi-linkedin me-1"></i> LinkedIn
136
- </a>
122
+ <div class="container">
123
+ <div class="author-card p-4">
124
+ <div class="row align-items-center g-4">
125
+ <!-- Columna para la Imagen -->
126
+ <div class="col-md-2 text-center">
127
+ <img src="{{ url_for('static', filename='images/fernando.jpeg') }}" alt="Foto de Fernando Libedinsky" class="img-fluid rounded-circle" style="max-width: 120px;">
128
+ </div>
129
+ <!-- Columna para el Texto -->
130
+ <div class="col-md-10">
131
+ <div class="d-flex flex-wrap align-items-center mb-2">
132
+ <h5 class="mb-0 me-3" style="color: var(--brand-primary-color);">Sobre el autor</h5>
133
+ <a href="https://www.linkedin.com/in/fernandolibedinsky" target="_blank" rel="noopener" class="author-linkedin ms-auto">
134
+ <i class="bi bi-linkedin me-1"></i> LinkedIn
135
+ </a>
136
+ </div>
137
+ <p class="author-bio mb-0">
138
+ Soy <strong>Fernando Libedinsky</strong>, ingeniero de software y creador de <strong>IAToolkit</strong>.
139
+ <br>Tras una extensa trayectoria en el desarrollo de tecnología, sigo movido por la misma curiosidad que me llevó a programar por primera vez: aprender, explorar y construir cosas nuevas.
140
+ <br>IAToolkit es la continuación de ese impulso, una plataforma creada para conectar rapidamente empresas con la inteligencia artificial.
141
+ </p>
137
142
  </div>
138
- <p class="author-bio mt-2 mb-0">
139
- Soy <strong>Fernando Libedinsky</strong>, ingeniero de software y creador de <strong>IAToolkit</strong>.
140
- <br>Tras una extensa trayectoria en el desarrollo de tecnología, sigo movido por la misma curiosidad que me llevó a programar por primera vez: aprender, explorar y construir cosas nuevas.
141
- <br>IAToolkit es la continuación de ese impulso, una plataforma creada para conectar rapidamente una empresas con la IA.
142
- </p>
143
143
  </div>
144
144
  </div>
145
145
  </div>
146
- </div>
147
- </section>
146
+ </section>
148
147
 
149
148
 
150
149
  <!-- 4. Footer (sin cambios) -->
@@ -0,0 +1,91 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}Prueba de Login para {{ company_short_name }}{% endblock %}
4
+
5
+ {% block content %}
6
+
7
+ <div class="container-fluid">
8
+ <div class="row flex-fill mt-5 justify-content-center">
9
+ <!-- login desde sistema externo -->
10
+ <div class="col-12 col-lg-6">
11
+ <div class="border rounded p-4 p-md-5 shadow-sm bg-light">
12
+ {# El título ahora muestra dinámicamente el nombre de la empresa #}
13
+ <h3 class="text-muted fw-semibold text-start mb-3">
14
+ Login Externo para <span style="color:#0d6efd;">{{ company_short_name }}</span>
15
+ </h3>
16
+ <div class="text-center mb-4">
17
+ <p class="text-muted widget-intro-text">
18
+ Este formulario permite testear el acceso a IAToolkit desde un portal externo utilizando una api-key. El external user ID es el nombre del usuario ya autentificado en algún portal interno de la empresa.
19
+ </p>
20
+ </div>
21
+
22
+ {# El 'action' y 'method' son manejados por JS, pero el id es crucial #}
23
+ <form id="external-login">
24
+
25
+ <div class="mb-3">
26
+ <label for="external_user_id" class="form-label d-block">External user ID</label>
27
+ <input type="text" id="external_user_id" name="external_user_id" class="form-control" required>
28
+ </div>
29
+
30
+ <button type="submit" id="loginButton" class="btn btn-primary">
31
+ Iniciar Sesión
32
+ </button>
33
+ </form>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ {% endblock %}
39
+
40
+ {% block scripts %}
41
+ <script>
42
+ document.addEventListener('DOMContentLoaded', function() {
43
+ const companyShortName = "{{ company_short_name }}";
44
+ const apiKey = "{{ api_key }}";
45
+ const loginForm = document.getElementById('external-login');
46
+ const userIdInput = document.getElementById('external_user_id');
47
+
48
+ loginForm.addEventListener('submit', function(event) {
49
+ event.preventDefault();
50
+ const userIdentifier = userIdInput.value.trim();
51
+ if (!userIdentifier) return;
52
+
53
+ // 1. Reemplazamos la página actual con un mensaje de carga.
54
+ document.body.innerHTML = '<div style="display:flex; align-items:center; justify-content:center; height:100vh; font-family:sans-serif;">Iniciando sesión, por favor espera...</div>';
55
+
56
+ const apiUrl = `/${companyShortName}/external_login`;
57
+
58
+ fetch(apiUrl, {
59
+ method: 'POST',
60
+ headers: {
61
+ 'Content-Type': 'application/json',
62
+ 'Authorization': `Bearer ${apiKey}`
63
+ },
64
+ body: JSON.stringify({
65
+ // Usamos 'userIdentifier' como el valor para el campo 'external_user_id'
66
+ user_identifier: userIdentifier
67
+ })
68
+ })
69
+ .then(async response => {
70
+ if (response.ok) {
71
+ return response.text();
72
+ } else {
73
+ const errorText = await response.text();
74
+ throw new Error(errorText || `Error ${response.status}`);
75
+ }
76
+ })
77
+ .then(htmlContent => {
78
+ // 2. Reemplazamos todo el documento con el nuevo HTML recibido.
79
+ // Esto asegura que todos los scripts y estilos del nuevo HTML se carguen correctamente.
80
+ document.open();
81
+ document.write(htmlContent);
82
+ document.close();
83
+ })
84
+ .catch(error => {
85
+ // Si hay un error, lo mostramos en la página.
86
+ document.body.innerHTML = `<div style="padding:20px; font-family:monospace; color:red;"><h2>Error</h2><pre>${error.message}</pre></div>`;
87
+ });
88
+ });
89
+ });
90
+ </script>
91
+ {% endblock %}
@@ -43,16 +43,16 @@ class ExternalLoginView(BaseLoginView):
43
43
 
44
44
  def post(self, company_short_name: str):
45
45
  data = request.get_json()
46
- if not data or 'external_user_id' not in data:
47
- return jsonify({"error": "Falta external_user_id"}), 400
46
+ if not data or 'user_identifier' not in data:
47
+ return jsonify({"error": "Falta user_identifier"}), 400
48
48
 
49
49
  company = self.profile_service.get_company_by_short_name(company_short_name)
50
50
  if not company:
51
51
  return jsonify({"error": "Empresa no encontrada"}), 404
52
52
 
53
- external_user_id = data['external_user_id']
54
- if not external_user_id:
55
- return jsonify({"error": "missing external_user_id"}), 404
53
+ user_identifier = data.get('user_identifier')
54
+ if not user_identifier:
55
+ return jsonify({"error": "missing user_identifier"}), 404
56
56
 
57
57
  # 1. Authenticate the API call.
58
58
  iaut = self.iauthentication.verify()
@@ -60,11 +60,11 @@ class ExternalLoginView(BaseLoginView):
60
60
  return jsonify(iaut), 401
61
61
 
62
62
  # 2. Create the external user session.
63
- self.profile_service.create_external_user_session(company, external_user_id)
63
+ self.profile_service.create_external_user_session(company, user_identifier)
64
64
 
65
65
  # 3. Delegate the path decision to the centralized logic.
66
66
  try:
67
- return self._handle_login_path(company_short_name, external_user_id, company)
67
+ return self._handle_login_path(company_short_name, user_identifier, company)
68
68
  except Exception as e:
69
- logging.exception(f"Error processing external login path for {company_short_name}/{external_user_id}: {e}")
69
+ logging.exception(f"Error processing external login path for {company_short_name}/{user_identifier}: {e}")
70
70
  return jsonify({"error": f"Internal server error while starting chat. {str(e)}"}), 500
@@ -1,60 +1,27 @@
1
- import requests
2
- import json
3
- import os
1
+ # Copyright (c) 2024 Fernando Libedinsky
2
+ # Product: IAToolkit
3
+ #
4
+ # IAToolkit is open source software.
5
+
4
6
  from flask.views import MethodView
5
- from flask import Response, abort, request, make_response
7
+ from flask import render_template, request
8
+ from injector import inject
9
+ from iatoolkit.services.profile_service import ProfileService
10
+ import os
6
11
 
7
12
 
8
13
  class LoginSimulationView(MethodView):
9
- """
10
- Simula un portal externo que llama a la API de IAToolkit de servidor a servidor,
11
- replicando el flujo real de `dispatch_request`.
12
- Para usarlo, visita /login_test/<company_short_name>/<external_user_id>
13
- """
14
-
15
- def get(self, company_short_name: str, external_user_id: str):
16
- api_key = os.getenv("IATOOLKIT_API_KEY")
17
- base_url = request.host_url.rstrip('/')
18
-
19
- if not api_key:
20
- abort(500, "Error: IATOOLKIT_API_KEY no está configurada en el servidor de prueba.")
21
- if not external_user_id:
22
- abort(400, "Error: Debes proporcionar un external_user_id en la URL.")
23
-
24
- target_url = f"{base_url}/{company_short_name}/external_login"
25
-
26
- # --- INICIO DE LA CORRECCIÓN ---
27
- # Usamos el formato de header 'Authorization: Bearer' como solicitaste.
28
- headers = {
29
- 'Content-Type': 'application/json',
30
- 'Authorization': f'Bearer {api_key}'
31
- }
32
- # --- FIN DE LA CORRECCIÓN ---
33
-
34
- payload = {'external_user_id': external_user_id}
35
-
36
- try:
37
- # Llamada POST interna. 'stream=True' es importante para manejar la respuesta.
38
- internal_response = requests.post(target_url, headers=headers, data=json.dumps(payload), timeout=120,
39
- stream=True)
40
- internal_response.raise_for_status()
41
-
42
- # Creamos una nueva Response de Flask para el navegador del usuario.
43
- user_response = Response(
44
- internal_response.iter_content(chunk_size=1024),
45
- status=internal_response.status_code
46
- )
14
+ @inject
15
+ def __init__(self,
16
+ profile_service: ProfileService):
17
+ self.profile_service = profile_service
47
18
 
48
- # Copiamos TODAS las cabeceras de la respuesta interna a la respuesta final,
49
- # incluyendo 'Content-Type' y, crucialmente, 'Set-Cookie'.
50
- for key, value in internal_response.headers.items():
51
- if key.lower() not in ['content-encoding', 'content-length', 'transfer-encoding', 'connection']:
52
- user_response.headers[key] = value
19
+ def get(self, company_short_name: str = None):
53
20
 
54
- return user_response
21
+ # Esta API_KEY para el login
22
+ api_key_for_login = os.getenv("IATOOLKIT_API_KEY", "tu_api_key_por_defecto_o_error")
55
23
 
56
- except requests.exceptions.HTTPError as e:
57
- error_text = f"Error en la llamada interna a la API: {e.response.status_code}. Respuesta: {e.response.text}"
58
- return Response(error_text, status=e.response.status_code, mimetype='text/plain')
59
- except requests.exceptions.RequestException as e:
60
- return Response(f'Error de conexión con el servicio de IA: {str(e)}', status=502, mimetype='text/plain')
24
+ return render_template('login_simulation.html',
25
+ company_short_name=company_short_name,
26
+ api_key=api_key_for_login
27
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 0.55.1
3
+ Version: 0.55.3
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
@@ -2,10 +2,10 @@ iatoolkit/__init__.py,sha256=4PWjMJjktixtrxF6BY405qyA50Sv967kEP2x-oil6qk,1120
2
2
  iatoolkit/base_company.py,sha256=uFJmy77LPAceVqkTeuJqo15-auDiq4aTwvC_bbBD0mQ,4607
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=HxmkgpwobTvw7jiDGhKh6yJFJC3o6ZgfW4muirO51W8,17305
5
+ iatoolkit/iatoolkit.py,sha256=1Uo619dvzhZzMwgycUFLb0iH3eIJiBw-FuDZbQmMsGE,17645
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=LniD_xsVZ6iHx1Z2_daOChhXZ9AQwsYFtqWSuFkQVyk,6117
8
+ iatoolkit/common/routes.py,sha256=EcfJXJ3e3sQX_jRiMRKambcYoAaLnnjMuKS9H7NqkDM,6082
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
@@ -26,7 +26,7 @@ iatoolkit/infra/connectors/google_drive_connector.py,sha256=WR1AlO5-Bl3W89opdja0
26
26
  iatoolkit/infra/connectors/local_file_connector.py,sha256=hrzIgpMJOTuwTqzlQeTIU_50ZbZ6yl8lcWPv6hMnoqI,1739
27
27
  iatoolkit/infra/connectors/s3_connector.py,sha256=Nj4_YaLobjfcnbZewJf21_K2EXohgcc3mJll1Pzn4zg,1123
28
28
  iatoolkit/repositories/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCLkX7X4,102
29
- iatoolkit/repositories/database_manager.py,sha256=UaU7k3s7IRuXhCHTy9GoCeP9K1ad0LBdj_n1a_QjGS0,3108
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
32
  iatoolkit/repositories/models.py,sha256=3YbIJXNMZiTkMbPyiSOiyzqUKEQF0JIfN4VSWYzwr44,13146
@@ -46,23 +46,15 @@ iatoolkit/services/jwt_service.py,sha256=YoZ9h7_o9xBko-arNQv4MbcwnxoSWVNj4VbZmMo
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=Y1EbTDNFDWrUQ4QqiaM2OfluZqiJfL9hhbeqJ7nsbtw,20235
49
+ iatoolkit/services/profile_service.py,sha256=w4ZhMuhZMH5_UHbDgvKeZHXNNecEIiExtuKI8fKZL-k,20232
50
50
  iatoolkit/services/prompt_manager_service.py,sha256=U-XmSpkeXvv1KRN4dytdMxSYBMRSB7y-UHcb18mk0nA,8342
51
- iatoolkit/services/query_service.py,sha256=sEmdBDLTpdZbNu43KAUO6vXqqWBBxTWKWlQm4oFGtSk,17796
51
+ iatoolkit/services/query_service.py,sha256=J42KzthYIIsUX8cUS2E7vcXA6IZOJ28Y8I637m-9za4,17649
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
55
  iatoolkit/services/user_feedback_service.py,sha256=ooy750qWmYOeJi-IJQofu8pLG4svGjGU_JKpKMURZkw,2353
56
56
  iatoolkit/services/user_session_context_service.py,sha256=TeYi4xF04Xk-4Qnp6cVTmxuNzA4B1nMVUuFDjTHeiZQ,6764
57
- iatoolkit/static/images/arrow_up.png,sha256=vPcWMebWVOOt13x5U7pzsyukysoiXO0siSyErUMleHw,2781
58
- iatoolkit/static/images/diagrama_iatoolkit.jpg,sha256=GhauwUVqWjt9dWtsW53Uon_mo61j73brmC4ogQc6tkY,93527
59
- iatoolkit/static/images/logo_clinica.png,sha256=vLX6X7O9RPUHYTQHvO_Fx28sI4JulpRZsRpkuJ9XIu0,56806
60
- iatoolkit/static/images/logo_iatoolkit.png,sha256=7XSxLy8v_mGxQMM8EA0gund-MmnQoIy1RV6DkJ-eukI,185089
61
- iatoolkit/static/images/logo_maxxa.png,sha256=X4JtTu6ooe2RUHRo1stxGGquXlkiEWcTUxH5vbFdMv0,10592
62
- iatoolkit/static/images/logo_notaria.png,sha256=FLDK1mHydFpzudKLKQSqHdSqq6vbqs77a_bO0PI_yHE,105105
63
- iatoolkit/static/images/logo_tarjeta.png,sha256=HQe2lp_jHFWq5hnx3SCBEcqW5IFfvSCktv9oyN--5cQ,1700
64
- iatoolkit/static/images/logo_umayor.png,sha256=FHr1wvI8uDn1YRbRcLSRLBOc0mYusHx9Um6ZzVZUbOQ,19199
65
- iatoolkit/static/images/upload.png,sha256=zh5FiINURpaWZQF86bF_gALBX4W1c4aLp5wPQO9xGXI,296
57
+ iatoolkit/static/images/fernando.jpeg,sha256=W68TYMuo5hZVpbP-evwH6Nu4xWFv2bc8pJzSKDoLTeQ,100612
66
58
  iatoolkit/static/js/chat_feedback.js,sha256=DDT2NPgglrLnW75vtEAVXS72MNt7vlMcavzr94q80qQ,4398
67
59
  iatoolkit/static/js/chat_filepond.js,sha256=mzXafm7a506EpM37KATTK3zvAswO1E0KSUY1vKbwuRc,3163
68
60
  iatoolkit/static/js/chat_history.js,sha256=4hYNODIwYNd5vaQqkR28HZyXYIFKgSayrnmOuT_DUac,4381
@@ -88,8 +80,8 @@ iatoolkit/templates/chat_modals.html,sha256=NwwgPoOmVbjy4aO2eHsy1TUMXRiOfTOC5Jx_
88
80
  iatoolkit/templates/error.html,sha256=c3dxieMygsvdjQMiQu_sn6kqqag9zFtVu-z5FunX6so,580
89
81
  iatoolkit/templates/forgot_password.html,sha256=NRZqbNHJXSLNArF_KLbzuem-U57v07awS0ikI_DJbfM,2360
90
82
  iatoolkit/templates/header.html,sha256=179agI7rnYwP_rvJNXIiVde5E8Ec5649_XKq6eew2Hk,1263
91
- iatoolkit/templates/index.html,sha256=jBLCqZoIMmUCA1KQESW9BcEgaxE0Rjs-4-i2V0ueSMo,7654
92
- iatoolkit/templates/login_test.html,sha256=ApvW4l0UmcjJ9pBl3jm-0BojWwiFV6ZyCWz3caM1DN4,4722
83
+ iatoolkit/templates/index.html,sha256=Q2wBHDv3HTBt-jURVLggye7XOqoqDY8E0LnrmLdc3SQ,7910
84
+ iatoolkit/templates/login_simulation.html,sha256=DqzH0ygJzkGHjdQcoITdONe8GJmv4u_rgoAw1TizYeo,3513
93
85
  iatoolkit/templates/onboarding_shell.html,sha256=r1ivSR2ci8GrDSm1uaD-cf78rfO1bKT5gXa-v5aHLAk,4659
94
86
  iatoolkit/templates/signup.html,sha256=9ArDvcNQgHFR2dwxy-37AXzGUOeOsT7Nz5u0y6fAB3U,4385
95
87
  iatoolkit/templates/test.html,sha256=rwNtxC83tbCl5COZFXYvmRBxxmgFJtPNuVBd_nq9KWY,133
@@ -97,7 +89,7 @@ iatoolkit/views/__init__.py,sha256=5JqK9sZ6jBuK83zDQokUhxQ0wuJJJ9DXB8pYCLkX7X4,1
97
89
  iatoolkit/views/base_login_view.py,sha256=6IWJXEg_6K9cPYoKnG_-Kpt_hIdYnv5ZNC5anIrytXk,2819
98
90
  iatoolkit/views/change_password_view.py,sha256=tM0woZyKdhY4XYjS_YXg2sKq3RYkXGfcq_eVAKrNvNM,4498
99
91
  iatoolkit/views/chat_token_request_view.py,sha256=wf32_A2Sq8NHYWshCwL10Tovd1znLoD0jQjzutR3sVE,4408
100
- iatoolkit/views/external_login_view.py,sha256=qY5wxh98HyKI0Q1Phf6pkOzRGD7PvbH27GUZn0ccYu4,2889
92
+ iatoolkit/views/external_login_view.py,sha256=IC3X0IZYAfTjHfWSzCwrjqlnBin3Ky7hJZzj3AGk8ZI,2884
101
93
  iatoolkit/views/file_store_api_view.py,sha256=Uz9f6sey3_F5K8zuyQz6SwYRKAalCjD1ekf-Mkl_Kfo,2326
102
94
  iatoolkit/views/forgot_password_view.py,sha256=-qKJeeOBqJFdvDUk7rCNg1E1cDQnJQkozPpb0T0FgwA,3159
103
95
  iatoolkit/views/history_api_view.py,sha256=x-tZhB8UzqrD2n-WDIfmHK9iVhGZ9f0yncsGs9mxwt0,1953
@@ -105,7 +97,7 @@ iatoolkit/views/index_view.py,sha256=P5aVdEWxsYOZGbzcXd6WFE733qZ7YXIoeqriUMAM6V8
105
97
  iatoolkit/views/init_context_api_view.py,sha256=1j8NKfODfPrffbA5YO8TPMHh-ildlLNzReIxv_qO-W4,2586
106
98
  iatoolkit/views/llmquery_api_view.py,sha256=Rh-y-VENwwtNsDrYAD_SWKwjK16fW-pFRWlEvI-OYwY,2120
107
99
  iatoolkit/views/llmquery_web_view.py,sha256=WhjlA1mfsoL8hL9tlKQfjCUcaTzT43odlp_uQKmT314,1500
108
- iatoolkit/views/login_simulation_view.py,sha256=hujduAuq84YyHXfG1iVVbypkwYOreKr9FnQNvIXBDyQ,2675
100
+ iatoolkit/views/login_simulation_view.py,sha256=GFKtrHXzU_bPRgtPO1quoPknV_GeI-fUHq9YVPqNrJs,854
109
101
  iatoolkit/views/login_view.py,sha256=3mV-I1KWrVCi4GBmSWCPmIIKj0reZEBMmyTZdBKFCFM,4630
110
102
  iatoolkit/views/prompt_api_view.py,sha256=MP0r-MiswwKcbNc_5KY7aVbHkrR218I8XCiCX1D0yTA,1244
111
103
  iatoolkit/views/signup_view.py,sha256=BCjhM2lMiDPwYrlW_eEwPl-ZLupblbFfsonWtq0E4vU,3922
@@ -113,7 +105,7 @@ iatoolkit/views/tasks_review_view.py,sha256=keLsLCyOTTlcoIapnB_lbuSvLwrPVZVpBiFC
113
105
  iatoolkit/views/tasks_view.py,sha256=a3anTXrJTTvbQuc6PSpOzidLKQFL4hWa7PI2Cppcz8w,4110
114
106
  iatoolkit/views/user_feedback_api_view.py,sha256=59XB9uQLHI4Q6QA4_XhK787HzfXb-c6EY7k1Ccyr4hI,2424
115
107
  iatoolkit/views/verify_user_view.py,sha256=7XLSaxvs8LjBr3cYOUDa9B8DqW_50IGlq0IvmOQcD0Y,2340
116
- iatoolkit-0.55.1.dist-info/METADATA,sha256=lZ5zZ-aChnuT972ggAucr4472SkboG8DDnUWIn8CfzQ,9301
117
- iatoolkit-0.55.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
118
- iatoolkit-0.55.1.dist-info/top_level.txt,sha256=V_w4QvDx0b1RXiy8zTCrD1Bp7AZkFe3_O0-9fMiwogg,10
119
- iatoolkit-0.55.1.dist-info/RECORD,,
108
+ iatoolkit-0.55.3.dist-info/METADATA,sha256=JYdzDOAsjbP7j9lAd6wtdNf6Lj2LjJEBpbqIPpCLt6Y,9301
109
+ iatoolkit-0.55.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
110
+ iatoolkit-0.55.3.dist-info/top_level.txt,sha256=V_w4QvDx0b1RXiy8zTCrD1Bp7AZkFe3_O0-9fMiwogg,10
111
+ iatoolkit-0.55.3.dist-info/RECORD,,
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,118 +0,0 @@
1
- {% extends "base.html" %}
2
-
3
- {% block title %}Prueba de Login para {{ branding.name }}{% endblock %}
4
-
5
- {% block styles %}
6
- {# Este bloque asegura que los colores de la marca estén disponibles como variables CSS #}
7
- {% if branding and branding.css_variables %}
8
- <style>
9
- {{ branding.css_variables|safe }}
10
- /* Usa la variable de CSS para el color primario del botón */
11
- #initiateJwtChatButton {
12
- background-color: var(--brand-primary-color, #0d6efd);
13
- border-color: var(--brand-primary-color, #0d6efd);
14
- }
15
- </style>
16
- {% endif %}
17
- {% endblock %}
18
-
19
- {% block content %}
20
- <div class="container-fluid">
21
- <div class="row flex-fill mt-5 justify-content-center">
22
- <!-- login desde sistema externo -->
23
- <div class="col-12 col-lg-6">
24
- <div class="border rounded p-4 p-md-5 shadow-sm bg-light">
25
- {# El título ahora muestra dinámicamente el nombre de la empresa #}
26
- <h3 class="text-muted fw-semibold text-start mb-3">
27
- Login Externo para <span style="color: var(--brand-primary-color, #0d6efd);">{{ branding.name }}</span>
28
- </h3>
29
- <div class="text-center mb-4">
30
- <p class="text-muted widget-intro-text">
31
- Este formulario permite testear el acceso a IAToolkit desde un portal externo utilizando una api-key. El external user ID es el nombre del usuario ya autentificado en algún portal interno de la empresa.
32
- </p>
33
- </div>
34
-
35
- {# El 'action' y 'method' son manejados por JS, pero el id es crucial #}
36
- <form id="jwt-form">
37
-
38
- <div class="mb-3">
39
- <label for="external_user_id" class="form-label d-block">External user ID</label>
40
- <input type="text" id="external_user_id" name="external_user_id" class="form-control" required>
41
- </div>
42
-
43
- <button type="submit" id="initiateJwtChatButton" class="btn btn-primary">
44
- <span class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
45
- Iniciar Sesión
46
- </button>
47
- </form>
48
- </div>
49
- </div>
50
- </div>
51
- </div>
52
- {% endblock %}
53
-
54
- {% block scripts %}
55
- <script>
56
- document.addEventListener('DOMContentLoaded', function() {
57
- const companyShortName = "{{ company_short_name }}";
58
- const apiKey = "{{ api_key }}";
59
- const loginForm = document.getElementById('jwt-form');
60
- const userIdInput = document.getElementById('external_user_id');
61
- const submitButton = document.getElementById('initiateJwtChatButton');
62
- const spinner = submitButton.querySelector('.spinner-border');
63
-
64
- loginForm.addEventListener('submit', function(event) {
65
- event.preventDefault();
66
- const userIdentifier = userIdInput.value.trim();
67
- if (!userIdentifier) return;
68
-
69
- // Deshabilitar botón y mostrar spinner
70
- submitButton.disabled = true;
71
- spinner.classList.remove('d-none');
72
-
73
- const newWindow = window.open('', '_blank');
74
- const apiUrl = `/${companyShortName}/external_login`;
75
-
76
- fetch(apiUrl, {
77
- method: 'POST',
78
- headers: {
79
- 'Content-Type': 'application/json',
80
- 'Authorization': `Bearer ${apiKey}`
81
- },
82
- body: JSON.stringify({ external_user_id: userIdentifier })
83
- })
84
- .then(response => response.ok ? response.text() : response.text().then(text => { throw new Error(text) }))
85
- .then(html => {
86
- newWindow.document.documentElement.innerHTML = html;
87
- // Buscamos todos los scripts en el HTML inyectado y los re-ejecutamos.
88
- const scripts = newWindow.document.querySelectorAll('script');
89
- scripts.forEach(oldScript => {
90
- const newScript = newWindow.document.createElement('script');
91
-
92
- // Copiamos los atributos (como 'src' para archivos externos)
93
- Array.from(oldScript.attributes).forEach(attr => {
94
- newScript.setAttribute(attr.name, attr.value);
95
- });
96
-
97
- // Copiamos el contenido para scripts inline
98
- newScript.appendChild(newWindow.document.createTextNode(oldScript.innerHTML));
99
-
100
- // Reemplazamos el script viejo por el nuevo para que se ejecute.
101
- oldScript.parentNode.replaceChild(newScript, oldScript);
102
- });
103
-
104
- })
105
- .catch(error => {
106
- console.error("Falló la prueba de login externo:", error);
107
- const errorMessage = error.message || "Error desconocido.";
108
- newWindow.document.body.innerHTML = `<div style="padding: 20px; font-family: monospace;"><h2>Error</h2><pre>${errorMessage}</pre></div>`;
109
- })
110
- .finally(() => {
111
- // Volver a habilitar el botón y ocultar el spinner
112
- submitButton.disabled = false;
113
- spinner.classList.add('d-none');
114
- });
115
- });
116
- });
117
- </script>
118
- {% endblock %}