iatoolkit 1.10.0__tar.gz → 1.21.0__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 (150) hide show
  1. {iatoolkit-1.10.0/src/iatoolkit.egg-info → iatoolkit-1.21.0}/PKG-INFO +1 -1
  2. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/__init__.py +3 -3
  3. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/cli_commands.py +16 -0
  4. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/common/routes.py +6 -0
  5. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/common/util.py +8 -123
  6. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/core.py +3 -2
  7. iatoolkit-1.21.0/src/iatoolkit/infra/connectors/file_connector.py +28 -0
  8. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/file_connector_factory.py +22 -27
  9. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +14 -0
  10. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/google_drive_connector.py +32 -1
  11. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/local_file_connector.py +22 -0
  12. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/s3_connector.py +27 -1
  13. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +17 -1
  14. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +117 -18
  15. iatoolkit-1.21.0/src/iatoolkit/infra/llm_providers/openai_adapter.py +219 -0
  16. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/llm_response.py +13 -0
  17. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/locales/en.yaml +87 -2
  18. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/locales/es.yaml +86 -1
  19. iatoolkit-1.21.0/src/iatoolkit/repositories/document_repo.py +72 -0
  20. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/repositories/llm_query_repo.py +46 -34
  21. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/repositories/models.py +52 -3
  22. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/repositories/vs_repo.py +1 -1
  23. iatoolkit-1.21.0/src/iatoolkit/services/company_context_service.py +397 -0
  24. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/configuration_service.py +28 -1
  25. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/dispatcher_service.py +1 -1
  26. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/file_processor_service.py +0 -3
  27. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/i18n_service.py +7 -0
  28. iatoolkit-1.21.0/src/iatoolkit/services/ingestor_service.py +297 -0
  29. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/knowledge_base_service.py +41 -9
  30. iatoolkit-1.21.0/src/iatoolkit/services/language_service.py +131 -0
  31. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/llm_client_service.py +61 -2
  32. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/prompt_service.py +186 -344
  33. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/query_service.py +49 -20
  34. iatoolkit-1.21.0/src/iatoolkit/services/storage_service.py +184 -0
  35. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/tool_service.py +3 -2
  36. iatoolkit-1.21.0/src/iatoolkit/static/js/chat_filepond.js +210 -0
  37. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_main.js +105 -52
  38. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/chat_iatoolkit.css +96 -0
  39. iatoolkit-1.21.0/src/iatoolkit/system_prompts/query_main.prompt +59 -0
  40. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/templates/chat.html +15 -6
  41. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/base_login_view.py +1 -1
  42. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/chat_view.py +1 -1
  43. iatoolkit-1.21.0/src/iatoolkit/views/ingestion_api_view.py +100 -0
  44. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/login_view.py +1 -1
  45. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/prompt_api_view.py +1 -1
  46. {iatoolkit-1.10.0 → iatoolkit-1.21.0/src/iatoolkit.egg-info}/PKG-INFO +1 -1
  47. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit.egg-info/SOURCES.txt +3 -1
  48. iatoolkit-1.10.0/src/iatoolkit/infra/connectors/file_connector.py +0 -17
  49. iatoolkit-1.10.0/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -124
  50. iatoolkit-1.10.0/src/iatoolkit/repositories/document_repo.py +0 -40
  51. iatoolkit-1.10.0/src/iatoolkit/services/company_context_service.py +0 -236
  52. iatoolkit-1.10.0/src/iatoolkit/services/language_service.py +0 -89
  53. iatoolkit-1.10.0/src/iatoolkit/services/load_documents_service.py +0 -152
  54. iatoolkit-1.10.0/src/iatoolkit/static/js/chat_filepond.js +0 -85
  55. iatoolkit-1.10.0/src/iatoolkit/system_prompts/query_main.prompt +0 -74
  56. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/LICENSE +0 -0
  57. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/LICENSE_COMMUNITY.md +0 -0
  58. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/pyproject.toml +0 -0
  59. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/readme.md +0 -0
  60. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/requirements.txt +0 -0
  61. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/setup.cfg +0 -0
  62. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/base_company.py +0 -0
  63. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/common/__init__.py +0 -0
  64. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/common/exceptions.py +0 -0
  65. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/common/interfaces/__init__.py +0 -0
  66. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
  67. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
  68. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/common/model_registry.py +0 -0
  69. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/common/session_manager.py +0 -0
  70. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/company_registry.py +0 -0
  71. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/__init__.py +0 -0
  72. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
  73. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/call_service.py +0 -0
  74. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/__init__.py +0 -0
  75. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/google_chat_app.py +0 -0
  76. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
  77. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/llm_proxy.py +0 -0
  78. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/infra/redis_session_manager.py +0 -0
  79. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/repositories/__init__.py +0 -0
  80. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/repositories/database_manager.py +0 -0
  81. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
  82. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/repositories/profile_repo.py +0 -0
  83. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/__init__.py +0 -0
  84. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/auth_service.py +0 -0
  85. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/benchmark_service.py +0 -0
  86. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/branding_service.py +0 -0
  87. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/document_service.py +0 -0
  88. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/embedding_service.py +0 -0
  89. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/excel_service.py +0 -0
  90. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/history_manager_service.py +0 -0
  91. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/jwt_service.py +0 -0
  92. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/license_service.py +0 -0
  93. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/mail_service.py +0 -0
  94. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/profile_service.py +0 -0
  95. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/sql_service.py +0 -0
  96. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/user_feedback_service.py +0 -0
  97. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/services/user_session_context_service.py +0 -0
  98. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/images/fernando.jpeg +0 -0
  99. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
  100. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
  101. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
  102. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_help_content.js +0 -0
  103. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_history_button.js +0 -0
  104. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
  105. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
  106. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
  107. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
  108. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
  109. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/chat_modal.css +0 -0
  110. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/chat_public.css +0 -0
  111. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/documents.css +0 -0
  112. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/landing_page.css +0 -0
  113. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/llm_output.css +0 -0
  114. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/onboarding.css +0 -0
  115. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/system_prompts/__init__.py +0 -0
  116. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/system_prompts/format_styles.prompt +0 -0
  117. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/system_prompts/sql_rules.prompt +0 -0
  118. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/templates/_company_header.html +0 -0
  119. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/templates/_login_widget.html +0 -0
  120. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/templates/base.html +0 -0
  121. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/templates/change_password.html +0 -0
  122. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/templates/chat_modals.html +0 -0
  123. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/templates/error.html +0 -0
  124. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/templates/forgot_password.html +0 -0
  125. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/templates/onboarding_shell.html +0 -0
  126. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/templates/signup.html +0 -0
  127. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/__init__.py +0 -0
  128. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/categories_api_view.py +0 -0
  129. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/change_password_view.py +0 -0
  130. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/configuration_api_view.py +0 -0
  131. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/embedding_api_view.py +0 -0
  132. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/forgot_password_view.py +0 -0
  133. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/help_content_api_view.py +0 -0
  134. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/history_api_view.py +0 -0
  135. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/home_view.py +0 -0
  136. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/init_context_api_view.py +0 -0
  137. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/llmquery_api_view.py +0 -0
  138. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/load_document_api_view.py +0 -0
  139. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/logout_api_view.py +0 -0
  140. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/profile_api_view.py +0 -0
  141. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/rag_api_view.py +0 -0
  142. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/root_redirect_view.py +0 -0
  143. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/signup_view.py +0 -0
  144. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/static_page_view.py +0 -0
  145. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
  146. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/users_api_view.py +0 -0
  147. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit/views/verify_user_view.py +0 -0
  148. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
  149. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/src/iatoolkit.egg-info/requires.txt +0 -0
  150. {iatoolkit-1.10.0 → iatoolkit-1.21.0}/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: 1.10.0
3
+ Version: 1.21.0
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
- __version__ = "1.10.0"
6
+ __version__ = "1.21.0"
7
7
 
8
8
  # Expose main classes and functions at the top level of the package
9
9
 
@@ -19,7 +19,7 @@ from iatoolkit.services.query_service import QueryService
19
19
  from iatoolkit.services.document_service import DocumentService
20
20
  from iatoolkit.services.knowledge_base_service import KnowledgeBaseService
21
21
  from iatoolkit.services.sql_service import SqlService
22
- from iatoolkit.services.load_documents_service import LoadDocumentsService
22
+ from iatoolkit.services.ingestor_service import IngestorService
23
23
  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
@@ -37,7 +37,7 @@ __all__ = [
37
37
  'SqlService',
38
38
  'DocumentService',
39
39
  'KnowledgeBaseService',
40
- 'LoadDocumentsService',
40
+ 'IngestorService',
41
41
  'CallServiceClient',
42
42
  'ProfileService',
43
43
  'MailService',
@@ -7,6 +7,7 @@ import click
7
7
  import logging
8
8
  from iatoolkit.core import IAToolkit
9
9
  from iatoolkit.services.profile_service import ProfileService
10
+ from iatoolkit.services.prompt_service import PromptService
10
11
 
11
12
 
12
13
  def register_core_commands(app):
@@ -32,6 +33,21 @@ def register_core_commands(app):
32
33
  logging.exception(e)
33
34
  click.echo(f"❌ unexpectd error during the configuration: {e}")
34
35
 
36
+ @app.cli.command("init-company")
37
+ @click.argument("company_short_name")
38
+ def init_company(company_short_name: str):
39
+ """⚙️ Bootstrap a new company."""
40
+ try:
41
+ prompt_service = IAToolkit.get_instance().get_injector().get(PromptService)
42
+ click.echo(f"🔑 Bootstrap company: '{company_short_name}'...")
43
+ result = prompt_service.register_system_prompts(company_short_name)
44
+
45
+ if result:
46
+ click.echo("✅ System prompts registered successfully!")
47
+ except Exception as e:
48
+ logging.exception(e)
49
+ click.echo(f"❌ unexpected error during the configuration: {e}")
50
+
35
51
  @app.cli.command("encrypt-key")
36
52
  @click.argument("key")
37
53
  def encrypt_llm_api_key(key: str):
@@ -33,6 +33,7 @@ def register_views(app):
33
33
  from iatoolkit.views.users_api_view import UsersApiView
34
34
  from iatoolkit.views.rag_api_view import RagApiView
35
35
  from iatoolkit.views.categories_api_view import CategoriesApiView
36
+ from iatoolkit.views.ingestion_api_view import IngestionApiView
36
37
 
37
38
  # assign root '/' to our new redirect logic
38
39
  app.add_url_rule('/home', view_func=RootRedirectView.as_view('root_redirect'))
@@ -136,6 +137,11 @@ def register_views(app):
136
137
  # this endpoint is for upload documents into the vector store (api-key)
137
138
  app.add_url_rule('/api/load-document', view_func=LoadDocumentApiView.as_view('load-document'), methods=['POST'])
138
139
 
140
+ # Document ingestion
141
+ ingestion_view = IngestionApiView.as_view('ingestion_api')
142
+ app.add_url_rule('/<company_short_name>/api/ingestion-sources', view_func=ingestion_view, methods=['GET', 'POST'])
143
+ app.add_url_rule('/<company_short_name>/api/ingestion-sources/<int:source_id>/<action>', view_func=ingestion_view, methods=['POST'])
144
+
139
145
  # this endpoint is for generating embeddings for a given text
140
146
  app.add_url_rule('/<company_short_name>/api/embedding',
141
147
  view_func=EmbeddingApiView.as_view('embedding_api'))
@@ -162,8 +162,15 @@ class Utility:
162
162
  Parses a YAML string into a dictionary securely.
163
163
  """
164
164
  try:
165
+ if not yaml_content:
166
+ return {}
167
+
168
+ # Normalizar tabulaciones que rompen YAML
165
169
  yaml_content = yaml_content.replace('\t', ' ')
166
- return yaml.safe_load(yaml_content) or {}
170
+
171
+ loaded = yaml.safe_load(yaml_content)
172
+ # Asegurar que siempre retornamos un dict, incluso si el YAML es una lista o escalar
173
+ return loaded if isinstance(loaded, dict) else {}
167
174
  except yaml.YAMLError as e:
168
175
  logging.error(f"Error parsing YAML string: {e}")
169
176
  return {}
@@ -182,128 +189,6 @@ class Utility:
182
189
  raise IAToolkitException(IAToolkitException.ErrorType.FILE_IO_ERROR,
183
190
  f"Failed to generate YAML: {e}")
184
191
 
185
- def generate_context_for_schema(self, entity_name: str, schema_file: str = None, schema: dict = {}) -> str:
186
- if not schema_file and not schema:
187
- raise IAToolkitException(IAToolkitException.ErrorType.FILE_IO_ERROR,
188
- f'No se pudo obtener schema de la entidad: {entity_name}')
189
-
190
- try:
191
- if schema_file:
192
- schema = self.load_schema_from_yaml(schema_file)
193
- table_schema = self.generate_schema_table(schema)
194
- return table_schema
195
- except Exception as e:
196
- logging.exception(e)
197
- raise IAToolkitException(IAToolkitException.ErrorType.FILE_IO_ERROR,
198
- f'No se pudo leer el schema de la entidad: {entity_name}') from e
199
-
200
- def generate_schema_table(self, schema: dict) -> str:
201
- """
202
- Genera una descripción detallada y formateada en Markdown de un esquema.
203
- Esta función está diseñada para manejar el formato específico de nuestros
204
- archivos YAML, donde el esquema se define bajo una única clave raíz.
205
- """
206
- if not schema or not isinstance(schema, dict):
207
- return ""
208
-
209
- # Asumimos que el YAML tiene una única clave raíz que nombra a la entidad.
210
- if len(schema) == 1:
211
- root_name = list(schema.keys())[0]
212
- root_details = schema[root_name]
213
-
214
- # support this format
215
- if root_details.get('columns'):
216
- root_details = root_details['columns']
217
-
218
- if isinstance(root_details, dict):
219
- # Las claves de metadatos describen el objeto en sí, no sus propiedades hijas.
220
- METADATA_KEYS = ['description', 'type', 'format', 'items', 'properties', 'pk']
221
-
222
- # Las propiedades son las claves restantes en el diccionario.
223
- properties = {
224
- k: v for k, v in root_details.items() if k not in METADATA_KEYS
225
- }
226
-
227
- # La descripción del objeto raíz.
228
- root_description = root_details.get('description', '')
229
-
230
- # Formatea las propiedades extraídas usando la función auxiliar recursiva.
231
- formatted_properties = self._format_json_schema(properties, 0)
232
-
233
- # Construcción del resultado final, incluyendo el nombre del objeto raíz.
234
- output_parts = [f"\n\n### Objeto: `{root_name}`"]
235
- if root_description:
236
- # Limpia la descripción para que se muestre bien
237
- cleaned_description = '\n'.join(line.strip() for line in root_description.strip().split('\n'))
238
- output_parts.append(f"{cleaned_description}")
239
-
240
- if formatted_properties:
241
- output_parts.append(f"**Campos del objeto `{root_name}`:**\n{formatted_properties}")
242
-
243
- return "\n".join(output_parts)
244
-
245
- # Si el esquema (como tender_schema.yaml) no tiene un objeto raíz,
246
- # se formatea directamente como una lista de propiedades.
247
- return self._format_json_schema(schema, 0)
248
-
249
- def _format_json_schema(self, properties: dict, indent_level: int) -> str:
250
- """
251
- Formatea de manera recursiva las propiedades de un esquema JSON/YAML.
252
- """
253
- output = []
254
- indent_str = ' ' * indent_level
255
-
256
- for name, details in properties.items():
257
- if not isinstance(details, dict):
258
- continue
259
-
260
- description = details.get('description', '')
261
- data_type = details.get('type', 'any')
262
- output.append(f"{indent_str}- **`{name.lower()}`** ({data_type}): {description}")
263
- # if 'pk' in details and details['pk']:
264
- # output.append(f"{indent_str}- **Primary Key**: {details['pk']}")
265
-
266
- child_indent_str = ' ' * (indent_level + 1)
267
-
268
- # Manejo de 'oneOf' para mostrar valores constantes
269
- if 'oneOf' in details:
270
- for item in details['oneOf']:
271
- if 'const' in item:
272
- const_desc = item.get('description', '')
273
- output.append(f"{child_indent_str}- `{item['const']}`: {const_desc}")
274
-
275
- # Manejo de 'items' para arrays
276
- if 'items' in details:
277
- items_details = details.get('items', {})
278
- if isinstance(items_details, dict):
279
- item_description = items_details.get('description')
280
- if item_description:
281
- # Limpiamos y añadimos la descripción del item
282
- cleaned_description = '\n'.join(
283
- f"{line.strip()}" for line in item_description.strip().split('\n')
284
- )
285
- output.append(
286
- f"{child_indent_str}*Descripción de los elementos del array:*\n{child_indent_str}{cleaned_description}")
287
-
288
- if 'properties' in items_details:
289
- nested_properties = self._format_json_schema(items_details['properties'], indent_level + 1)
290
- output.append(nested_properties)
291
-
292
- # Manejo de 'properties' para objetos anidados estándar
293
- if 'properties' in details:
294
- nested_properties = self._format_json_schema(details['properties'], indent_level + 1)
295
- output.append(nested_properties)
296
-
297
- elif 'additionalProperties' in details and 'properties' in details.get('additionalProperties', {}):
298
- # Imprime un marcador de posición para la clave dinámica.
299
- output.append(
300
- f"{child_indent_str}- **[*]** (object): Las claves de este objeto son dinámicas (ej. un ID).")
301
- # Procesa las propiedades del objeto anidado.
302
- nested_properties = self._format_json_schema(details['additionalProperties']['properties'],
303
- indent_level + 2)
304
- output.append(nested_properties)
305
-
306
- return '\n'.join(output)
307
192
 
308
193
  def load_markdown_context(self, filepath: str) -> str:
309
194
  with open(filepath, 'r', encoding='utf-8') as f:
@@ -58,6 +58,7 @@ class IAToolkit:
58
58
  self._injector = Injector() # init empty injector
59
59
  self.version = IATOOLKIT_VERSION
60
60
  self.license = "Community Edition"
61
+ self.is_community = True
61
62
 
62
63
  @classmethod
63
64
  def get_instance(cls) -> 'IAToolkit':
@@ -321,7 +322,7 @@ class IAToolkit:
321
322
  from iatoolkit.services.prompt_service import PromptService
322
323
  from iatoolkit.services.excel_service import ExcelService
323
324
  from iatoolkit.services.mail_service import MailService
324
- from iatoolkit.services.load_documents_service import LoadDocumentsService
325
+ from iatoolkit.services.ingestor_service import IngestorService
325
326
  from iatoolkit.services.profile_service import ProfileService
326
327
  from iatoolkit.services.jwt_service import JWTService
327
328
  from iatoolkit.services.dispatcher_service import Dispatcher
@@ -343,7 +344,7 @@ class IAToolkit:
343
344
  binder.bind(PromptService, to=PromptService)
344
345
  binder.bind(ExcelService, to=ExcelService)
345
346
  binder.bind(MailService, to=MailService)
346
- binder.bind(LoadDocumentsService, to=LoadDocumentsService)
347
+ binder.bind(IngestorService, to=IngestorService)
347
348
  binder.bind(ProfileService, to=ProfileService)
348
349
  binder.bind(JWTService, to=JWTService)
349
350
  binder.bind(Dispatcher, to=Dispatcher)
@@ -0,0 +1,28 @@
1
+ # Copyright (c) 2024 Fernando Libedinsky
2
+ # Product: IAToolkit
3
+ #
4
+ # IAToolkit is open source software.
5
+
6
+ from abc import ABC, abstractmethod
7
+ from typing import List, Optional
8
+
9
+
10
+ class FileConnector(ABC):
11
+ @abstractmethod
12
+ def list_files(self) -> List[str]:
13
+ pass
14
+
15
+ @abstractmethod
16
+ def get_file_content(self, file_path: str) -> bytes:
17
+ pass
18
+
19
+ @abstractmethod
20
+ def delete_file(self, file_path: str) -> None:
21
+ pass
22
+
23
+ @abstractmethod
24
+ def upload_file(self, file_path: str, content: bytes, content_type: str = None) -> None:
25
+ pass
26
+
27
+ def generate_presigned_url(self, file_path: str, expiration: int = 3600) -> Optional[str]:
28
+ return None
@@ -1,29 +1,19 @@
1
- # Copyright (c) 2024 Fernando Libedinsky
2
- # Product: IAToolkit
3
- #
4
- # IAToolkit is open source software.
5
-
1
+ # ... existing code ...
2
+ import os
3
+ from typing import Dict
6
4
  from iatoolkit.infra.connectors.file_connector import FileConnector
7
5
  from iatoolkit.infra.connectors.local_file_connector import LocalFileConnector
8
6
  from iatoolkit.infra.connectors.s3_connector import S3Connector
9
- from iatoolkit.infra.connectors.google_drive_connector import GoogleDriveConnector
10
7
  from iatoolkit.infra.connectors.google_cloud_storage_connector import GoogleCloudStorageConnector
11
- from typing import Dict
12
- import os
13
-
8
+ from iatoolkit.infra.connectors.google_drive_connector import GoogleDriveConnector
14
9
 
15
10
  class FileConnectorFactory:
16
11
  @staticmethod
17
12
  def create(config: Dict) -> FileConnector:
18
13
  """
19
- Configuración esperada:
20
- {
21
- "type": "local" | "s3" | "gdrive" | "gcs",
22
- "path": "/ruta/local", # solo para local
23
- "bucket": "mi-bucket", "prefix": "datos/", "auth": {...}, # solo para S3
24
- "folder_id": "xxxxxxx", # solo para Google Drive
25
- "bucket": "mi-bucket", "service_account": "/ruta/service_account.json" # solo para GCS
26
- }
14
+ Crea un conector basado en un diccionario de configuración.
15
+ Permite pasar credenciales explícitas en 'auth' o 'service_account_path',
16
+ o dejar que el conector use sus defaults.
27
17
  """
28
18
  connector_type = config.get('type')
29
19
 
@@ -31,11 +21,14 @@ class FileConnectorFactory:
31
21
  return LocalFileConnector(config['path'])
32
22
 
33
23
  elif connector_type == 's3':
34
- auth = {
35
- 'aws_access_key_id': os.getenv('AWS_ACCESS_KEY_ID'),
36
- 'aws_secret_access_key': os.getenv('AWS_SECRET_ACCESS_KEY'),
37
- 'region_name': os.getenv('AWS_REGION', 'us-east-1')
38
- }
24
+ # Permite inyectar auth ya resuelto, o usar defaults de entorno
25
+ auth = config.get('auth')
26
+ if not auth:
27
+ auth = {
28
+ 'aws_access_key_id': os.getenv('AWS_ACCESS_KEY_ID'),
29
+ 'aws_secret_access_key': os.getenv('AWS_SECRET_ACCESS_KEY'),
30
+ 'region_name': os.getenv('AWS_REGION', 'us-east-1')
31
+ }
39
32
 
40
33
  return S3Connector(
41
34
  bucket=config['bucket'],
@@ -45,14 +38,16 @@ class FileConnectorFactory:
45
38
  )
46
39
 
47
40
  elif connector_type == 'gdrive':
48
- return GoogleDriveConnector(config['folder_id'],
49
- 'service_account.json')
41
+ return GoogleDriveConnector(
42
+ folder_id=config['folder_id'],
43
+ service_account_path=config.get('service_account', 'service_account.json')
44
+ )
50
45
 
51
- elif connector_type == 'gcs':
46
+ elif connector_type in ['gcs', 'google_cloud_storage']:
52
47
  return GoogleCloudStorageConnector(
53
48
  bucket_name=config['bucket'],
54
- service_account_path=config.get('service_account', 'service_account.json')
49
+ service_account_path=config.get('service_account_path', 'service_account.json')
55
50
  )
56
51
 
57
52
  else:
58
- raise ValueError(f"Unknown connector type: {connector_type}")
53
+ raise ValueError(f"Unknown connector type: {connector_type}")
@@ -51,3 +51,17 @@ class GoogleCloudStorageConnector(FileConnector):
51
51
  file_buffer = blob.download_as_bytes() # Descarga el contenido como bytes
52
52
 
53
53
  return file_buffer
54
+
55
+ def delete_file(self, file_path: str) -> None:
56
+ """
57
+ Elimina un archivo del bucket dado su path.
58
+ """
59
+ blob = self.bucket.blob(file_path)
60
+ blob.delete()
61
+
62
+ def upload_file(self, file_path: str, content: bytes, content_type: str = None) -> None:
63
+ """
64
+ Sube un archivo al bucket.
65
+ """
66
+ blob = self.bucket.blob(file_path)
67
+ blob.upload_from_string(content, content_type=content_type)
@@ -5,7 +5,7 @@
5
5
 
6
6
  from iatoolkit.infra.connectors.file_connector import FileConnector
7
7
  from googleapiclient.discovery import build
8
- from googleapiclient.http import MediaIoBaseDownload
8
+ from googleapiclient.http import MediaIoBaseDownload, MediaIoBaseUpload
9
9
  from google.oauth2.service_account import Credentials
10
10
  import io
11
11
  from typing import List
@@ -66,3 +66,34 @@ class GoogleDriveConnector(FileConnector):
66
66
  status, done = downloader.next_chunk()
67
67
 
68
68
  return file_buffer.getvalue()
69
+
70
+ def upload_file(self, file_path: str, content: bytes, content_type: str = None) -> None:
71
+ """
72
+ Sube un archivo a Google Drive.
73
+ Nota: 'file_path' en este contexto se interpreta como el nombre del archivo,
74
+ ya que GDrive usa IDs para carpetas. El archivo se subirá a la carpeta configurada (self.folder_id).
75
+ """
76
+ file_metadata = {
77
+ 'name': file_path, # Usamos file_path como nombre
78
+ 'parents': [self.folder_id]
79
+ }
80
+
81
+ media = MediaIoBaseUpload(io.BytesIO(content), mimetype=content_type, resumable=True)
82
+
83
+ self.drive_service.files().create(
84
+ body=file_metadata,
85
+ media_body=media,
86
+ fields='id'
87
+ ).execute()
88
+
89
+ def delete_file(self, file_path: str) -> None:
90
+ """
91
+ Elimina un archivo de Google Drive.
92
+ Nota: 'file_path' aquí DEBE ser el ID del archivo (fileId), no su nombre.
93
+ """
94
+ try:
95
+ self.drive_service.files().delete(fileId=file_path).execute()
96
+ except Exception:
97
+ # Si falla (ej: no existe), podríamos loguear o ignorar según diseño.
98
+ # Aquí asumimos propagación de error o manejo silencioso si no crítico.
99
+ pass
@@ -44,3 +44,25 @@ class LocalFileConnector(FileConnector):
44
44
  except Exception as e:
45
45
  raise IAToolkitException(IAToolkitException.ErrorType.FILE_IO_ERROR,
46
46
  f"Error leyendo el archivo {file_path}: {e}")
47
+
48
+ def upload_file(self, file_path: str, content: bytes, content_type: str = None) -> None:
49
+ # Nota: file_path debe ser relativo al directorio raíz configurado
50
+ full_path = os.path.join(self.directory, file_path)
51
+
52
+ # Asegurar que el directorio destino existe
53
+ try:
54
+ os.makedirs(os.path.dirname(full_path), exist_ok=True)
55
+ with open(full_path, 'wb') as f:
56
+ f.write(content)
57
+ except Exception as e:
58
+ raise IAToolkitException(IAToolkitException.ErrorType.FILE_IO_ERROR,
59
+ f"Error escribiendo el archivo {file_path}: {e}")
60
+
61
+ def delete_file(self, file_path: str) -> None:
62
+ full_path = os.path.join(self.directory, file_path)
63
+ try:
64
+ if os.path.exists(full_path):
65
+ os.remove(full_path)
66
+ except Exception as e:
67
+ raise IAToolkitException(IAToolkitException.ErrorType.FILE_IO_ERROR,
68
+ f"Error eliminando el archivo {file_path}: {e}")
@@ -32,4 +32,30 @@ class S3Connector(FileConnector):
32
32
 
33
33
  def get_file_content(self, file_path: str) -> bytes:
34
34
  response = self.s3.get_object(Bucket=self.bucket, Key=file_path)
35
- return response['Body'].read()
35
+ return response['Body'].read()
36
+
37
+ def delete_file(self, file_path: str) -> None:
38
+ self.s3.delete_object(Bucket=self.bucket, Key=file_path)
39
+
40
+ def upload_file(self, file_path: str, content: bytes, content_type: str = None) -> None:
41
+ # If the path doesn't start with the prefix, add it (optional, depends on your logic)'
42
+ # Assuming file_path is either a full path or relative to the root of the bucket for flexibility
43
+ full_path = file_path
44
+
45
+ extra_args = {}
46
+ if content_type:
47
+ extra_args['ContentType'] = content_type
48
+
49
+ self.s3.put_object(
50
+ Bucket=self.bucket,
51
+ Key=full_path,
52
+ Body=content,
53
+ **extra_args
54
+ )
55
+
56
+ def generate_presigned_url(self, file_path: str, expiration: int = 3600) -> str:
57
+ return self.s3.generate_presigned_url(
58
+ 'get_object',
59
+ Params={'Bucket': self.bucket, 'Key': file_path},
60
+ ExpiresIn=expiration
61
+ )
@@ -40,6 +40,13 @@ class DeepseekAdapter:
40
40
  tools = kwargs.get("tools") or []
41
41
  tool_choice = kwargs.get("tool_choice", "auto")
42
42
  context_history = kwargs.get("context_history") or []
43
+ images = kwargs.get("images") or []
44
+
45
+ if images:
46
+ logging.warning(
47
+ f"[DeepseekAdapter] Images provided but DeepSeek models are not multimodal. "
48
+ f"Ignoring {len(images)} images."
49
+ )
43
50
 
44
51
  try:
45
52
  # 1) Build messages from history (if any)
@@ -232,6 +239,7 @@ class DeepseekAdapter:
232
239
 
233
240
  # If the model produced tool calls, fills this list
234
241
  tool_calls_out: List[ToolCall] = []
242
+ content_parts: List[Dict] = [] # Initialize content_parts
235
243
 
236
244
  tool_calls = getattr(message, "tool_calls", None) or []
237
245
  if not tool_calls:
@@ -239,6 +247,13 @@ class DeepseekAdapter:
239
247
  output_text = getattr(message, "content", "") or ""
240
248
  status = "completed"
241
249
 
250
+ # Fill content_parts for text response
251
+ if output_text:
252
+ content_parts.append({
253
+ "type": "text",
254
+ "text": output_text
255
+ })
256
+
242
257
  else:
243
258
  logging.debug(f"[DeepSeek] RAW tool_calls: {tool_calls}")
244
259
 
@@ -274,5 +289,6 @@ class DeepseekAdapter:
274
289
  output_text=output_text,
275
290
  output=tool_calls_out,
276
291
  usage=usage,
277
- reasoning_content=reasoning_content
292
+ reasoning_content=reasoning_content,
293
+ content_parts=content_parts # Pass content_parts
278
294
  )