iatoolkit 0.55.1__tar.gz → 0.55.3__tar.gz

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 (128) hide show
  1. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/PKG-INFO +1 -1
  2. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/pyproject.toml +1 -1
  3. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/common/routes.py +2 -2
  4. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/iatoolkit.py +10 -1
  5. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/repositories/database_manager.py +17 -2
  6. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/profile_service.py +3 -3
  7. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/query_service.py +0 -4
  8. iatoolkit-0.55.3/src/iatoolkit/static/images/fernando.jpeg +0 -0
  9. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/index.html +22 -23
  10. iatoolkit-0.55.3/src/iatoolkit/templates/login_simulation.html +91 -0
  11. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/external_login_view.py +8 -8
  12. iatoolkit-0.55.3/src/iatoolkit/views/login_simulation_view.py +27 -0
  13. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit.egg-info/PKG-INFO +1 -1
  14. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit.egg-info/SOURCES.txt +2 -10
  15. iatoolkit-0.55.1/src/iatoolkit/static/images/arrow_up.png +0 -0
  16. iatoolkit-0.55.1/src/iatoolkit/static/images/diagrama_iatoolkit.jpg +0 -0
  17. iatoolkit-0.55.1/src/iatoolkit/static/images/logo_clinica.png +0 -0
  18. iatoolkit-0.55.1/src/iatoolkit/static/images/logo_iatoolkit.png +0 -0
  19. iatoolkit-0.55.1/src/iatoolkit/static/images/logo_maxxa.png +0 -0
  20. iatoolkit-0.55.1/src/iatoolkit/static/images/logo_notaria.png +0 -0
  21. iatoolkit-0.55.1/src/iatoolkit/static/images/logo_tarjeta.png +0 -0
  22. iatoolkit-0.55.1/src/iatoolkit/static/images/logo_umayor.png +0 -0
  23. iatoolkit-0.55.1/src/iatoolkit/static/images/upload.png +0 -0
  24. iatoolkit-0.55.1/src/iatoolkit/templates/login_test.html +0 -118
  25. iatoolkit-0.55.1/src/iatoolkit/views/login_simulation_view.py +0 -60
  26. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/readme.md +0 -0
  27. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/requirements.txt +0 -0
  28. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/setup.cfg +0 -0
  29. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/__init__.py +0 -0
  30. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/base_company.py +0 -0
  31. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/cli_commands.py +0 -0
  32. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/common/__init__.py +0 -0
  33. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/common/exceptions.py +0 -0
  34. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/common/session_manager.py +0 -0
  35. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/common/util.py +0 -0
  36. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/company_registry.py +0 -0
  37. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/__init__.py +0 -0
  38. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/call_service.py +0 -0
  39. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/connectors/__init__.py +0 -0
  40. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
  41. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
  42. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
  43. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
  44. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
  45. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
  46. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/gemini_adapter.py +0 -0
  47. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/google_chat_app.py +0 -0
  48. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/llm_client.py +0 -0
  49. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/llm_proxy.py +0 -0
  50. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/llm_response.py +0 -0
  51. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/mail_app.py +0 -0
  52. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/openai_adapter.py +0 -0
  53. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/infra/redis_session_manager.py +0 -0
  54. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/repositories/__init__.py +0 -0
  55. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/repositories/document_repo.py +0 -0
  56. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
  57. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/repositories/models.py +0 -0
  58. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/repositories/profile_repo.py +0 -0
  59. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/repositories/tasks_repo.py +0 -0
  60. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/repositories/vs_repo.py +0 -0
  61. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/__init__.py +0 -0
  62. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/auth_service.py +0 -0
  63. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/benchmark_service.py +0 -0
  64. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/branding_service.py +0 -0
  65. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/dispatcher_service.py +0 -0
  66. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/document_service.py +0 -0
  67. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/excel_service.py +0 -0
  68. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/file_processor_service.py +0 -0
  69. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/history_service.py +0 -0
  70. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/jwt_service.py +0 -0
  71. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/load_documents_service.py +0 -0
  72. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/mail_service.py +0 -0
  73. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/onboarding_service.py +0 -0
  74. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/prompt_manager_service.py +0 -0
  75. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/search_service.py +0 -0
  76. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/sql_service.py +0 -0
  77. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/tasks_service.py +0 -0
  78. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/user_feedback_service.py +0 -0
  79. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/services/user_session_context_service.py +0 -0
  80. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/static/js/chat_feedback.js +0 -0
  81. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/static/js/chat_filepond.js +0 -0
  82. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/static/js/chat_history.js +0 -0
  83. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/static/js/chat_main.js +0 -0
  84. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/static/js/chat_onboarding.js +0 -0
  85. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
  86. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/static/styles/chat_info.css +0 -0
  87. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/static/styles/chat_modal.css +0 -0
  88. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/static/styles/landing_page.css +0 -0
  89. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/static/styles/llm_output.css +0 -0
  90. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/static/styles/onboarding.css +0 -0
  91. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/system_prompts/format_styles.prompt +0 -0
  92. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/system_prompts/query_main.prompt +0 -0
  93. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/system_prompts/sql_rules.prompt +0 -0
  94. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/_branding_styles.html +0 -0
  95. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/_login_widget.html +0 -0
  96. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/_navbar.html +0 -0
  97. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/about.html +0 -0
  98. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/base.html +0 -0
  99. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/change_password.html +0 -0
  100. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/chat.html +0 -0
  101. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/chat_modals.html +0 -0
  102. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/error.html +0 -0
  103. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/forgot_password.html +0 -0
  104. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/header.html +0 -0
  105. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/onboarding_shell.html +0 -0
  106. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/signup.html +0 -0
  107. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/templates/test.html +0 -0
  108. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/__init__.py +0 -0
  109. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/base_login_view.py +0 -0
  110. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/change_password_view.py +0 -0
  111. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/chat_token_request_view.py +0 -0
  112. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/file_store_api_view.py +0 -0
  113. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/forgot_password_view.py +0 -0
  114. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/history_api_view.py +0 -0
  115. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/index_view.py +0 -0
  116. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/init_context_api_view.py +0 -0
  117. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/llmquery_api_view.py +0 -0
  118. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/llmquery_web_view.py +0 -0
  119. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/login_view.py +0 -0
  120. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/prompt_api_view.py +0 -0
  121. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/signup_view.py +0 -0
  122. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/tasks_review_view.py +0 -0
  123. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/tasks_view.py +0 -0
  124. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
  125. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit/views/verify_user_view.py +0 -0
  126. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
  127. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit.egg-info/requires.txt +0 -0
  128. {iatoolkit-0.55.1 → iatoolkit-0.55.3}/src/iatoolkit.egg-info/top_level.txt +0 -0
@@ -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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "iatoolkit"
7
- version = "0.55.1"
7
+ version = "0.55.3"
8
8
  requires-python = ">=3.12"
9
9
  description = "IAToolkit"
10
10
  readme = "readme.md"
@@ -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(
@@ -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)
@@ -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
@@ -0,0 +1,27 @@
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 render_template, request
8
+ from injector import inject
9
+ from iatoolkit.services.profile_service import ProfileService
10
+ import os
11
+
12
+
13
+ class LoginSimulationView(MethodView):
14
+ @inject
15
+ def __init__(self,
16
+ profile_service: ProfileService):
17
+ self.profile_service = profile_service
18
+
19
+ def get(self, company_short_name: str = None):
20
+
21
+ # Esta API_KEY para el login
22
+ api_key_for_login = os.getenv("IATOOLKIT_API_KEY", "tu_api_key_por_defecto_o_error")
23
+
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
@@ -62,15 +62,7 @@ src/iatoolkit/services/sql_service.py
62
62
  src/iatoolkit/services/tasks_service.py
63
63
  src/iatoolkit/services/user_feedback_service.py
64
64
  src/iatoolkit/services/user_session_context_service.py
65
- src/iatoolkit/static/images/arrow_up.png
66
- src/iatoolkit/static/images/diagrama_iatoolkit.jpg
67
- src/iatoolkit/static/images/logo_clinica.png
68
- src/iatoolkit/static/images/logo_iatoolkit.png
69
- src/iatoolkit/static/images/logo_maxxa.png
70
- src/iatoolkit/static/images/logo_notaria.png
71
- src/iatoolkit/static/images/logo_tarjeta.png
72
- src/iatoolkit/static/images/logo_umayor.png
73
- src/iatoolkit/static/images/upload.png
65
+ src/iatoolkit/static/images/fernando.jpeg
74
66
  src/iatoolkit/static/js/chat_feedback.js
75
67
  src/iatoolkit/static/js/chat_filepond.js
76
68
  src/iatoolkit/static/js/chat_history.js
@@ -97,7 +89,7 @@ src/iatoolkit/templates/error.html
97
89
  src/iatoolkit/templates/forgot_password.html
98
90
  src/iatoolkit/templates/header.html
99
91
  src/iatoolkit/templates/index.html
100
- src/iatoolkit/templates/login_test.html
92
+ src/iatoolkit/templates/login_simulation.html
101
93
  src/iatoolkit/templates/onboarding_shell.html
102
94
  src/iatoolkit/templates/signup.html
103
95
  src/iatoolkit/templates/test.html
@@ -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 %}
@@ -1,60 +0,0 @@
1
- import requests
2
- import json
3
- import os
4
- from flask.views import MethodView
5
- from flask import Response, abort, request, make_response
6
-
7
-
8
- 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
- )
47
-
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
53
-
54
- return user_response
55
-
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')
File without changes
File without changes
File without changes