iatoolkit 0.91.1__py3-none-any.whl → 1.7.0__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.
- iatoolkit/__init__.py +6 -4
- iatoolkit/base_company.py +0 -16
- iatoolkit/cli_commands.py +3 -14
- iatoolkit/common/exceptions.py +1 -0
- iatoolkit/common/interfaces/__init__.py +0 -0
- iatoolkit/common/interfaces/asset_storage.py +34 -0
- iatoolkit/common/interfaces/database_provider.py +43 -0
- iatoolkit/common/model_registry.py +159 -0
- iatoolkit/common/routes.py +47 -5
- iatoolkit/common/util.py +32 -13
- iatoolkit/company_registry.py +5 -0
- iatoolkit/core.py +51 -20
- iatoolkit/infra/connectors/file_connector_factory.py +1 -0
- iatoolkit/infra/connectors/s3_connector.py +4 -2
- iatoolkit/infra/llm_providers/__init__.py +0 -0
- iatoolkit/infra/llm_providers/deepseek_adapter.py +278 -0
- iatoolkit/infra/{gemini_adapter.py → llm_providers/gemini_adapter.py} +11 -17
- iatoolkit/infra/{openai_adapter.py → llm_providers/openai_adapter.py} +41 -7
- iatoolkit/infra/llm_proxy.py +235 -134
- iatoolkit/infra/llm_response.py +5 -0
- iatoolkit/locales/en.yaml +158 -2
- iatoolkit/locales/es.yaml +158 -0
- iatoolkit/repositories/database_manager.py +52 -47
- iatoolkit/repositories/document_repo.py +7 -0
- iatoolkit/repositories/filesystem_asset_repository.py +36 -0
- iatoolkit/repositories/llm_query_repo.py +2 -0
- iatoolkit/repositories/models.py +72 -79
- iatoolkit/repositories/profile_repo.py +59 -3
- iatoolkit/repositories/vs_repo.py +22 -24
- iatoolkit/services/company_context_service.py +126 -53
- iatoolkit/services/configuration_service.py +299 -73
- iatoolkit/services/dispatcher_service.py +21 -3
- iatoolkit/services/file_processor_service.py +0 -5
- iatoolkit/services/history_manager_service.py +43 -24
- iatoolkit/services/knowledge_base_service.py +425 -0
- iatoolkit/{infra/llm_client.py → services/llm_client_service.py} +38 -29
- iatoolkit/services/load_documents_service.py +26 -48
- iatoolkit/services/profile_service.py +32 -4
- iatoolkit/services/prompt_service.py +32 -30
- iatoolkit/services/query_service.py +51 -26
- iatoolkit/services/sql_service.py +122 -74
- iatoolkit/services/tool_service.py +26 -11
- iatoolkit/services/user_session_context_service.py +115 -63
- iatoolkit/static/js/chat_main.js +44 -4
- iatoolkit/static/js/chat_model_selector.js +227 -0
- iatoolkit/static/js/chat_onboarding_button.js +1 -1
- iatoolkit/static/js/chat_reload_button.js +4 -1
- iatoolkit/static/styles/chat_iatoolkit.css +58 -2
- iatoolkit/static/styles/llm_output.css +34 -1
- iatoolkit/system_prompts/query_main.prompt +26 -2
- iatoolkit/templates/base.html +13 -0
- iatoolkit/templates/chat.html +45 -2
- iatoolkit/templates/onboarding_shell.html +0 -1
- iatoolkit/views/base_login_view.py +7 -2
- iatoolkit/views/chat_view.py +76 -0
- iatoolkit/views/configuration_api_view.py +163 -0
- iatoolkit/views/load_document_api_view.py +14 -10
- iatoolkit/views/login_view.py +8 -3
- iatoolkit/views/rag_api_view.py +216 -0
- iatoolkit/views/users_api_view.py +33 -0
- {iatoolkit-0.91.1.dist-info → iatoolkit-1.7.0.dist-info}/METADATA +4 -4
- {iatoolkit-0.91.1.dist-info → iatoolkit-1.7.0.dist-info}/RECORD +66 -58
- iatoolkit/repositories/tasks_repo.py +0 -52
- iatoolkit/services/search_service.py +0 -55
- iatoolkit/services/tasks_service.py +0 -188
- iatoolkit/views/tasks_api_view.py +0 -72
- iatoolkit/views/tasks_review_api_view.py +0 -55
- {iatoolkit-0.91.1.dist-info → iatoolkit-1.7.0.dist-info}/WHEEL +0 -0
- {iatoolkit-0.91.1.dist-info → iatoolkit-1.7.0.dist-info}/licenses/LICENSE +0 -0
- {iatoolkit-0.91.1.dist-info → iatoolkit-1.7.0.dist-info}/licenses/LICENSE_COMMUNITY.md +0 -0
- {iatoolkit-0.91.1.dist-info → iatoolkit-1.7.0.dist-info}/top_level.txt +0 -0
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
# Product: IAToolkit
|
|
3
|
-
#
|
|
4
|
-
# IAToolkit is open source software.
|
|
5
|
-
|
|
6
|
-
from injector import inject
|
|
7
|
-
from iatoolkit.repositories.models import Task, TaskStatus
|
|
8
|
-
from iatoolkit.services.query_service import QueryService
|
|
9
|
-
from iatoolkit.repositories.tasks_repo import TaskRepo
|
|
10
|
-
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
11
|
-
from iatoolkit.infra.call_service import CallServiceClient
|
|
12
|
-
from iatoolkit.common.exceptions import IAToolkitException
|
|
13
|
-
from datetime import datetime
|
|
14
|
-
from werkzeug.utils import secure_filename
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class TaskService:
|
|
18
|
-
@inject
|
|
19
|
-
def __init__(self,
|
|
20
|
-
task_repo: TaskRepo,
|
|
21
|
-
query_service: QueryService,
|
|
22
|
-
profile_repo: ProfileRepo,
|
|
23
|
-
call_service: CallServiceClient):
|
|
24
|
-
self.task_repo = task_repo
|
|
25
|
-
self.query_service = query_service
|
|
26
|
-
self.profile_repo = profile_repo
|
|
27
|
-
self.call_service = call_service
|
|
28
|
-
|
|
29
|
-
def create_task(self,
|
|
30
|
-
company_short_name: str,
|
|
31
|
-
task_type_name: str,
|
|
32
|
-
client_data: dict,
|
|
33
|
-
company_task_id: int= 0,
|
|
34
|
-
execute_at: datetime = None,
|
|
35
|
-
files: list = []
|
|
36
|
-
) -> Task:
|
|
37
|
-
|
|
38
|
-
# validate company
|
|
39
|
-
company = self.profile_repo.get_company_by_short_name(company_short_name)
|
|
40
|
-
if not company:
|
|
41
|
-
raise IAToolkitException(IAToolkitException.ErrorType.INVALID_NAME,
|
|
42
|
-
f'No existe la empresa: {company_short_name}')
|
|
43
|
-
|
|
44
|
-
# validate task_type
|
|
45
|
-
task_type = self.task_repo.get_task_type(task_type_name)
|
|
46
|
-
if not task_type:
|
|
47
|
-
raise IAToolkitException(IAToolkitException.ErrorType.INVALID_NAME,
|
|
48
|
-
f'No existe el task_type: {task_type_name}')
|
|
49
|
-
|
|
50
|
-
# process the task files
|
|
51
|
-
task_files = self.get_task_files(files)
|
|
52
|
-
|
|
53
|
-
# create Task object
|
|
54
|
-
new_task = Task(
|
|
55
|
-
company_id=company.id,
|
|
56
|
-
task_type_id=task_type.id,
|
|
57
|
-
company_task_id=company_task_id,
|
|
58
|
-
client_data=client_data,
|
|
59
|
-
execute_at=execute_at,
|
|
60
|
-
files=task_files
|
|
61
|
-
)
|
|
62
|
-
new_task = self.task_repo.create_task(new_task)
|
|
63
|
-
if execute_at and execute_at > datetime.now():
|
|
64
|
-
self.execute_task(new_task)
|
|
65
|
-
|
|
66
|
-
return new_task
|
|
67
|
-
|
|
68
|
-
def review_task(self, task_id: int, review_user: str, approved: bool, comment: str):
|
|
69
|
-
# get the task
|
|
70
|
-
task = self.task_repo.get_task_by_id(task_id)
|
|
71
|
-
if not task:
|
|
72
|
-
raise IAToolkitException(IAToolkitException.ErrorType.TASK_NOT_FOUND,
|
|
73
|
-
f'No existe la tarea: {task_id}')
|
|
74
|
-
|
|
75
|
-
if task.status != TaskStatus.ejecutado:
|
|
76
|
-
raise IAToolkitException(IAToolkitException.ErrorType.INVALID_STATE,
|
|
77
|
-
f'La tarea debe estar en estado ejecutada: {task_id}')
|
|
78
|
-
|
|
79
|
-
# update the task
|
|
80
|
-
task.approved = approved
|
|
81
|
-
task.status = TaskStatus.aprobada if approved else TaskStatus.rechazada
|
|
82
|
-
task.review_user = review_user
|
|
83
|
-
task.comment = comment
|
|
84
|
-
task.review_date = datetime.now()
|
|
85
|
-
self.task_repo.update_task(task)
|
|
86
|
-
return task
|
|
87
|
-
|
|
88
|
-
def execute_task(self, task: Task):
|
|
89
|
-
# in this case do nothing
|
|
90
|
-
if (task.status != TaskStatus.pendiente or
|
|
91
|
-
(task.execute_at and task.execute_at > datetime.now())):
|
|
92
|
-
return task
|
|
93
|
-
|
|
94
|
-
# get the Task template prompt
|
|
95
|
-
if not task.task_type.prompt_template:
|
|
96
|
-
raise IAToolkitException(IAToolkitException.ErrorType.INVALID_NAME,
|
|
97
|
-
f'No existe el prompt_template para el task_type: {task.task_type.name}')
|
|
98
|
-
|
|
99
|
-
template_dir = f'companies/{task.company.short_name}/prompts'
|
|
100
|
-
|
|
101
|
-
# call the IA
|
|
102
|
-
response = self.query_service.llm_query(
|
|
103
|
-
task=task,
|
|
104
|
-
user_identifier='task-monitor',
|
|
105
|
-
company_short_name=task.company.short_name,
|
|
106
|
-
prompt_name=task.task_type.name,
|
|
107
|
-
client_data=task.client_data,
|
|
108
|
-
files=task.files
|
|
109
|
-
)
|
|
110
|
-
if 'error' in response:
|
|
111
|
-
raise IAToolkitException(IAToolkitException.ErrorType.LLM_ERROR,
|
|
112
|
-
response.get('error'))
|
|
113
|
-
|
|
114
|
-
# update the Task with the response from llm_query
|
|
115
|
-
task.llm_query_id = response.get('query_id', 0)
|
|
116
|
-
|
|
117
|
-
# update task status
|
|
118
|
-
if not response.get('valid_response'):
|
|
119
|
-
task.status = TaskStatus.fallida
|
|
120
|
-
else:
|
|
121
|
-
task.status = TaskStatus.ejecutado
|
|
122
|
-
self.task_repo.update_task(task)
|
|
123
|
-
|
|
124
|
-
# call the callback url
|
|
125
|
-
if task.callback_url:
|
|
126
|
-
self.notify_callback(task, response)
|
|
127
|
-
|
|
128
|
-
return task
|
|
129
|
-
|
|
130
|
-
def notify_callback(self, task: Task, response: dict):
|
|
131
|
-
response_data = {
|
|
132
|
-
'task_id': task.id,
|
|
133
|
-
'company_task_id': task.company_task_id,
|
|
134
|
-
'status': task.status.name,
|
|
135
|
-
'answer': response.get('answer', ''),
|
|
136
|
-
'additional_data': response.get('additional_data', {}),
|
|
137
|
-
'client_data': task.client_data,
|
|
138
|
-
}
|
|
139
|
-
try:
|
|
140
|
-
response, status_code = self.call_service.post(task.callback_url, response_data)
|
|
141
|
-
except Exception as e:
|
|
142
|
-
raise IAToolkitException(
|
|
143
|
-
IAToolkitException.ErrorType.REQUEST_ERROR,
|
|
144
|
-
f"Error al notificar callback {task.callback_url}: {str(e)}"
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
def get_task_files(self, uploaded_files):
|
|
148
|
-
files_info = []
|
|
149
|
-
|
|
150
|
-
for file in uploaded_files:
|
|
151
|
-
filename = secure_filename(file.filename)
|
|
152
|
-
|
|
153
|
-
try:
|
|
154
|
-
# the file is already in base64
|
|
155
|
-
file_content = file.read().decode('utf-8')
|
|
156
|
-
except Exception as e:
|
|
157
|
-
raise IAToolkitException(
|
|
158
|
-
IAToolkitException.ErrorType.FILE_IO_ERROR,
|
|
159
|
-
f"Error al extraer el contenido del archivo {filename}: {str(e)}"
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
files_info.append({
|
|
163
|
-
'filename': filename,
|
|
164
|
-
'content': file_content, # file in base64
|
|
165
|
-
'type': file.content_type
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
return files_info
|
|
169
|
-
|
|
170
|
-
def trigger_pending_tasks(self, company_short_name: str):
|
|
171
|
-
n_tasks = 0
|
|
172
|
-
try:
|
|
173
|
-
company = self.profile_repo.get_company_by_short_name(company_short_name)
|
|
174
|
-
pending_tasks = self.task_repo.get_pending_tasks(company.id)
|
|
175
|
-
for task in pending_tasks:
|
|
176
|
-
self.execute_task(task)
|
|
177
|
-
n_tasks += 1
|
|
178
|
-
except Exception as e:
|
|
179
|
-
raise IAToolkitException(
|
|
180
|
-
IAToolkitException.ErrorType.TASK_EXECUTION_ERROR,
|
|
181
|
-
f"Error ejecutando tareas pendientes: {str(e)}"
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
return {'message': f'{n_tasks} tareas ejecutadas.'}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
# Product: IAToolkit
|
|
3
|
-
#
|
|
4
|
-
# IAToolkit is open source software.
|
|
5
|
-
|
|
6
|
-
from flask.views import MethodView
|
|
7
|
-
from flask import request, jsonify
|
|
8
|
-
from iatoolkit.services.tasks_service import TaskService
|
|
9
|
-
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
10
|
-
from iatoolkit.services.auth_service import AuthService
|
|
11
|
-
from injector import inject
|
|
12
|
-
from datetime import datetime
|
|
13
|
-
import logging
|
|
14
|
-
from typing import Optional
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class TaskApiView(MethodView):
|
|
18
|
-
@inject
|
|
19
|
-
def __init__(self,
|
|
20
|
-
auth_service: AuthService,
|
|
21
|
-
task_service: TaskService,
|
|
22
|
-
profile_repo: ProfileRepo):
|
|
23
|
-
self.auth_service = auth_service
|
|
24
|
-
self.task_service = task_service
|
|
25
|
-
self.profile_repo = profile_repo
|
|
26
|
-
|
|
27
|
-
def post(self):
|
|
28
|
-
try:
|
|
29
|
-
auth_result = self.auth_service.verify(anonymous=True)
|
|
30
|
-
if not auth_result.get("success"):
|
|
31
|
-
return jsonify(auth_result), auth_result.get("status_code")
|
|
32
|
-
|
|
33
|
-
req_data = request.get_json()
|
|
34
|
-
files = request.files.getlist('files')
|
|
35
|
-
|
|
36
|
-
required_fields = ['company', 'task_type', 'client_data']
|
|
37
|
-
for field in required_fields:
|
|
38
|
-
if field not in req_data:
|
|
39
|
-
return jsonify({"error": f"El campo {field} es requerido"}), 400
|
|
40
|
-
|
|
41
|
-
company_short_name = req_data.get('company', '')
|
|
42
|
-
task_type = req_data.get('task_type', '')
|
|
43
|
-
client_data = req_data.get('client_data', {})
|
|
44
|
-
company_task_id = req_data.get('company_task_id', 0)
|
|
45
|
-
execute_at = req_data.get('execute_at', None)
|
|
46
|
-
|
|
47
|
-
# validate date format is parameter is present
|
|
48
|
-
if execute_at:
|
|
49
|
-
try:
|
|
50
|
-
# date in iso format
|
|
51
|
-
execute_at = datetime.fromisoformat(execute_at)
|
|
52
|
-
except ValueError:
|
|
53
|
-
return jsonify({
|
|
54
|
-
"error": "El formato de execute_at debe ser YYYY-MM-DD HH:MM:SS"
|
|
55
|
-
}), 400
|
|
56
|
-
|
|
57
|
-
new_task = self.task_service.create_task(
|
|
58
|
-
company_short_name=company_short_name,
|
|
59
|
-
task_type_name=task_type,
|
|
60
|
-
client_data=client_data,
|
|
61
|
-
company_task_id=company_task_id,
|
|
62
|
-
execute_at=execute_at,
|
|
63
|
-
files=files)
|
|
64
|
-
|
|
65
|
-
return jsonify({
|
|
66
|
-
"task_id": new_task.id,
|
|
67
|
-
"status": new_task.status.name
|
|
68
|
-
}), 201
|
|
69
|
-
|
|
70
|
-
except Exception as e:
|
|
71
|
-
logging.exception("Error al crear la tarea: %s", str(e))
|
|
72
|
-
return jsonify({"error": str(e)}), 500
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
# Product: IAToolkit
|
|
3
|
-
#
|
|
4
|
-
# IAToolkit is open source software.
|
|
5
|
-
|
|
6
|
-
from flask.views import MethodView
|
|
7
|
-
from flask import request, jsonify
|
|
8
|
-
from iatoolkit.services.tasks_service import TaskService
|
|
9
|
-
from iatoolkit.repositories.profile_repo import ProfileRepo
|
|
10
|
-
from iatoolkit.services.auth_service import AuthService
|
|
11
|
-
from injector import inject
|
|
12
|
-
import logging
|
|
13
|
-
from typing import Optional
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class TaskReviewApiView(MethodView):
|
|
17
|
-
@inject
|
|
18
|
-
def __init__(self,
|
|
19
|
-
auth_service: AuthService,
|
|
20
|
-
task_service: TaskService,
|
|
21
|
-
profile_repo: ProfileRepo):
|
|
22
|
-
self.auth_service = auth_service
|
|
23
|
-
self.task_service = task_service
|
|
24
|
-
self.profile_repo = profile_repo
|
|
25
|
-
|
|
26
|
-
def post(self, task_id: int):
|
|
27
|
-
auth_result = self.auth_service.verify(anonymous=True)
|
|
28
|
-
if not auth_result.get("success"):
|
|
29
|
-
return jsonify(auth_result), auth_result.get("status_code")
|
|
30
|
-
|
|
31
|
-
try:
|
|
32
|
-
req_data = request.get_json()
|
|
33
|
-
required_fields = ['review_user', 'approved']
|
|
34
|
-
for field in required_fields:
|
|
35
|
-
if field not in req_data:
|
|
36
|
-
return jsonify({"error": f"El campo {field} es requerido"}), 400
|
|
37
|
-
|
|
38
|
-
review_user = req_data.get('review_user', '')
|
|
39
|
-
approved = req_data.get('approved', False)
|
|
40
|
-
comment = req_data.get('comment', '')
|
|
41
|
-
|
|
42
|
-
new_task = self.task_service.review_task(
|
|
43
|
-
task_id=task_id,
|
|
44
|
-
review_user=review_user,
|
|
45
|
-
approved=approved,
|
|
46
|
-
comment=comment)
|
|
47
|
-
|
|
48
|
-
return jsonify({
|
|
49
|
-
"task_id": new_task.id,
|
|
50
|
-
"status": new_task.status.name
|
|
51
|
-
}), 200
|
|
52
|
-
|
|
53
|
-
except Exception as e:
|
|
54
|
-
logging.exception("Error al revisar la tarea: %s", str(e))
|
|
55
|
-
return jsonify({"error": str(e)}), 500
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|