iatoolkit 0.3.6__py3-none-any.whl → 0.3.8__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 0.3.6
3
+ Version: 0.3.8
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
@@ -1,27 +1,31 @@
1
1
  iatoolkit/__init__.py,sha256=JdcXIcvFFTMy2XI9ccEws_pCklilDkPPEb7RaQNf7oY,1444
2
- iatoolkit/base_company.py,sha256=VWfpNofFlmgHQQK8BCw2KSOPLd2DM9eA68lGQ3SDE7M,1639
2
+ iatoolkit/base_company.py,sha256=gIFaMbQcZ5RAegfNxpjhFLacnOtCvB3sBiXpMtjfM1w,1859
3
3
  iatoolkit/company_registry.py,sha256=cRaez-VBo1icnUNKmkQqo_Xlr8UKWFoYEMZ70XP6Jgk,2702
4
- iatoolkit/iatoolkit.py,sha256=K7d8fKKTk-Q-sMW6Ip58oleiosRP4e-pdc3E5GSoSMw,16450
4
+ iatoolkit/iatoolkit.py,sha256=OKGh5bYQAlyBn8_iI5UKgVykD9V0lW9x4wZE5rCSp5A,18054
5
+ iatoolkit/system_prompts/arquitectura.prompt,sha256=2W-7NWy6P6y1Gh5_-zD1iK-BWq1Siu8TuvGCouP67bQ,1267
6
+ iatoolkit/system_prompts/format_styles.prompt,sha256=MSMe1qvR3cF_0IbFshn8R0z6Wx6VCHQq1p37rpu5wwk,3576
7
+ iatoolkit/system_prompts/query_main.prompt,sha256=Eu5VOQzUygJ45Ct1WKYGbi0JMltgI6FQIZWlGmN1bdk,3214
8
+ iatoolkit/system_prompts/sql_rules.prompt,sha256=y4nURVnb9AyFwt-lrbMNBHHtZlhk6kC9grYoOhRnrJo,59174
5
9
  services/__init__.py,sha256=fSvSfIcPW1dHwTBY1hQ5dBEhaoorzk_GzR4G46gD8tY,173
6
10
  services/api_service.py,sha256=InIKTc64BWcp4U4tYKHz28x4ErPxIfvR9x3ZlxJZlXs,2911
7
11
  services/benchmark_service.py,sha256=g9JVrmAqIe_iI0D1DwdQ6DJ2_FJRCTndarESNSVfhbw,5907
8
- services/dispatcher_service.py,sha256=CBslIE5FsrST46JjpKW1k_XHnIRtN9WehEr6LKadZlU,13360
12
+ services/dispatcher_service.py,sha256=wWIw9QwyNZ6yaDbANkNdAkdp-f_EAHbJFegjWDcxTAM,14781
9
13
  services/document_service.py,sha256=sm5QtbrKs2dF9hpLuSLMB-IMWYNBD7yWHv3rd80aD0o,5960
10
14
  services/excel_service.py,sha256=wE9Udbyb96kGRSnZZ6KM2mbE484rKjTEhta9GKKpy-8,3630
11
15
  services/file_processor_service.py,sha256=82UArWtwpr94CAMkkoRP0_nPtoqItymdKSIABS0Xkxw,2943
12
- services/history_service.py,sha256=dl-D7qgdnzpY9QhjuxJokBYZZ1AF0y59HbRzwpPet58,1654
16
+ services/history_service.py,sha256=6fGSSWxy60nxtkwp_fodwDHoVKhpIUbHnzAzUSiNi-Y,1657
13
17
  services/jwt_service.py,sha256=dC45Sn6FyzdzRiQJnzgkjN3Hy21V1imRxB0hTyWRvlA,3979
14
18
  services/load_documents_service.py,sha256=_-OTUih8Zk0m4dHqAhkE7kAwU2mbz_QoMrOKnrq7ZWs,8821
15
19
  services/mail_service.py,sha256=ystFit1LuYUC4ekYYebyiy1rqYQmxeL6K8h58MxEkOY,2233
16
- services/profile_service.py,sha256=YtnlXofXtvud4AHOFMmPlX9VO7mhs_Fglpc1PTulExc,17861
17
- services/prompt_manager_service.py,sha256=kKsqZyt2ZUWIHYTA5C6sfBk8sbXhvYF4QAUN9sOYk_s,7915
18
- services/query_service.py,sha256=Fx_P1WcuoAp_TyocN5LGlv_hc_03ImzU5QdAIqQg0ek,15591
20
+ services/profile_service.py,sha256=vZV0cregZQiPKYcNLaD7xjez2y6-3Mq97cDndC8NL8w,17922
21
+ services/prompt_manager_service.py,sha256=g499zeWZODqoDvqQZX6eHWmsWj7oLWkEhge7UV_y8IE,7679
22
+ services/query_service.py,sha256=zpaDzjzh2HqAG1F2Ap8WHBpfAMVtzZ_6v1JGVEguwvs,15687
19
23
  services/search_service.py,sha256=oJD6WRXCJBD7WUVHWWKxexRkhR8nQSrFtcPV3pFO2KQ,1153
20
24
  services/sql_service.py,sha256=H7CIPpXTcxLXLojD2fBFr_mIAD0PW1vEJhKHLfJi4Hk,1418
21
25
  services/tasks_service.py,sha256=hHJDlcsSOPtEleD6_Vv3pocfxWNmthIhmZSdnoWFpEM,6861
22
26
  services/user_feedback_service.py,sha256=YtCndRBekDEWYEbac431Ksn2gMO5iBrI3WqKK0xtShE,2513
23
27
  services/user_session_context_service.py,sha256=5qn7fqpuiU8KgMpU4M5-iRUsETumz1raBw-EeZLuE1A,3868
24
- iatoolkit-0.3.6.dist-info/METADATA,sha256=njGTmLJWG0JmIXWV2d50TDtYlHIhGL7lS-6wgELu58Q,8801
25
- iatoolkit-0.3.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
- iatoolkit-0.3.6.dist-info/top_level.txt,sha256=dqlBbmgo9okD9d_WMR9uYzdup7Rxgj26yFF85jRGeu4,19
27
- iatoolkit-0.3.6.dist-info/RECORD,,
28
+ iatoolkit-0.3.8.dist-info/METADATA,sha256=ojUeydEjMKJIbnIpXnuYhgzdN3Q0PcWnDf_MbBcojtY,8801
29
+ iatoolkit-0.3.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
+ iatoolkit-0.3.8.dist-info/top_level.txt,sha256=dqlBbmgo9okD9d_WMR9uYzdup7Rxgj26yFF85jRGeu4,19
31
+ iatoolkit-0.3.8.dist-info/RECORD,,
@@ -12,6 +12,7 @@ from repositories.models import Company, Function
12
12
  from services.excel_service import ExcelService
13
13
  from services.mail_service import MailService
14
14
  from iatoolkit.company_registry import get_company_registry
15
+ from common.session_manager import SessionManager
15
16
  from common.util import Utility
16
17
  from injector import inject
17
18
  import logging
@@ -98,10 +99,9 @@ class Dispatcher:
98
99
  )
99
100
  i += 1
100
101
 
101
- # initialize the database for every company class
102
+ # register in the database every company class
102
103
  for company in self.company_classes.values():
103
- print(f'company: {company.__class__.__name__}')
104
- company.init_db()
104
+ company.register_company()
105
105
 
106
106
  def dispatch(self, company_name: str, action: str, **kwargs) -> str:
107
107
  company_key = company_name.lower()
@@ -179,19 +179,51 @@ class Dispatcher:
179
179
  tools.append(ai_tool)
180
180
  return tools
181
181
 
182
- def get_user_info(self, company_name: str, **kwargs) -> dict:
182
+ def get_user_info(self, company_name: str, user_identifier: str, is_local_user: bool) -> dict:
183
183
  if company_name not in self.company_classes:
184
184
  raise IAToolkitException(IAToolkitException.ErrorType.EXTERNAL_SOURCE_ERROR,
185
- f"Empresa no configurada: {company_name}")
185
+ f"Empresa no configurada: {company_name}")
186
+
187
+ raw_user_data = {}
188
+ if is_local_user:
189
+ # source 1: local user login into IAToolkit
190
+ raw_user_data = SessionManager.get('user', {})
191
+ else:
192
+ # source 2: external company user
193
+ company_instance = self.company_classes[company_name]
194
+ try:
195
+ raw_user_data = company_instance.get_user_info(user_identifier)
196
+ except Exception as e:
197
+ logging.exception(e)
198
+ raise IAToolkitException(IAToolkitException.ErrorType.EXTERNAL_SOURCE_ERROR,
199
+ f"Error en get_user_info de {company_name}: {str(e)}") from e
200
+
201
+ # always normalize the data for consistent structure
202
+ return self._normalize_user_data(raw_user_data, is_local_user)
203
+
204
+ def _normalize_user_data(self, raw_data: dict, is_local: bool) -> dict:
205
+ """
206
+ Asegura que los datos del usuario siempre tengan una estructura consistente.
207
+ """
208
+ # Valores por defecto para un perfil robusto
209
+ normalized_user = {
210
+ "id": raw_data.get("id", 0),
211
+ "user_email": raw_data.get("email", ""),
212
+ "user_fullname": raw_data.get("user_fullname", ""),
213
+ "super_user": raw_data.get("super_user", False),
214
+ "company_id": raw_data.get("company_id", 0),
215
+ "company_name": raw_data.get("company", ""),
216
+ "company_short_name": raw_data.get("company_short_name", ""),
217
+ "is_local": is_local,
218
+ "extras": raw_data.get("extras", {})
219
+ }
186
220
 
187
- company_instance = self.company_classes[company_name]
188
- try:
189
- return company_instance.get_user_info(**kwargs)
190
- except Exception as e:
191
- logging.exception(e)
192
- raise IAToolkitException(IAToolkitException.ErrorType.EXTERNAL_SOURCE_ERROR,
193
- f"Error en get_user_info de {company_name}: {str(e)}") from e
221
+ # get the extras from the raw data, if any
222
+ extras = raw_data.get("extras", {})
223
+ if isinstance(extras, dict):
224
+ normalized_user.update(extras)
194
225
 
226
+ return normalized_user
195
227
 
196
228
  def get_metadata_from_filename(self, company_name: str, filename: str) -> dict:
197
229
  if company_name not in self.company_classes:
@@ -23,7 +23,7 @@ class HistoryService:
23
23
  external_user_id: str = None,
24
24
  local_user_id: int = 0) -> dict:
25
25
  try:
26
- user_identifier = self.util.resolve_user_identifier(external_user_id, local_user_id)
26
+ user_identifier, _ = self.util.resolve_user_identifier(external_user_id, local_user_id)
27
27
  if not user_identifier:
28
28
  return {'error': "No se pudo resolver el identificador del usuario"}
29
29
 
@@ -79,13 +79,13 @@ class ProfileService:
79
79
  user_data = {
80
80
  "id": user.id,
81
81
  "email": user.email,
82
- "first_name": user.first_name,
83
- "last_name": user.last_name,
82
+ "user_fullname": f'{user.first_name} {user.last_name}',
84
83
  "super_user": user.super_user,
85
84
  "company_id": company.id,
86
85
  "company": company.name,
87
86
  "company_short_name": company.short_name,
88
- "company_logo": company.logo_file
87
+ "user_is_local": True, # origin of data
88
+ "extras": {} # company specific data
89
89
  }
90
90
  SessionManager.set('user', user_data)
91
91
 
@@ -12,6 +12,7 @@ from repositories.models import Prompt, PromptCategory, Company
12
12
  import os
13
13
  from common.exceptions import IAToolkitException
14
14
  from pathlib import Path
15
+ import importlib.resources
15
16
 
16
17
 
17
18
  class PromptService:
@@ -33,18 +34,16 @@ class PromptService:
33
34
 
34
35
  prompt_filename = prompt_name.lower() + '.prompt'
35
36
  if is_system_prompt:
36
- template_dir = 'src/system_prompts'
37
+ if not importlib.resources.files('iatoolkit.system_prompts').joinpath(prompt_filename).is_file():
38
+ raise IAToolkitException(IAToolkitException.ErrorType.INVALID_NAME,
39
+ f'No existe el archivo de prompt de sistemas: {prompt_filename}')
37
40
  else:
38
41
  template_dir = f'companies/{company.short_name}/prompts'
39
42
 
40
- # Guardar el filepath como una ruta relativa
41
- relative_prompt_path = os.path.join(template_dir, prompt_filename)
42
-
43
- # Validar la existencia del archivo usando la ruta absoluta
44
- absolute_prompt_path = os.path.join(os.getcwd(), relative_prompt_path)
45
- if not os.path.exists(absolute_prompt_path):
46
- raise IAToolkitException(IAToolkitException.ErrorType.INVALID_NAME,
47
- f'No existe el archivo de prompt: {absolute_prompt_path}')
43
+ relative_prompt_path = os.path.join(template_dir, prompt_filename)
44
+ if not os.path.exists(relative_prompt_path):
45
+ raise IAToolkitException(IAToolkitException.ErrorType.INVALID_NAME,
46
+ f'No existe el archivo de prompt: {relative_prompt_path}')
48
47
 
49
48
  prompt = Prompt(
50
49
  company_id=company.id if company else None,
@@ -53,7 +52,7 @@ class PromptService:
53
52
  order=order,
54
53
  category_id=category.id if category and not is_system_prompt else None,
55
54
  active=active,
56
- filepath=relative_prompt_path,
55
+ filename=prompt_filename,
57
56
  is_system_prompt=is_system_prompt,
58
57
  parameters=params
59
58
  )
@@ -75,7 +74,7 @@ class PromptService:
75
74
  raise IAToolkitException(IAToolkitException.ErrorType.DOCUMENT_NOT_FOUND,
76
75
  f"No se encontró el prompt '{prompt_name}' para la empresa '{company.short_name}'")
77
76
 
78
- absolute_filepath = os.path.join(execution_dir, user_prompt.filepath)
77
+ absolute_filepath = os.path.join(execution_dir, user_prompt.filename)
79
78
  if not os.path.exists(absolute_filepath):
80
79
  raise IAToolkitException(IAToolkitException.ErrorType.FILE_IO_ERROR,
81
80
  f"El archivo para el prompt '{prompt_name}' no existe: {absolute_filepath}")
@@ -103,25 +102,18 @@ class PromptService:
103
102
  try:
104
103
  system_prompt_content = []
105
104
 
106
- # get the filepaths for all system prompts
107
- current_dir = Path(__file__).resolve().parent
108
- project_root = current_dir.parent.parent
109
-
110
105
  # read all the system prompts from the database
111
106
  system_prompts = self.llm_query_repo.get_system_prompts()
112
107
 
113
108
  for prompt in system_prompts:
114
- # build the absolute filepath for reading it
115
- absolute_filepath = os.path.join(project_root, prompt.filepath)
116
- if not os.path.exists(absolute_filepath):
117
- logging.warning(f"El archivo para el prompt de sistema no existe: {absolute_filepath}")
118
- continue
119
109
  try:
120
- with open(absolute_filepath, 'r', encoding='utf-8') as f:
121
- system_prompt_content.append(f.read())
110
+ content = importlib.resources.read_text('iatoolkit.system_prompts', prompt.filename)
111
+ system_prompt_content.append(content)
112
+ except FileNotFoundError:
113
+ logging.warning(f"El archivo para el prompt de sistema no existe en el paquete: {prompt.filename}")
122
114
  except Exception as e:
123
115
  raise IAToolkitException(IAToolkitException.ErrorType.FILE_IO_ERROR,
124
- f"Error leyendo el archivo de prompt del sistema {absolute_filepath}: {e}")
116
+ f"Error leyendo el archivo de prompt del sistema '{prompt.filename}': {e}")
125
117
 
126
118
  # join the system prompts into a single string
127
119
  return "\n".join(system_prompt_content)
services/query_service.py CHANGED
@@ -65,7 +65,7 @@ class QueryService:
65
65
  model = self.model
66
66
 
67
67
  # Validate the user and company
68
- user_identifier = self.util.resolve_user_identifier(external_user_id, local_user_id)
68
+ user_identifier, is_local_user = self.util.resolve_user_identifier(external_user_id, local_user_id)
69
69
  if not user_identifier:
70
70
  raise IAToolkitException(IAToolkitException.ErrorType.INVALID_USER,
71
71
  "No se pudo resolver el identificador del usuario")
@@ -85,23 +85,25 @@ class QueryService:
85
85
 
86
86
  # 2. get dictionary with user information from company DB
87
87
  # user roles are read at this point from company db
88
- user_info = self.dispatcher.get_user_info(
88
+ user_profile = self.dispatcher.get_user_info(
89
89
  company_name=company_short_name,
90
- user_id=user_identifier
90
+ user_identifier=user_identifier,
91
+ is_local_user=is_local_user
91
92
  )
93
+
92
94
  # add the user logged in to the user_info dictionary
93
- user_info['user_id'] = user_identifier
95
+ user_profile['user_id'] = user_identifier
94
96
 
95
97
  # save the user information in the session context
96
98
  # it's needed for the jinja predefined prompts (filtering)
97
- self.session_context.save_user_session_data(company_short_name, user_identifier, user_info)
99
+ self.session_context.save_user_session_data(company_short_name, user_identifier, user_profile)
98
100
 
99
101
  # 3. render the iatoolkit main system prompt with the company/user information
100
102
  system_prompt_template = self.prompt_service.get_system_prompt()
101
103
  rendered_system_prompt = self.util.render_prompt_from_string(
102
104
  template_string=system_prompt_template,
103
105
  question=None,
104
- client_data=user_info,
106
+ client_data=user_profile,
105
107
  company=company,
106
108
  service_list=self.dispatcher.get_company_services(company)
107
109
  )
@@ -148,7 +150,7 @@ class QueryService:
148
150
  client_data: dict = {},
149
151
  files: list = []) -> dict:
150
152
  try:
151
- user_identifier = self.util.resolve_user_identifier(external_user_id, local_user_id)
153
+ user_identifier, is_local_user = self.util.resolve_user_identifier(external_user_id, local_user_id)
152
154
  if not user_identifier:
153
155
  return {"error": True,
154
156
  "error_message": "No se pudo identificar al usuario"}