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.

Files changed (150) hide show
  1. iatoolkit/__init__.py +27 -35
  2. iatoolkit/base_company.py +3 -35
  3. iatoolkit/cli_commands.py +18 -47
  4. iatoolkit/common/__init__.py +0 -0
  5. iatoolkit/common/exceptions.py +48 -0
  6. iatoolkit/common/interfaces/__init__.py +0 -0
  7. iatoolkit/common/interfaces/asset_storage.py +34 -0
  8. iatoolkit/common/interfaces/database_provider.py +39 -0
  9. iatoolkit/common/model_registry.py +159 -0
  10. iatoolkit/common/routes.py +138 -0
  11. iatoolkit/common/session_manager.py +26 -0
  12. iatoolkit/common/util.py +353 -0
  13. iatoolkit/company_registry.py +66 -29
  14. iatoolkit/core.py +514 -0
  15. iatoolkit/infra/__init__.py +5 -0
  16. iatoolkit/infra/brevo_mail_app.py +123 -0
  17. iatoolkit/infra/call_service.py +140 -0
  18. iatoolkit/infra/connectors/__init__.py +5 -0
  19. iatoolkit/infra/connectors/file_connector.py +17 -0
  20. iatoolkit/infra/connectors/file_connector_factory.py +57 -0
  21. iatoolkit/infra/connectors/google_cloud_storage_connector.py +53 -0
  22. iatoolkit/infra/connectors/google_drive_connector.py +68 -0
  23. iatoolkit/infra/connectors/local_file_connector.py +46 -0
  24. iatoolkit/infra/connectors/s3_connector.py +33 -0
  25. iatoolkit/infra/google_chat_app.py +57 -0
  26. iatoolkit/infra/llm_providers/__init__.py +0 -0
  27. iatoolkit/infra/llm_providers/deepseek_adapter.py +278 -0
  28. iatoolkit/infra/llm_providers/gemini_adapter.py +350 -0
  29. iatoolkit/infra/llm_providers/openai_adapter.py +124 -0
  30. iatoolkit/infra/llm_proxy.py +268 -0
  31. iatoolkit/infra/llm_response.py +45 -0
  32. iatoolkit/infra/redis_session_manager.py +122 -0
  33. iatoolkit/locales/en.yaml +222 -0
  34. iatoolkit/locales/es.yaml +225 -0
  35. iatoolkit/repositories/__init__.py +5 -0
  36. iatoolkit/repositories/database_manager.py +187 -0
  37. iatoolkit/repositories/document_repo.py +33 -0
  38. iatoolkit/repositories/filesystem_asset_repository.py +36 -0
  39. iatoolkit/repositories/llm_query_repo.py +105 -0
  40. iatoolkit/repositories/models.py +279 -0
  41. iatoolkit/repositories/profile_repo.py +171 -0
  42. iatoolkit/repositories/vs_repo.py +150 -0
  43. iatoolkit/services/__init__.py +5 -0
  44. iatoolkit/services/auth_service.py +193 -0
  45. {services → iatoolkit/services}/benchmark_service.py +7 -7
  46. iatoolkit/services/branding_service.py +153 -0
  47. iatoolkit/services/company_context_service.py +214 -0
  48. iatoolkit/services/configuration_service.py +375 -0
  49. iatoolkit/services/dispatcher_service.py +134 -0
  50. {services → iatoolkit/services}/document_service.py +20 -8
  51. iatoolkit/services/embedding_service.py +148 -0
  52. iatoolkit/services/excel_service.py +156 -0
  53. {services → iatoolkit/services}/file_processor_service.py +36 -21
  54. iatoolkit/services/history_manager_service.py +208 -0
  55. iatoolkit/services/i18n_service.py +104 -0
  56. iatoolkit/services/jwt_service.py +80 -0
  57. iatoolkit/services/language_service.py +89 -0
  58. iatoolkit/services/license_service.py +82 -0
  59. iatoolkit/services/llm_client_service.py +438 -0
  60. iatoolkit/services/load_documents_service.py +174 -0
  61. iatoolkit/services/mail_service.py +213 -0
  62. {services → iatoolkit/services}/profile_service.py +200 -101
  63. iatoolkit/services/prompt_service.py +303 -0
  64. iatoolkit/services/query_service.py +467 -0
  65. iatoolkit/services/search_service.py +55 -0
  66. iatoolkit/services/sql_service.py +169 -0
  67. iatoolkit/services/tool_service.py +246 -0
  68. iatoolkit/services/user_feedback_service.py +117 -0
  69. iatoolkit/services/user_session_context_service.py +213 -0
  70. iatoolkit/static/images/fernando.jpeg +0 -0
  71. iatoolkit/static/images/iatoolkit_core.png +0 -0
  72. iatoolkit/static/images/iatoolkit_logo.png +0 -0
  73. iatoolkit/static/js/chat_feedback_button.js +80 -0
  74. iatoolkit/static/js/chat_filepond.js +85 -0
  75. iatoolkit/static/js/chat_help_content.js +124 -0
  76. iatoolkit/static/js/chat_history_button.js +110 -0
  77. iatoolkit/static/js/chat_logout_button.js +36 -0
  78. iatoolkit/static/js/chat_main.js +401 -0
  79. iatoolkit/static/js/chat_model_selector.js +227 -0
  80. iatoolkit/static/js/chat_onboarding_button.js +103 -0
  81. iatoolkit/static/js/chat_prompt_manager.js +94 -0
  82. iatoolkit/static/js/chat_reload_button.js +38 -0
  83. iatoolkit/static/styles/chat_iatoolkit.css +559 -0
  84. iatoolkit/static/styles/chat_modal.css +133 -0
  85. iatoolkit/static/styles/chat_public.css +135 -0
  86. iatoolkit/static/styles/documents.css +598 -0
  87. iatoolkit/static/styles/landing_page.css +398 -0
  88. iatoolkit/static/styles/llm_output.css +148 -0
  89. iatoolkit/static/styles/onboarding.css +176 -0
  90. iatoolkit/system_prompts/__init__.py +0 -0
  91. iatoolkit/system_prompts/query_main.prompt +30 -23
  92. iatoolkit/system_prompts/sql_rules.prompt +47 -12
  93. iatoolkit/templates/_company_header.html +45 -0
  94. iatoolkit/templates/_login_widget.html +42 -0
  95. iatoolkit/templates/base.html +78 -0
  96. iatoolkit/templates/change_password.html +66 -0
  97. iatoolkit/templates/chat.html +337 -0
  98. iatoolkit/templates/chat_modals.html +185 -0
  99. iatoolkit/templates/error.html +51 -0
  100. iatoolkit/templates/forgot_password.html +51 -0
  101. iatoolkit/templates/onboarding_shell.html +106 -0
  102. iatoolkit/templates/signup.html +79 -0
  103. iatoolkit/views/__init__.py +5 -0
  104. iatoolkit/views/base_login_view.py +96 -0
  105. iatoolkit/views/change_password_view.py +116 -0
  106. iatoolkit/views/chat_view.py +76 -0
  107. iatoolkit/views/embedding_api_view.py +65 -0
  108. iatoolkit/views/forgot_password_view.py +75 -0
  109. iatoolkit/views/help_content_api_view.py +54 -0
  110. iatoolkit/views/history_api_view.py +56 -0
  111. iatoolkit/views/home_view.py +63 -0
  112. iatoolkit/views/init_context_api_view.py +74 -0
  113. iatoolkit/views/llmquery_api_view.py +59 -0
  114. iatoolkit/views/load_company_configuration_api_view.py +49 -0
  115. iatoolkit/views/load_document_api_view.py +65 -0
  116. iatoolkit/views/login_view.py +170 -0
  117. iatoolkit/views/logout_api_view.py +57 -0
  118. iatoolkit/views/profile_api_view.py +46 -0
  119. iatoolkit/views/prompt_api_view.py +37 -0
  120. iatoolkit/views/root_redirect_view.py +22 -0
  121. iatoolkit/views/signup_view.py +100 -0
  122. iatoolkit/views/static_page_view.py +27 -0
  123. iatoolkit/views/user_feedback_api_view.py +60 -0
  124. iatoolkit/views/users_api_view.py +33 -0
  125. iatoolkit/views/verify_user_view.py +60 -0
  126. iatoolkit-0.107.4.dist-info/METADATA +268 -0
  127. iatoolkit-0.107.4.dist-info/RECORD +132 -0
  128. iatoolkit-0.107.4.dist-info/licenses/LICENSE +21 -0
  129. iatoolkit-0.107.4.dist-info/licenses/LICENSE_COMMUNITY.md +15 -0
  130. {iatoolkit-0.3.9.dist-info → iatoolkit-0.107.4.dist-info}/top_level.txt +0 -1
  131. iatoolkit/iatoolkit.py +0 -413
  132. iatoolkit/system_prompts/arquitectura.prompt +0 -32
  133. iatoolkit-0.3.9.dist-info/METADATA +0 -252
  134. iatoolkit-0.3.9.dist-info/RECORD +0 -32
  135. services/__init__.py +0 -5
  136. services/api_service.py +0 -75
  137. services/dispatcher_service.py +0 -351
  138. services/excel_service.py +0 -98
  139. services/history_service.py +0 -45
  140. services/jwt_service.py +0 -91
  141. services/load_documents_service.py +0 -212
  142. services/mail_service.py +0 -62
  143. services/prompt_manager_service.py +0 -172
  144. services/query_service.py +0 -334
  145. services/search_service.py +0 -32
  146. services/sql_service.py +0 -42
  147. services/tasks_service.py +0 -188
  148. services/user_feedback_service.py +0 -67
  149. services/user_session_context_service.py +0 -85
  150. {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 Package
3
- """
1
+ # Copyright (c) 2024 Fernando Libedinsky
2
+ # Product: IAToolkit
3
+ #
4
+ # IAToolkit is open source software.
4
5
 
5
- # Expose main classes and functions at the top level of the package
6
+ __version__ = "0.107.4"
6
7
 
7
- # Assuming 'toolkit.py' contains the IAToolkit class
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
- # Assuming 'app_factory.py' contains create_app and register_company
12
- from .company_registry import register_company
10
+ # main IAToolkit class
11
+ from iatoolkit.core import IAToolkit, create_app, current_iatoolkit
13
12
 
14
- # Assuming 'base_company.py' contains BaseCompany
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
- # Assuming they are in a 'services' sub-package
19
- from services.sql_service import SqlService
20
- from services.excel_service import ExcelService
21
- from services.dispatcher_service import Dispatcher
22
- from services.document_service import DocumentService
23
- from services.search_service import SearchService
24
- from services.query_service import QueryService
25
- from repositories.profile_repo import ProfileRepo
26
- from repositories.llm_query_repo import LLMQueryRepo
27
- from repositories.database_manager import DatabaseManager
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
- 'ProfileRepo',
46
- 'LLMQueryRepo',
47
- 'DatabaseManager',
40
+ 'LoadDocumentsService',
48
41
  'CallServiceClient',
49
- 'Utility',
50
- 'Company',
51
- 'Function',
52
- 'TaskType',
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
- # Producto: IAToolkit
3
- # Todos los derechos reservados.
4
- # En trámite de registro en el Registro de Propiedad Intelectual de Chile.
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.dispatcher_service import Dispatcher
5
- from services.profile_service import ProfileService
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("setup-company")
15
+ @app.cli.command("api-key")
23
16
  @click.argument("company_short_name")
24
- def setup_company(company_short_name: str):
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"🔑 Generando API key para '{company_short_name}'...")
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("👉 Asegúrate de que el nombre de la compañía es correcto y está registrada.")
26
+ click.echo("👉 Make sure the company is registered and valid.")
34
27
  else:
35
- click.echo("✅ ¡Configuración lista! Agrega esta variable a tu entorno:")
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"❌ Ocurrió un error inesperado durante la configuración: {e}")
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 api_key(key: str):
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 clave encriptada es: {encrypt_key} \n')
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
+