iatoolkit 0.66.2__py3-none-any.whl → 0.71.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.
Files changed (73) hide show
  1. iatoolkit/__init__.py +2 -6
  2. iatoolkit/base_company.py +3 -31
  3. iatoolkit/cli_commands.py +1 -1
  4. iatoolkit/common/routes.py +5 -1
  5. iatoolkit/common/session_manager.py +2 -0
  6. iatoolkit/company_registry.py +1 -2
  7. iatoolkit/iatoolkit.py +13 -13
  8. iatoolkit/infra/llm_client.py +8 -12
  9. iatoolkit/infra/llm_proxy.py +38 -10
  10. iatoolkit/locales/en.yaml +25 -2
  11. iatoolkit/locales/es.yaml +27 -4
  12. iatoolkit/repositories/database_manager.py +8 -3
  13. iatoolkit/repositories/document_repo.py +1 -1
  14. iatoolkit/repositories/models.py +6 -8
  15. iatoolkit/repositories/profile_repo.py +0 -4
  16. iatoolkit/repositories/vs_repo.py +26 -20
  17. iatoolkit/services/auth_service.py +2 -2
  18. iatoolkit/services/branding_service.py +11 -7
  19. iatoolkit/services/company_context_service.py +155 -0
  20. iatoolkit/services/configuration_service.py +133 -0
  21. iatoolkit/services/dispatcher_service.py +75 -70
  22. iatoolkit/services/document_service.py +5 -2
  23. iatoolkit/services/embedding_service.py +145 -0
  24. iatoolkit/services/excel_service.py +15 -11
  25. iatoolkit/services/file_processor_service.py +4 -12
  26. iatoolkit/services/history_service.py +7 -7
  27. iatoolkit/services/i18n_service.py +4 -4
  28. iatoolkit/services/jwt_service.py +7 -9
  29. iatoolkit/services/language_service.py +29 -23
  30. iatoolkit/services/load_documents_service.py +100 -113
  31. iatoolkit/services/mail_service.py +9 -4
  32. iatoolkit/services/profile_service.py +10 -7
  33. iatoolkit/services/prompt_manager_service.py +20 -16
  34. iatoolkit/services/query_service.py +112 -43
  35. iatoolkit/services/search_service.py +11 -4
  36. iatoolkit/services/sql_service.py +57 -25
  37. iatoolkit/services/user_feedback_service.py +15 -13
  38. iatoolkit/static/js/chat_history_button.js +3 -5
  39. iatoolkit/static/js/chat_main.js +2 -17
  40. iatoolkit/static/js/chat_onboarding_button.js +6 -0
  41. iatoolkit/static/styles/chat_iatoolkit.css +69 -158
  42. iatoolkit/static/styles/chat_modal.css +1 -37
  43. iatoolkit/static/styles/onboarding.css +7 -0
  44. iatoolkit/system_prompts/query_main.prompt +2 -10
  45. iatoolkit/templates/change_password.html +1 -1
  46. iatoolkit/templates/chat.html +12 -4
  47. iatoolkit/templates/chat_modals.html +4 -0
  48. iatoolkit/templates/error.html +1 -1
  49. iatoolkit/templates/login_simulation.html +17 -6
  50. iatoolkit/templates/onboarding_shell.html +4 -1
  51. iatoolkit/views/base_login_view.py +7 -8
  52. iatoolkit/views/change_password_view.py +2 -3
  53. iatoolkit/views/embedding_api_view.py +65 -0
  54. iatoolkit/views/external_login_view.py +1 -1
  55. iatoolkit/views/file_store_api_view.py +1 -1
  56. iatoolkit/views/forgot_password_view.py +2 -4
  57. iatoolkit/views/help_content_api_view.py +9 -9
  58. iatoolkit/views/history_api_view.py +1 -1
  59. iatoolkit/views/home_view.py +2 -2
  60. iatoolkit/views/init_context_api_view.py +18 -17
  61. iatoolkit/views/llmquery_api_view.py +3 -2
  62. iatoolkit/views/login_simulation_view.py +14 -2
  63. iatoolkit/views/login_view.py +9 -9
  64. iatoolkit/views/signup_view.py +2 -4
  65. iatoolkit/views/verify_user_view.py +2 -4
  66. {iatoolkit-0.66.2.dist-info → iatoolkit-0.71.4.dist-info}/METADATA +40 -22
  67. iatoolkit-0.71.4.dist-info/RECORD +122 -0
  68. iatoolkit-0.71.4.dist-info/licenses/LICENSE +21 -0
  69. iatoolkit/services/help_content_service.py +0 -30
  70. iatoolkit/services/onboarding_service.py +0 -43
  71. iatoolkit-0.66.2.dist-info/RECORD +0 -119
  72. {iatoolkit-0.66.2.dist-info → iatoolkit-0.71.4.dist-info}/WHEEL +0 -0
  73. {iatoolkit-0.66.2.dist-info → iatoolkit-0.71.4.dist-info}/top_level.txt +0 -0
iatoolkit/__init__.py CHANGED
@@ -5,20 +5,18 @@ IAToolkit Package
5
5
  # Expose main classes and functions at the top level of the package
6
6
 
7
7
  # main IAToolkit class
8
- from .iatoolkit import IAToolkit, current_iatoolkit, create_app
8
+ from .iatoolkit import IAToolkit, create_app, current_iatoolkit
9
9
 
10
10
  # for registering the client companies
11
11
  from .company_registry import register_company
12
12
  from .base_company import BaseCompany
13
- from iatoolkit.repositories.database_manager import DatabaseManager
14
13
 
15
14
  # --- Services ---
16
15
  from iatoolkit.services.query_service import QueryService
17
- from iatoolkit.services.sql_service import SqlService
18
16
  from iatoolkit.services.document_service import DocumentService
19
17
  from iatoolkit.services.search_service import SearchService
18
+ from iatoolkit.services.sql_service import SqlService
20
19
  from iatoolkit.services.load_documents_service import LoadDocumentsService
21
- from iatoolkit.services.excel_service import ExcelService
22
20
  from iatoolkit.infra.call_service import CallServiceClient
23
21
 
24
22
  __all__ = [
@@ -27,10 +25,8 @@ __all__ = [
27
25
  'current_iatoolkit',
28
26
  'register_company',
29
27
  'BaseCompany',
30
- 'DatabaseManager',
31
28
  'QueryService',
32
29
  'SqlService',
33
- 'ExcelService',
34
30
  'DocumentService',
35
31
  'SearchService',
36
32
  'LoadDocumentsService',
iatoolkit/base_company.py CHANGED
@@ -7,10 +7,9 @@
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
-
11
10
  from iatoolkit.services.prompt_manager_service import PromptService
12
11
  from iatoolkit.repositories.models import Company, Function, PromptCategory
13
- from iatoolkit import IAToolkit
12
+ from .iatoolkit import IAToolkit
14
13
 
15
14
 
16
15
  class BaseCompany(ABC):
@@ -21,23 +20,16 @@ class BaseCompany(ABC):
21
20
  self.llm_query_repo: LLMQueryRepo = injector.get(LLMQueryRepo)
22
21
  self.prompt_service: PromptService = injector.get(PromptService)
23
22
  self.company: Company | None = None
24
-
25
- def _load_company_by_short_name(self, short_name: str) -> Company:
26
- self.company = self.profile_repo.get_company_by_short_name(short_name)
27
- return self.company
23
+ self.company_short_name: str
28
24
 
29
25
  def _create_company(self,
30
26
  short_name: str,
31
27
  name: str,
32
28
  parameters: dict | None = None,
33
- branding: dict | None = None,
34
- onboarding_cards: dict | None = None,
35
29
  ) -> Company:
36
30
  company_obj = Company(short_name=short_name,
37
31
  name=name,
38
- parameters=parameters,
39
- branding=branding,
40
- onboarding_cards=onboarding_cards)
32
+ parameters=parameters)
41
33
  self.company = self.profile_repo.create_company(company_obj)
42
34
  return self.company
43
35
 
@@ -77,17 +69,6 @@ class BaseCompany(ABC):
77
69
  **kwargs
78
70
  )
79
71
 
80
-
81
- @abstractmethod
82
- # initialize all the database tables needed
83
- def register_company(self):
84
- raise NotImplementedError("La subclase debe implementar el método create_company()")
85
-
86
- @abstractmethod
87
- # get context specific for this company
88
- def get_company_context(self, **kwargs) -> str:
89
- raise NotImplementedError("La subclase debe implementar el método get_company_context()")
90
-
91
72
  @abstractmethod
92
73
  # get context specific for this company
93
74
  def get_user_info(self, user_identifier: str) -> dict:
@@ -98,15 +79,6 @@ class BaseCompany(ABC):
98
79
  def handle_request(self, tag: str, params: dict) -> dict:
99
80
  raise NotImplementedError("La subclase debe implementar el método handle_request()")
100
81
 
101
- @abstractmethod
102
- # get context specific for the query
103
- def start_execution(self):
104
- raise NotImplementedError("La subclase debe implementar el método start_execution()")
105
-
106
- @abstractmethod
107
- # get context specific for the query
108
- def get_metadata_from_filename(self, filename: str) -> dict:
109
- raise NotImplementedError("La subclase debe implementar el método get_query_context()")
110
82
 
111
83
  def register_cli_commands(self, app):
112
84
  """
iatoolkit/cli_commands.py CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  import click
7
7
  import logging
8
- from iatoolkit import IAToolkit
8
+ from .iatoolkit import IAToolkit
9
9
  from iatoolkit.services.profile_service import ProfileService
10
10
 
11
11
  def register_core_commands(app):
@@ -26,7 +26,7 @@ def register_views(injector, app):
26
26
  from iatoolkit.views.history_api_view import HistoryApiView
27
27
  from iatoolkit.views.help_content_api_view import HelpContentApiView
28
28
  from iatoolkit.views.profile_api_view import UserLanguageApiView # <-- Importa la nueva vista
29
-
29
+ from iatoolkit.views.embedding_api_view import EmbeddingApiView
30
30
  from iatoolkit.views.login_view import LoginView, FinalizeContextView
31
31
  from iatoolkit.views.external_login_view import ExternalLoginView, RedeemTokenApiView
32
32
  from iatoolkit.views.logout_api_view import LogoutApiView
@@ -99,6 +99,10 @@ def register_views(injector, app):
99
99
  # this endpoint is for upload documents into the vector store (api-key)
100
100
  app.add_url_rule('/api/load', view_func=FileStoreApiView.as_view('load_api'))
101
101
 
102
+ # this endpoint is for generating embeddings for a given text
103
+ app.add_url_rule('/<company_short_name>/api/embedding',
104
+ view_func=EmbeddingApiView.as_view('embedding_api'))
105
+
102
106
 
103
107
  @app.route('/download/<path:filename>')
104
108
  def download_file(filename):
@@ -3,6 +3,8 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
+ # This is the Flask connected session manager for IAToolkit
7
+
6
8
  from flask import session
7
9
 
8
10
  class SessionManager:
@@ -31,10 +31,9 @@ class CompanyRegistry:
31
31
 
32
32
  # save the created instance in the registry
33
33
  self._company_instances[company_key] = company_instance
34
- logging.info(f"company '{company_key}' instantiated")
35
34
 
36
35
  except Exception as e:
37
- logging.error(f"Error instanciando empresa {company_key}: {e}")
36
+ logging.error(f"Error while creating company instance for {company_key}: {e}")
38
37
  logging.exception(e)
39
38
  raise
40
39
 
iatoolkit/iatoolkit.py CHANGED
@@ -19,7 +19,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix
19
19
  from injector import Binder, Injector, singleton
20
20
  from importlib.metadata import version as _pkg_version, PackageNotFoundError
21
21
 
22
- IATOOLKIT_VERSION = "0.66.2"
22
+ IATOOLKIT_VERSION = "0.71.4"
23
23
 
24
24
  # global variable for the unique instance of IAToolkit
25
25
  _iatoolkit_instance: Optional['IAToolkit'] = None
@@ -91,6 +91,9 @@ class IAToolkit:
91
91
  # Step 6: initialize dispatcher and registered companies
92
92
  self._init_dispatcher_and_company_instances()
93
93
 
94
+ # Re-apply logging configuration in case it was modified by company-specific code
95
+ self._setup_logging()
96
+
94
97
  # Step 7: Finalize setup within the application context
95
98
  self._setup_redis_sessions()
96
99
  self._setup_cors()
@@ -155,19 +158,12 @@ class IAToolkit:
155
158
  static_folder=static_folder,
156
159
  template_folder=template_folder)
157
160
 
158
- is_https = self._get_config_value('USE_HTTPS', 'false').lower() == 'true'
159
- is_dev = self._get_config_value('FLASK_ENV') == 'development'
160
-
161
- # get the iatoolkit domain
162
- parsed_url = urlparse(os.getenv('IATOOLKIT_BASE_URL'))
163
- domain = parsed_url.netloc
164
-
161
+ # get the IATOOLKIT_VERSION from the package metadata
165
162
  try:
166
163
  self.version = _pkg_version("iatoolkit")
167
164
  except PackageNotFoundError:
168
165
  pass
169
166
 
170
-
171
167
  self.app.config.update({
172
168
  'VERSION': self.version,
173
169
  'SECRET_KEY': self._get_config_value('FLASK_SECRET_KEY', 'iatoolkit-default-secret'),
@@ -180,6 +176,7 @@ class IAToolkit:
180
176
  'JWT_EXPIRATION_SECONDS_CHAT': int(self._get_config_value('JWT_EXPIRATION_SECONDS_CHAT', 3600))
181
177
  })
182
178
 
179
+ parsed_url = urlparse(os.getenv('IATOOLKIT_BASE_URL'))
183
180
  if parsed_url.scheme == 'https':
184
181
  self.app.config['PREFERRED_URL_SCHEME'] = 'https'
185
182
 
@@ -187,7 +184,7 @@ class IAToolkit:
187
184
  self.app.wsgi_app = ProxyFix(self.app.wsgi_app, x_proto=1)
188
185
 
189
186
  # Configuración para tokenizers en desarrollo
190
- if is_dev:
187
+ if self._get_config_value('FLASK_ENV') == 'dev':
191
188
  os.environ["TOKENIZERS_PARALLELISM"] = "false"
192
189
 
193
190
  def _setup_database(self):
@@ -293,7 +290,6 @@ class IAToolkit:
293
290
  from iatoolkit.repositories.document_repo import DocumentRepo
294
291
  from iatoolkit.repositories.profile_repo import ProfileRepo
295
292
  from iatoolkit.repositories.llm_query_repo import LLMQueryRepo
296
-
297
293
  from iatoolkit.repositories.vs_repo import VSRepo
298
294
  from iatoolkit.repositories.tasks_repo import TaskRepo
299
295
 
@@ -318,6 +314,8 @@ class IAToolkit:
318
314
  from iatoolkit.services.branding_service import BrandingService
319
315
  from iatoolkit.services.i18n_service import I18nService
320
316
  from iatoolkit.services.language_service import LanguageService
317
+ from iatoolkit.services.configuration_service import ConfigurationService
318
+ from iatoolkit.services.embedding_service import EmbeddingService
321
319
 
322
320
  binder.bind(QueryService, to=QueryService)
323
321
  binder.bind(TaskService, to=TaskService)
@@ -333,6 +331,8 @@ class IAToolkit:
333
331
  binder.bind(BrandingService, to=BrandingService)
334
332
  binder.bind(I18nService, to=I18nService)
335
333
  binder.bind(LanguageService, to=LanguageService)
334
+ binder.bind(ConfigurationService, to=ConfigurationService)
335
+ binder.bind(EmbeddingService, to=EmbeddingService)
336
336
 
337
337
  def _bind_infrastructure(self, binder: Binder):
338
338
  from iatoolkit.infra.llm_client import llmClient
@@ -359,9 +359,9 @@ class IAToolkit:
359
359
  # instantiate all the registered companies
360
360
  get_company_registry().instantiate_companies(self._injector)
361
361
 
362
- # use the dispatcher to start the execution of every company
362
+ # use the dispatcher to load the company config.yaml file and prepare the execution
363
363
  dispatcher = self._injector.get(Dispatcher)
364
- dispatcher.start_execution()
364
+ dispatcher.load_company_configs()
365
365
 
366
366
  def _setup_cli_commands(self):
367
367
  from iatoolkit.cli_commands import register_core_commands
@@ -38,12 +38,6 @@ class llmClient:
38
38
  self.util = util
39
39
  self._dispatcher = None # Cache for the lazy-loaded dispatcher
40
40
 
41
- # get the model from the environment variable
42
- self.model = os.getenv("LLM_MODEL", "")
43
- if not self.model:
44
- raise IAToolkitException(IAToolkitException.ErrorType.API_KEY,
45
- "La variable de entorno 'LLM_MODEL' no está configurada.")
46
-
47
41
  # library for counting tokens
48
42
  self.encoding = tiktoken.encoding_for_model("gpt-4o")
49
43
 
@@ -70,6 +64,7 @@ class llmClient:
70
64
  context: str,
71
65
  tools: list[dict],
72
66
  text: dict,
67
+ model: str,
73
68
  context_history: Optional[List[Dict]] = None,
74
69
  ) -> dict:
75
70
 
@@ -80,13 +75,13 @@ class llmClient:
80
75
  force_tool_name = None
81
76
  reasoning = {}
82
77
 
83
- if 'gpt-5' in self.model:
78
+ if 'gpt-5' in model:
84
79
  text['verbosity'] = "low"
85
80
  reasoning = {"effort": 'minimal'}
86
81
 
87
82
  try:
88
83
  start_time = time.time()
89
- logging.info(f"calling llm model '{self.model}' with {self.count_tokens(context)} tokens...")
84
+ logging.info(f"calling llm model '{model}' with {self.count_tokens(context)} tokens...")
90
85
 
91
86
  # get the proxy for the company
92
87
  llm_proxy = self.llm_proxy_factory.create_for_company(company)
@@ -99,7 +94,7 @@ class llmClient:
99
94
  }]
100
95
 
101
96
  response = llm_proxy.create_response(
102
- model=self.model,
97
+ model=model,
103
98
  previous_response_id=previous_response_id,
104
99
  context_history=context_history,
105
100
  input=input_messages,
@@ -136,7 +131,7 @@ class llmClient:
136
131
  logging.info(f"start execution fcall: {function_name}")
137
132
  try:
138
133
  result = self.dispatcher.dispatch(
139
- company_name=company.short_name,
134
+ company_short_name=company.short_name,
140
135
  action=function_name,
141
136
  **args
142
137
  )
@@ -186,7 +181,7 @@ class llmClient:
186
181
  tool_choice_value = "required"
187
182
 
188
183
  response = llm_proxy.create_response(
189
- model=self.model,
184
+ model=model,
190
185
  input=input_messages,
191
186
  previous_response_id=response.id,
192
187
  context_history=context_history,
@@ -200,7 +195,7 @@ class llmClient:
200
195
  # save the statistices
201
196
  stats['response_time']=int(time.time() - start_time)
202
197
  stats['sql_retry_count'] = sql_retry_count
203
- stats['model'] = response.model
198
+ stats['model'] = model
204
199
 
205
200
  # decode the LLM response
206
201
  decoded_response = self.decode_response(response)
@@ -392,6 +387,7 @@ class llmClient:
392
387
 
393
388
  def add_stats(self, stats1: dict, stats2: dict) -> dict:
394
389
  stats_dict = {
390
+ "model": stats1.get('model', ''),
395
391
  "input_tokens": stats1.get('input_tokens', 0) + stats2.get('input_tokens', 0),
396
392
  "output_tokens": stats1.get('output_tokens', 0) + stats2.get('output_tokens', 0),
397
393
  "total_tokens": stats1.get('total_tokens', 0) + stats2.get('total_tokens', 0),
@@ -7,6 +7,7 @@ from typing import Dict, List, Any
7
7
  from abc import ABC, abstractmethod
8
8
  from iatoolkit.common.util import Utility
9
9
  from iatoolkit.infra.llm_response import LLMResponse
10
+ from iatoolkit.services.configuration_service import ConfigurationService
10
11
  from iatoolkit.infra.openai_adapter import OpenAIAdapter
11
12
  from iatoolkit.infra.gemini_adapter import GeminiAdapter
12
13
  from iatoolkit.common.exceptions import IAToolkitException
@@ -41,12 +42,16 @@ class LLMProxy:
41
42
  _clients_cache_lock = threading.Lock()
42
43
 
43
44
  @inject
44
- def __init__(self, util: Utility, openai_client = None, gemini_client = None):
45
+ def __init__(self, util: Utility,
46
+ configuration_service: ConfigurationService,
47
+ openai_client = None,
48
+ gemini_client = None):
45
49
  """
46
50
  Inicializa una instancia del proxy. Puede ser una instancia "base" (fábrica)
47
51
  o una instancia de "trabajo" con clientes configurados.
48
52
  """
49
53
  self.util = util
54
+ self.configuration_service = configuration_service
50
55
  self.openai_adapter = OpenAIAdapter(openai_client) if openai_client else None
51
56
  self.gemini_adapter = GeminiAdapter(gemini_client) if gemini_client else None
52
57
 
@@ -71,7 +76,11 @@ class LLMProxy:
71
76
  )
72
77
 
73
78
  # Devuelve una NUEVA instancia con los clientes configurados
74
- return LLMProxy(util=self.util, openai_client=openai_client, gemini_client=gemini_client)
79
+ return LLMProxy(
80
+ util=self.util,
81
+ configuration_service=self.configuration_service,
82
+ openai_client=openai_client,
83
+ gemini_client=gemini_client)
75
84
 
76
85
  def create_response(self, model: str, input: List[Dict], **kwargs) -> LLMResponse:
77
86
  """Enruta la llamada al adaptador correcto basado en el modelo."""
@@ -103,7 +112,7 @@ class LLMProxy:
103
112
  elif provider == LLMProvider.GEMINI:
104
113
  client = self._create_gemini_client(company)
105
114
  else:
106
- raise IAToolkitException(f"Proveedor no soportado: {provider.value}")
115
+ raise IAToolkitException(f"provider not supported: {provider.value}")
107
116
 
108
117
  if client:
109
118
  LLMProxy._clients_cache[cache_key] = client
@@ -115,22 +124,41 @@ class LLMProxy:
115
124
 
116
125
  def _create_openai_client(self, company: Company) -> OpenAI:
117
126
  """Crea un cliente de OpenAI con la API key."""
118
- if company.openai_api_key:
119
- decrypted_api_key = self.util.decrypt_key(company.openai_api_key)
127
+ decrypted_api_key = ''
128
+ llm_config = self.configuration_service.get_configuration(company.short_name, 'llm')
129
+
130
+ # Try to get API key name from config first
131
+ if llm_config and llm_config.get('api-key'):
132
+ api_key_env_var = llm_config['api-key']
133
+ decrypted_api_key = os.getenv(api_key_env_var, '')
120
134
  else:
121
- decrypted_api_key = os.getenv("OPENAI_API_KEY", '')
135
+ # Fallback to old logic
136
+ if company.openai_api_key:
137
+ decrypted_api_key = self.util.decrypt_key(company.openai_api_key)
138
+ else:
139
+ decrypted_api_key = os.getenv("OPENAI_API_KEY", '')
140
+
122
141
  if not decrypted_api_key:
123
142
  raise IAToolkitException(IAToolkitException.ErrorType.API_KEY,
124
- f"La empresa '{company.name}' no tiene API key de OpenAI.")
143
+ f"La empresa '{company.name}' no tiene API key de OpenAI.")
125
144
  return OpenAI(api_key=decrypted_api_key)
126
145
 
127
146
  def _create_gemini_client(self, company: Company) -> Any:
128
147
  """Configura y devuelve el cliente de Gemini."""
129
148
 
130
- if company.gemini_api_key:
131
- decrypted_api_key = self.util.decrypt_key(company.gemini_api_key)
149
+ decrypted_api_key = ''
150
+ llm_config = self.configuration_service.get_configuration(company.short_name, 'llm')
151
+
152
+ # Try to get API key name from config first
153
+ if llm_config and llm_config.get('api-key'):
154
+ api_key_env_var = llm_config['api-key']
155
+ decrypted_api_key = os.getenv(api_key_env_var, '')
132
156
  else:
133
- decrypted_api_key = os.getenv("GEMINI_API_KEY", '')
157
+ # Fallback to old logic
158
+ if company.gemini_api_key:
159
+ decrypted_api_key = self.util.decrypt_key(company.gemini_api_key)
160
+ else:
161
+ decrypted_api_key = os.getenv("GEMINI_API_KEY", '')
134
162
 
135
163
  if not decrypted_api_key:
136
164
  return None
iatoolkit/locales/en.yaml CHANGED
@@ -77,6 +77,8 @@ ui:
77
77
  stop: "Stop"
78
78
 
79
79
  errors:
80
+ company_not_found: "The company {company_short_name} does not exist."
81
+ timeout: "timeout expired."
80
82
  auth:
81
83
  invalid_password: "The provided password is incorrect."
82
84
  user_not_found: "A user with that email address was not found."
@@ -87,7 +89,7 @@ errors:
87
89
  no_user_identifier_api: "No user_identifier provided for API call."
88
90
  templates:
89
91
  company_not_found: "Company not found."
90
- home_template_not_found: "The home page template for the company '{company_name}' is not configured."
92
+ home_template_not_found: "The home page template for the company '{company_short_name}' is not configured."
91
93
  template_not_found: "Template not found: '{template_name}'."
92
94
 
93
95
  processing_error: "An error occurred while processing the custom home page template: {error}"
@@ -95,7 +97,7 @@ errors:
95
97
  unexpected_error: "An unexpected error has occurred. Please contact support."
96
98
  unsupported_language: "The selected language is not supported."
97
99
  signup:
98
- company_not_found: "The company {company_name} does not exist."
100
+ company_not_found: "The company {company_short_name} does not exist."
99
101
  incorrect_password_for_existing_user: "The password for the user {email} is incorrect."
100
102
  user_already_registered: "The user with email '{email}' already exists in this company."
101
103
  password_mismatch: "The passwords do not match. Please try again."
@@ -115,9 +117,29 @@ errors:
115
117
  password_no_digit: "Password must contain at least one number."
116
118
  password_no_special_char: "Password must contain at least one special character."
117
119
 
120
+ services:
121
+ no_text_file: "The file is not text or the encoding is not UTF-8"
122
+ no_output_file: "Missing output file name"
123
+ no_data_for_excel: "Missing data or it is not a list of dictionaries"
124
+ no_download_directory: "Temporary directory for saving Excel files is not configured"
125
+ cannot_create_excel: "Could not create the Excel file"
126
+ invalid_filename: "Invalid filename"
127
+ file_not_exist: "File not found"
128
+ path_is_not_a_file: "The path does not correspond to a file"
129
+ file_validation_error: "Error validating file"
130
+ user_not_authorized: "user is not authorized for this company"
131
+ account_not_verified: "Your account has not been verified. Please check your email."
132
+ missing_response_id: "Can not found 'previous_response_id' for '{company_short_name}/{user_identifier}'. Reinit context"
133
+
134
+
118
135
  api_responses:
119
136
  context_reloaded_success: "The context has been successfully reloaded."
120
137
 
138
+ services:
139
+ mail_sent: "Email sent successfully."
140
+ start_query: "Hello, what can I help you with today?"
141
+
142
+
121
143
  flash_messages:
122
144
  password_changed_success: "Your password has been successfully reset. You can now log in."
123
145
  login_required: "Please log in to continue."
@@ -142,3 +164,4 @@ js_messages:
142
164
  unknown_server_error: "Unknown server error."
143
165
  loading: "Loading..."
144
166
  reload_init: "init reloading context in background..."
167
+ no_history_found: "No query history found."
iatoolkit/locales/es.yaml CHANGED
@@ -73,6 +73,10 @@
73
73
  stop: "Detener"
74
74
 
75
75
  errors:
76
+ company_not_found: "La empresa {company_short_name} no existe."
77
+ timeout: "El tiempo de espera ha expirado."
78
+
79
+
76
80
  auth:
77
81
  invalid_password: "La contraseña proporcionada es incorrecta."
78
82
  user_not_found: "No se encontró un usuario con ese correo."
@@ -83,15 +87,15 @@
83
87
  no_user_identifier_api: "No se proporcionó user_identifier para la llamada a la API."
84
88
  templates:
85
89
  company_not_found: "Empresa no encontrada."
86
- home_template_not_found: "La plantilla de la página de inicio para la empresa '{company_name}' no está configurada."
90
+ home_template_not_found: "La plantilla de la página de inicio para la empresa '{company_short_name}' no está configurada."
91
+ processing_error: "Error al procesar el template: {error}"
87
92
  template_not_found: "No se encontro el template: '{template_name}'."
88
- processing_error: "Ocurrió un error al procesar la plantilla personalizada de la página de inicio: {error}"
89
93
 
90
94
  general:
91
- unexpected_error: "Ha ocurrido un error inesperado. Por favor, contacta a soporte."
95
+ unexpected_error: "Ha ocurrido un error inesperado: {error}."
92
96
  unsupported_language: "El idioma seleccionado no es válido."
93
97
  signup:
94
- company_not_found: "La empresa {company_name} no existe."
98
+ company_not_found: "La empresa {company_short_name} no existe."
95
99
  incorrect_password_for_existing_user: "La contraseña para el usuario {email} es incorrecta."
96
100
  user_already_registered: "El usuario con email '{email}' ya existe en esta empresa."
97
101
  password_mismatch: "Las contraseñas no coinciden. Por favor, inténtalo de nuevo."
@@ -111,9 +115,27 @@
111
115
  password_no_digit: "La contraseña debe tener al menos un número."
112
116
  password_no_special_char: "La contraseña debe tener al menos un carácter especial."
113
117
 
118
+ services:
119
+ no_text_file: "El archivo no es texto o la codificación no es UTF-8"
120
+ no_output_file: "falta el nombre del archivo de salida"
121
+ no_data_for_excel: "faltan los datos o no es una lista de diccionarios"
122
+ no_download_directory: "no esta configurado el directorio temporal para guardar excels"
123
+ cannot_create_excel: "no se pudo crear el archivo excel"
124
+ invalid_filename: "Nombre de archivo inválido"
125
+ file_not_exist : "Archivo no encontrado"
126
+ path_is_not_a_file : "La ruta no corresponde a un archivo"
127
+ file_validation_error : "Error validando archivo"
128
+ user_not_authorized: "Usuario no esta autorizado para esta empresa"
129
+ account_not_verified: "Tu cuenta no ha sido verificada. Por favor, revisa tu correo."
130
+ missing_response_id: "No se encontró 'previous_response_id' para '{company_short_name}/{user_identifier}'. Reinicia el contexto."
131
+
114
132
  api_responses:
115
133
  context_reloaded_success: "El contexto se ha recargado con éxito."
116
134
 
135
+ services:
136
+ mail_sent: "mail enviado exitosamente."
137
+ start_query: "Hola, cual es tu pregunta?"
138
+
117
139
  flash_messages:
118
140
  password_changed_success: "Tu contraseña ha sido restablecida exitosamente. Ahora puedes iniciar sesión."
119
141
  signup_success: "Registro exitoso. Por favor, revisa tu correo para verificar tu cuenta."
@@ -137,4 +159,5 @@
137
159
  unknown_server_error: "Error desconocido del servidor."
138
160
  loading: "Cargando..."
139
161
  reload_init: "Iniciando recarga de contexto en segundo plano..."
162
+ no_history_found: "No existe historial de consultas."
140
163
 
@@ -27,8 +27,8 @@ class DatabaseManager:
27
27
  self._engine = create_engine(
28
28
  database_url,
29
29
  echo=False,
30
- pool_size=2, # per worker
31
- max_overflow=3,
30
+ pool_size=10, # per worker
31
+ max_overflow=20,
32
32
  pool_timeout=30,
33
33
  pool_recycle=1800,
34
34
  pool_pre_ping=True,
@@ -70,6 +70,11 @@ class DatabaseManager:
70
70
  def remove_session(self):
71
71
  self.scoped_session.remove()
72
72
 
73
+ def get_all_table_names(self) -> list[str]:
74
+ # Returns a list of all table names in the database
75
+ inspector = inspect(self._engine)
76
+ return inspector.get_table_names()
77
+
73
78
  def get_table_schema(self,
74
79
  table_name: str,
75
80
  schema_name: str | None = None,
@@ -77,7 +82,7 @@ class DatabaseManager:
77
82
  inspector = inspect(self._engine)
78
83
 
79
84
  if table_name not in inspector.get_table_names():
80
- raise RuntimeError(f"La tabla '{table_name}' no existe en la BD.")
85
+ raise RuntimeError(f"Table '{table_name}' does not exist.")
81
86
 
82
87
  if exclude_columns is None:
83
88
  exclude_columns = []
@@ -22,7 +22,7 @@ class DocumentRepo:
22
22
  def get(self, company_id, filename: str ) -> Document:
23
23
  if not company_id or not filename:
24
24
  raise IAToolkitException(IAToolkitException.ErrorType.PARAM_NOT_FILLED,
25
- 'Falta empresa o filename')
25
+ 'missing company_id or filename')
26
26
 
27
27
  return self.session.query(Document).filter_by(company_id=company_id, filename=filename).first()
28
28
 
@@ -53,17 +53,12 @@ class Company(Base):
53
53
  id = Column(Integer, primary_key=True)
54
54
  short_name = Column(String(20), nullable=False, unique=True, index=True)
55
55
  name = Column(String(256), nullable=False)
56
- default_language = Column(String(5), nullable=False, default='es')
57
56
 
58
57
  # encrypted api-key
59
58
  openai_api_key = Column(String, nullable=True)
60
59
  gemini_api_key = Column(String, nullable=True)
61
-
62
- branding = Column(JSON, nullable=True)
63
- onboarding_cards = Column(JSON, nullable=True)
64
60
  parameters = Column(JSON, nullable=True)
65
61
  created_at = Column(DateTime, default=datetime.now)
66
- allow_jwt = Column(Boolean, default=True, nullable=True)
67
62
 
68
63
  documents = relationship("Document",
69
64
  back_populates="company",
@@ -161,10 +156,10 @@ class Document(Base):
161
156
  company_id = Column(Integer, ForeignKey('iat_companies.id',
162
157
  ondelete='CASCADE'), nullable=False)
163
158
  filename = Column(String(256), nullable=False, index=True)
164
- content = Column(Text, nullable=False)
165
- content_b64 = Column(Text, nullable=False)
166
159
  meta = Column(JSON, nullable=True)
167
160
  created_at = Column(DateTime, default=datetime.now)
161
+ content = Column(Text, nullable=False)
162
+ content_b64 = Column(Text, nullable=False)
168
163
 
169
164
  company = relationship("Company", back_populates="documents")
170
165
 
@@ -207,7 +202,10 @@ class VSDoc(Base):
207
202
  document_id = Column(Integer, ForeignKey('iat_documents.id',
208
203
  ondelete='CASCADE'), nullable=False)
209
204
  text = Column(Text, nullable=False)
210
- embedding = Column(Vector(384), nullable=False) # Ajusta la dimensión si es necesario
205
+
206
+ # the size of this vector should be set depending on the embedding model used
207
+ # for OpenAI is 1536, and for huggingface is 384
208
+ embedding = Column(Vector(1536), nullable=False)
211
209
 
212
210
  company = relationship("Company", back_populates="vsdocs")
213
211
 
@@ -74,10 +74,6 @@ class ProfileRepo:
74
74
  if company:
75
75
  if company.parameters != new_company.parameters:
76
76
  company.parameters = new_company.parameters
77
- if company.branding != new_company.branding:
78
- company.branding = new_company.branding
79
- if company.onboarding_cards != new_company.onboarding_cards:
80
- company.onboarding_cards = new_company.onboarding_cards
81
77
  else:
82
78
  # Si la compañía no existe, la añade a la sesión.
83
79
  self.session.add(new_company)