iatoolkit 0.108.1__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.
- {iatoolkit-0.108.1/src/iatoolkit.egg-info → iatoolkit-1.21.0}/PKG-INFO +1 -1
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/__init__.py +5 -5
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/cli_commands.py +16 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/common/interfaces/database_provider.py +13 -8
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/common/routes.py +58 -6
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/common/util.py +19 -114
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/core.py +5 -2
- iatoolkit-1.21.0/src/iatoolkit/infra/connectors/file_connector.py +28 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/file_connector_factory.py +23 -27
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +14 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/google_drive_connector.py +32 -1
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/local_file_connector.py +22 -0
- iatoolkit-1.21.0/src/iatoolkit/infra/connectors/s3_connector.py +61 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +17 -1
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +117 -18
- iatoolkit-1.21.0/src/iatoolkit/infra/llm_providers/openai_adapter.py +219 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/llm_response.py +13 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/locales/en.yaml +235 -3
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/locales/es.yaml +233 -5
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/repositories/database_manager.py +27 -47
- iatoolkit-1.21.0/src/iatoolkit/repositories/document_repo.py +72 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/repositories/llm_query_repo.py +53 -19
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/repositories/models.py +109 -5
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/repositories/profile_repo.py +3 -4
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/repositories/vs_repo.py +23 -25
- iatoolkit-1.21.0/src/iatoolkit/services/company_context_service.py +397 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/configuration_service.py +272 -76
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/dispatcher_service.py +1 -4
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/file_processor_service.py +0 -8
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/i18n_service.py +7 -0
- iatoolkit-1.21.0/src/iatoolkit/services/ingestor_service.py +297 -0
- iatoolkit-1.21.0/src/iatoolkit/services/knowledge_base_service.py +479 -0
- iatoolkit-1.21.0/src/iatoolkit/services/language_service.py +131 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/llm_client_service.py +61 -2
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/prompt_service.py +248 -170
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/query_service.py +49 -20
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/sql_service.py +17 -0
- iatoolkit-1.21.0/src/iatoolkit/services/storage_service.py +184 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/tool_service.py +3 -2
- iatoolkit-1.21.0/src/iatoolkit/static/js/chat_filepond.js +210 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_main.js +105 -52
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/chat_iatoolkit.css +96 -0
- iatoolkit-1.21.0/src/iatoolkit/system_prompts/query_main.prompt +59 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/templates/chat.html +18 -8
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/base_login_view.py +1 -1
- iatoolkit-1.21.0/src/iatoolkit/views/categories_api_view.py +111 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/chat_view.py +1 -1
- iatoolkit-1.21.0/src/iatoolkit/views/configuration_api_view.py +163 -0
- iatoolkit-1.21.0/src/iatoolkit/views/ingestion_api_view.py +100 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/load_document_api_view.py +14 -10
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/login_view.py +1 -1
- iatoolkit-1.21.0/src/iatoolkit/views/prompt_api_view.py +118 -0
- iatoolkit-1.21.0/src/iatoolkit/views/rag_api_view.py +216 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0/src/iatoolkit.egg-info}/PKG-INFO +1 -1
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit.egg-info/SOURCES.txt +7 -3
- iatoolkit-0.108.1/src/iatoolkit/infra/connectors/file_connector.py +0 -17
- iatoolkit-0.108.1/src/iatoolkit/infra/connectors/s3_connector.py +0 -33
- iatoolkit-0.108.1/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -124
- iatoolkit-0.108.1/src/iatoolkit/repositories/document_repo.py +0 -33
- iatoolkit-0.108.1/src/iatoolkit/services/company_context_service.py +0 -212
- iatoolkit-0.108.1/src/iatoolkit/services/language_service.py +0 -89
- iatoolkit-0.108.1/src/iatoolkit/services/load_documents_service.py +0 -174
- iatoolkit-0.108.1/src/iatoolkit/services/search_service.py +0 -55
- iatoolkit-0.108.1/src/iatoolkit/static/js/chat_filepond.js +0 -85
- iatoolkit-0.108.1/src/iatoolkit/system_prompts/query_main.prompt +0 -74
- iatoolkit-0.108.1/src/iatoolkit/views/load_company_configuration_api_view.py +0 -49
- iatoolkit-0.108.1/src/iatoolkit/views/prompt_api_view.py +0 -37
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/LICENSE +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/LICENSE_COMMUNITY.md +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/pyproject.toml +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/readme.md +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/requirements.txt +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/setup.cfg +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/base_company.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/common/__init__.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/common/exceptions.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/common/interfaces/__init__.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/common/model_registry.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/common/session_manager.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/company_registry.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/__init__.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/call_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/__init__.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/google_chat_app.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/llm_proxy.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/redis_session_manager.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/repositories/__init__.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/__init__.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/auth_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/benchmark_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/branding_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/document_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/embedding_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/excel_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/history_manager_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/jwt_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/license_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/mail_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/profile_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/user_feedback_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/services/user_session_context_service.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/images/fernando.jpeg +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_help_content.js +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_history_button.js +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/chat_modal.css +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/chat_public.css +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/documents.css +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/landing_page.css +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/llm_output.css +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/static/styles/onboarding.css +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/system_prompts/__init__.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/system_prompts/format_styles.prompt +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/system_prompts/sql_rules.prompt +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/templates/_company_header.html +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/templates/_login_widget.html +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/templates/base.html +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/templates/change_password.html +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/templates/chat_modals.html +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/templates/error.html +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/templates/forgot_password.html +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/templates/onboarding_shell.html +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/templates/signup.html +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/__init__.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/change_password_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/embedding_api_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/forgot_password_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/help_content_api_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/history_api_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/home_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/init_context_api_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/llmquery_api_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/logout_api_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/profile_api_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/root_redirect_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/signup_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/static_page_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/users_api_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/views/verify_user_view.py +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit.egg-info/requires.txt +0 -0
- {iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit.egg-info/top_level.txt +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
#
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
|
-
__version__ = "
|
|
6
|
+
__version__ = "1.21.0"
|
|
7
7
|
|
|
8
8
|
# Expose main classes and functions at the top level of the package
|
|
9
9
|
|
|
@@ -17,9 +17,9 @@ from .base_company import BaseCompany
|
|
|
17
17
|
# --- Services ---
|
|
18
18
|
from iatoolkit.services.query_service import QueryService
|
|
19
19
|
from iatoolkit.services.document_service import DocumentService
|
|
20
|
-
from iatoolkit.services.
|
|
20
|
+
from iatoolkit.services.knowledge_base_service import KnowledgeBaseService
|
|
21
21
|
from iatoolkit.services.sql_service import SqlService
|
|
22
|
-
from iatoolkit.services.
|
|
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
|
|
@@ -36,8 +36,8 @@ __all__ = [
|
|
|
36
36
|
'QueryService',
|
|
37
37
|
'SqlService',
|
|
38
38
|
'DocumentService',
|
|
39
|
-
'
|
|
40
|
-
'
|
|
39
|
+
'KnowledgeBaseService',
|
|
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):
|
|
@@ -9,14 +9,19 @@ class DatabaseProvider(abc.ABC):
|
|
|
9
9
|
|
|
10
10
|
# --- Schema Methods ---
|
|
11
11
|
@abc.abstractmethod
|
|
12
|
-
def
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
def get_database_structure(self) -> dict:
|
|
13
|
+
"""
|
|
14
|
+
Returns the structure of the database (tables, columns, types)
|
|
15
|
+
Format:
|
|
16
|
+
{
|
|
17
|
+
"table_name": {
|
|
18
|
+
"columns": [
|
|
19
|
+
{"name": "col1", "type": "VARCHAR", "nullable": True, "pk": True},
|
|
20
|
+
...
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
"""
|
|
20
25
|
pass
|
|
21
26
|
|
|
22
27
|
# --- Execution Methods ---
|
|
@@ -24,13 +24,16 @@ 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.
|
|
27
|
+
from iatoolkit.views.configuration_api_view import ConfigurationApiView, ValidateConfigurationApiView
|
|
28
28
|
from iatoolkit.views.logout_api_view import LogoutApiView
|
|
29
29
|
from iatoolkit.views.home_view import HomeView
|
|
30
30
|
from iatoolkit.views.chat_view import ChatView
|
|
31
31
|
from iatoolkit.views.static_page_view import StaticPageView
|
|
32
32
|
from iatoolkit.views.root_redirect_view import RootRedirectView
|
|
33
33
|
from iatoolkit.views.users_api_view import UsersApiView
|
|
34
|
+
from iatoolkit.views.rag_api_view import RagApiView
|
|
35
|
+
from iatoolkit.views.categories_api_view import CategoriesApiView
|
|
36
|
+
from iatoolkit.views.ingestion_api_view import IngestionApiView
|
|
34
37
|
|
|
35
38
|
# assign root '/' to our new redirect logic
|
|
36
39
|
app.add_url_rule('/home', view_func=RootRedirectView.as_view('root_redirect'))
|
|
@@ -84,24 +87,73 @@ def register_views(app):
|
|
|
84
87
|
# can be used also for executing iatoolkit prompts
|
|
85
88
|
app.add_url_rule('/<company_short_name>/api/llm_query', view_func=LLMQueryApiView.as_view('llm_query_api'))
|
|
86
89
|
|
|
87
|
-
#
|
|
88
|
-
app.add_url_rule('/<company_short_name>/api/
|
|
89
|
-
|
|
90
|
+
# Categories Endpoint
|
|
91
|
+
app.add_url_rule('/<company_short_name>/api/categories',
|
|
92
|
+
view_func=CategoriesApiView.as_view('categories_api'),
|
|
93
|
+
methods=['GET', 'POST'])
|
|
94
|
+
|
|
95
|
+
# open the promt directory and specific prompt management
|
|
96
|
+
prompt_view = PromptApiView.as_view('prompt')
|
|
97
|
+
app.add_url_rule('/<company_short_name>/api/prompts',
|
|
98
|
+
view_func=prompt_view,
|
|
99
|
+
methods=['GET', 'POST'],
|
|
100
|
+
defaults={'prompt_name': None})
|
|
101
|
+
|
|
102
|
+
app.add_url_rule('/<company_short_name>/api/prompts/<prompt_name>',
|
|
103
|
+
view_func=prompt_view,
|
|
104
|
+
methods=['GET', 'POST','PUT', 'DELETE'])
|
|
90
105
|
# toolbar buttons
|
|
91
106
|
app.add_url_rule('/<company_short_name>/api/feedback', view_func=UserFeedbackApiView.as_view('feedback'))
|
|
92
107
|
app.add_url_rule('/<company_short_name>/api/history', view_func=HistoryApiView.as_view('history'))
|
|
93
108
|
app.add_url_rule('/<company_short_name>/api/help-content', view_func=HelpContentApiView.as_view('help-content'))
|
|
94
109
|
|
|
110
|
+
# --- RAG API Routes ---
|
|
111
|
+
rag_view = RagApiView.as_view('rag_api')
|
|
112
|
+
|
|
113
|
+
# 1. List Files (POST for filters)
|
|
114
|
+
app.add_url_rule('/api/rag/<company_short_name>/files',
|
|
115
|
+
view_func=rag_view,
|
|
116
|
+
methods=['POST'],
|
|
117
|
+
defaults={'action': 'list_files'})
|
|
118
|
+
|
|
119
|
+
# 2. Delete File
|
|
120
|
+
app.add_url_rule('/api/rag/<company_short_name>/files/<int:document_id>',
|
|
121
|
+
view_func=rag_view,
|
|
122
|
+
methods=['DELETE'],
|
|
123
|
+
defaults={'action': 'delete_file'})
|
|
124
|
+
|
|
125
|
+
# 3. Search Lab
|
|
126
|
+
app.add_url_rule('/api/rag/<company_short_name>/search',
|
|
127
|
+
view_func=rag_view,
|
|
128
|
+
methods=['POST'],
|
|
129
|
+
defaults={'action': 'search'})
|
|
130
|
+
|
|
131
|
+
# 4. Get File Content (View/Download)
|
|
132
|
+
app.add_url_rule('/api/rag/<company_short_name>/files/<int:document_id>/content',
|
|
133
|
+
view_func=rag_view,
|
|
134
|
+
methods=['GET'],
|
|
135
|
+
defaults={'action': 'get_file_content'})
|
|
136
|
+
|
|
95
137
|
# this endpoint is for upload documents into the vector store (api-key)
|
|
96
138
|
app.add_url_rule('/api/load-document', view_func=LoadDocumentApiView.as_view('load-document'), methods=['POST'])
|
|
97
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
|
+
|
|
98
145
|
# this endpoint is for generating embeddings for a given text
|
|
99
146
|
app.add_url_rule('/<company_short_name>/api/embedding',
|
|
100
147
|
view_func=EmbeddingApiView.as_view('embedding_api'))
|
|
101
148
|
|
|
102
149
|
# company configuration
|
|
103
|
-
app.add_url_rule('/<company_short_name>/api/
|
|
104
|
-
view_func=
|
|
150
|
+
app.add_url_rule('/<company_short_name>/api/configuration',
|
|
151
|
+
view_func=ConfigurationApiView.as_view('configuration'),
|
|
152
|
+
methods=['GET', 'POST', 'PATCH'],)
|
|
153
|
+
|
|
154
|
+
app.add_url_rule('/<company_short_name>/api/configuration/validate',
|
|
155
|
+
view_func=ValidateConfigurationApiView.as_view('configuration-validate'),
|
|
156
|
+
methods=['GET'])
|
|
105
157
|
|
|
106
158
|
# static pages
|
|
107
159
|
# url: /pages/foundation o /pages/implementation_plan
|
|
@@ -162,128 +162,33 @@ 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
|
-
|
|
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 {}
|
|
170
177
|
|
|
171
|
-
def
|
|
172
|
-
if not schema_file and not schema:
|
|
173
|
-
raise IAToolkitException(IAToolkitException.ErrorType.FILE_IO_ERROR,
|
|
174
|
-
f'No se pudo obtener schema de la entidad: {entity_name}')
|
|
175
|
-
|
|
176
|
-
try:
|
|
177
|
-
if schema_file:
|
|
178
|
-
schema = self.load_schema_from_yaml(schema_file)
|
|
179
|
-
table_schema = self.generate_schema_table(schema)
|
|
180
|
-
return table_schema
|
|
181
|
-
except Exception as e:
|
|
182
|
-
logging.exception(e)
|
|
183
|
-
raise IAToolkitException(IAToolkitException.ErrorType.FILE_IO_ERROR,
|
|
184
|
-
f'No se pudo leer el schema de la entidad: {entity_name}') from e
|
|
185
|
-
|
|
186
|
-
def generate_schema_table(self, schema: dict) -> str:
|
|
178
|
+
def dump_yaml_to_string(self, config: dict) -> str:
|
|
187
179
|
"""
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
archivos YAML, donde el esquema se define bajo una única clave raíz.
|
|
180
|
+
Dumps a dictionary into a YAML formatted string.
|
|
181
|
+
It uses default flow style False to ensure block format (readable YAML).
|
|
191
182
|
"""
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if isinstance(root_details, dict):
|
|
201
|
-
# Las claves de metadatos describen el objeto en sí, no sus propiedades hijas.
|
|
202
|
-
METADATA_KEYS = ['description', 'type', 'format', 'items', 'properties']
|
|
203
|
-
|
|
204
|
-
# Las propiedades son las claves restantes en el diccionario.
|
|
205
|
-
properties = {
|
|
206
|
-
k: v for k, v in root_details.items() if k not in METADATA_KEYS
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
# La descripción del objeto raíz.
|
|
210
|
-
root_description = root_details.get('description', '')
|
|
211
|
-
|
|
212
|
-
# Formatea las propiedades extraídas usando la función auxiliar recursiva.
|
|
213
|
-
formatted_properties = self._format_json_schema(properties, 0)
|
|
214
|
-
|
|
215
|
-
# Construcción del resultado final, incluyendo el nombre del objeto raíz.
|
|
216
|
-
output_parts = [f"\n\n### Objeto: `{root_name}`"]
|
|
217
|
-
if root_description:
|
|
218
|
-
# Limpia la descripción para que se muestre bien
|
|
219
|
-
cleaned_description = '\n'.join(line.strip() for line in root_description.strip().split('\n'))
|
|
220
|
-
output_parts.append(f"{cleaned_description}")
|
|
221
|
-
|
|
222
|
-
if formatted_properties:
|
|
223
|
-
output_parts.append(f"**Campos del objeto `{root_name}`:**\n{formatted_properties}")
|
|
224
|
-
|
|
225
|
-
return "\n".join(output_parts)
|
|
226
|
-
|
|
227
|
-
# Si el esquema (como tender_schema.yaml) no tiene un objeto raíz,
|
|
228
|
-
# se formatea directamente como una lista de propiedades.
|
|
229
|
-
return self._format_json_schema(schema, 0)
|
|
183
|
+
try:
|
|
184
|
+
# default_flow_style=False ensures lists and dicts are expanded (not inline like JSON)
|
|
185
|
+
# allow_unicode=True ensures characters like accents are preserved
|
|
186
|
+
return yaml.safe_dump(config, default_flow_style=False, allow_unicode=True, sort_keys=False)
|
|
187
|
+
except yaml.YAMLError as e:
|
|
188
|
+
logging.error(f"Error dumping YAML to string: {e}")
|
|
189
|
+
raise IAToolkitException(IAToolkitException.ErrorType.FILE_IO_ERROR,
|
|
190
|
+
f"Failed to generate YAML: {e}")
|
|
230
191
|
|
|
231
|
-
def _format_json_schema(self, properties: dict, indent_level: int) -> str:
|
|
232
|
-
"""
|
|
233
|
-
Formatea de manera recursiva las propiedades de un esquema JSON/YAML.
|
|
234
|
-
"""
|
|
235
|
-
output = []
|
|
236
|
-
indent_str = ' ' * indent_level
|
|
237
|
-
|
|
238
|
-
for name, details in properties.items():
|
|
239
|
-
if not isinstance(details, dict):
|
|
240
|
-
continue
|
|
241
|
-
|
|
242
|
-
description = details.get('description', '')
|
|
243
|
-
data_type = details.get('type', 'any')
|
|
244
|
-
output.append(f"{indent_str}- **`{name.lower()}`** ({data_type}): {description}")
|
|
245
|
-
|
|
246
|
-
child_indent_str = ' ' * (indent_level + 1)
|
|
247
|
-
|
|
248
|
-
# Manejo de 'oneOf' para mostrar valores constantes
|
|
249
|
-
if 'oneOf' in details:
|
|
250
|
-
for item in details['oneOf']:
|
|
251
|
-
if 'const' in item:
|
|
252
|
-
const_desc = item.get('description', '')
|
|
253
|
-
output.append(f"{child_indent_str}- `{item['const']}`: {const_desc}")
|
|
254
|
-
|
|
255
|
-
# Manejo de 'items' para arrays
|
|
256
|
-
if 'items' in details:
|
|
257
|
-
items_details = details.get('items', {})
|
|
258
|
-
if isinstance(items_details, dict):
|
|
259
|
-
item_description = items_details.get('description')
|
|
260
|
-
if item_description:
|
|
261
|
-
# Limpiamos y añadimos la descripción del item
|
|
262
|
-
cleaned_description = '\n'.join(
|
|
263
|
-
f"{line.strip()}" for line in item_description.strip().split('\n')
|
|
264
|
-
)
|
|
265
|
-
output.append(
|
|
266
|
-
f"{child_indent_str}*Descripción de los elementos del array:*\n{child_indent_str}{cleaned_description}")
|
|
267
|
-
|
|
268
|
-
if 'properties' in items_details:
|
|
269
|
-
nested_properties = self._format_json_schema(items_details['properties'], indent_level + 1)
|
|
270
|
-
output.append(nested_properties)
|
|
271
|
-
|
|
272
|
-
# Manejo de 'properties' para objetos anidados estándar
|
|
273
|
-
if 'properties' in details:
|
|
274
|
-
nested_properties = self._format_json_schema(details['properties'], indent_level + 1)
|
|
275
|
-
output.append(nested_properties)
|
|
276
|
-
|
|
277
|
-
elif 'additionalProperties' in details and 'properties' in details.get('additionalProperties', {}):
|
|
278
|
-
# Imprime un marcador de posición para la clave dinámica.
|
|
279
|
-
output.append(
|
|
280
|
-
f"{child_indent_str}- **[*]** (object): Las claves de este objeto son dinámicas (ej. un ID).")
|
|
281
|
-
# Procesa las propiedades del objeto anidado.
|
|
282
|
-
nested_properties = self._format_json_schema(details['additionalProperties']['properties'],
|
|
283
|
-
indent_level + 2)
|
|
284
|
-
output.append(nested_properties)
|
|
285
|
-
|
|
286
|
-
return '\n'.join(output)
|
|
287
192
|
|
|
288
193
|
def load_markdown_context(self, filepath: str) -> str:
|
|
289
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.
|
|
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
|
|
@@ -335,6 +336,7 @@ class IAToolkit:
|
|
|
335
336
|
from iatoolkit.services.llm_client_service import llmClient
|
|
336
337
|
from iatoolkit.services.auth_service import AuthService
|
|
337
338
|
from iatoolkit.services.sql_service import SqlService
|
|
339
|
+
from iatoolkit.services.knowledge_base_service import KnowledgeBaseService
|
|
338
340
|
|
|
339
341
|
binder.bind(QueryService, to=QueryService)
|
|
340
342
|
binder.bind(BenchmarkService, to=BenchmarkService)
|
|
@@ -342,7 +344,7 @@ class IAToolkit:
|
|
|
342
344
|
binder.bind(PromptService, to=PromptService)
|
|
343
345
|
binder.bind(ExcelService, to=ExcelService)
|
|
344
346
|
binder.bind(MailService, to=MailService)
|
|
345
|
-
binder.bind(
|
|
347
|
+
binder.bind(IngestorService, to=IngestorService)
|
|
346
348
|
binder.bind(ProfileService, to=ProfileService)
|
|
347
349
|
binder.bind(JWTService, to=JWTService)
|
|
348
350
|
binder.bind(Dispatcher, to=Dispatcher)
|
|
@@ -356,6 +358,7 @@ class IAToolkit:
|
|
|
356
358
|
binder.bind(llmClient, to=llmClient)
|
|
357
359
|
binder.bind(AuthService, to=AuthService)
|
|
358
360
|
binder.bind(SqlService, to=SqlService)
|
|
361
|
+
binder.bind(KnowledgeBaseService, to=KnowledgeBaseService)
|
|
359
362
|
|
|
360
363
|
def _bind_infrastructure(self, binder: Binder):
|
|
361
364
|
from iatoolkit.infra.llm_proxy import LLMProxy
|
|
@@ -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
|
{iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/file_connector_factory.py
RENAMED
|
@@ -1,29 +1,19 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
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
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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,27 +21,33 @@ class FileConnectorFactory:
|
|
|
31
21
|
return LocalFileConnector(config['path'])
|
|
32
22
|
|
|
33
23
|
elif connector_type == 's3':
|
|
34
|
-
auth
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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'],
|
|
42
35
|
prefix=config.get('prefix', ''),
|
|
36
|
+
folder=config.get('folder', ''),
|
|
43
37
|
auth=auth
|
|
44
38
|
)
|
|
45
39
|
|
|
46
40
|
elif connector_type == 'gdrive':
|
|
47
|
-
return GoogleDriveConnector(
|
|
48
|
-
|
|
41
|
+
return GoogleDriveConnector(
|
|
42
|
+
folder_id=config['folder_id'],
|
|
43
|
+
service_account_path=config.get('service_account', 'service_account.json')
|
|
44
|
+
)
|
|
49
45
|
|
|
50
|
-
elif connector_type
|
|
46
|
+
elif connector_type in ['gcs', 'google_cloud_storage']:
|
|
51
47
|
return GoogleCloudStorageConnector(
|
|
52
48
|
bucket_name=config['bucket'],
|
|
53
|
-
service_account_path=config.get('
|
|
49
|
+
service_account_path=config.get('service_account_path', 'service_account.json')
|
|
54
50
|
)
|
|
55
51
|
|
|
56
52
|
else:
|
|
57
|
-
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)
|
{iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/google_drive_connector.py
RENAMED
|
@@ -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
|
{iatoolkit-0.108.1 → iatoolkit-1.21.0}/src/iatoolkit/infra/connectors/local_file_connector.py
RENAMED
|
@@ -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}")
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
import boto3
|
|
7
|
+
from iatoolkit.infra.connectors.file_connector import FileConnector
|
|
8
|
+
from typing import List
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class S3Connector(FileConnector):
|
|
12
|
+
def __init__(self, bucket: str, prefix: str, folder: str, auth: dict):
|
|
13
|
+
self.bucket = bucket
|
|
14
|
+
self.prefix = prefix
|
|
15
|
+
self.folder = folder
|
|
16
|
+
self.s3 = boto3.client('s3', **auth)
|
|
17
|
+
|
|
18
|
+
def list_files(self) -> List[dict]:
|
|
19
|
+
# list all the files as dictionaries, with keys: 'path', 'name' y 'metadata'.
|
|
20
|
+
prefix = f'{self.prefix}/{self.folder}/'
|
|
21
|
+
response = self.s3.list_objects_v2(Bucket=self.bucket, Prefix=prefix)
|
|
22
|
+
files = response.get('Contents', [])
|
|
23
|
+
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
"path": obj['Key'], # s3 key
|
|
27
|
+
"name": obj['Key'].split('/')[-1], # filename
|
|
28
|
+
"metadata": {"size": obj.get('Size'), "last_modified": obj.get('LastModified')}
|
|
29
|
+
}
|
|
30
|
+
for obj in files
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
def get_file_content(self, file_path: str) -> bytes:
|
|
34
|
+
response = self.s3.get_object(Bucket=self.bucket, Key=file_path)
|
|
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
|
+
)
|