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.
- iatoolkit/__init__.py +27 -35
- iatoolkit/base_company.py +3 -35
- iatoolkit/cli_commands.py +18 -47
- iatoolkit/common/__init__.py +0 -0
- iatoolkit/common/exceptions.py +48 -0
- iatoolkit/common/interfaces/__init__.py +0 -0
- iatoolkit/common/interfaces/asset_storage.py +34 -0
- iatoolkit/common/interfaces/database_provider.py +39 -0
- iatoolkit/common/model_registry.py +159 -0
- iatoolkit/common/routes.py +138 -0
- iatoolkit/common/session_manager.py +26 -0
- iatoolkit/common/util.py +353 -0
- iatoolkit/company_registry.py +66 -29
- iatoolkit/core.py +514 -0
- iatoolkit/infra/__init__.py +5 -0
- iatoolkit/infra/brevo_mail_app.py +123 -0
- iatoolkit/infra/call_service.py +140 -0
- iatoolkit/infra/connectors/__init__.py +5 -0
- iatoolkit/infra/connectors/file_connector.py +17 -0
- iatoolkit/infra/connectors/file_connector_factory.py +57 -0
- iatoolkit/infra/connectors/google_cloud_storage_connector.py +53 -0
- iatoolkit/infra/connectors/google_drive_connector.py +68 -0
- iatoolkit/infra/connectors/local_file_connector.py +46 -0
- iatoolkit/infra/connectors/s3_connector.py +33 -0
- iatoolkit/infra/google_chat_app.py +57 -0
- iatoolkit/infra/llm_providers/__init__.py +0 -0
- iatoolkit/infra/llm_providers/deepseek_adapter.py +278 -0
- iatoolkit/infra/llm_providers/gemini_adapter.py +350 -0
- iatoolkit/infra/llm_providers/openai_adapter.py +124 -0
- iatoolkit/infra/llm_proxy.py +268 -0
- iatoolkit/infra/llm_response.py +45 -0
- iatoolkit/infra/redis_session_manager.py +122 -0
- iatoolkit/locales/en.yaml +222 -0
- iatoolkit/locales/es.yaml +225 -0
- iatoolkit/repositories/__init__.py +5 -0
- iatoolkit/repositories/database_manager.py +187 -0
- iatoolkit/repositories/document_repo.py +33 -0
- iatoolkit/repositories/filesystem_asset_repository.py +36 -0
- iatoolkit/repositories/llm_query_repo.py +105 -0
- iatoolkit/repositories/models.py +279 -0
- iatoolkit/repositories/profile_repo.py +171 -0
- iatoolkit/repositories/vs_repo.py +150 -0
- iatoolkit/services/__init__.py +5 -0
- iatoolkit/services/auth_service.py +193 -0
- {services → iatoolkit/services}/benchmark_service.py +7 -7
- iatoolkit/services/branding_service.py +153 -0
- iatoolkit/services/company_context_service.py +214 -0
- iatoolkit/services/configuration_service.py +375 -0
- iatoolkit/services/dispatcher_service.py +134 -0
- {services → iatoolkit/services}/document_service.py +20 -8
- iatoolkit/services/embedding_service.py +148 -0
- iatoolkit/services/excel_service.py +156 -0
- {services → iatoolkit/services}/file_processor_service.py +36 -21
- iatoolkit/services/history_manager_service.py +208 -0
- iatoolkit/services/i18n_service.py +104 -0
- iatoolkit/services/jwt_service.py +80 -0
- iatoolkit/services/language_service.py +89 -0
- iatoolkit/services/license_service.py +82 -0
- iatoolkit/services/llm_client_service.py +438 -0
- iatoolkit/services/load_documents_service.py +174 -0
- iatoolkit/services/mail_service.py +213 -0
- {services → iatoolkit/services}/profile_service.py +200 -101
- iatoolkit/services/prompt_service.py +303 -0
- iatoolkit/services/query_service.py +467 -0
- iatoolkit/services/search_service.py +55 -0
- iatoolkit/services/sql_service.py +169 -0
- iatoolkit/services/tool_service.py +246 -0
- iatoolkit/services/user_feedback_service.py +117 -0
- iatoolkit/services/user_session_context_service.py +213 -0
- iatoolkit/static/images/fernando.jpeg +0 -0
- iatoolkit/static/images/iatoolkit_core.png +0 -0
- iatoolkit/static/images/iatoolkit_logo.png +0 -0
- iatoolkit/static/js/chat_feedback_button.js +80 -0
- iatoolkit/static/js/chat_filepond.js +85 -0
- iatoolkit/static/js/chat_help_content.js +124 -0
- iatoolkit/static/js/chat_history_button.js +110 -0
- iatoolkit/static/js/chat_logout_button.js +36 -0
- iatoolkit/static/js/chat_main.js +401 -0
- iatoolkit/static/js/chat_model_selector.js +227 -0
- iatoolkit/static/js/chat_onboarding_button.js +103 -0
- iatoolkit/static/js/chat_prompt_manager.js +94 -0
- iatoolkit/static/js/chat_reload_button.js +38 -0
- iatoolkit/static/styles/chat_iatoolkit.css +559 -0
- iatoolkit/static/styles/chat_modal.css +133 -0
- iatoolkit/static/styles/chat_public.css +135 -0
- iatoolkit/static/styles/documents.css +598 -0
- iatoolkit/static/styles/landing_page.css +398 -0
- iatoolkit/static/styles/llm_output.css +148 -0
- iatoolkit/static/styles/onboarding.css +176 -0
- iatoolkit/system_prompts/__init__.py +0 -0
- iatoolkit/system_prompts/query_main.prompt +30 -23
- iatoolkit/system_prompts/sql_rules.prompt +47 -12
- iatoolkit/templates/_company_header.html +45 -0
- iatoolkit/templates/_login_widget.html +42 -0
- iatoolkit/templates/base.html +78 -0
- iatoolkit/templates/change_password.html +66 -0
- iatoolkit/templates/chat.html +337 -0
- iatoolkit/templates/chat_modals.html +185 -0
- iatoolkit/templates/error.html +51 -0
- iatoolkit/templates/forgot_password.html +51 -0
- iatoolkit/templates/onboarding_shell.html +106 -0
- iatoolkit/templates/signup.html +79 -0
- iatoolkit/views/__init__.py +5 -0
- iatoolkit/views/base_login_view.py +96 -0
- iatoolkit/views/change_password_view.py +116 -0
- iatoolkit/views/chat_view.py +76 -0
- iatoolkit/views/embedding_api_view.py +65 -0
- iatoolkit/views/forgot_password_view.py +75 -0
- iatoolkit/views/help_content_api_view.py +54 -0
- iatoolkit/views/history_api_view.py +56 -0
- iatoolkit/views/home_view.py +63 -0
- iatoolkit/views/init_context_api_view.py +74 -0
- iatoolkit/views/llmquery_api_view.py +59 -0
- iatoolkit/views/load_company_configuration_api_view.py +49 -0
- iatoolkit/views/load_document_api_view.py +65 -0
- iatoolkit/views/login_view.py +170 -0
- iatoolkit/views/logout_api_view.py +57 -0
- iatoolkit/views/profile_api_view.py +46 -0
- iatoolkit/views/prompt_api_view.py +37 -0
- iatoolkit/views/root_redirect_view.py +22 -0
- iatoolkit/views/signup_view.py +100 -0
- iatoolkit/views/static_page_view.py +27 -0
- iatoolkit/views/user_feedback_api_view.py +60 -0
- iatoolkit/views/users_api_view.py +33 -0
- iatoolkit/views/verify_user_view.py +60 -0
- iatoolkit-0.107.4.dist-info/METADATA +268 -0
- iatoolkit-0.107.4.dist-info/RECORD +132 -0
- iatoolkit-0.107.4.dist-info/licenses/LICENSE +21 -0
- iatoolkit-0.107.4.dist-info/licenses/LICENSE_COMMUNITY.md +15 -0
- {iatoolkit-0.3.9.dist-info → iatoolkit-0.107.4.dist-info}/top_level.txt +0 -1
- iatoolkit/iatoolkit.py +0 -413
- iatoolkit/system_prompts/arquitectura.prompt +0 -32
- iatoolkit-0.3.9.dist-info/METADATA +0 -252
- iatoolkit-0.3.9.dist-info/RECORD +0 -32
- services/__init__.py +0 -5
- services/api_service.py +0 -75
- services/dispatcher_service.py +0 -351
- services/excel_service.py +0 -98
- services/history_service.py +0 -45
- services/jwt_service.py +0 -91
- services/load_documents_service.py +0 -212
- services/mail_service.py +0 -62
- services/prompt_manager_service.py +0 -172
- services/query_service.py +0 -334
- services/search_service.py +0 -32
- services/sql_service.py +0 -42
- services/tasks_service.py +0 -188
- services/user_feedback_service.py +0 -67
- services/user_session_context_service.py +0 -85
- {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()
|