iatoolkit 0.95.4__tar.gz → 0.108.1__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.
Files changed (140) hide show
  1. {iatoolkit-0.95.4/src/iatoolkit.egg-info → iatoolkit-0.108.1}/PKG-INFO +1 -2
  2. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/requirements.txt +0 -1
  3. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/__init__.py +4 -2
  4. iatoolkit-0.108.1/src/iatoolkit/base_company.py +21 -0
  5. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/cli_commands.py +3 -2
  6. iatoolkit-0.108.1/src/iatoolkit/common/interfaces/asset_storage.py +34 -0
  7. iatoolkit-0.108.1/src/iatoolkit/common/interfaces/database_provider.py +38 -0
  8. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/common/model_registry.py +52 -1
  9. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/common/routes.py +15 -0
  10. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/common/util.py +11 -0
  11. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/company_registry.py +5 -0
  12. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/core.py +43 -13
  13. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +14 -15
  14. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/llm_providers/openai_adapter.py +41 -3
  15. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/llm_response.py +5 -0
  16. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/locales/en.yaml +52 -2
  17. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/locales/es.yaml +55 -0
  18. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/repositories/database_manager.py +40 -18
  19. iatoolkit-0.108.1/src/iatoolkit/repositories/filesystem_asset_repository.py +36 -0
  20. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/repositories/llm_query_repo.py +2 -0
  21. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/repositories/models.py +2 -3
  22. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/repositories/profile_repo.py +60 -3
  23. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/company_context_service.py +57 -34
  24. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/configuration_service.py +122 -69
  25. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/dispatcher_service.py +15 -3
  26. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/history_manager_service.py +5 -7
  27. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/llm_client_service.py +14 -10
  28. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/profile_service.py +32 -4
  29. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/prompt_service.py +30 -28
  30. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/query_service.py +6 -6
  31. iatoolkit-0.108.1/src/iatoolkit/services/sql_service.py +174 -0
  32. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/tool_service.py +15 -4
  33. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/user_session_context_service.py +1 -1
  34. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/js/chat_main.js +41 -3
  35. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/styles/chat_iatoolkit.css +38 -34
  36. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/styles/llm_output.css +34 -1
  37. iatoolkit-0.108.1/src/iatoolkit/system_prompts/__init__.py +0 -0
  38. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/templates/base.html +13 -0
  39. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/templates/chat.html +10 -7
  40. iatoolkit-0.108.1/src/iatoolkit/views/chat_view.py +76 -0
  41. iatoolkit-0.108.1/src/iatoolkit/views/load_company_configuration_api_view.py +49 -0
  42. iatoolkit-0.108.1/src/iatoolkit/views/users_api_view.py +33 -0
  43. {iatoolkit-0.95.4 → iatoolkit-0.108.1/src/iatoolkit.egg-info}/PKG-INFO +1 -2
  44. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit.egg-info/SOURCES.txt +7 -0
  45. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit.egg-info/requires.txt +0 -1
  46. iatoolkit-0.95.4/src/iatoolkit/base_company.py +0 -37
  47. iatoolkit-0.95.4/src/iatoolkit/services/sql_service.py +0 -159
  48. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/LICENSE +0 -0
  49. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/LICENSE_COMMUNITY.md +0 -0
  50. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/pyproject.toml +0 -0
  51. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/readme.md +0 -0
  52. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/setup.cfg +0 -0
  53. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/common/__init__.py +0 -0
  54. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/common/exceptions.py +0 -0
  55. {iatoolkit-0.95.4/src/iatoolkit/infra/llm_providers → iatoolkit-0.108.1/src/iatoolkit/common/interfaces}/__init__.py +0 -0
  56. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/common/session_manager.py +0 -0
  57. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/__init__.py +0 -0
  58. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
  59. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/call_service.py +0 -0
  60. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/connectors/__init__.py +0 -0
  61. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
  62. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
  63. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
  64. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
  65. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
  66. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
  67. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/google_chat_app.py +0 -0
  68. {iatoolkit-0.95.4/src/iatoolkit/system_prompts → iatoolkit-0.108.1/src/iatoolkit/infra/llm_providers}/__init__.py +0 -0
  69. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
  70. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/llm_proxy.py +0 -0
  71. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/infra/redis_session_manager.py +0 -0
  72. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/repositories/__init__.py +0 -0
  73. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/repositories/document_repo.py +0 -0
  74. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/repositories/vs_repo.py +0 -0
  75. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/__init__.py +0 -0
  76. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/auth_service.py +0 -0
  77. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/benchmark_service.py +0 -0
  78. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/branding_service.py +0 -0
  79. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/document_service.py +0 -0
  80. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/embedding_service.py +0 -0
  81. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/excel_service.py +0 -0
  82. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/file_processor_service.py +0 -0
  83. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/i18n_service.py +0 -0
  84. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/jwt_service.py +0 -0
  85. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/language_service.py +0 -0
  86. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/license_service.py +0 -0
  87. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/load_documents_service.py +0 -0
  88. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/mail_service.py +0 -0
  89. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/search_service.py +0 -0
  90. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/services/user_feedback_service.py +0 -0
  91. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/images/fernando.jpeg +0 -0
  92. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
  93. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
  94. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
  95. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/js/chat_filepond.js +0 -0
  96. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/js/chat_help_content.js +0 -0
  97. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/js/chat_history_button.js +0 -0
  98. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
  99. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
  100. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
  101. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
  102. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
  103. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/styles/chat_modal.css +0 -0
  104. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/styles/chat_public.css +0 -0
  105. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/styles/documents.css +0 -0
  106. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/styles/landing_page.css +0 -0
  107. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/static/styles/onboarding.css +0 -0
  108. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/system_prompts/format_styles.prompt +0 -0
  109. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/system_prompts/query_main.prompt +0 -0
  110. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/system_prompts/sql_rules.prompt +0 -0
  111. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/templates/_company_header.html +0 -0
  112. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/templates/_login_widget.html +0 -0
  113. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/templates/change_password.html +0 -0
  114. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/templates/chat_modals.html +0 -0
  115. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/templates/error.html +0 -0
  116. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/templates/forgot_password.html +0 -0
  117. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/templates/onboarding_shell.html +0 -0
  118. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/templates/signup.html +0 -0
  119. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/__init__.py +0 -0
  120. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/base_login_view.py +0 -0
  121. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/change_password_view.py +0 -0
  122. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/embedding_api_view.py +0 -0
  123. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/forgot_password_view.py +0 -0
  124. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/help_content_api_view.py +0 -0
  125. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/history_api_view.py +0 -0
  126. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/home_view.py +0 -0
  127. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/init_context_api_view.py +0 -0
  128. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/llmquery_api_view.py +0 -0
  129. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/load_document_api_view.py +0 -0
  130. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/login_view.py +0 -0
  131. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/logout_api_view.py +0 -0
  132. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/profile_api_view.py +0 -0
  133. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/prompt_api_view.py +0 -0
  134. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/root_redirect_view.py +0 -0
  135. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/signup_view.py +0 -0
  136. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/static_page_view.py +0 -0
  137. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
  138. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit/views/verify_user_view.py +0 -0
  139. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
  140. {iatoolkit-0.95.4 → iatoolkit-0.108.1}/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.95.4
3
+ Version: 0.108.1
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
@@ -14,7 +14,6 @@ Requires-Dist: botocore==1.36.22
14
14
  Requires-Dist: build==1.2.2.post1
15
15
  Requires-Dist: click==8.1.8
16
16
  Requires-Dist: cryptography==44.0.3
17
- Requires-Dist: deepseek==1.0.0
18
17
  Requires-Dist: Flask==3.1.0
19
18
  Requires-Dist: Flask-Bcrypt==1.0.1
20
19
  Requires-Dist: flask-cors==6.0.0
@@ -4,7 +4,6 @@ botocore==1.36.22
4
4
  build==1.2.2.post1
5
5
  click==8.1.8
6
6
  cryptography==44.0.3
7
- deepseek==1.0.0
8
7
  Flask==3.1.0
9
8
  Flask-Bcrypt==1.0.1
10
9
  flask-cors==6.0.0
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
- __version__ = "0.95.4"
6
+ __version__ = "0.108.1"
7
7
 
8
8
  # Expose main classes and functions at the top level of the package
9
9
 
@@ -24,6 +24,7 @@ from iatoolkit.infra.call_service import CallServiceClient
24
24
  from iatoolkit.services.profile_service import ProfileService
25
25
  from iatoolkit.services.mail_service import MailService
26
26
  from iatoolkit.repositories.models import Base as OrmModel
27
+ from iatoolkit.base_company import BaseCompany
27
28
 
28
29
  __all__ = [
29
30
  'IAToolkit',
@@ -40,5 +41,6 @@ __all__ = [
40
41
  'CallServiceClient',
41
42
  'ProfileService',
42
43
  'MailService',
43
- 'OrmModel'
44
+ 'OrmModel',
45
+ 'BaseCompany',
44
46
  ]
@@ -0,0 +1,21 @@
1
+ # Copyright (c) 2024 Fernando Libedinsky
2
+ # Product: IAToolkit
3
+ #
4
+ # IAToolkit is open source software.
5
+
6
+ # companies/base_company.py
7
+ from abc import ABC, abstractmethod
8
+
9
+ class BaseCompany(ABC):
10
+
11
+ @abstractmethod
12
+ # execute the specific action configured in the intent table
13
+ def handle_request(self, tag: str, params: dict) -> dict:
14
+ raise NotImplementedError("La subclase debe implementar el método handle_request()")
15
+
16
+ @abstractmethod
17
+ def register_cli_commands(self, app):
18
+ pass
19
+
20
+ def unsupported_operation(self, tag):
21
+ raise NotImplementedError(f"La operación '{tag}' no está soportada por esta empresa.")
@@ -14,12 +14,13 @@ def register_core_commands(app):
14
14
 
15
15
  @app.cli.command("api-key")
16
16
  @click.argument("company_short_name")
17
- def api_key(company_short_name: str):
17
+ @click.argument("key_name")
18
+ def api_key(company_short_name: str, key_name: str):
18
19
  """⚙️ Genera una nueva API key para una compañía ya registrada."""
19
20
  try:
20
21
  profile_service = IAToolkit.get_instance().get_injector().get(ProfileService)
21
22
  click.echo(f"🔑 Generating API-KEY for company: '{company_short_name}'...")
22
- result = profile_service.new_api_key(company_short_name)
23
+ result = profile_service.new_api_key(company_short_name, key_name)
23
24
 
24
25
  if 'error' in result:
25
26
  click.echo(f"❌ Error: {result['error']}")
@@ -0,0 +1,34 @@
1
+ from enum import Enum
2
+ from typing import List
3
+ import abc
4
+
5
+
6
+ class AssetType(Enum):
7
+ CONFIG = "config"
8
+ PROMPT = "prompts"
9
+ SCHEMA = "schema"
10
+ CONTEXT = "context"
11
+
12
+
13
+ class AssetRepository(abc.ABC):
14
+ @abc.abstractmethod
15
+ def exists(self, company_short_name: str, asset_type: AssetType, filename: str) -> bool:
16
+ pass
17
+
18
+ @abc.abstractmethod
19
+ def read_text(self, company_short_name: str, asset_type: AssetType, filename: str) -> str:
20
+ pass
21
+
22
+ @abc.abstractmethod
23
+ def list_files(self, company_short_name: str, asset_type: AssetType, extension: str = None) -> List[str]:
24
+ pass
25
+
26
+ @abc.abstractmethod
27
+ def write_text(self, company_short_name: str, asset_type: AssetType, filename: str, content: str) -> None:
28
+ """Creates or updates a text asset."""
29
+ pass
30
+
31
+ @abc.abstractmethod
32
+ def delete(self, company_short_name: str, asset_type: AssetType, filename: str) -> None:
33
+ """Deletes an asset if it exists."""
34
+ pass
@@ -0,0 +1,38 @@
1
+ import abc
2
+ from typing import Any, List, Dict, Union
3
+
4
+ class DatabaseProvider(abc.ABC):
5
+ """
6
+ Abstract interface for interacting with a database source.
7
+ Handles both metadata introspection and query execution.
8
+ """
9
+
10
+ # --- Schema Methods ---
11
+ @abc.abstractmethod
12
+ def get_all_table_names(self) -> List[str]:
13
+ pass
14
+
15
+ @abc.abstractmethod
16
+ def get_table_description(self,
17
+ table_name: str,
18
+ schema_object_name: str | None = None,
19
+ exclude_columns: List[str] | None = None) -> str:
20
+ pass
21
+
22
+ # --- Execution Methods ---
23
+ @abc.abstractmethod
24
+ def execute_query(self, query: str, commit: bool = False) -> Union[List[Dict[str, Any]], Dict[str, int]]:
25
+ """
26
+ Executes a query and returns:
27
+ - A list of dicts for SELECT (rows).
28
+ - A dict {'rowcount': N} for INSERT/UPDATE/DELETE.
29
+ """
30
+ pass
31
+
32
+ @abc.abstractmethod
33
+ def commit(self) -> None:
34
+ pass
35
+
36
+ @abc.abstractmethod
37
+ def rollback(self) -> None:
38
+ pass
@@ -42,7 +42,7 @@ class ModelRegistry:
42
42
  # Hardcoded rules for now; can be extended or loaded from config later.
43
43
  # The order of patterns matters: first match wins.
44
44
  self._provider_patterns: dict[ProviderType, tuple[str, ...]] = {
45
- "openai": ("gpt", "gpt-5"),
45
+ "openai": ("gpt", "gpt-5", "gpt-5-mini", "gpt-5.1"),
46
46
  "gemini": ("gemini", "gemini-3"),
47
47
  "deepseek": ("deepseek",),
48
48
  "xai": ("grok", "grok-1", "grok-beta"),
@@ -72,6 +72,57 @@ class ModelRegistry:
72
72
 
73
73
  return "unknown"
74
74
 
75
+ def get_request_defaults(self, model: str) -> dict:
76
+ """
77
+ Return per-model request defaults to keep model-specific policy centralized.
78
+
79
+ Notes:
80
+ - This should only include keys that are supported by the target provider.
81
+ - Callers should merge these defaults with user-provided params (do not mutate inputs).
82
+ """
83
+ model_lower = (model or "").lower()
84
+ provider = self.get_provider(model_lower)
85
+
86
+ # Conservative defaults: do not send provider-specific knobs unless we know they are supported.
87
+ defaults = {"text": {}, "reasoning": {}}
88
+
89
+ # OpenAI/xAI (OpenAI-compatible) support 'text.verbosity' and 'reasoning.effort' in our current integration.
90
+ if provider in ("openai", "xai"):
91
+ defaults["text"] = {"verbosity": "low"}
92
+
93
+ # Fine-grained per-model tuning.
94
+ if model_lower in ("gpt-5", "gpt-5-mini"):
95
+ defaults["reasoning"] = {"effort": "minimal"}
96
+ elif model_lower == "gpt-5.1":
97
+ defaults["reasoning"] = {"effort": "low", "summary": "auto"}
98
+
99
+ # Gemini/DeepSeek/unknown: keep defaults empty to avoid sending unsupported parameters.
100
+ return defaults
101
+
102
+ def resolve_request_params(self, model: str, text: dict | None = None, reasoning: dict | None = None) -> dict:
103
+ """
104
+ Resolve provider/model defaults and merge them with caller-provided overrides.
105
+
106
+ Rules:
107
+ - Defaults come from get_request_defaults(model).
108
+ - Caller overrides win over defaults.
109
+ - Input dictionaries are never mutated.
110
+ """
111
+ defaults = self.get_request_defaults(model)
112
+
113
+ merged_text: dict = {}
114
+ merged_text.update(defaults.get("text") or {})
115
+ merged_text.update(text or {})
116
+
117
+ merged_reasoning: dict = {}
118
+ merged_reasoning.update(defaults.get("reasoning") or {})
119
+ merged_reasoning.update(reasoning or {})
120
+
121
+ return {
122
+ "text": merged_text,
123
+ "reasoning": merged_reasoning,
124
+ }
125
+
75
126
  def get_history_type(self, model: str) -> HistoryType:
76
127
  """
77
128
  Returns the history strategy for a given model.
@@ -24,10 +24,13 @@ def register_views(app):
24
24
  from iatoolkit.views.profile_api_view import UserLanguageApiView
25
25
  from iatoolkit.views.embedding_api_view import EmbeddingApiView
26
26
  from iatoolkit.views.login_view import LoginView, FinalizeContextView
27
+ from iatoolkit.views.load_company_configuration_api_view import LoadCompanyConfigurationApiView
27
28
  from iatoolkit.views.logout_api_view import LogoutApiView
28
29
  from iatoolkit.views.home_view import HomeView
30
+ from iatoolkit.views.chat_view import ChatView
29
31
  from iatoolkit.views.static_page_view import StaticPageView
30
32
  from iatoolkit.views.root_redirect_view import RootRedirectView
33
+ from iatoolkit.views.users_api_view import UsersApiView
31
34
 
32
35
  # assign root '/' to our new redirect logic
33
36
  app.add_url_rule('/home', view_func=RootRedirectView.as_view('root_redirect'))
@@ -38,6 +41,10 @@ def register_views(app):
38
41
  # login for the iatoolkit integrated frontend
39
42
  app.add_url_rule('/<company_short_name>/login', view_func=LoginView.as_view('login'))
40
43
 
44
+ # Chat Route (Direct Access)
45
+ app.add_url_rule('/<company_short_name>/chat',
46
+ view_func=ChatView.as_view('chat'))
47
+
41
48
  # this endpoint is called when onboarding_shell finish the context load
42
49
  app.add_url_rule(
43
50
  '/<company_short_name>/finalize',
@@ -68,6 +75,10 @@ def register_views(app):
68
75
  app.add_url_rule('/<company_short_name>/verify/<token>', view_func=VerifyAccountView.as_view('verify_account'))
69
76
  app.add_url_rule('/<company_short_name>/forgot-password', view_func=ForgotPasswordView.as_view('forgot_password'))
70
77
  app.add_url_rule('/<company_short_name>/change-password/<token>', view_func=ChangePasswordView.as_view('change_password'))
78
+ app.add_url_rule(
79
+ '/<string:company_short_name>/api/company-users',
80
+ view_func=UsersApiView.as_view('company-users')
81
+ )
71
82
 
72
83
  # main chat query, used by the JS in the browser (with credentials)
73
84
  # can be used also for executing iatoolkit prompts
@@ -88,6 +99,10 @@ def register_views(app):
88
99
  app.add_url_rule('/<company_short_name>/api/embedding',
89
100
  view_func=EmbeddingApiView.as_view('embedding_api'))
90
101
 
102
+ # company configuration
103
+ app.add_url_rule('/<company_short_name>/api/load_configuration',
104
+ view_func=LoadCompanyConfigurationApiView.as_view('load-configuration'))
105
+
91
106
  # static pages
92
107
  # url: /pages/foundation o /pages/implementation_plan
93
108
  static_view = StaticPageView.as_view('static_pages')
@@ -157,6 +157,17 @@ class Utility:
157
157
  schema = yaml.safe_load(f)
158
158
  return schema
159
159
 
160
+ def load_yaml_from_string(self, yaml_content: str) -> dict:
161
+ """
162
+ Parses a YAML string into a dictionary securely.
163
+ """
164
+ try:
165
+ yaml_content = yaml_content.replace('\t', ' ')
166
+ return yaml.safe_load(yaml_content) or {}
167
+ except yaml.YAMLError as e:
168
+ logging.error(f"Error parsing YAML string: {e}")
169
+ return {}
170
+
160
171
  def generate_context_for_schema(self, entity_name: str, schema_file: str = None, schema: dict = {}) -> str:
161
172
  if not schema_file and not schema:
162
173
  raise IAToolkitException(IAToolkitException.ErrorType.FILE_IO_ERROR,
@@ -6,6 +6,7 @@
6
6
  from typing import Dict, Type, Any, Optional
7
7
  from .base_company import BaseCompany
8
8
  import logging
9
+ from injector import inject
9
10
 
10
11
 
11
12
  class CompanyRegistry:
@@ -14,6 +15,7 @@ class CompanyRegistry:
14
15
  Allow the client to register companies and instantiate them with dependency injection.
15
16
  """
16
17
 
18
+ @inject
17
19
  def __init__(self):
18
20
  self._company_classes: Dict[str, Type[BaseCompany]] = {}
19
21
  self._company_instances: Dict[str, BaseCompany] = {}
@@ -85,6 +87,9 @@ def get_company_registry() -> CompanyRegistry:
85
87
  """Get the global company registry instance."""
86
88
  return _company_registry
87
89
 
90
+ def get_registered_companies() -> Dict[str, Type[BaseCompany]]:
91
+ return _company_registry.get_registered_companies()
92
+
88
93
 
89
94
  def set_company_registry(registry: CompanyRegistry) -> None:
90
95
  """
@@ -9,20 +9,26 @@ from flask_injector import FlaskInjector
9
9
  from flask_bcrypt import Bcrypt
10
10
  from flask_cors import CORS
11
11
  from iatoolkit.common.exceptions import IAToolkitException
12
- from typing import Optional, Dict, Any
13
12
  from iatoolkit.repositories.database_manager import DatabaseManager
13
+ from iatoolkit.common.interfaces.asset_storage import AssetRepository
14
+ from iatoolkit.company_registry import get_registered_companies
14
15
  from werkzeug.middleware.proxy_fix import ProxyFix
15
16
  from injector import Binder, Injector, singleton
17
+ from typing import Optional, Dict, Any
16
18
  from urllib.parse import urlparse
17
19
  import redis
18
20
  import logging
19
21
  import os
20
22
 
21
23
  from iatoolkit import __version__ as IATOOLKIT_VERSION
24
+ from iatoolkit.services.configuration_service import ConfigurationService
22
25
 
23
26
  # global variable for the unique instance of IAToolkit
24
27
  _iatoolkit_instance: Optional['IAToolkit'] = None
25
28
 
29
+ def is_bound(injector: Injector, cls) -> bool:
30
+ return cls in injector.binder._bindings
31
+
26
32
  class IAToolkit:
27
33
  """
28
34
  IAToolkit main class
@@ -49,8 +55,8 @@ class IAToolkit:
49
55
  self.config = config or {}
50
56
  self.app = None
51
57
  self.db_manager = None
52
- self._injector = None
53
- self.version = IATOOLKIT_VERSION # default version
58
+ self._injector = Injector() # init empty injector
59
+ self.version = IATOOLKIT_VERSION
54
60
  self.license = "Community Edition"
55
61
 
56
62
  @classmethod
@@ -61,7 +67,7 @@ class IAToolkit:
61
67
  _iatoolkit_instance = cls()
62
68
  return _iatoolkit_instance
63
69
 
64
- def create_iatoolkit(self):
70
+ def create_iatoolkit(self, start: bool = True):
65
71
  """
66
72
  Creates, configures, and returns the Flask application instance.
67
73
  this is the main entry point for the application factory.
@@ -77,8 +83,8 @@ class IAToolkit:
77
83
  # Step 2: Set up the core components that DI depends on
78
84
  self._setup_database()
79
85
 
80
- # Step 3: Create the Injector and configure all dependencies in one place
81
- self._injector = Injector(self._configure_core_dependencies)
86
+ # Step 3: Configure dependencies using the existing injector
87
+ self._configure_core_dependencies(self._injector)
82
88
 
83
89
  # Step 4: Register routes using the fully configured injector
84
90
  self._register_routes()
@@ -98,6 +104,7 @@ class IAToolkit:
98
104
 
99
105
  # Step 8: Finalize setup within the application context
100
106
  self._setup_redis_sessions()
107
+
101
108
  self._setup_cors()
102
109
  self._setup_additional_services()
103
110
  self._setup_cli_commands()
@@ -107,11 +114,21 @@ class IAToolkit:
107
114
  # Step 9: define the download_dir
108
115
  self._setup_download_dir()
109
116
 
117
+ # register data source
118
+ if start:
119
+ self.register_data_sources()
120
+
110
121
  logging.info(f"🎉 IAToolkit {self.license} version {self.version} correctly initialized.")
111
122
  self._initialized = True
112
123
 
113
124
  return self.app
114
125
 
126
+ def register_data_sources(self):
127
+ # load the company configurations
128
+ configuration_service = self._injector.get(ConfigurationService)
129
+ for company in get_registered_companies():
130
+ configuration_service.register_data_sources(company)
131
+
115
132
  def _get_config_value(self, key: str, default=None):
116
133
  # get a value from the config dict or the environment variable
117
134
  return self.config.get(key, os.getenv(key, default))
@@ -181,11 +198,11 @@ class IAToolkit:
181
198
  os.environ["TOKENIZERS_PARALLELISM"] = "false"
182
199
 
183
200
  def _setup_database(self):
184
- database_uri = self._get_config_value('DATABASE_URI')
201
+ database_uri = self._get_config_value('DATABASE_URI') or self._get_config_value('DATABASE_URL')
185
202
  if not database_uri:
186
203
  raise IAToolkitException(
187
204
  IAToolkitException.ErrorType.CONFIG_ERROR,
188
- "DATABASE_URI is requires (config dict or env. variable)"
205
+ "DATABASE_URI is required (config dict or env. variable)"
189
206
  )
190
207
 
191
208
  self.db_manager = DatabaseManager(database_url=database_uri, schema='iatoolkit')
@@ -240,8 +257,9 @@ class IAToolkit:
240
257
  extra_origins = []
241
258
  all_company_instances = get_company_registry().get_all_company_instances()
242
259
  for company_name, company_instance in all_company_instances.items():
243
- cors_origin = company_instance.company.parameters.get('cors_origin', [])
244
- extra_origins += cors_origin
260
+ if company_instance.company:
261
+ cors_origin = company_instance.company.parameters.get('cors_origin', [])
262
+ extra_origins += cors_origin
245
263
 
246
264
  all_origins = default_origins + extra_origins
247
265
 
@@ -256,8 +274,11 @@ class IAToolkit:
256
274
 
257
275
  logging.info(f"✅ CORS configured for: {all_origins}")
258
276
 
259
- def _configure_core_dependencies(self, binder: Binder):
277
+ def _configure_core_dependencies(self, injector: Injector):
260
278
  """⚙️ Configures all system dependencies."""
279
+
280
+ # get the binder from injector
281
+ binder = injector.binder
261
282
  try:
262
283
  # Core dependencies
263
284
  binder.bind(Flask, to=self.app)
@@ -282,12 +303,17 @@ class IAToolkit:
282
303
  from iatoolkit.repositories.profile_repo import ProfileRepo
283
304
  from iatoolkit.repositories.llm_query_repo import LLMQueryRepo
284
305
  from iatoolkit.repositories.vs_repo import VSRepo
306
+ from iatoolkit.repositories.filesystem_asset_repository import FileSystemAssetRepository
285
307
 
286
308
  binder.bind(DocumentRepo, to=DocumentRepo)
287
309
  binder.bind(ProfileRepo, to=ProfileRepo)
288
310
  binder.bind(LLMQueryRepo, to=LLMQueryRepo)
289
311
  binder.bind(VSRepo, to=VSRepo)
290
312
 
313
+ # this class can be setup befor by iatoolkit enterprise
314
+ if not is_bound(self._injector, AssetRepository):
315
+ binder.bind(AssetRepository, to=FileSystemAssetRepository)
316
+
291
317
  def _bind_services(self, binder: Binder):
292
318
  from iatoolkit.services.query_service import QueryService
293
319
  from iatoolkit.services.benchmark_service import BenchmarkService
@@ -308,7 +334,7 @@ class IAToolkit:
308
334
  from iatoolkit.services.tool_service import ToolService
309
335
  from iatoolkit.services.llm_client_service import llmClient
310
336
  from iatoolkit.services.auth_service import AuthService
311
-
337
+ from iatoolkit.services.sql_service import SqlService
312
338
 
313
339
  binder.bind(QueryService, to=QueryService)
314
340
  binder.bind(BenchmarkService, to=BenchmarkService)
@@ -329,6 +355,7 @@ class IAToolkit:
329
355
  binder.bind(ToolService, to=ToolService)
330
356
  binder.bind(llmClient, to=llmClient)
331
357
  binder.bind(AuthService, to=AuthService)
358
+ binder.bind(SqlService, to=SqlService)
332
359
 
333
360
  def _bind_infrastructure(self, binder: Binder):
334
361
  from iatoolkit.infra.llm_proxy import LLMProxy
@@ -405,11 +432,13 @@ class IAToolkit:
405
432
  'app_name': 'IAToolkit',
406
433
  'user_identifier': SessionManager.get('user_identifier'),
407
434
  'company_short_name': SessionManager.get('company_short_name'),
435
+ 'user_role': user_profile.get('user_role'),
408
436
  'user_is_local': user_profile.get('user_is_local'),
409
437
  'user_email': user_profile.get('user_email'),
410
438
  'iatoolkit_base_url': request.url_root,
411
439
  'flashed_messages': get_flashed_messages(with_categories=True),
412
- 't': translate_for_template
440
+ 't': translate_for_template,
441
+ 'google_analytics_id': self._get_config_value('GOOGLE_ANALYTICS_ID', ''),
413
442
  }
414
443
 
415
444
  def _get_default_static_folder(self) -> str:
@@ -473,6 +502,7 @@ class IAToolkit:
473
502
  logging.info(f"✅ download dir created in: {download_dir}")
474
503
 
475
504
 
505
+
476
506
  def current_iatoolkit() -> IAToolkit:
477
507
  return IAToolkit.get_instance()
478
508
 
@@ -9,7 +9,7 @@ from typing import Dict, List, Optional, Any
9
9
 
10
10
  from iatoolkit.infra.llm_response import LLMResponse, ToolCall, Usage
11
11
  from iatoolkit.common.exceptions import IAToolkitException
12
-
12
+ import json
13
13
 
14
14
  class DeepseekAdapter:
15
15
  """
@@ -85,6 +85,7 @@ class DeepseekAdapter:
85
85
  if tool_choice:
86
86
  call_kwargs["tool_choice"] = tool_choice
87
87
 
88
+ logging.debug(f"[DeepseekAdapter] Calling DeepSeek chat.completions API...: {json.dumps(messages, indent=2)}")
88
89
  response = self.client.chat.completions.create(**call_kwargs)
89
90
 
90
91
  return self._map_deepseek_chat_response(response)
@@ -127,7 +128,7 @@ class DeepseekAdapter:
127
128
 
128
129
  messages.append(
129
130
  {
130
- "role": "assistant",
131
+ "role": "user",
131
132
  "content": f"Tool result:\n{output}",
132
133
  }
133
134
  )
@@ -141,10 +142,6 @@ class DeepseekAdapter:
141
142
  logging.warning(f"[DeepseekAdapter] Skipping tool-role message: {item}")
142
143
  continue
143
144
 
144
- # Map internal 'model' role to 'assistant'
145
- if role == "model":
146
- role = "assistant"
147
-
148
145
  if not role:
149
146
  logging.warning(f"[DeepseekAdapter] Skipping message without role: {item}")
150
147
  continue
@@ -230,13 +227,19 @@ class DeepseekAdapter:
230
227
  total_tokens=getattr(getattr(response, "usage", None), "total_tokens", 0) or 0,
231
228
  )
232
229
 
230
+ # Capture reasoning content (specific to deepseek-reasoner)
231
+ reasoning_content = getattr(message, "reasoning_content", "") or ""
232
+
233
+ # If the model produced tool calls, fills this list
233
234
  tool_calls_out: List[ToolCall] = []
234
- status = "completed"
235
- output_text = ""
236
235
 
237
- # If the model produced tool calls:
238
236
  tool_calls = getattr(message, "tool_calls", None) or []
239
- if tool_calls:
237
+ if not tool_calls:
238
+ # No tool calls: standard assistant message
239
+ output_text = getattr(message, "content", "") or ""
240
+ status = "completed"
241
+
242
+ else:
240
243
  logging.debug(f"[DeepSeek] RAW tool_calls: {tool_calls}")
241
244
 
242
245
  for tc in tool_calls:
@@ -264,11 +267,6 @@ class DeepseekAdapter:
264
267
  status = "tool_calls"
265
268
  output_text = "" # caller will inspect tool_calls in .output
266
269
 
267
- else:
268
- # No tool calls: standard assistant message
269
- output_text = getattr(message, "content", "") or ""
270
- status = "completed"
271
-
272
270
  return LLMResponse(
273
271
  id=getattr(response, "id", "deepseek-unknown"),
274
272
  model=getattr(response, "model", "deepseek-unknown"),
@@ -276,4 +274,5 @@ class DeepseekAdapter:
276
274
  output_text=output_text,
277
275
  output=tool_calls_out,
278
276
  usage=usage,
277
+ reasoning_content=reasoning_content
279
278
  )
@@ -7,7 +7,8 @@ import logging
7
7
  from typing import Dict, List, Optional
8
8
  from iatoolkit.infra.llm_response import LLMResponse, ToolCall, Usage
9
9
  from iatoolkit.common.exceptions import IAToolkitException
10
-
10
+ import html
11
+ from typing import List
11
12
 
12
13
  class OpenAIAdapter:
13
14
  """Adaptador para la API de OpenAI"""
@@ -76,11 +77,48 @@ class OpenAIAdapter:
76
77
  total_tokens=openai_response.usage.total_tokens if openai_response.usage else 0
77
78
  )
78
79
 
80
+ # Reasoning content extracted from Responses output items (type="reasoning")
81
+ reasoning_list = self._extract_reasoning_content(openai_response)
82
+ reasoning_str = "\n".join(reasoning_list)
83
+
79
84
  return LLMResponse(
80
85
  id=openai_response.id,
81
86
  model=openai_response.model,
82
87
  status=openai_response.status,
83
88
  output_text=getattr(openai_response, 'output_text', ''),
84
89
  output=tool_calls,
85
- usage=usage
86
- )
90
+ usage=usage,
91
+ reasoning_content=reasoning_str
92
+ )
93
+
94
+ def _extract_reasoning_content(self, openai_response) -> List[str]:
95
+ """
96
+ Extract reasoning summaries (preferred) or reasoning content fragments from Responses API output.
97
+
98
+ Format required by caller:
99
+ 1. reason is ...
100
+ 2. reason is ...
101
+ """
102
+ reasons: List[str] = []
103
+
104
+ output_items = getattr(openai_response, "output", None) or []
105
+ for item in output_items:
106
+ if getattr(item, "type", None) != "reasoning":
107
+ continue
108
+
109
+ # 1) Preferred: reasoning summaries (requires reasoning={"summary":"auto"} or similar)
110
+ summary = getattr(item, "summary", None) or []
111
+ for s in summary:
112
+ text = getattr(s, "text", None)
113
+ if text:
114
+ reasons.append(str(text).strip())
115
+
116
+ # 2) Fallback: some responses may carry reasoning content in "content"
117
+ # (e.g., content parts like {"type":"reasoning_text","text":"..."}).
118
+ content = getattr(item, "content", None) or []
119
+ for c in content:
120
+ text = getattr(c, "text", None)
121
+ if text:
122
+ reasons.append(str(text).strip())
123
+
124
+ return reasons
@@ -32,9 +32,14 @@ class LLMResponse:
32
32
  output_text: str
33
33
  output: List[ToolCall] # lista de tool calls
34
34
  usage: Usage
35
+ reasoning_content: str = None # campo opcional para Chain of Thought
36
+
35
37
 
36
38
  def __post_init__(self):
37
39
  """Asegura que output sea una lista"""
38
40
  if self.output is None:
39
41
  self.output = []
40
42
 
43
+ if self.reasoning_content is None:
44
+ self.reasoning_content = ""
45
+