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
@@ -0,0 +1,213 @@
1
+ # Copyright (c) 2024 Fernando Libedinsky
2
+ # Product: IAToolkit
3
+ #
4
+ # IAToolkit is open source software.
5
+
6
+ from iatoolkit.services.configuration_service import ConfigurationService
7
+ from iatoolkit.services.i18n_service import I18nService
8
+ from iatoolkit.infra.brevo_mail_app import BrevoMailApp
9
+ from injector import inject
10
+ from pathlib import Path
11
+ import base64
12
+ import os
13
+ import smtplib
14
+ from email.message import EmailMessage
15
+ from iatoolkit.common.exceptions import IAToolkitException
16
+
17
+
18
+ TEMP_DIR = Path("static/temp")
19
+
20
+ class MailService:
21
+ @inject
22
+ def __init__(self,
23
+ config_service: ConfigurationService,
24
+ mail_app: BrevoMailApp,
25
+ i18n_service: I18nService,
26
+ brevo_mail_app: BrevoMailApp):
27
+ self.mail_app = mail_app
28
+ self.config_service = config_service
29
+ self.i18n_service = i18n_service
30
+ self.brevo_mail_app = brevo_mail_app
31
+
32
+
33
+ def send_mail(self, company_short_name: str, **kwargs):
34
+ recipient = kwargs.get('recipient')
35
+ subject = kwargs.get('subject')
36
+ body = kwargs.get('body')
37
+ attachments = kwargs.get('attachments')
38
+
39
+ # Normalizar a payload de BrevoMailApp (name + base64 content)
40
+ norm_attachments = []
41
+ for a in attachments or []:
42
+ if a.get("attachment_token"):
43
+ raw = self._read_token_bytes(a["attachment_token"])
44
+ norm_attachments.append({
45
+ "filename": a["filename"],
46
+ "content": base64.b64encode(raw).decode("utf-8"),
47
+ })
48
+ else:
49
+ # asumo que ya viene un base64
50
+ norm_attachments.append({
51
+ "filename": a["filename"],
52
+ "content": a["content"]
53
+ })
54
+
55
+ # build provider configuration from company.yaml
56
+ provider, provider_config = self._build_provider_config(company_short_name)
57
+
58
+ # define the email sender
59
+ sender = {
60
+ "email": provider_config.get("sender_email"),
61
+ "name": provider_config.get("sender_name"),
62
+ }
63
+
64
+ # select provider and send the email through it
65
+ if provider == "brevo_mail":
66
+ response = self.brevo_mail_app.send_email(
67
+ provider_config=provider_config,
68
+ sender=sender,
69
+ to=recipient,
70
+ subject=subject,
71
+ body=body,
72
+ attachments=norm_attachments
73
+ )
74
+ elif provider == "smtplib":
75
+ response = self._send_with_smtplib(
76
+ provider_config=provider_config,
77
+ sender=sender,
78
+ recipient=recipient,
79
+ subject=subject,
80
+ body=body,
81
+ attachments=norm_attachments,
82
+ )
83
+ response = None
84
+ else:
85
+ raise IAToolkitException(
86
+ IAToolkitException.ErrorType.MAIL_ERROR,
87
+ f"Unknown mail provider '{provider}'"
88
+ )
89
+
90
+ return self.i18n_service.t('services.mail_sent')
91
+
92
+ def _build_provider_config(self, company_short_name: str) -> tuple[str, dict]:
93
+ """
94
+ Determina el provider activo (brevo_mail / smtplib) y construye
95
+ el diccionario de configuración a partir de las variables de entorno
96
+ cuyos nombres están en company.yaml (mail_provider).
97
+ """
98
+ # get company mail configuration and provider
99
+ mail_config = self.config_service.get_configuration(company_short_name, "mail_provider")
100
+ provider = mail_config.get("provider", "brevo_mail")
101
+
102
+ # get mail common parameteres
103
+ sender_email = mail_config.get("sender_email")
104
+ sender_name = mail_config.get("sender_name")
105
+
106
+ # get parameters depending on provider
107
+ if provider == "brevo_mail":
108
+ brevo_cfg = mail_config.get("brevo_mail", {})
109
+ api_key_env = brevo_cfg.get("brevo_api", "BREVO_API_KEY")
110
+ return provider, {
111
+ "api_key": os.getenv(api_key_env),
112
+ "sender_name": sender_name,
113
+ "sender_email": sender_email,
114
+ }
115
+
116
+ if provider == "smtplib":
117
+ smtp_cfg = mail_config.get("smtplib", {})
118
+ host = os.getenv(smtp_cfg.get("host_env", "SMTP_HOST"))
119
+ port = os.getenv(smtp_cfg.get("port_env", "SMTP_PORT"))
120
+ username = os.getenv(smtp_cfg.get("username_env", "SMTP_USERNAME"))
121
+ password = os.getenv(smtp_cfg.get("password_env", "SMTP_PASSWORD"))
122
+ use_tls = os.getenv(smtp_cfg.get("use_tls_env", "SMTP_USE_TLS"))
123
+ use_ssl = os.getenv(smtp_cfg.get("use_ssl_env", "SMTP_USE_SSL"))
124
+
125
+ return provider, {
126
+ "host": host,
127
+ "port": int(port) if port is not None else None,
128
+ "username": username,
129
+ "password": password,
130
+ "use_tls": str(use_tls).lower() == "true",
131
+ "use_ssl": str(use_ssl).lower() == "true",
132
+ "sender_name": sender_name,
133
+ "sender_email": sender_email,
134
+ }
135
+
136
+ # Fallback simple si el provider no es reconocido
137
+ raise IAToolkitException(IAToolkitException.ErrorType.MAIL_ERROR,
138
+ f"missing mail provider in mail configuration for company '{company_short_name}'")
139
+
140
+ def _send_with_smtplib(self,
141
+ provider_config: dict,
142
+ sender: dict,
143
+ recipient: str,
144
+ subject: str,
145
+ body: str,
146
+ attachments: list[dict] | None):
147
+ """
148
+ Envía correo usando smtplib, utilizando la configuración normalizada
149
+ en provider_config.
150
+ """
151
+ host = provider_config.get("host")
152
+ port = provider_config.get("port")
153
+ username = provider_config.get("username")
154
+ password = provider_config.get("password")
155
+ use_tls = provider_config.get("use_tls")
156
+ use_ssl = provider_config.get("use_ssl")
157
+
158
+ if not host or not port:
159
+ raise IAToolkitException(
160
+ IAToolkitException.ErrorType.MAIL_ERROR,
161
+ "smtplib configuration is incomplete (host/port missing)"
162
+ )
163
+
164
+ msg = EmailMessage()
165
+ msg["From"] = f"{sender.get('name', '')} <{sender.get('email')}>"
166
+ msg["To"] = recipient
167
+ msg["Subject"] = subject
168
+ msg.set_content(body, subtype="html")
169
+
170
+ # Adjuntos: ya vienen como filename + base64 content
171
+ for a in attachments or []:
172
+ filename = a.get("filename")
173
+ content_b64 = a.get("content")
174
+ if not filename or not content_b64:
175
+ continue
176
+ try:
177
+ raw = base64.b64decode(content_b64, validate=True)
178
+ except Exception:
179
+ raise IAToolkitException(
180
+ IAToolkitException.ErrorType.MAIL_ERROR,
181
+ f"Invalid base64 for attachment '{filename}'"
182
+ )
183
+ msg.add_attachment(
184
+ raw,
185
+ maintype="application",
186
+ subtype="octet-stream",
187
+ filename=filename,
188
+ )
189
+
190
+ if use_ssl:
191
+ with smtplib.SMTP_SSL(host, port) as server:
192
+ if username and password:
193
+ server.login(username, password)
194
+ server.send_message(msg)
195
+ else:
196
+ with smtplib.SMTP(host, port) as server:
197
+ if use_tls:
198
+ server.starttls()
199
+ if username and password:
200
+ server.login(username, password)
201
+ server.send_message(msg)
202
+
203
+
204
+ def _read_token_bytes(self, token: str) -> bytes:
205
+ # Defensa simple contra path traversal
206
+ if not token or "/" in token or "\\" in token or token.startswith("."):
207
+ raise IAToolkitException(IAToolkitException.ErrorType.MAIL_ERROR,
208
+ "attachment_token invalid")
209
+ path = TEMP_DIR / token
210
+ if not path.is_file():
211
+ raise IAToolkitException(IAToolkitException.ErrorType.MAIL_ERROR,
212
+ f"attach file not found: {token}")
213
+ return path.read_bytes()