iatoolkit 0.3.9__py3-none-any.whl → 0.107.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of iatoolkit might be problematic. Click here for more details.
- iatoolkit/__init__.py +27 -35
- iatoolkit/base_company.py +3 -35
- iatoolkit/cli_commands.py +18 -47
- iatoolkit/common/__init__.py +0 -0
- iatoolkit/common/exceptions.py +48 -0
- iatoolkit/common/interfaces/__init__.py +0 -0
- iatoolkit/common/interfaces/asset_storage.py +34 -0
- iatoolkit/common/interfaces/database_provider.py +39 -0
- iatoolkit/common/model_registry.py +159 -0
- iatoolkit/common/routes.py +138 -0
- iatoolkit/common/session_manager.py +26 -0
- iatoolkit/common/util.py +353 -0
- iatoolkit/company_registry.py +66 -29
- iatoolkit/core.py +514 -0
- iatoolkit/infra/__init__.py +5 -0
- iatoolkit/infra/brevo_mail_app.py +123 -0
- iatoolkit/infra/call_service.py +140 -0
- iatoolkit/infra/connectors/__init__.py +5 -0
- iatoolkit/infra/connectors/file_connector.py +17 -0
- iatoolkit/infra/connectors/file_connector_factory.py +57 -0
- iatoolkit/infra/connectors/google_cloud_storage_connector.py +53 -0
- iatoolkit/infra/connectors/google_drive_connector.py +68 -0
- iatoolkit/infra/connectors/local_file_connector.py +46 -0
- iatoolkit/infra/connectors/s3_connector.py +33 -0
- iatoolkit/infra/google_chat_app.py +57 -0
- iatoolkit/infra/llm_providers/__init__.py +0 -0
- iatoolkit/infra/llm_providers/deepseek_adapter.py +278 -0
- iatoolkit/infra/llm_providers/gemini_adapter.py +350 -0
- iatoolkit/infra/llm_providers/openai_adapter.py +124 -0
- iatoolkit/infra/llm_proxy.py +268 -0
- iatoolkit/infra/llm_response.py +45 -0
- iatoolkit/infra/redis_session_manager.py +122 -0
- iatoolkit/locales/en.yaml +222 -0
- iatoolkit/locales/es.yaml +225 -0
- iatoolkit/repositories/__init__.py +5 -0
- iatoolkit/repositories/database_manager.py +187 -0
- iatoolkit/repositories/document_repo.py +33 -0
- iatoolkit/repositories/filesystem_asset_repository.py +36 -0
- iatoolkit/repositories/llm_query_repo.py +105 -0
- iatoolkit/repositories/models.py +279 -0
- iatoolkit/repositories/profile_repo.py +171 -0
- iatoolkit/repositories/vs_repo.py +150 -0
- iatoolkit/services/__init__.py +5 -0
- iatoolkit/services/auth_service.py +193 -0
- {services → iatoolkit/services}/benchmark_service.py +7 -7
- iatoolkit/services/branding_service.py +153 -0
- iatoolkit/services/company_context_service.py +214 -0
- iatoolkit/services/configuration_service.py +375 -0
- iatoolkit/services/dispatcher_service.py +134 -0
- {services → iatoolkit/services}/document_service.py +20 -8
- iatoolkit/services/embedding_service.py +148 -0
- iatoolkit/services/excel_service.py +156 -0
- {services → iatoolkit/services}/file_processor_service.py +36 -21
- iatoolkit/services/history_manager_service.py +208 -0
- iatoolkit/services/i18n_service.py +104 -0
- iatoolkit/services/jwt_service.py +80 -0
- iatoolkit/services/language_service.py +89 -0
- iatoolkit/services/license_service.py +82 -0
- iatoolkit/services/llm_client_service.py +438 -0
- iatoolkit/services/load_documents_service.py +174 -0
- iatoolkit/services/mail_service.py +213 -0
- {services → iatoolkit/services}/profile_service.py +200 -101
- iatoolkit/services/prompt_service.py +303 -0
- iatoolkit/services/query_service.py +467 -0
- iatoolkit/services/search_service.py +55 -0
- iatoolkit/services/sql_service.py +169 -0
- iatoolkit/services/tool_service.py +246 -0
- iatoolkit/services/user_feedback_service.py +117 -0
- iatoolkit/services/user_session_context_service.py +213 -0
- iatoolkit/static/images/fernando.jpeg +0 -0
- iatoolkit/static/images/iatoolkit_core.png +0 -0
- iatoolkit/static/images/iatoolkit_logo.png +0 -0
- iatoolkit/static/js/chat_feedback_button.js +80 -0
- iatoolkit/static/js/chat_filepond.js +85 -0
- iatoolkit/static/js/chat_help_content.js +124 -0
- iatoolkit/static/js/chat_history_button.js +110 -0
- iatoolkit/static/js/chat_logout_button.js +36 -0
- iatoolkit/static/js/chat_main.js +401 -0
- iatoolkit/static/js/chat_model_selector.js +227 -0
- iatoolkit/static/js/chat_onboarding_button.js +103 -0
- iatoolkit/static/js/chat_prompt_manager.js +94 -0
- iatoolkit/static/js/chat_reload_button.js +38 -0
- iatoolkit/static/styles/chat_iatoolkit.css +559 -0
- iatoolkit/static/styles/chat_modal.css +133 -0
- iatoolkit/static/styles/chat_public.css +135 -0
- iatoolkit/static/styles/documents.css +598 -0
- iatoolkit/static/styles/landing_page.css +398 -0
- iatoolkit/static/styles/llm_output.css +148 -0
- iatoolkit/static/styles/onboarding.css +176 -0
- iatoolkit/system_prompts/__init__.py +0 -0
- iatoolkit/system_prompts/query_main.prompt +30 -23
- iatoolkit/system_prompts/sql_rules.prompt +47 -12
- iatoolkit/templates/_company_header.html +45 -0
- iatoolkit/templates/_login_widget.html +42 -0
- iatoolkit/templates/base.html +78 -0
- iatoolkit/templates/change_password.html +66 -0
- iatoolkit/templates/chat.html +337 -0
- iatoolkit/templates/chat_modals.html +185 -0
- iatoolkit/templates/error.html +51 -0
- iatoolkit/templates/forgot_password.html +51 -0
- iatoolkit/templates/onboarding_shell.html +106 -0
- iatoolkit/templates/signup.html +79 -0
- iatoolkit/views/__init__.py +5 -0
- iatoolkit/views/base_login_view.py +96 -0
- iatoolkit/views/change_password_view.py +116 -0
- iatoolkit/views/chat_view.py +76 -0
- iatoolkit/views/embedding_api_view.py +65 -0
- iatoolkit/views/forgot_password_view.py +75 -0
- iatoolkit/views/help_content_api_view.py +54 -0
- iatoolkit/views/history_api_view.py +56 -0
- iatoolkit/views/home_view.py +63 -0
- iatoolkit/views/init_context_api_view.py +74 -0
- iatoolkit/views/llmquery_api_view.py +59 -0
- iatoolkit/views/load_company_configuration_api_view.py +49 -0
- iatoolkit/views/load_document_api_view.py +65 -0
- iatoolkit/views/login_view.py +170 -0
- iatoolkit/views/logout_api_view.py +57 -0
- iatoolkit/views/profile_api_view.py +46 -0
- iatoolkit/views/prompt_api_view.py +37 -0
- iatoolkit/views/root_redirect_view.py +22 -0
- iatoolkit/views/signup_view.py +100 -0
- iatoolkit/views/static_page_view.py +27 -0
- iatoolkit/views/user_feedback_api_view.py +60 -0
- iatoolkit/views/users_api_view.py +33 -0
- iatoolkit/views/verify_user_view.py +60 -0
- iatoolkit-0.107.4.dist-info/METADATA +268 -0
- iatoolkit-0.107.4.dist-info/RECORD +132 -0
- iatoolkit-0.107.4.dist-info/licenses/LICENSE +21 -0
- iatoolkit-0.107.4.dist-info/licenses/LICENSE_COMMUNITY.md +15 -0
- {iatoolkit-0.3.9.dist-info → iatoolkit-0.107.4.dist-info}/top_level.txt +0 -1
- iatoolkit/iatoolkit.py +0 -413
- iatoolkit/system_prompts/arquitectura.prompt +0 -32
- iatoolkit-0.3.9.dist-info/METADATA +0 -252
- iatoolkit-0.3.9.dist-info/RECORD +0 -32
- services/__init__.py +0 -5
- services/api_service.py +0 -75
- services/dispatcher_service.py +0 -351
- services/excel_service.py +0 -98
- services/history_service.py +0 -45
- services/jwt_service.py +0 -91
- services/load_documents_service.py +0 -212
- services/mail_service.py +0 -62
- services/prompt_manager_service.py +0 -172
- services/query_service.py +0 -334
- services/search_service.py +0 -32
- services/sql_service.py +0 -42
- services/tasks_service.py +0 -188
- services/user_feedback_service.py +0 -67
- services/user_session_context_service.py +0 -85
- {iatoolkit-0.3.9.dist-info → iatoolkit-0.107.4.dist-info}/WHEEL +0 -0
iatoolkit/__init__.py
CHANGED
|
@@ -1,54 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
IAToolkit
|
|
3
|
-
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
__version__ = "0.107.4"
|
|
6
7
|
|
|
7
|
-
#
|
|
8
|
-
from .iatoolkit import IAToolkit, create_app
|
|
9
|
-
from .iatoolkit import current_iatoolkit
|
|
8
|
+
# Expose main classes and functions at the top level of the package
|
|
10
9
|
|
|
11
|
-
#
|
|
12
|
-
from .
|
|
10
|
+
# main IAToolkit class
|
|
11
|
+
from iatoolkit.core import IAToolkit, create_app, current_iatoolkit
|
|
13
12
|
|
|
14
|
-
#
|
|
13
|
+
# for registering the client companies
|
|
14
|
+
from .company_registry import register_company, set_company_registry
|
|
15
15
|
from .base_company import BaseCompany
|
|
16
16
|
|
|
17
17
|
# --- Services ---
|
|
18
|
-
|
|
19
|
-
from services.
|
|
20
|
-
from services.
|
|
21
|
-
from services.
|
|
22
|
-
from services.
|
|
23
|
-
from
|
|
24
|
-
from services.
|
|
25
|
-
from
|
|
26
|
-
from repositories.
|
|
27
|
-
from
|
|
28
|
-
from infra.call_service import CallServiceClient
|
|
29
|
-
from common.util import Utility
|
|
30
|
-
from repositories.models import Base, Company, Function, TaskType
|
|
31
|
-
|
|
18
|
+
from iatoolkit.services.query_service import QueryService
|
|
19
|
+
from iatoolkit.services.document_service import DocumentService
|
|
20
|
+
from iatoolkit.services.search_service import SearchService
|
|
21
|
+
from iatoolkit.services.sql_service import SqlService
|
|
22
|
+
from iatoolkit.services.load_documents_service import LoadDocumentsService
|
|
23
|
+
from iatoolkit.infra.call_service import CallServiceClient
|
|
24
|
+
from iatoolkit.services.profile_service import ProfileService
|
|
25
|
+
from iatoolkit.services.mail_service import MailService
|
|
26
|
+
from iatoolkit.repositories.models import Base as OrmModel
|
|
27
|
+
from iatoolkit.base_company import BaseCompany
|
|
32
28
|
|
|
33
29
|
__all__ = [
|
|
34
30
|
'IAToolkit',
|
|
35
31
|
'create_app',
|
|
36
32
|
'current_iatoolkit',
|
|
37
33
|
'register_company',
|
|
34
|
+
'set_company_registry',
|
|
38
35
|
'BaseCompany',
|
|
36
|
+
'QueryService',
|
|
39
37
|
'SqlService',
|
|
40
|
-
'ExcelService',
|
|
41
|
-
'Dispatcher',
|
|
42
38
|
'DocumentService',
|
|
43
|
-
'QueryService',
|
|
44
39
|
'SearchService',
|
|
45
|
-
'
|
|
46
|
-
'LLMQueryRepo',
|
|
47
|
-
'DatabaseManager',
|
|
40
|
+
'LoadDocumentsService',
|
|
48
41
|
'CallServiceClient',
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'Base',
|
|
42
|
+
'ProfileService',
|
|
43
|
+
'MailService',
|
|
44
|
+
'OrmModel',
|
|
45
|
+
'BaseCompany',
|
|
54
46
|
]
|
iatoolkit/base_company.py
CHANGED
|
@@ -1,32 +1,12 @@
|
|
|
1
1
|
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
6
|
# companies/base_company.py
|
|
7
7
|
from abc import ABC, abstractmethod
|
|
8
|
-
from typing import Any
|
|
9
|
-
|
|
10
8
|
|
|
11
9
|
class BaseCompany(ABC):
|
|
12
|
-
def __init__(self, profile_repo: Any = None, llm_query_repo: Any = None):
|
|
13
|
-
self.profile_repo = profile_repo
|
|
14
|
-
self.llm_query_repo = llm_query_repo
|
|
15
|
-
|
|
16
|
-
@abstractmethod
|
|
17
|
-
# initialize all the database tables needed
|
|
18
|
-
def register_company(self):
|
|
19
|
-
raise NotImplementedError("La subclase debe implementar el método create_company()")
|
|
20
|
-
|
|
21
|
-
@abstractmethod
|
|
22
|
-
# get context specific for this company
|
|
23
|
-
def get_company_context(self, **kwargs) -> str:
|
|
24
|
-
raise NotImplementedError("La subclase debe implementar el método get_company_context()")
|
|
25
|
-
|
|
26
|
-
@abstractmethod
|
|
27
|
-
# get context specific for this company
|
|
28
|
-
def get_user_info(self, **kwargs) -> str:
|
|
29
|
-
raise NotImplementedError("La subclase debe implementar el método get_user_info()")
|
|
30
10
|
|
|
31
11
|
@abstractmethod
|
|
32
12
|
# execute the specific action configured in the intent table
|
|
@@ -34,19 +14,7 @@ class BaseCompany(ABC):
|
|
|
34
14
|
raise NotImplementedError("La subclase debe implementar el método handle_request()")
|
|
35
15
|
|
|
36
16
|
@abstractmethod
|
|
37
|
-
# get context specific for the query
|
|
38
|
-
def start_execution(self):
|
|
39
|
-
raise NotImplementedError("La subclase debe implementar el método start_execution()")
|
|
40
|
-
|
|
41
|
-
@abstractmethod
|
|
42
|
-
# get context specific for the query
|
|
43
|
-
def get_metadata_from_filename(self, filename: str) -> dict:
|
|
44
|
-
raise NotImplementedError("La subclase debe implementar el método get_query_context()")
|
|
45
|
-
|
|
46
17
|
def register_cli_commands(self, app):
|
|
47
|
-
"""
|
|
48
|
-
optional method for a company definition of it's cli commands
|
|
49
|
-
"""
|
|
50
18
|
pass
|
|
51
19
|
|
|
52
20
|
def unsupported_operation(self, tag):
|
iatoolkit/cli_commands.py
CHANGED
|
@@ -1,77 +1,48 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
1
6
|
import click
|
|
2
7
|
import logging
|
|
3
|
-
from iatoolkit import IAToolkit
|
|
4
|
-
from services.
|
|
5
|
-
|
|
8
|
+
from iatoolkit.core import IAToolkit
|
|
9
|
+
from iatoolkit.services.profile_service import ProfileService
|
|
10
|
+
|
|
6
11
|
|
|
7
12
|
def register_core_commands(app):
|
|
8
13
|
"""Registra los comandos CLI del núcleo de IAToolkit."""
|
|
9
|
-
|
|
10
|
-
@app.cli.command("setup-all-companies")
|
|
11
|
-
def setup_all_companies():
|
|
12
|
-
"""🗄️ Inicializa todas las compañías registradas en la base de datos."""
|
|
13
|
-
try:
|
|
14
|
-
dispatcher = IAToolkit.get_instance().get_injector().get(Dispatcher)
|
|
15
|
-
click.echo("🚀 Inicializando base de datos y compañías...")
|
|
16
|
-
dispatcher.setup_all_companies()
|
|
17
|
-
click.echo("✅ Base de datos y compañías inicializadas correctamente.")
|
|
18
|
-
except Exception as e:
|
|
19
|
-
logging.exception(e)
|
|
20
|
-
click.echo(f"❌ Error: {e}")
|
|
21
14
|
|
|
22
|
-
@app.cli.command("
|
|
15
|
+
@app.cli.command("api-key")
|
|
23
16
|
@click.argument("company_short_name")
|
|
24
|
-
def
|
|
17
|
+
def api_key(company_short_name: str):
|
|
25
18
|
"""⚙️ Genera una nueva API key para una compañía ya registrada."""
|
|
26
19
|
try:
|
|
27
20
|
profile_service = IAToolkit.get_instance().get_injector().get(ProfileService)
|
|
28
|
-
click.echo(f"🔑
|
|
21
|
+
click.echo(f"🔑 Generating API-KEY for company: '{company_short_name}'...")
|
|
29
22
|
result = profile_service.new_api_key(company_short_name)
|
|
30
23
|
|
|
31
24
|
if 'error' in result:
|
|
32
25
|
click.echo(f"❌ Error: {result['error']}")
|
|
33
|
-
click.echo("👉
|
|
26
|
+
click.echo("👉 Make sure the company is registered and valid.")
|
|
34
27
|
else:
|
|
35
|
-
click.echo("✅ ¡
|
|
36
|
-
click.echo(f"IATOOLKIT_API_KEY={result['api-key']}")
|
|
28
|
+
click.echo("✅ ¡Api-key is ready! add this variable to your environment:")
|
|
29
|
+
click.echo(f"IATOOLKIT_API_KEY='{result['api-key']}'")
|
|
37
30
|
except Exception as e:
|
|
38
31
|
logging.exception(e)
|
|
39
|
-
click.echo(f"❌
|
|
32
|
+
click.echo(f"❌ unexpectd error during the configuration: {e}")
|
|
40
33
|
|
|
41
34
|
@app.cli.command("encrypt-key")
|
|
42
35
|
@click.argument("key")
|
|
43
|
-
def
|
|
44
|
-
from common.util import Utility
|
|
36
|
+
def encrypt_llm_api_key(key: str):
|
|
37
|
+
from iatoolkit.common.util import Utility
|
|
45
38
|
|
|
46
39
|
util = IAToolkit.get_instance().get_injector().get(Utility)
|
|
47
40
|
try:
|
|
48
41
|
encrypt_key = util.encrypt_key(key)
|
|
49
|
-
click.echo(f'la
|
|
42
|
+
click.echo(f'la api-key del LLM encriptada es: {encrypt_key} \n')
|
|
50
43
|
except Exception as e:
|
|
51
44
|
logging.exception(e)
|
|
52
45
|
click.echo(f"Error: {str(e)}")
|
|
53
46
|
|
|
54
|
-
@app.cli.command("exec-tasks")
|
|
55
|
-
@click.argument("company_short_name")
|
|
56
|
-
def exec_pending_tasks(company_short_name: str):
|
|
57
|
-
from services.tasks_service import TaskService
|
|
58
|
-
task_service = IAToolkit.get_instance().get_injector().get(TaskService)
|
|
59
47
|
|
|
60
|
-
try:
|
|
61
|
-
result = task_service.trigger_pending_tasks(company_short_name)
|
|
62
|
-
click.echo(result['message'])
|
|
63
|
-
except Exception as e:
|
|
64
|
-
logging.exception(e)
|
|
65
|
-
click.echo(f"Error: {str(e)}")
|
|
66
48
|
|
|
67
|
-
@app.cli.command("load")
|
|
68
|
-
def load_documents():
|
|
69
|
-
from services.load_documents_service import LoadDocumentsService
|
|
70
|
-
|
|
71
|
-
load_documents_service = IAToolkit.get_instance().get_injector().get(LoadDocumentsService)
|
|
72
|
-
try:
|
|
73
|
-
result = load_documents_service.load()
|
|
74
|
-
click.echo(result['message'])
|
|
75
|
-
except Exception as e:
|
|
76
|
-
logging.exception(e)
|
|
77
|
-
click.echo(f"Error: {str(e)}")
|
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class IAToolkitException(Exception):
|
|
10
|
+
|
|
11
|
+
class ErrorType(Enum):
|
|
12
|
+
SYSTEM_ERROR = 0
|
|
13
|
+
DATABASE_ERROR = 1
|
|
14
|
+
LLM_ERROR = 2
|
|
15
|
+
CLOUD_STORAGE_ERROR = 3
|
|
16
|
+
DOCUMENT_NOT_FOUND = 4
|
|
17
|
+
INVALID_PARAMETER = 5
|
|
18
|
+
MISSING_PARAMETER = 6
|
|
19
|
+
PARAM_NOT_FILLED = 7
|
|
20
|
+
PERMISSION = 8
|
|
21
|
+
EXIST = 9
|
|
22
|
+
API_KEY = 10
|
|
23
|
+
CALL_ERROR = 11
|
|
24
|
+
PROMPT_ERROR = 12
|
|
25
|
+
FILE_FORMAT_ERROR = 13
|
|
26
|
+
FILE_IO_ERROR = 14
|
|
27
|
+
TEMPLATE_ERROR = 15
|
|
28
|
+
EXTERNAL_SOURCE_ERROR = 16
|
|
29
|
+
MAIL_ERROR = 17
|
|
30
|
+
CONFIG_ERROR = 18
|
|
31
|
+
INVALID_NAME = 19
|
|
32
|
+
REQUEST_ERROR = 20
|
|
33
|
+
TASK_EXECUTION_ERROR = 21
|
|
34
|
+
TASK_NOT_FOUND = 22
|
|
35
|
+
INVALID_STATE = 23
|
|
36
|
+
CRYPT_ERROR = 24
|
|
37
|
+
LOAD_DOCUMENT_ERROR = 25
|
|
38
|
+
INVALID_USER = 26
|
|
39
|
+
VECTOR_STORE_ERROR = 27
|
|
40
|
+
EMBEDDING_ERROR = 28
|
|
41
|
+
MODEL = 29
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def __init__(self, error_type: ErrorType = ErrorType.SYSTEM_ERROR, message=None):
|
|
46
|
+
self.error_type = error_type
|
|
47
|
+
self.message = message
|
|
48
|
+
super().__init__(self.message)
|
|
File without changes
|
|
@@ -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,39 @@
|
|
|
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_schema(self,
|
|
17
|
+
table_name: str,
|
|
18
|
+
db_schema: str,
|
|
19
|
+
schema_object_name: str | None = None,
|
|
20
|
+
exclude_columns: List[str] | None = None) -> str:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
# --- Execution Methods ---
|
|
24
|
+
@abc.abstractmethod
|
|
25
|
+
def execute_query(self, query: str, commit: bool = False) -> Union[List[Dict[str, Any]], Dict[str, int]]:
|
|
26
|
+
"""
|
|
27
|
+
Executes a query and returns:
|
|
28
|
+
- A list of dicts for SELECT (rows).
|
|
29
|
+
- A dict {'rowcount': N} for INSERT/UPDATE/DELETE.
|
|
30
|
+
"""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
@abc.abstractmethod
|
|
34
|
+
def commit(self) -> None:
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
@abc.abstractmethod
|
|
38
|
+
def rollback(self) -> None:
|
|
39
|
+
pass
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
7
|
+
# Product: IAToolkit
|
|
8
|
+
#
|
|
9
|
+
# IAToolkit is open source software.
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from injector import inject, singleton
|
|
15
|
+
from typing import Literal
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
HistoryType = Literal["server_side", "client_side"]
|
|
19
|
+
ProviderType = Literal["openai", "gemini", "deepseek", "xai", "anthropic", "unknown"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class ModelMetadata:
|
|
24
|
+
"""Static metadata for a logical family of models."""
|
|
25
|
+
provider: ProviderType
|
|
26
|
+
history_type: HistoryType
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@singleton
|
|
30
|
+
class ModelRegistry:
|
|
31
|
+
"""
|
|
32
|
+
Central registry for model metadata.
|
|
33
|
+
|
|
34
|
+
Responsibilities:
|
|
35
|
+
- Map a model name to its provider (openai, gemini, deepseek, etc.).
|
|
36
|
+
- Decide which history strategy to use for a model (server_side / client_side).
|
|
37
|
+
- Provide convenience helpers (is_openai, is_gemini, is_deepseek, etc.).
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
@inject
|
|
41
|
+
def __init__(self):
|
|
42
|
+
# Hardcoded rules for now; can be extended or loaded from config later.
|
|
43
|
+
# The order of patterns matters: first match wins.
|
|
44
|
+
self._provider_patterns: dict[ProviderType, tuple[str, ...]] = {
|
|
45
|
+
"openai": ("gpt", "gpt-5", "gpt-5-mini", "gpt-5.1"),
|
|
46
|
+
"gemini": ("gemini", "gemini-3"),
|
|
47
|
+
"deepseek": ("deepseek",),
|
|
48
|
+
"xai": ("grok", "grok-1", "grok-beta"),
|
|
49
|
+
"anthropic": ("claude", "claude-3", "claude-2"),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# ------------------------------------------------------------------
|
|
53
|
+
# Public API
|
|
54
|
+
# ------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
def get_provider(self, model: str) -> ProviderType:
|
|
57
|
+
"""
|
|
58
|
+
Returns the logical provider for a given model name.
|
|
59
|
+
|
|
60
|
+
Examples:
|
|
61
|
+
"gpt-4o" -> "openai"
|
|
62
|
+
"gemini-pro" -> "gemini"
|
|
63
|
+
"deepseek-chat" -> "deepseek"
|
|
64
|
+
"""
|
|
65
|
+
if not model:
|
|
66
|
+
return "unknown"
|
|
67
|
+
|
|
68
|
+
model_lower = model.lower()
|
|
69
|
+
for provider, patterns in self._provider_patterns.items():
|
|
70
|
+
if any(pat in model_lower for pat in patterns):
|
|
71
|
+
return provider
|
|
72
|
+
|
|
73
|
+
return "unknown"
|
|
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
|
+
|
|
126
|
+
def get_history_type(self, model: str) -> HistoryType:
|
|
127
|
+
"""
|
|
128
|
+
Returns the history strategy for a given model.
|
|
129
|
+
|
|
130
|
+
Current rules:
|
|
131
|
+
- openai/xai/anthropic: server_side (API manages conversation state via ids)
|
|
132
|
+
- gemini/deepseek/unknown: client_side (we manage full message history)
|
|
133
|
+
"""
|
|
134
|
+
provider = self.get_provider(model)
|
|
135
|
+
|
|
136
|
+
if provider in ("openai", "xai", "anthropic"):
|
|
137
|
+
return "server_side"
|
|
138
|
+
|
|
139
|
+
# Default for gemini, deepseek and any unknown provider
|
|
140
|
+
return "client_side"
|
|
141
|
+
|
|
142
|
+
# ------------------------------------------------------------------
|
|
143
|
+
# Convenience helpers (used during migration)
|
|
144
|
+
# ------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
def is_openai_model(self, model: str) -> bool:
|
|
147
|
+
return self.get_provider(model) == "openai"
|
|
148
|
+
|
|
149
|
+
def is_gemini_model(self, model: str) -> bool:
|
|
150
|
+
return self.get_provider(model) == "gemini"
|
|
151
|
+
|
|
152
|
+
def is_deepseek_model(self, model: str) -> bool:
|
|
153
|
+
return self.get_provider(model) == "deepseek"
|
|
154
|
+
|
|
155
|
+
def is_xai_model(self, model: str) -> bool:
|
|
156
|
+
return self.get_provider(model) == "xai"
|
|
157
|
+
|
|
158
|
+
def is_anthropic_model(self, model: str) -> bool:
|
|
159
|
+
return self.get_provider(model) == "anthropic"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
from flask import render_template, redirect, url_for,send_from_directory, current_app, abort
|
|
7
|
+
from flask import jsonify
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# this function register all the views
|
|
11
|
+
def register_views(app):
|
|
12
|
+
|
|
13
|
+
from iatoolkit.views.init_context_api_view import InitContextApiView
|
|
14
|
+
from iatoolkit.views.llmquery_api_view import LLMQueryApiView
|
|
15
|
+
from iatoolkit.views.signup_view import SignupView
|
|
16
|
+
from iatoolkit.views.verify_user_view import VerifyAccountView
|
|
17
|
+
from iatoolkit.views.forgot_password_view import ForgotPasswordView
|
|
18
|
+
from iatoolkit.views.change_password_view import ChangePasswordView
|
|
19
|
+
from iatoolkit.views.load_document_api_view import LoadDocumentApiView
|
|
20
|
+
from iatoolkit.views.user_feedback_api_view import UserFeedbackApiView
|
|
21
|
+
from iatoolkit.views.prompt_api_view import PromptApiView
|
|
22
|
+
from iatoolkit.views.history_api_view import HistoryApiView
|
|
23
|
+
from iatoolkit.views.help_content_api_view import HelpContentApiView
|
|
24
|
+
from iatoolkit.views.profile_api_view import UserLanguageApiView
|
|
25
|
+
from iatoolkit.views.embedding_api_view import EmbeddingApiView
|
|
26
|
+
from iatoolkit.views.login_view import LoginView, FinalizeContextView
|
|
27
|
+
from iatoolkit.views.load_company_configuration_api_view import LoadCompanyConfigurationApiView
|
|
28
|
+
from iatoolkit.views.logout_api_view import LogoutApiView
|
|
29
|
+
from iatoolkit.views.home_view import HomeView
|
|
30
|
+
from iatoolkit.views.chat_view import ChatView
|
|
31
|
+
from iatoolkit.views.static_page_view import StaticPageView
|
|
32
|
+
from iatoolkit.views.root_redirect_view import RootRedirectView
|
|
33
|
+
from iatoolkit.views.users_api_view import UsersApiView
|
|
34
|
+
|
|
35
|
+
# assign root '/' to our new redirect logic
|
|
36
|
+
app.add_url_rule('/home', view_func=RootRedirectView.as_view('root_redirect'))
|
|
37
|
+
|
|
38
|
+
# company home view
|
|
39
|
+
app.add_url_rule('/<company_short_name>/home', view_func=HomeView.as_view('home'))
|
|
40
|
+
|
|
41
|
+
# login for the iatoolkit integrated frontend
|
|
42
|
+
app.add_url_rule('/<company_short_name>/login', view_func=LoginView.as_view('login'))
|
|
43
|
+
|
|
44
|
+
# Chat Route (Direct Access)
|
|
45
|
+
app.add_url_rule('/<company_short_name>/chat',
|
|
46
|
+
view_func=ChatView.as_view('chat'))
|
|
47
|
+
|
|
48
|
+
# this endpoint is called when onboarding_shell finish the context load
|
|
49
|
+
app.add_url_rule(
|
|
50
|
+
'/<company_short_name>/finalize',
|
|
51
|
+
view_func=FinalizeContextView.as_view('finalize_no_token')
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
app.add_url_rule(
|
|
55
|
+
'/<company_short_name>/finalize/<token>',
|
|
56
|
+
view_func=FinalizeContextView.as_view('finalize_with_token')
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
app.add_url_rule(
|
|
60
|
+
'/api/profile/language',
|
|
61
|
+
view_func=UserLanguageApiView.as_view('user_language_api')
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# logout
|
|
65
|
+
app.add_url_rule('/<company_short_name>/api/logout',
|
|
66
|
+
view_func=LogoutApiView.as_view('logout'))
|
|
67
|
+
|
|
68
|
+
# init (reset) the company context
|
|
69
|
+
app.add_url_rule('/<company_short_name>/api/init-context',
|
|
70
|
+
view_func=InitContextApiView.as_view('init-context'),
|
|
71
|
+
methods=['POST', 'OPTIONS'])
|
|
72
|
+
|
|
73
|
+
# register new user, account verification and forgot password
|
|
74
|
+
app.add_url_rule('/<company_short_name>/signup',view_func=SignupView.as_view('signup'))
|
|
75
|
+
app.add_url_rule('/<company_short_name>/verify/<token>', view_func=VerifyAccountView.as_view('verify_account'))
|
|
76
|
+
app.add_url_rule('/<company_short_name>/forgot-password', view_func=ForgotPasswordView.as_view('forgot_password'))
|
|
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
|
+
)
|
|
82
|
+
|
|
83
|
+
# main chat query, used by the JS in the browser (with credentials)
|
|
84
|
+
# can be used also for executing iatoolkit prompts
|
|
85
|
+
app.add_url_rule('/<company_short_name>/api/llm_query', view_func=LLMQueryApiView.as_view('llm_query_api'))
|
|
86
|
+
|
|
87
|
+
# open the promt directory
|
|
88
|
+
app.add_url_rule('/<company_short_name>/api/prompts', view_func=PromptApiView.as_view('prompt'))
|
|
89
|
+
|
|
90
|
+
# toolbar buttons
|
|
91
|
+
app.add_url_rule('/<company_short_name>/api/feedback', view_func=UserFeedbackApiView.as_view('feedback'))
|
|
92
|
+
app.add_url_rule('/<company_short_name>/api/history', view_func=HistoryApiView.as_view('history'))
|
|
93
|
+
app.add_url_rule('/<company_short_name>/api/help-content', view_func=HelpContentApiView.as_view('help-content'))
|
|
94
|
+
|
|
95
|
+
# this endpoint is for upload documents into the vector store (api-key)
|
|
96
|
+
app.add_url_rule('/api/load-document', view_func=LoadDocumentApiView.as_view('load-document'), methods=['POST'])
|
|
97
|
+
|
|
98
|
+
# this endpoint is for generating embeddings for a given text
|
|
99
|
+
app.add_url_rule('/<company_short_name>/api/embedding',
|
|
100
|
+
view_func=EmbeddingApiView.as_view('embedding_api'))
|
|
101
|
+
|
|
102
|
+
# company configuration
|
|
103
|
+
app.add_url_rule('/<company_short_name>/api/load_configuration',
|
|
104
|
+
view_func=LoadCompanyConfigurationApiView.as_view('load-configuration'))
|
|
105
|
+
|
|
106
|
+
# static pages
|
|
107
|
+
# url: /pages/foundation o /pages/implementation_plan
|
|
108
|
+
static_view = StaticPageView.as_view('static_pages')
|
|
109
|
+
app.add_url_rule('/pages/<page_name>', view_func=static_view, methods=['GET'])
|
|
110
|
+
|
|
111
|
+
@app.route('/download/<path:filename>')
|
|
112
|
+
def download_file(filename):
|
|
113
|
+
"""
|
|
114
|
+
Esta vista sirve un archivo previamente generado desde el directorio
|
|
115
|
+
configurado en IATOOLKIT_DOWNLOAD_DIR.
|
|
116
|
+
"""
|
|
117
|
+
# Valida que la configuración exista
|
|
118
|
+
if 'IATOOLKIT_DOWNLOAD_DIR' not in current_app.config:
|
|
119
|
+
abort(500, "Error de configuración: IATOOLKIT_DOWNLOAD_DIR no está definido.")
|
|
120
|
+
|
|
121
|
+
download_dir = current_app.config['IATOOLKIT_DOWNLOAD_DIR']
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
return send_from_directory(
|
|
125
|
+
download_dir,
|
|
126
|
+
filename,
|
|
127
|
+
as_attachment=True # Fuerza la descarga en lugar de la visualización
|
|
128
|
+
)
|
|
129
|
+
except FileNotFoundError:
|
|
130
|
+
abort(404)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
app.add_url_rule('/version', 'version',
|
|
134
|
+
lambda: jsonify({"iatoolkit_version": current_app.config.get('VERSION', 'N/A')}))
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|