iatoolkit 0.71.2__py3-none-any.whl → 0.91.1__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.
- iatoolkit/__init__.py +15 -5
- iatoolkit/base_company.py +4 -58
- iatoolkit/cli_commands.py +6 -7
- iatoolkit/common/exceptions.py +1 -0
- iatoolkit/common/routes.py +12 -28
- iatoolkit/common/util.py +7 -1
- iatoolkit/company_registry.py +50 -14
- iatoolkit/{iatoolkit.py → core.py} +54 -55
- iatoolkit/infra/{mail_app.py → brevo_mail_app.py} +15 -37
- iatoolkit/infra/llm_client.py +9 -5
- iatoolkit/locales/en.yaml +10 -2
- iatoolkit/locales/es.yaml +171 -162
- iatoolkit/repositories/database_manager.py +59 -14
- iatoolkit/repositories/llm_query_repo.py +34 -22
- iatoolkit/repositories/models.py +16 -18
- iatoolkit/repositories/profile_repo.py +5 -10
- iatoolkit/repositories/vs_repo.py +9 -4
- iatoolkit/services/auth_service.py +1 -1
- iatoolkit/services/branding_service.py +1 -1
- iatoolkit/services/company_context_service.py +19 -11
- iatoolkit/services/configuration_service.py +219 -46
- iatoolkit/services/dispatcher_service.py +31 -225
- iatoolkit/services/document_service.py +10 -1
- iatoolkit/services/embedding_service.py +43 -41
- iatoolkit/services/excel_service.py +50 -2
- iatoolkit/services/history_manager_service.py +189 -0
- iatoolkit/services/jwt_service.py +1 -1
- iatoolkit/services/language_service.py +8 -2
- iatoolkit/services/license_service.py +82 -0
- iatoolkit/services/mail_service.py +171 -25
- iatoolkit/services/profile_service.py +37 -32
- iatoolkit/services/{prompt_manager_service.py → prompt_service.py} +110 -1
- iatoolkit/services/query_service.py +192 -191
- iatoolkit/services/sql_service.py +63 -12
- iatoolkit/services/tool_service.py +231 -0
- iatoolkit/services/user_feedback_service.py +18 -6
- iatoolkit/services/user_session_context_service.py +18 -0
- iatoolkit/static/images/iatoolkit_core.png +0 -0
- iatoolkit/static/images/iatoolkit_logo.png +0 -0
- iatoolkit/static/js/chat_feedback_button.js +1 -1
- iatoolkit/static/js/chat_help_content.js +4 -4
- iatoolkit/static/js/chat_main.js +17 -5
- iatoolkit/static/js/chat_onboarding_button.js +1 -1
- iatoolkit/static/styles/chat_iatoolkit.css +1 -1
- iatoolkit/static/styles/chat_public.css +28 -0
- iatoolkit/static/styles/documents.css +598 -0
- iatoolkit/static/styles/landing_page.css +223 -7
- iatoolkit/system_prompts/__init__.py +0 -0
- iatoolkit/system_prompts/query_main.prompt +2 -1
- iatoolkit/system_prompts/sql_rules.prompt +47 -12
- iatoolkit/templates/_company_header.html +30 -5
- iatoolkit/templates/_login_widget.html +3 -3
- iatoolkit/templates/chat.html +1 -1
- iatoolkit/templates/forgot_password.html +3 -2
- iatoolkit/templates/onboarding_shell.html +1 -1
- iatoolkit/templates/signup.html +3 -0
- iatoolkit/views/base_login_view.py +1 -1
- iatoolkit/views/change_password_view.py +1 -1
- iatoolkit/views/forgot_password_view.py +9 -4
- iatoolkit/views/history_api_view.py +3 -3
- iatoolkit/views/home_view.py +4 -2
- iatoolkit/views/init_context_api_view.py +1 -1
- iatoolkit/views/llmquery_api_view.py +4 -3
- iatoolkit/views/{file_store_api_view.py → load_document_api_view.py} +1 -1
- iatoolkit/views/login_view.py +17 -5
- iatoolkit/views/logout_api_view.py +10 -2
- iatoolkit/views/prompt_api_view.py +1 -1
- iatoolkit/views/root_redirect_view.py +22 -0
- iatoolkit/views/signup_view.py +12 -4
- iatoolkit/views/static_page_view.py +27 -0
- iatoolkit/views/verify_user_view.py +1 -1
- iatoolkit-0.91.1.dist-info/METADATA +268 -0
- iatoolkit-0.91.1.dist-info/RECORD +125 -0
- iatoolkit-0.91.1.dist-info/licenses/LICENSE_COMMUNITY.md +15 -0
- iatoolkit/services/history_service.py +0 -37
- iatoolkit/templates/about.html +0 -13
- iatoolkit/templates/index.html +0 -145
- iatoolkit/templates/login_simulation.html +0 -45
- iatoolkit/views/external_login_view.py +0 -73
- iatoolkit/views/index_view.py +0 -14
- iatoolkit/views/login_simulation_view.py +0 -93
- iatoolkit-0.71.2.dist-info/METADATA +0 -276
- iatoolkit-0.71.2.dist-info/RECORD +0 -122
- {iatoolkit-0.71.2.dist-info → iatoolkit-0.91.1.dist-info}/WHEEL +0 -0
- {iatoolkit-0.71.2.dist-info → iatoolkit-0.91.1.dist-info}/licenses/LICENSE +0 -0
- {iatoolkit-0.71.2.dist-info → iatoolkit-0.91.1.dist-info}/top_level.txt +0 -0
iatoolkit/__init__.py
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
IAToolkit
|
|
3
|
-
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Product: IAToolkit
|
|
3
|
+
#
|
|
4
|
+
# IAToolkit is open source software.
|
|
5
|
+
|
|
6
|
+
__version__ = "0.91.1"
|
|
4
7
|
|
|
5
8
|
# Expose main classes and functions at the top level of the package
|
|
6
9
|
|
|
7
10
|
# main IAToolkit class
|
|
8
|
-
from .
|
|
11
|
+
from iatoolkit.core import IAToolkit, create_app, current_iatoolkit
|
|
9
12
|
|
|
10
13
|
# for registering the client companies
|
|
11
|
-
from .company_registry import register_company
|
|
14
|
+
from .company_registry import register_company, set_company_registry
|
|
12
15
|
from .base_company import BaseCompany
|
|
13
16
|
|
|
14
17
|
# --- Services ---
|
|
@@ -18,12 +21,16 @@ from iatoolkit.services.search_service import SearchService
|
|
|
18
21
|
from iatoolkit.services.sql_service import SqlService
|
|
19
22
|
from iatoolkit.services.load_documents_service import LoadDocumentsService
|
|
20
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
|
|
21
27
|
|
|
22
28
|
__all__ = [
|
|
23
29
|
'IAToolkit',
|
|
24
30
|
'create_app',
|
|
25
31
|
'current_iatoolkit',
|
|
26
32
|
'register_company',
|
|
33
|
+
'set_company_registry',
|
|
27
34
|
'BaseCompany',
|
|
28
35
|
'QueryService',
|
|
29
36
|
'SqlService',
|
|
@@ -31,4 +38,7 @@ __all__ = [
|
|
|
31
38
|
'SearchService',
|
|
32
39
|
'LoadDocumentsService',
|
|
33
40
|
'CallServiceClient',
|
|
41
|
+
'ProfileService',
|
|
42
|
+
'MailService',
|
|
43
|
+
'OrmModel'
|
|
34
44
|
]
|
iatoolkit/base_company.py
CHANGED
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
from abc import ABC, abstractmethod
|
|
8
8
|
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
9
9
|
from iatoolkit.repositories.llm_query_repo import LLMQueryRepo
|
|
10
|
-
from iatoolkit.
|
|
11
|
-
from iatoolkit.
|
|
12
|
-
from .iatoolkit import IAToolkit
|
|
10
|
+
from iatoolkit.repositories.models import Company
|
|
11
|
+
from iatoolkit.core import IAToolkit
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
class BaseCompany(ABC):
|
|
@@ -18,74 +17,21 @@ class BaseCompany(ABC):
|
|
|
18
17
|
injector = IAToolkit.get_instance().get_injector()
|
|
19
18
|
self.profile_repo: ProfileRepo = injector.get(ProfileRepo)
|
|
20
19
|
self.llm_query_repo: LLMQueryRepo = injector.get(LLMQueryRepo)
|
|
21
|
-
self.prompt_service: PromptService = injector.get(PromptService)
|
|
22
20
|
self.company: Company | None = None
|
|
23
|
-
self.company_short_name
|
|
21
|
+
self.company_short_name = ''
|
|
24
22
|
|
|
25
|
-
def _create_company(self,
|
|
26
|
-
short_name: str,
|
|
27
|
-
name: str,
|
|
28
|
-
parameters: dict | None = None,
|
|
29
|
-
) -> Company:
|
|
30
|
-
company_obj = Company(short_name=short_name,
|
|
31
|
-
name=name,
|
|
32
|
-
parameters=parameters)
|
|
33
|
-
self.company = self.profile_repo.create_company(company_obj)
|
|
34
|
-
return self.company
|
|
35
|
-
|
|
36
|
-
def _create_function(self, function_name: str, description: str, params: dict, **kwargs):
|
|
37
|
-
if not self.company:
|
|
38
|
-
raise ValueError("La compañía debe estar definida antes de crear una función.")
|
|
39
|
-
|
|
40
|
-
self.llm_query_repo.create_or_update_function(
|
|
41
|
-
Function(
|
|
42
|
-
company_id=self.company.id,
|
|
43
|
-
name=function_name,
|
|
44
|
-
description=description,
|
|
45
|
-
parameters=params,
|
|
46
|
-
system_function=False,
|
|
47
|
-
**kwargs
|
|
48
|
-
)
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
def _create_prompt_category(self, name: str, order: int) -> PromptCategory:
|
|
52
|
-
if not self.company:
|
|
53
|
-
raise ValueError("La compañía debe estar definida antes de crear una categoría.")
|
|
54
|
-
|
|
55
|
-
return self.llm_query_repo.create_or_update_prompt_category(
|
|
56
|
-
PromptCategory(name=name, order=order, company_id=self.company.id)
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
def _create_prompt(self, prompt_name: str, description: str, category: PromptCategory, order: int, **kwargs):
|
|
60
|
-
if not self.company:
|
|
61
|
-
raise ValueError("La compañía debe estar definida antes de crear un prompt.")
|
|
62
|
-
|
|
63
|
-
self.prompt_service.create_prompt(
|
|
64
|
-
prompt_name=prompt_name,
|
|
65
|
-
description=description,
|
|
66
|
-
order=order,
|
|
67
|
-
company=self.company,
|
|
68
|
-
category=category,
|
|
69
|
-
**kwargs
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
@abstractmethod
|
|
73
|
-
# get context specific for this company
|
|
74
|
-
def get_user_info(self, user_identifier: str) -> dict:
|
|
75
|
-
raise NotImplementedError("La subclase debe implementar el método get_user_info()")
|
|
76
23
|
|
|
77
24
|
@abstractmethod
|
|
78
25
|
# execute the specific action configured in the intent table
|
|
79
26
|
def handle_request(self, tag: str, params: dict) -> dict:
|
|
80
27
|
raise NotImplementedError("La subclase debe implementar el método handle_request()")
|
|
81
28
|
|
|
82
|
-
|
|
29
|
+
@abstractmethod
|
|
83
30
|
def register_cli_commands(self, app):
|
|
84
31
|
"""
|
|
85
32
|
optional method for a company definition of it's cli commands
|
|
86
33
|
"""
|
|
87
34
|
pass
|
|
88
35
|
|
|
89
|
-
|
|
90
36
|
def unsupported_operation(self, tag):
|
|
91
37
|
raise NotImplementedError(f"La operación '{tag}' no está soportada por esta empresa.")
|
iatoolkit/cli_commands.py
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
import click
|
|
7
7
|
import logging
|
|
8
|
-
from .
|
|
8
|
+
from iatoolkit.core import IAToolkit
|
|
9
9
|
from iatoolkit.services.profile_service import ProfileService
|
|
10
10
|
|
|
11
|
+
|
|
11
12
|
def register_core_commands(app):
|
|
12
13
|
"""Registra los comandos CLI del núcleo de IAToolkit."""
|
|
13
14
|
|
|
@@ -17,26 +18,24 @@ def register_core_commands(app):
|
|
|
17
18
|
"""⚙️ Genera una nueva API key para una compañía ya registrada."""
|
|
18
19
|
try:
|
|
19
20
|
profile_service = IAToolkit.get_instance().get_injector().get(ProfileService)
|
|
20
|
-
click.echo(f"🔑
|
|
21
|
+
click.echo(f"🔑 Generating API-KEY for company: '{company_short_name}'...")
|
|
21
22
|
result = profile_service.new_api_key(company_short_name)
|
|
22
23
|
|
|
23
24
|
if 'error' in result:
|
|
24
25
|
click.echo(f"❌ Error: {result['error']}")
|
|
25
|
-
click.echo("👉
|
|
26
|
+
click.echo("👉 Make sure the company is registered and valid.")
|
|
26
27
|
else:
|
|
27
|
-
click.echo("✅ ¡
|
|
28
|
+
click.echo("✅ ¡Api-key is ready! add this variable to your environment:")
|
|
28
29
|
click.echo(f"IATOOLKIT_API_KEY='{result['api-key']}'")
|
|
29
30
|
except Exception as e:
|
|
30
31
|
logging.exception(e)
|
|
31
|
-
click.echo(f"❌
|
|
32
|
+
click.echo(f"❌ unexpectd error during the configuration: {e}")
|
|
32
33
|
|
|
33
34
|
@app.cli.command("encrypt-key")
|
|
34
35
|
@click.argument("key")
|
|
35
36
|
def encrypt_llm_api_key(key: str):
|
|
36
37
|
from iatoolkit.common.util import Utility
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
39
|
util = IAToolkit.get_instance().get_injector().get(Utility)
|
|
41
40
|
try:
|
|
42
41
|
encrypt_key = util.encrypt_key(key)
|
iatoolkit/common/exceptions.py
CHANGED
iatoolkit/common/routes.py
CHANGED
|
@@ -8,32 +8,31 @@ from flask import jsonify
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
# this function register all the views
|
|
11
|
-
def register_views(
|
|
11
|
+
def register_views(app):
|
|
12
12
|
|
|
13
|
-
from iatoolkit.views.index_view import IndexView
|
|
14
13
|
from iatoolkit.views.init_context_api_view import InitContextApiView
|
|
15
14
|
from iatoolkit.views.llmquery_api_view import LLMQueryApiView
|
|
16
15
|
from iatoolkit.views.tasks_api_view import TaskApiView
|
|
17
16
|
from iatoolkit.views.tasks_review_api_view import TaskReviewApiView
|
|
18
|
-
from iatoolkit.views.login_simulation_view import LoginSimulationView
|
|
19
17
|
from iatoolkit.views.signup_view import SignupView
|
|
20
18
|
from iatoolkit.views.verify_user_view import VerifyAccountView
|
|
21
19
|
from iatoolkit.views.forgot_password_view import ForgotPasswordView
|
|
22
20
|
from iatoolkit.views.change_password_view import ChangePasswordView
|
|
23
|
-
from iatoolkit.views.
|
|
21
|
+
from iatoolkit.views.load_document_api_view import LoadDocumentApiView
|
|
24
22
|
from iatoolkit.views.user_feedback_api_view import UserFeedbackApiView
|
|
25
23
|
from iatoolkit.views.prompt_api_view import PromptApiView
|
|
26
24
|
from iatoolkit.views.history_api_view import HistoryApiView
|
|
27
25
|
from iatoolkit.views.help_content_api_view import HelpContentApiView
|
|
28
|
-
from iatoolkit.views.profile_api_view import UserLanguageApiView
|
|
26
|
+
from iatoolkit.views.profile_api_view import UserLanguageApiView
|
|
29
27
|
from iatoolkit.views.embedding_api_view import EmbeddingApiView
|
|
30
28
|
from iatoolkit.views.login_view import LoginView, FinalizeContextView
|
|
31
|
-
from iatoolkit.views.external_login_view import ExternalLoginView, RedeemTokenApiView
|
|
32
29
|
from iatoolkit.views.logout_api_view import LogoutApiView
|
|
33
30
|
from iatoolkit.views.home_view import HomeView
|
|
31
|
+
from iatoolkit.views.static_page_view import StaticPageView
|
|
32
|
+
from iatoolkit.views.root_redirect_view import RootRedirectView
|
|
34
33
|
|
|
35
|
-
#
|
|
36
|
-
app.add_url_rule('/', view_func=
|
|
34
|
+
# assign root '/' to our new redirect logic
|
|
35
|
+
app.add_url_rule('/home', view_func=RootRedirectView.as_view('root_redirect'))
|
|
37
36
|
|
|
38
37
|
# company home view
|
|
39
38
|
app.add_url_rule('/<company_short_name>/home', view_func=HomeView.as_view('home'))
|
|
@@ -41,10 +40,6 @@ def register_views(injector, app):
|
|
|
41
40
|
# login for the iatoolkit integrated frontend
|
|
42
41
|
app.add_url_rule('/<company_short_name>/login', view_func=LoginView.as_view('login'))
|
|
43
42
|
|
|
44
|
-
# this is the login for external users
|
|
45
|
-
app.add_url_rule('/<company_short_name>/external_login',
|
|
46
|
-
view_func=ExternalLoginView.as_view('external_login'))
|
|
47
|
-
|
|
48
43
|
# this endpoint is called when onboarding_shell finish the context load
|
|
49
44
|
app.add_url_rule(
|
|
50
45
|
'/<company_short_name>/finalize',
|
|
@@ -65,10 +60,6 @@ def register_views(injector, app):
|
|
|
65
60
|
app.add_url_rule('/<company_short_name>/api/logout',
|
|
66
61
|
view_func=LogoutApiView.as_view('logout'))
|
|
67
62
|
|
|
68
|
-
# this endpoint is called by the JS for changing the token for a session
|
|
69
|
-
app.add_url_rule('/<string:company_short_name>/api/redeem_token',
|
|
70
|
-
view_func = RedeemTokenApiView.as_view('redeem_token'))
|
|
71
|
-
|
|
72
63
|
# init (reset) the company context
|
|
73
64
|
app.add_url_rule('/<company_short_name>/api/init-context',
|
|
74
65
|
view_func=InitContextApiView.as_view('init-context'),
|
|
@@ -97,12 +88,16 @@ def register_views(injector, app):
|
|
|
97
88
|
app.add_url_rule('/tasks/review/<int:task_id>', view_func=TaskReviewApiView.as_view('tasks-review'))
|
|
98
89
|
|
|
99
90
|
# this endpoint is for upload documents into the vector store (api-key)
|
|
100
|
-
app.add_url_rule('/api/load', view_func=
|
|
91
|
+
app.add_url_rule('/api/load-document', view_func=LoadDocumentApiView.as_view('load-document'), methods=['POST'])
|
|
101
92
|
|
|
102
93
|
# this endpoint is for generating embeddings for a given text
|
|
103
94
|
app.add_url_rule('/<company_short_name>/api/embedding',
|
|
104
95
|
view_func=EmbeddingApiView.as_view('embedding_api'))
|
|
105
96
|
|
|
97
|
+
# static pages
|
|
98
|
+
# url: /pages/foundation o /pages/implementation_plan
|
|
99
|
+
static_view = StaticPageView.as_view('static_pages')
|
|
100
|
+
app.add_url_rule('/pages/<page_name>', view_func=static_view, methods=['GET'])
|
|
106
101
|
|
|
107
102
|
@app.route('/download/<path:filename>')
|
|
108
103
|
def download_file(filename):
|
|
@@ -125,21 +120,10 @@ def register_views(injector, app):
|
|
|
125
120
|
except FileNotFoundError:
|
|
126
121
|
abort(404)
|
|
127
122
|
|
|
128
|
-
# login testing
|
|
129
|
-
app.add_url_rule('/<company_short_name>/login_test',
|
|
130
|
-
view_func=LoginSimulationView.as_view('login_test'))
|
|
131
|
-
|
|
132
|
-
app.add_url_rule(
|
|
133
|
-
'/about', # URL de la ruta
|
|
134
|
-
view_func=lambda: render_template('about.html'))
|
|
135
123
|
|
|
136
124
|
app.add_url_rule('/version', 'version',
|
|
137
125
|
lambda: jsonify({"iatoolkit_version": current_app.config.get('VERSION', 'N/A')}))
|
|
138
126
|
|
|
139
127
|
|
|
140
|
-
# hacer que la raíz '/' vaya al home de iatoolkit
|
|
141
|
-
@app.route('/')
|
|
142
|
-
def root_redirect():
|
|
143
|
-
return redirect(url_for('index'))
|
|
144
128
|
|
|
145
129
|
|
iatoolkit/common/util.py
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import logging
|
|
7
7
|
from typing import List
|
|
8
8
|
from iatoolkit.common.exceptions import IAToolkitException
|
|
9
|
+
from flask import request
|
|
9
10
|
from injector import inject
|
|
10
11
|
import os
|
|
11
12
|
from jinja2 import Environment, FileSystemLoader
|
|
@@ -16,6 +17,7 @@ from cryptography.fernet import Fernet
|
|
|
16
17
|
import base64
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
|
|
19
21
|
class Utility:
|
|
20
22
|
@inject
|
|
21
23
|
def __init__(self):
|
|
@@ -97,6 +99,10 @@ class Utility:
|
|
|
97
99
|
logging.exception(e)
|
|
98
100
|
return None
|
|
99
101
|
|
|
102
|
+
def get_template_by_language(self, template_name: str, default_langueage: str = 'en') -> str:
|
|
103
|
+
# english is default
|
|
104
|
+
lang = request.args.get("lang", default_langueage)
|
|
105
|
+
return f'{template_name}_{lang}.html'
|
|
100
106
|
|
|
101
107
|
def serialize(self, obj):
|
|
102
108
|
if isinstance(obj, datetime) or isinstance(obj, date):
|
|
@@ -343,6 +349,6 @@ class Utility:
|
|
|
343
349
|
|
|
344
350
|
def is_gemini_model(self, model: str) -> bool:
|
|
345
351
|
gemini_models = [
|
|
346
|
-
'gemini', 'gemini-2.5-pro'
|
|
352
|
+
'gemini', 'gemini-2.5-pro', 'gemini-3'
|
|
347
353
|
]
|
|
348
354
|
return any(gemini_model in model.lower() for gemini_model in gemini_models)
|
iatoolkit/company_registry.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
#
|
|
4
4
|
# IAToolkit is open source software.
|
|
5
5
|
|
|
6
|
-
from typing import Dict, Type, Any
|
|
6
|
+
from typing import Dict, Type, Any, Optional
|
|
7
7
|
from .base_company import BaseCompany
|
|
8
8
|
import logging
|
|
9
9
|
|
|
@@ -18,6 +18,30 @@ class CompanyRegistry:
|
|
|
18
18
|
self._company_classes: Dict[str, Type[BaseCompany]] = {}
|
|
19
19
|
self._company_instances: Dict[str, BaseCompany] = {}
|
|
20
20
|
|
|
21
|
+
def register(self, name: str, company_class: Type[BaseCompany]) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Registers a company in the registry.
|
|
24
|
+
|
|
25
|
+
COMMUNITY EDITION LIMITATION:
|
|
26
|
+
This base implementation enforces a strict single-tenant limit.
|
|
27
|
+
It raises a RuntimeError if a second company is registered.
|
|
28
|
+
"""
|
|
29
|
+
if not issubclass(company_class, BaseCompany):
|
|
30
|
+
raise ValueError(f"The class {company_class.__name__} must be a subclass of BaseCompany")
|
|
31
|
+
|
|
32
|
+
company_key = name.lower()
|
|
33
|
+
|
|
34
|
+
# --- STRICT SINGLE-TENANT ENFORCEMENT ---
|
|
35
|
+
# If a company is already registered (and it's not an update to the same key)
|
|
36
|
+
if len(self._company_classes) > 0 and company_key not in self._company_classes:
|
|
37
|
+
logging.error(f"❌ Community Edition Restriction: Cannot register '{name}'. Limit reached (1).")
|
|
38
|
+
raise RuntimeError(
|
|
39
|
+
"IAToolkit Community Edition allows only one company instance. "
|
|
40
|
+
"Upgrade to IAToolkit Enterprise to enable multi-tenancy."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
self._company_classes[company_key] = company_class
|
|
44
|
+
logging.info(f"Company registered: {name}")
|
|
21
45
|
|
|
22
46
|
def instantiate_companies(self, injector) -> Dict[str, BaseCompany]:
|
|
23
47
|
"""
|
|
@@ -34,15 +58,16 @@ class CompanyRegistry:
|
|
|
34
58
|
|
|
35
59
|
except Exception as e:
|
|
36
60
|
logging.error(f"Error while creating company instance for {company_key}: {e}")
|
|
37
|
-
|
|
38
|
-
raise
|
|
61
|
+
raise e
|
|
39
62
|
|
|
40
63
|
return self._company_instances.copy()
|
|
41
64
|
|
|
42
65
|
def get_all_company_instances(self) -> Dict[str, BaseCompany]:
|
|
43
|
-
"""Devuelve un diccionario con todas las instancias de empresas creadas."""
|
|
44
66
|
return self._company_instances.copy()
|
|
45
67
|
|
|
68
|
+
def get_company_instance(self, company_name: str) -> Optional[BaseCompany]:
|
|
69
|
+
return self._company_instances.get(company_name.lower())
|
|
70
|
+
|
|
46
71
|
def get_registered_companies(self) -> Dict[str, Type[BaseCompany]]:
|
|
47
72
|
return self._company_classes.copy()
|
|
48
73
|
|
|
@@ -50,11 +75,30 @@ class CompanyRegistry:
|
|
|
50
75
|
self._company_classes.clear()
|
|
51
76
|
self._company_instances.clear()
|
|
52
77
|
|
|
78
|
+
# --- Singleton Management ---
|
|
53
79
|
|
|
54
|
-
#
|
|
80
|
+
# Global instance (Default: Community Edition)
|
|
55
81
|
_company_registry = CompanyRegistry()
|
|
56
82
|
|
|
57
83
|
|
|
84
|
+
def get_company_registry() -> CompanyRegistry:
|
|
85
|
+
"""Get the global company registry instance."""
|
|
86
|
+
return _company_registry
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def set_company_registry(registry: CompanyRegistry) -> None:
|
|
90
|
+
"""
|
|
91
|
+
Sets the global company registry instance.
|
|
92
|
+
Use this to inject an Enterprise-compatible registry implementation.
|
|
93
|
+
"""
|
|
94
|
+
global _company_registry
|
|
95
|
+
if not isinstance(registry, CompanyRegistry):
|
|
96
|
+
raise ValueError("Registry must inherit from CompanyRegistry")
|
|
97
|
+
|
|
98
|
+
_company_registry = registry
|
|
99
|
+
logging.info(f"✅ Company Registry implementation swapped: {type(registry).__name__}")
|
|
100
|
+
|
|
101
|
+
|
|
58
102
|
def register_company(name: str, company_class: Type[BaseCompany]) -> None:
|
|
59
103
|
"""
|
|
60
104
|
Public function to register a company.
|
|
@@ -63,13 +107,5 @@ def register_company(name: str, company_class: Type[BaseCompany]) -> None:
|
|
|
63
107
|
name: Name of the company
|
|
64
108
|
company_class: Class that inherits from BaseCompany
|
|
65
109
|
"""
|
|
66
|
-
|
|
67
|
-
raise ValueError(f"La clase {company_class.__name__} debe heredar de BaseCompany")
|
|
68
|
-
|
|
69
|
-
company_key = name.lower()
|
|
70
|
-
_company_registry._company_classes[company_key] = company_class
|
|
110
|
+
_company_registry.register(name, company_class)
|
|
71
111
|
|
|
72
|
-
|
|
73
|
-
def get_company_registry() -> CompanyRegistry:
|
|
74
|
-
"""get the global company registry instance"""
|
|
75
|
-
return _company_registry
|