iatoolkit 0.3.2__tar.gz → 0.3.4__tar.gz
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-0.3.2 → iatoolkit-0.3.4}/PKG-INFO +46 -2
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/pyproject.toml +3 -3
- iatoolkit-0.3.4/readme.md +43 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/iatoolkit/__init__.py +11 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/iatoolkit/company_registry.py +12 -31
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/iatoolkit/iatoolkit.py +41 -17
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/iatoolkit.egg-info/PKG-INFO +46 -2
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/iatoolkit.egg-info/SOURCES.txt +1 -0
- iatoolkit-0.3.4/src/services/api_service.py +75 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/dispatcher_service.py +13 -10
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/profile_service.py +1 -1
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/prompt_manager_service.py +8 -8
- iatoolkit-0.3.2/src/services/api_service.py +0 -30
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/requirements.txt +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/setup.cfg +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/iatoolkit/base_company.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/iatoolkit.egg-info/requires.txt +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/iatoolkit.egg-info/top_level.txt +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/__init__.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/benchmark_service.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/document_service.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/excel_service.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/file_processor_service.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/history_service.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/jwt_service.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/load_documents_service.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/mail_service.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/query_service.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/search_service.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/sql_service.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/tasks_service.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/user_feedback_service.py +0 -0
- {iatoolkit-0.3.2 → iatoolkit-0.3.4}/src/services/user_session_context_service.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iatoolkit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: IAToolkit
|
|
5
5
|
Author: Fernando Libedinsky
|
|
6
6
|
License-Expression: MIT
|
|
7
|
-
Requires-Python: >=3.
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
Requires-Dist: aiohappyeyeballs==2.4.4
|
|
10
10
|
Requires-Dist: aiohttp==3.11.9
|
|
@@ -206,3 +206,47 @@ Requires-Dist: wrapt==1.17.0
|
|
|
206
206
|
Requires-Dist: yarl==1.18.3
|
|
207
207
|
Requires-Dist: zipp==3.21.0
|
|
208
208
|
Requires-Dist: zstandard==0.23.0
|
|
209
|
+
|
|
210
|
+
# iatoolkit
|
|
211
|
+
|
|
212
|
+
IAToolkit is a comprehensive, open-source framework designed for building enterprise-grade
|
|
213
|
+
AI chatbots and conversational applications.
|
|
214
|
+
Built on Flask with dependency injection, it provides a robust foundation for scalable AI solutions.
|
|
215
|
+
|
|
216
|
+
## 🚀 Key Features
|
|
217
|
+
- **Universal LLM Integration**: OpenAI GPT, Google Gemini
|
|
218
|
+
- **Template System**: Jinja2-powered prompt templates with variables
|
|
219
|
+
- **Context Management**: Maintain conversation context across sessions
|
|
220
|
+
|
|
221
|
+
### 🔒 **Enterprise Security**
|
|
222
|
+
- **JWT Authentication**: Secure token-based authentication
|
|
223
|
+
- **Session Management**: Redis-backed secure sessions
|
|
224
|
+
- **CORS Configuration**: Flexible cross-origin resource sharing
|
|
225
|
+
|
|
226
|
+
### 🛠 **Function Calling & Tools**
|
|
227
|
+
- **Native Function Calls**: Direct integration with LLM function calling
|
|
228
|
+
- **Custom Tools**: Build and register custom tools for your chatbot
|
|
229
|
+
- **SQL Query Generation**: Natural language to SQL conversion
|
|
230
|
+
- **API Integrations**: Connect to external services and APIs
|
|
231
|
+
|
|
232
|
+
### 🗄 **Database & Storage**
|
|
233
|
+
- **Multi-Database Support**: PostgreSQL, MySQL, SQLite via SQLAlchemy
|
|
234
|
+
- **Vector Store Integration**: Semantic search and retrieval
|
|
235
|
+
- **Document Processing**: PDF, Word, Excel, and text file handling
|
|
236
|
+
|
|
237
|
+
### 📊 **Analytics & Monitoring**
|
|
238
|
+
- **Query Logging**: Track all LLM interactions
|
|
239
|
+
- **Performance Metrics**: Response times, token usage, costs
|
|
240
|
+
- **Benchmarking**: Compare model performance
|
|
241
|
+
- **Task Management**: Async task processing with status tracking
|
|
242
|
+
|
|
243
|
+
### 🔧 **Developer Experience**
|
|
244
|
+
- **Dependency Injection**: Clean, testable architecture
|
|
245
|
+
- **CLI Tools**: Command-line interface for common tasks
|
|
246
|
+
- **Hot Reloading**: Development-friendly configuration
|
|
247
|
+
- **Comprehensive Logging**: Debug and monitor easily
|
|
248
|
+
|
|
249
|
+
## License
|
|
250
|
+
MIT License
|
|
251
|
+
|
|
252
|
+
|
|
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "iatoolkit"
|
|
7
|
-
version = "0.3.
|
|
8
|
-
requires-python = ">=3.
|
|
7
|
+
version = "0.3.4"
|
|
8
|
+
requires-python = ">=3.11"
|
|
9
9
|
description = "IAToolkit"
|
|
10
|
-
readme = "
|
|
10
|
+
readme = "readme.md"
|
|
11
11
|
license = "MIT"
|
|
12
12
|
authors = [{ name = "Fernando Libedinsky" }]
|
|
13
13
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# iatoolkit
|
|
2
|
+
|
|
3
|
+
IAToolkit is a comprehensive, open-source framework designed for building enterprise-grade
|
|
4
|
+
AI chatbots and conversational applications.
|
|
5
|
+
Built on Flask with dependency injection, it provides a robust foundation for scalable AI solutions.
|
|
6
|
+
|
|
7
|
+
## 🚀 Key Features
|
|
8
|
+
- **Universal LLM Integration**: OpenAI GPT, Google Gemini
|
|
9
|
+
- **Template System**: Jinja2-powered prompt templates with variables
|
|
10
|
+
- **Context Management**: Maintain conversation context across sessions
|
|
11
|
+
|
|
12
|
+
### 🔒 **Enterprise Security**
|
|
13
|
+
- **JWT Authentication**: Secure token-based authentication
|
|
14
|
+
- **Session Management**: Redis-backed secure sessions
|
|
15
|
+
- **CORS Configuration**: Flexible cross-origin resource sharing
|
|
16
|
+
|
|
17
|
+
### 🛠 **Function Calling & Tools**
|
|
18
|
+
- **Native Function Calls**: Direct integration with LLM function calling
|
|
19
|
+
- **Custom Tools**: Build and register custom tools for your chatbot
|
|
20
|
+
- **SQL Query Generation**: Natural language to SQL conversion
|
|
21
|
+
- **API Integrations**: Connect to external services and APIs
|
|
22
|
+
|
|
23
|
+
### 🗄 **Database & Storage**
|
|
24
|
+
- **Multi-Database Support**: PostgreSQL, MySQL, SQLite via SQLAlchemy
|
|
25
|
+
- **Vector Store Integration**: Semantic search and retrieval
|
|
26
|
+
- **Document Processing**: PDF, Word, Excel, and text file handling
|
|
27
|
+
|
|
28
|
+
### 📊 **Analytics & Monitoring**
|
|
29
|
+
- **Query Logging**: Track all LLM interactions
|
|
30
|
+
- **Performance Metrics**: Response times, token usage, costs
|
|
31
|
+
- **Benchmarking**: Compare model performance
|
|
32
|
+
- **Task Management**: Async task processing with status tracking
|
|
33
|
+
|
|
34
|
+
### 🔧 **Developer Experience**
|
|
35
|
+
- **Dependency Injection**: Clean, testable architecture
|
|
36
|
+
- **CLI Tools**: Command-line interface for common tasks
|
|
37
|
+
- **Hot Reloading**: Development-friendly configuration
|
|
38
|
+
- **Comprehensive Logging**: Debug and monitor easily
|
|
39
|
+
|
|
40
|
+
## License
|
|
41
|
+
MIT License
|
|
42
|
+
|
|
43
|
+
|
|
@@ -22,7 +22,11 @@ from services.dispatcher_service import Dispatcher
|
|
|
22
22
|
from services.document_service import DocumentService
|
|
23
23
|
from services.search_service import SearchService
|
|
24
24
|
from repositories.profile_repo import ProfileRepo
|
|
25
|
+
from repositories.llm_query_repo import LLMQueryRepo
|
|
25
26
|
from repositories.database_manager import DatabaseManager
|
|
27
|
+
from infra.call_service import CallServiceClient
|
|
28
|
+
from common.util import Utility
|
|
29
|
+
from repositories.models import Base, Company, Function, TaskType
|
|
26
30
|
|
|
27
31
|
|
|
28
32
|
__all__ = [
|
|
@@ -37,5 +41,12 @@ __all__ = [
|
|
|
37
41
|
'DocumentService',
|
|
38
42
|
'SearchService',
|
|
39
43
|
'ProfileRepo',
|
|
44
|
+
'LLMQueryRepo',
|
|
40
45
|
'DatabaseManager',
|
|
46
|
+
'CallServiceClient',
|
|
47
|
+
'Utility',
|
|
48
|
+
'Company',
|
|
49
|
+
'Function',
|
|
50
|
+
'TaskType',
|
|
51
|
+
'Base',
|
|
41
52
|
]
|
|
@@ -8,10 +8,8 @@ import logging
|
|
|
8
8
|
|
|
9
9
|
class CompanyRegistry:
|
|
10
10
|
"""
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Permite a los clientes registrar sus clases de empresa de forma explícita
|
|
14
|
-
en lugar de usar autodiscovery.
|
|
11
|
+
Company registry with dependency injection support.
|
|
12
|
+
Allow the client to register companies and instantiate them with dependency injection.
|
|
15
13
|
"""
|
|
16
14
|
|
|
17
15
|
def __init__(self):
|
|
@@ -19,22 +17,6 @@ class CompanyRegistry:
|
|
|
19
17
|
self._company_instances: Dict[str, BaseCompany] = {}
|
|
20
18
|
self._injector = None
|
|
21
19
|
|
|
22
|
-
def register_company(self, name: str, company_class: Type[BaseCompany]) -> None:
|
|
23
|
-
"""
|
|
24
|
-
Registra una clase de empresa.
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
name: Nombre de la empresa (ej: 'maxxa')
|
|
28
|
-
company_class: Clase que hereda de BaseCompany
|
|
29
|
-
"""
|
|
30
|
-
if not issubclass(company_class, BaseCompany):
|
|
31
|
-
raise ValueError(f"La clase {company_class.__name__} debe heredar de BaseCompany")
|
|
32
|
-
|
|
33
|
-
company_key = name.lower()
|
|
34
|
-
self._company_classes[company_key] = company_class
|
|
35
|
-
|
|
36
|
-
logging.info(f"Empresa registrada: {company_key} -> {company_class.__name__}")
|
|
37
|
-
|
|
38
20
|
def set_injector(self, injector) -> None:
|
|
39
21
|
"""Establece el injector para crear instancias con dependencias"""
|
|
40
22
|
self._injector = injector
|
|
@@ -65,34 +47,33 @@ class CompanyRegistry:
|
|
|
65
47
|
return self._company_instances.copy()
|
|
66
48
|
|
|
67
49
|
def get_registered_companies(self) -> Dict[str, Type[BaseCompany]]:
|
|
68
|
-
"""Retorna las clases registradas"""
|
|
69
50
|
return self._company_classes.copy()
|
|
70
51
|
|
|
71
|
-
def get_company_instances(self) -> Dict[str, BaseCompany]:
|
|
72
|
-
"""Retorna las instancias de empresas"""
|
|
73
|
-
return self._company_instances.copy()
|
|
74
|
-
|
|
75
52
|
def clear(self) -> None:
|
|
76
53
|
"""Limpia el registro (útil para tests)"""
|
|
77
54
|
self._company_classes.clear()
|
|
78
55
|
self._company_instances.clear()
|
|
79
56
|
|
|
80
57
|
|
|
81
|
-
#
|
|
58
|
+
# global instance of the company registry
|
|
82
59
|
_company_registry = CompanyRegistry()
|
|
83
60
|
|
|
84
61
|
|
|
85
62
|
def register_company(name: str, company_class: Type[BaseCompany]) -> None:
|
|
86
63
|
"""
|
|
87
|
-
|
|
64
|
+
Public function to register a company.
|
|
88
65
|
|
|
89
66
|
Args:
|
|
90
|
-
name:
|
|
91
|
-
company_class:
|
|
67
|
+
name: Name of the company
|
|
68
|
+
company_class: Class that inherits from BaseCompany
|
|
92
69
|
"""
|
|
93
|
-
|
|
70
|
+
if not issubclass(company_class, BaseCompany):
|
|
71
|
+
raise ValueError(f"La clase {company_class.__name__} debe heredar de BaseCompany")
|
|
72
|
+
|
|
73
|
+
company_key = name.lower()
|
|
74
|
+
_company_registry._company_classes[company_key] = company_class
|
|
94
75
|
|
|
95
76
|
|
|
96
77
|
def get_company_registry() -> CompanyRegistry:
|
|
97
|
-
"""
|
|
78
|
+
"""get the global company registry instance"""
|
|
98
79
|
return _company_registry
|
|
@@ -19,7 +19,6 @@ import click
|
|
|
19
19
|
from typing import Optional, Dict, Any
|
|
20
20
|
from repositories.database_manager import DatabaseManager
|
|
21
21
|
from injector import Binder, singleton, Injector
|
|
22
|
-
from .toolkit_config import IAToolkitConfig
|
|
23
22
|
|
|
24
23
|
VERSION = "2.0.0"
|
|
25
24
|
|
|
@@ -51,9 +50,9 @@ class IAToolkit:
|
|
|
51
50
|
return
|
|
52
51
|
|
|
53
52
|
self.config = config or {}
|
|
54
|
-
self.app
|
|
55
|
-
self.db_manager
|
|
56
|
-
self._injector
|
|
53
|
+
self.app = None
|
|
54
|
+
self.db_manager = None
|
|
55
|
+
self._injector = None
|
|
57
56
|
|
|
58
57
|
@classmethod
|
|
59
58
|
def get_instance(cls) -> 'IAToolkit':
|
|
@@ -68,7 +67,7 @@ class IAToolkit:
|
|
|
68
67
|
def create_iatoolkit(self):
|
|
69
68
|
"""
|
|
70
69
|
Creates, configures, and returns the Flask application instance.
|
|
71
|
-
|
|
70
|
+
this is the main entry point for the application factory.
|
|
72
71
|
"""
|
|
73
72
|
self._setup_logging()
|
|
74
73
|
|
|
@@ -78,12 +77,8 @@ class IAToolkit:
|
|
|
78
77
|
# Step 2: Set up the core components that DI depends on
|
|
79
78
|
self._setup_database()
|
|
80
79
|
|
|
81
|
-
# Step 3: Create the Injector
|
|
82
|
-
|
|
83
|
-
self._injector = Injector([
|
|
84
|
-
toolkit_config_module,
|
|
85
|
-
self._configure_core_dependencies # This method binds services, repos, etc.
|
|
86
|
-
])
|
|
80
|
+
# Step 3: Create the Injector and configure all dependencies in one place
|
|
81
|
+
self._injector = Injector(self._configure_core_dependencies)
|
|
87
82
|
|
|
88
83
|
# Step 4: Register routes using the fully configured injector
|
|
89
84
|
self._register_routes()
|
|
@@ -102,7 +97,6 @@ class IAToolkit:
|
|
|
102
97
|
logging.info(f"🎉 IAToolkit v{VERSION} inicializado correctamente")
|
|
103
98
|
return self.app
|
|
104
99
|
|
|
105
|
-
|
|
106
100
|
def _get_config_value(self, key: str, default=None):
|
|
107
101
|
"""Obtiene un valor de configuración, primero del dict config, luego de env vars"""
|
|
108
102
|
return self.config.get(key, os.getenv(key, default))
|
|
@@ -228,7 +222,8 @@ class IAToolkit:
|
|
|
228
222
|
"""⚙️ Configures all system dependencies."""
|
|
229
223
|
try:
|
|
230
224
|
# Core dependencies
|
|
231
|
-
binder.bind(
|
|
225
|
+
binder.bind(Flask, to=self.app, scope=singleton)
|
|
226
|
+
binder.bind(DatabaseManager, to=self.db_manager, scope=singleton)
|
|
232
227
|
|
|
233
228
|
# Bind all application components by calling the specific methods
|
|
234
229
|
self._bind_repositories(binder)
|
|
@@ -317,16 +312,17 @@ class IAToolkit:
|
|
|
317
312
|
|
|
318
313
|
def _setup_cli_commands(self):
|
|
319
314
|
"""⌨️ Configura comandos CLI básicos"""
|
|
315
|
+
from services.dispatcher_service import Dispatcher
|
|
316
|
+
from services.profile_service import ProfileService
|
|
320
317
|
|
|
321
318
|
@self.app.cli.command("init-db")
|
|
322
319
|
def init_db():
|
|
323
320
|
"""🗄️ Inicializa la base de datos del sistema"""
|
|
324
321
|
try:
|
|
325
|
-
|
|
326
|
-
dispatcher = self._get_injector().get(Dispatcher)
|
|
322
|
+
dispatcher = self.get_injector().get(Dispatcher)
|
|
327
323
|
|
|
328
324
|
click.echo("🚀 Inicializando base de datos...")
|
|
329
|
-
dispatcher.
|
|
325
|
+
dispatcher.setup_all_companies()
|
|
330
326
|
click.echo("✅ Base de datos inicializada correctamente")
|
|
331
327
|
|
|
332
328
|
except Exception as e:
|
|
@@ -334,6 +330,34 @@ class IAToolkit:
|
|
|
334
330
|
click.echo(f"❌ Error: {e}")
|
|
335
331
|
|
|
336
332
|
|
|
333
|
+
@self.app.cli.command("setup-company")
|
|
334
|
+
@click.argument("company_short_name")
|
|
335
|
+
def setup_company(company_short_name: str):
|
|
336
|
+
"""⚙️ Ejecuta el proceso de configuración para una nueva empresa."""
|
|
337
|
+
try:
|
|
338
|
+
# step 1: init the database
|
|
339
|
+
dispatcher = self.get_injector().get(Dispatcher)
|
|
340
|
+
click.echo("🚀 step 1 of 2: init companies in the database...")
|
|
341
|
+
dispatcher.setup_all_companies()
|
|
342
|
+
click.echo("✅ database is ready.")
|
|
343
|
+
|
|
344
|
+
# step 2: generate the api key
|
|
345
|
+
profile_service = self.get_injector().get(ProfileService)
|
|
346
|
+
click.echo(f"🔑 step 2 of 2: generating api-key for use in '{company_short_name}'...")
|
|
347
|
+
result = profile_service.new_api_key(company_short_name)
|
|
348
|
+
|
|
349
|
+
if 'error' in result:
|
|
350
|
+
click.echo(f"❌ Error in step 2: {result['error']}")
|
|
351
|
+
click.echo("👉 Make sure company name is correct and it's initialized in your app.")
|
|
352
|
+
else:
|
|
353
|
+
click.echo("Configuration es ready, add this variable to your environment")
|
|
354
|
+
click.echo(f"IATOOLKIT_API_KEY={result['api-key']}")
|
|
355
|
+
|
|
356
|
+
except Exception as e:
|
|
357
|
+
logging.exception(e)
|
|
358
|
+
click.echo(f"❌ Ocurrió un error inesperado durante la configuración: {e}")
|
|
359
|
+
|
|
360
|
+
|
|
337
361
|
def _setup_context_processors(self):
|
|
338
362
|
# Configura context processors para templates
|
|
339
363
|
@self.app.context_processor
|
|
@@ -362,7 +386,7 @@ class IAToolkit:
|
|
|
362
386
|
except:
|
|
363
387
|
return 'templates'
|
|
364
388
|
|
|
365
|
-
def
|
|
389
|
+
def get_injector(self) -> Injector:
|
|
366
390
|
"""Obtiene el injector actual"""
|
|
367
391
|
if not self._injector:
|
|
368
392
|
raise IAToolkitException(
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iatoolkit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: IAToolkit
|
|
5
5
|
Author: Fernando Libedinsky
|
|
6
6
|
License-Expression: MIT
|
|
7
|
-
Requires-Python: >=3.
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
Requires-Dist: aiohappyeyeballs==2.4.4
|
|
10
10
|
Requires-Dist: aiohttp==3.11.9
|
|
@@ -206,3 +206,47 @@ Requires-Dist: wrapt==1.17.0
|
|
|
206
206
|
Requires-Dist: yarl==1.18.3
|
|
207
207
|
Requires-Dist: zipp==3.21.0
|
|
208
208
|
Requires-Dist: zstandard==0.23.0
|
|
209
|
+
|
|
210
|
+
# iatoolkit
|
|
211
|
+
|
|
212
|
+
IAToolkit is a comprehensive, open-source framework designed for building enterprise-grade
|
|
213
|
+
AI chatbots and conversational applications.
|
|
214
|
+
Built on Flask with dependency injection, it provides a robust foundation for scalable AI solutions.
|
|
215
|
+
|
|
216
|
+
## 🚀 Key Features
|
|
217
|
+
- **Universal LLM Integration**: OpenAI GPT, Google Gemini
|
|
218
|
+
- **Template System**: Jinja2-powered prompt templates with variables
|
|
219
|
+
- **Context Management**: Maintain conversation context across sessions
|
|
220
|
+
|
|
221
|
+
### 🔒 **Enterprise Security**
|
|
222
|
+
- **JWT Authentication**: Secure token-based authentication
|
|
223
|
+
- **Session Management**: Redis-backed secure sessions
|
|
224
|
+
- **CORS Configuration**: Flexible cross-origin resource sharing
|
|
225
|
+
|
|
226
|
+
### 🛠 **Function Calling & Tools**
|
|
227
|
+
- **Native Function Calls**: Direct integration with LLM function calling
|
|
228
|
+
- **Custom Tools**: Build and register custom tools for your chatbot
|
|
229
|
+
- **SQL Query Generation**: Natural language to SQL conversion
|
|
230
|
+
- **API Integrations**: Connect to external services and APIs
|
|
231
|
+
|
|
232
|
+
### 🗄 **Database & Storage**
|
|
233
|
+
- **Multi-Database Support**: PostgreSQL, MySQL, SQLite via SQLAlchemy
|
|
234
|
+
- **Vector Store Integration**: Semantic search and retrieval
|
|
235
|
+
- **Document Processing**: PDF, Word, Excel, and text file handling
|
|
236
|
+
|
|
237
|
+
### 📊 **Analytics & Monitoring**
|
|
238
|
+
- **Query Logging**: Track all LLM interactions
|
|
239
|
+
- **Performance Metrics**: Response times, token usage, costs
|
|
240
|
+
- **Benchmarking**: Compare model performance
|
|
241
|
+
- **Task Management**: Async task processing with status tracking
|
|
242
|
+
|
|
243
|
+
### 🔧 **Developer Experience**
|
|
244
|
+
- **Dependency Injection**: Clean, testable architecture
|
|
245
|
+
- **CLI Tools**: Command-line interface for common tasks
|
|
246
|
+
- **Hot Reloading**: Development-friendly configuration
|
|
247
|
+
- **Comprehensive Logging**: Debug and monitor easily
|
|
248
|
+
|
|
249
|
+
## License
|
|
250
|
+
MIT License
|
|
251
|
+
|
|
252
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
+
# Producto: IAToolkit
|
|
3
|
+
# Todos los derechos reservados.
|
|
4
|
+
# En trámite de registro en el Registro de Propiedad Intelectual de Chile.
|
|
5
|
+
|
|
6
|
+
from infra.call_service import CallServiceClient
|
|
7
|
+
from injector import inject
|
|
8
|
+
from common.exceptions import IAToolkitException
|
|
9
|
+
import json
|
|
10
|
+
from typing import Optional, Dict, Any, Union
|
|
11
|
+
|
|
12
|
+
class ApiService:
|
|
13
|
+
@inject
|
|
14
|
+
def __init__(self, call_service: CallServiceClient):
|
|
15
|
+
self.call_service = call_service
|
|
16
|
+
|
|
17
|
+
def call_api(
|
|
18
|
+
self,
|
|
19
|
+
endpoint: str,
|
|
20
|
+
method: str,
|
|
21
|
+
headers: Optional[Dict[str, str]] = None,
|
|
22
|
+
params: Optional[Dict[str, Union[str, int, float, bool]]] = None,
|
|
23
|
+
body: Optional[Dict[str, Any]] = None,
|
|
24
|
+
files: Optional[Dict[str, Any]] = None,
|
|
25
|
+
timeout: Union[int, float, tuple] = 10
|
|
26
|
+
) -> str:
|
|
27
|
+
"""
|
|
28
|
+
Ejecuta una llamada HTTP genérica.
|
|
29
|
+
|
|
30
|
+
- endpoint: URL completa
|
|
31
|
+
- method: GET | POST | PUT | DELETE (case-insensitive)
|
|
32
|
+
- headers: dict opcional de cabeceras
|
|
33
|
+
- params: dict opcional de query string
|
|
34
|
+
- body: dict opcional para JSON (POST/PUT/DELETE)
|
|
35
|
+
- files: dict opcional para multipart/form-data (prioriza POST files)
|
|
36
|
+
- timeout: segundos (int/float) o tuple (connect, read)
|
|
37
|
+
"""
|
|
38
|
+
m = (method or "").strip().lower()
|
|
39
|
+
|
|
40
|
+
if m == "get":
|
|
41
|
+
response, status_code = self.call_service.get(
|
|
42
|
+
endpoint, params=params, headers=headers, timeout=timeout
|
|
43
|
+
)
|
|
44
|
+
elif m == "post":
|
|
45
|
+
# Si vienen files → multipart; si no → JSON
|
|
46
|
+
if files:
|
|
47
|
+
response, status_code = self.call_service.post_files(
|
|
48
|
+
endpoint, data=files, params=params, headers=headers, timeout=timeout
|
|
49
|
+
)
|
|
50
|
+
else:
|
|
51
|
+
response, status_code = self.call_service.post(
|
|
52
|
+
endpoint=endpoint, json_dict=body, params=params, headers=headers, timeout=timeout
|
|
53
|
+
)
|
|
54
|
+
elif m == "put":
|
|
55
|
+
response, status_code = self.call_service.put(
|
|
56
|
+
endpoint, json_dict=body, params=params, headers=headers, timeout=timeout
|
|
57
|
+
)
|
|
58
|
+
elif m == "delete":
|
|
59
|
+
response, status_code = self.call_service.delete(
|
|
60
|
+
endpoint, json_dict=body, params=params, headers=headers, timeout=timeout
|
|
61
|
+
)
|
|
62
|
+
else:
|
|
63
|
+
raise IAToolkitException(
|
|
64
|
+
IAToolkitException.ErrorType.INVALID_PARAMETER,
|
|
65
|
+
f"API error: método '{method}' no soportado"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if status_code < 200 or status_code >= 300:
|
|
69
|
+
raise IAToolkitException(
|
|
70
|
+
IAToolkitException.ErrorType.CALL_ERROR,
|
|
71
|
+
f"API {endpoint} error: {status_code}"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Normalizamos a string JSON (para que el LLM lo consuma consistente)
|
|
75
|
+
return json.dumps(response)
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
from iatoolkit import current_iatoolkit
|
|
7
7
|
from common.exceptions import IAToolkitException
|
|
8
8
|
from services.prompt_manager_service import PromptService
|
|
9
|
+
from services.api_service import ApiService
|
|
9
10
|
from repositories.llm_query_repo import LLMQueryRepo
|
|
10
11
|
from repositories.models import Company, Function
|
|
11
12
|
from services.excel_service import ExcelService
|
|
@@ -23,10 +24,12 @@ class Dispatcher:
|
|
|
23
24
|
prompt_service: PromptService,
|
|
24
25
|
llmquery_repo: LLMQueryRepo,
|
|
25
26
|
util: Utility,
|
|
27
|
+
api_service: ApiService,
|
|
26
28
|
excel_service: ExcelService,
|
|
27
29
|
mail_service: MailService):
|
|
28
30
|
self.prompt_service = prompt_service
|
|
29
31
|
self.llmquery_repo = llmquery_repo
|
|
32
|
+
self.api_service = api_service
|
|
30
33
|
self.util = util
|
|
31
34
|
self.excel_service = excel_service
|
|
32
35
|
self.mail_service = mail_service
|
|
@@ -36,41 +39,42 @@ class Dispatcher:
|
|
|
36
39
|
# Use the global registry
|
|
37
40
|
self.company_registry = get_company_registry()
|
|
38
41
|
|
|
39
|
-
#
|
|
42
|
+
# load into the dispatcher the configured companies
|
|
40
43
|
self.company_classes = {}
|
|
41
44
|
self.initialize_companies()
|
|
42
45
|
|
|
46
|
+
# run the statrtup logic for all companies
|
|
47
|
+
self.start_execution()
|
|
48
|
+
|
|
43
49
|
self.tool_handlers = {
|
|
44
50
|
"iat_generate_excel": self.excel_service.excel_generator,
|
|
45
51
|
"iat_send_email": self.mail_service.send_mail,
|
|
52
|
+
"iat_api_call": self.api_service.call_api
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
def initialize_companies(self):
|
|
49
56
|
"""
|
|
50
57
|
Initializes and instantiates all registered company classes.
|
|
51
|
-
This method should be called *after* the main injector is fully configured
|
|
58
|
+
This method should be called *after* the main injector is fully configured
|
|
59
|
+
and the company registry is populated.
|
|
52
60
|
"""
|
|
53
61
|
if self.company_classes: # Prevent re-initialization
|
|
54
62
|
return
|
|
55
63
|
|
|
56
64
|
# ✅ NOW it is safe to get the injector and instantiate companies.
|
|
57
|
-
injector = current_iatoolkit().
|
|
65
|
+
injector = current_iatoolkit().get_injector()
|
|
58
66
|
self.company_registry.set_injector(injector)
|
|
59
67
|
self.company_classes = self.company_registry.instantiate_companies()
|
|
60
68
|
|
|
61
69
|
def start_execution(self):
|
|
62
70
|
"""Runs the startup logic for all registered companies."""
|
|
63
|
-
# Ensure companies are initialized before starting them
|
|
64
|
-
if not self.company_classes:
|
|
65
|
-
self.initialize_companies()
|
|
66
|
-
|
|
67
71
|
for company_name, company_instance in self.company_classes.items():
|
|
68
72
|
logging.info(f'Starting execution for company: {company_name}')
|
|
69
73
|
company_instance.start_execution()
|
|
70
74
|
|
|
71
75
|
return True
|
|
72
76
|
|
|
73
|
-
def
|
|
77
|
+
def setup_all_companies(self):
|
|
74
78
|
# create system functions
|
|
75
79
|
for function in self.system_functions:
|
|
76
80
|
self.llmquery_repo.create_or_update_function(
|
|
@@ -96,10 +100,9 @@ class Dispatcher:
|
|
|
96
100
|
|
|
97
101
|
# initialize the database for every company class
|
|
98
102
|
for company in self.company_classes.values():
|
|
99
|
-
print(f'
|
|
103
|
+
print(f'company: {company.__class__.__name__}')
|
|
100
104
|
company.init_db()
|
|
101
105
|
|
|
102
|
-
|
|
103
106
|
def dispatch(self, company_name: str, action: str, **kwargs) -> str:
|
|
104
107
|
company_key = company_name.lower()
|
|
105
108
|
|
|
@@ -266,7 +266,7 @@ class ProfileService:
|
|
|
266
266
|
|
|
267
267
|
api_key = ApiKey(key=key, company_id=company.id)
|
|
268
268
|
self.profile_repo.create_api_key(api_key)
|
|
269
|
-
return {"
|
|
269
|
+
return {"api-key": key}
|
|
270
270
|
|
|
271
271
|
|
|
272
272
|
def send_verification_email(self, new_user: User, company_short_name):
|
|
@@ -11,6 +11,7 @@ from collections import defaultdict
|
|
|
11
11
|
from repositories.models import Prompt, PromptCategory, Company
|
|
12
12
|
import os
|
|
13
13
|
from common.exceptions import IAToolkitException
|
|
14
|
+
from pathlib import Path
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class PromptService:
|
|
@@ -32,7 +33,7 @@ class PromptService:
|
|
|
32
33
|
|
|
33
34
|
prompt_filename = prompt_name.lower() + '.prompt'
|
|
34
35
|
if is_system_prompt:
|
|
35
|
-
template_dir = '
|
|
36
|
+
template_dir = 'src/system_prompts'
|
|
36
37
|
else:
|
|
37
38
|
template_dir = f'companies/{company.short_name}/prompts'
|
|
38
39
|
|
|
@@ -103,16 +104,15 @@ class PromptService:
|
|
|
103
104
|
system_prompt_content = []
|
|
104
105
|
|
|
105
106
|
# get the filepaths for all system prompts
|
|
106
|
-
current_dir =
|
|
107
|
-
|
|
108
|
-
system_prompt_dir = os.path.join(src_dir, "prompts")
|
|
107
|
+
current_dir = Path(__file__).resolve().parent
|
|
108
|
+
project_root = current_dir.parent.parent
|
|
109
109
|
|
|
110
|
-
#
|
|
110
|
+
# read all the system prompts from the database
|
|
111
111
|
system_prompts = self.llm_query_repo.get_system_prompts()
|
|
112
112
|
|
|
113
113
|
for prompt in system_prompts:
|
|
114
|
-
#
|
|
115
|
-
absolute_filepath = os.path.join(
|
|
114
|
+
# build the absolute filepath for reading it
|
|
115
|
+
absolute_filepath = os.path.join(project_root, prompt.filepath)
|
|
116
116
|
if not os.path.exists(absolute_filepath):
|
|
117
117
|
logging.warning(f"El archivo para el prompt de sistema no existe: {absolute_filepath}")
|
|
118
118
|
continue
|
|
@@ -123,7 +123,7 @@ class PromptService:
|
|
|
123
123
|
raise IAToolkitException(IAToolkitException.ErrorType.FILE_IO_ERROR,
|
|
124
124
|
f"Error leyendo el archivo de prompt del sistema {absolute_filepath}: {e}")
|
|
125
125
|
|
|
126
|
-
#
|
|
126
|
+
# join the system prompts into a single string
|
|
127
127
|
return "\n".join(system_prompt_content)
|
|
128
128
|
|
|
129
129
|
except IAToolkitException:
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Fernando Libedinsky
|
|
2
|
-
# Producto: IAToolkit
|
|
3
|
-
# Todos los derechos reservados.
|
|
4
|
-
# En trámite de registro en el Registro de Propiedad Intelectual de Chile.
|
|
5
|
-
|
|
6
|
-
from infra.call_service import CallServiceClient
|
|
7
|
-
from injector import inject
|
|
8
|
-
from common.exceptions import IAToolkitException
|
|
9
|
-
import json
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class ApiService:
|
|
13
|
-
@inject
|
|
14
|
-
def __init__(self, call_service: CallServiceClient):
|
|
15
|
-
self.call_service = call_service
|
|
16
|
-
|
|
17
|
-
def call_api(self, endpoint: str, method: str, **kwargs):
|
|
18
|
-
if method == 'get':
|
|
19
|
-
response, status_code = self.call_service.get(endpoint)
|
|
20
|
-
elif method == 'post':
|
|
21
|
-
response, status_code = self.call_service.post(endpoint=endpoint, json_dict=kwargs)
|
|
22
|
-
else:
|
|
23
|
-
raise IAToolkitException(IAToolkitException.ErrorType.INVALID_PARAMETER,
|
|
24
|
-
f'API error, {method} not supported')
|
|
25
|
-
|
|
26
|
-
if status_code != 200:
|
|
27
|
-
raise IAToolkitException(IAToolkitException.ErrorType.CALL_ERROR,
|
|
28
|
-
f'API {endpoint} error: {status_code}')
|
|
29
|
-
|
|
30
|
-
return json.dumps(response)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|