iatoolkit 0.3.7__tar.gz → 0.3.9__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.

Files changed (38) hide show
  1. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/PKG-INFO +1 -1
  2. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/pyproject.toml +6 -2
  3. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/iatoolkit/__init__.py +2 -0
  4. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/iatoolkit/base_company.py +13 -2
  5. iatoolkit-0.3.9/src/iatoolkit/cli_commands.py +77 -0
  6. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/iatoolkit/iatoolkit.py +20 -46
  7. iatoolkit-0.3.9/src/iatoolkit/system_prompts/arquitectura.prompt +32 -0
  8. iatoolkit-0.3.9/src/iatoolkit/system_prompts/format_styles.prompt +97 -0
  9. iatoolkit-0.3.9/src/iatoolkit/system_prompts/query_main.prompt +67 -0
  10. iatoolkit-0.3.9/src/iatoolkit/system_prompts/sql_rules.prompt +1337 -0
  11. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/iatoolkit.egg-info/PKG-INFO +1 -1
  12. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/iatoolkit.egg-info/SOURCES.txt +5 -0
  13. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/dispatcher_service.py +48 -12
  14. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/history_service.py +1 -1
  15. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/profile_service.py +3 -3
  16. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/prompt_manager_service.py +15 -23
  17. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/query_service.py +9 -7
  18. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/readme.md +0 -0
  19. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/requirements.txt +0 -0
  20. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/setup.cfg +0 -0
  21. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/iatoolkit/company_registry.py +0 -0
  22. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
  23. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/iatoolkit.egg-info/requires.txt +0 -0
  24. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/iatoolkit.egg-info/top_level.txt +0 -0
  25. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/__init__.py +0 -0
  26. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/api_service.py +0 -0
  27. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/benchmark_service.py +0 -0
  28. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/document_service.py +0 -0
  29. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/excel_service.py +0 -0
  30. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/file_processor_service.py +0 -0
  31. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/jwt_service.py +0 -0
  32. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/load_documents_service.py +0 -0
  33. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/mail_service.py +0 -0
  34. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/search_service.py +0 -0
  35. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/sql_service.py +0 -0
  36. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/tasks_service.py +0 -0
  37. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/user_feedback_service.py +0 -0
  38. {iatoolkit-0.3.7 → iatoolkit-0.3.9}/src/services/user_session_context_service.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 0.3.7
3
+ Version: 0.3.9
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "iatoolkit"
7
- version = "0.3.7"
7
+ version = "0.3.9"
8
8
  requires-python = ">=3.11"
9
9
  description = "IAToolkit"
10
10
  readme = "readme.md"
@@ -22,4 +22,8 @@ where = ["src"]
22
22
  include = ["iatoolkit*", "services*"]
23
23
 
24
24
  [tool.setuptools.dynamic]
25
- dependencies = { file = ["requirements.txt"] }
25
+ dependencies = { file = ["requirements.txt"] }
26
+
27
+ [tool.setuptools.package-data]
28
+ # 'src/system_prompts' relativo al paquete 'iatoolkit'
29
+ iatoolkit = ["system_prompts/*.prompt"]
@@ -21,6 +21,7 @@ from services.excel_service import ExcelService
21
21
  from services.dispatcher_service import Dispatcher
22
22
  from services.document_service import DocumentService
23
23
  from services.search_service import SearchService
24
+ from services.query_service import QueryService
24
25
  from repositories.profile_repo import ProfileRepo
25
26
  from repositories.llm_query_repo import LLMQueryRepo
26
27
  from repositories.database_manager import DatabaseManager
@@ -39,6 +40,7 @@ __all__ = [
39
40
  'ExcelService',
40
41
  'Dispatcher',
41
42
  'DocumentService',
43
+ 'QueryService',
42
44
  'SearchService',
43
45
  'ProfileRepo',
44
46
  'LLMQueryRepo',
@@ -15,14 +15,19 @@ class BaseCompany(ABC):
15
15
 
16
16
  @abstractmethod
17
17
  # initialize all the database tables needed
18
- def init_db(self):
19
- raise NotImplementedError("La subclase debe implementar el método init_db()")
18
+ def register_company(self):
19
+ raise NotImplementedError("La subclase debe implementar el método create_company()")
20
20
 
21
21
  @abstractmethod
22
22
  # get context specific for this company
23
23
  def get_company_context(self, **kwargs) -> str:
24
24
  raise NotImplementedError("La subclase debe implementar el método get_company_context()")
25
25
 
26
+ @abstractmethod
27
+ # get context specific for this company
28
+ def get_user_info(self, **kwargs) -> str:
29
+ raise NotImplementedError("La subclase debe implementar el método get_user_info()")
30
+
26
31
  @abstractmethod
27
32
  # execute the specific action configured in the intent table
28
33
  def handle_request(self, tag: str, params: dict) -> dict:
@@ -38,5 +43,11 @@ class BaseCompany(ABC):
38
43
  def get_metadata_from_filename(self, filename: str) -> dict:
39
44
  raise NotImplementedError("La subclase debe implementar el método get_query_context()")
40
45
 
46
+ def register_cli_commands(self, app):
47
+ """
48
+ optional method for a company definition of it's cli commands
49
+ """
50
+ pass
51
+
41
52
  def unsupported_operation(self, tag):
42
53
  raise NotImplementedError(f"La operación '{tag}' no está soportada por esta empresa.")
@@ -0,0 +1,77 @@
1
+ import click
2
+ import logging
3
+ from iatoolkit import IAToolkit
4
+ from services.dispatcher_service import Dispatcher
5
+ from services.profile_service import ProfileService
6
+
7
+ def register_core_commands(app):
8
+ """Registra los comandos CLI del núcleo de IAToolkit."""
9
+
10
+ @app.cli.command("setup-all-companies")
11
+ def setup_all_companies():
12
+ """🗄️ Inicializa todas las compañías registradas en la base de datos."""
13
+ try:
14
+ dispatcher = IAToolkit.get_instance().get_injector().get(Dispatcher)
15
+ click.echo("🚀 Inicializando base de datos y compañías...")
16
+ dispatcher.setup_all_companies()
17
+ click.echo("✅ Base de datos y compañías inicializadas correctamente.")
18
+ except Exception as e:
19
+ logging.exception(e)
20
+ click.echo(f"❌ Error: {e}")
21
+
22
+ @app.cli.command("setup-company")
23
+ @click.argument("company_short_name")
24
+ def setup_company(company_short_name: str):
25
+ """⚙️ Genera una nueva API key para una compañía ya registrada."""
26
+ try:
27
+ profile_service = IAToolkit.get_instance().get_injector().get(ProfileService)
28
+ click.echo(f"🔑 Generando API key para '{company_short_name}'...")
29
+ result = profile_service.new_api_key(company_short_name)
30
+
31
+ if 'error' in result:
32
+ click.echo(f"❌ Error: {result['error']}")
33
+ click.echo("👉 Asegúrate de que el nombre de la compañía es correcto y está registrada.")
34
+ else:
35
+ click.echo("✅ ¡Configuración lista! Agrega esta variable a tu entorno:")
36
+ click.echo(f"IATOOLKIT_API_KEY={result['api-key']}")
37
+ except Exception as e:
38
+ logging.exception(e)
39
+ click.echo(f"❌ Ocurrió un error inesperado durante la configuración: {e}")
40
+
41
+ @app.cli.command("encrypt-key")
42
+ @click.argument("key")
43
+ def api_key(key: str):
44
+ from common.util import Utility
45
+
46
+ util = IAToolkit.get_instance().get_injector().get(Utility)
47
+ try:
48
+ encrypt_key = util.encrypt_key(key)
49
+ click.echo(f'la clave encriptada es: {encrypt_key} \n')
50
+ except Exception as e:
51
+ logging.exception(e)
52
+ click.echo(f"Error: {str(e)}")
53
+
54
+ @app.cli.command("exec-tasks")
55
+ @click.argument("company_short_name")
56
+ def exec_pending_tasks(company_short_name: str):
57
+ from services.tasks_service import TaskService
58
+ task_service = IAToolkit.get_instance().get_injector().get(TaskService)
59
+
60
+ try:
61
+ result = task_service.trigger_pending_tasks(company_short_name)
62
+ click.echo(result['message'])
63
+ except Exception as e:
64
+ logging.exception(e)
65
+ click.echo(f"Error: {str(e)}")
66
+
67
+ @app.cli.command("load")
68
+ def load_documents():
69
+ from services.load_documents_service import LoadDocumentsService
70
+
71
+ load_documents_service = IAToolkit.get_instance().get_injector().get(LoadDocumentsService)
72
+ try:
73
+ result = load_documents_service.load()
74
+ click.echo(result['message'])
75
+ except Exception as e:
76
+ logging.exception(e)
77
+ click.echo(f"Error: {str(e)}")
@@ -15,7 +15,6 @@ from urllib.parse import urlparse
15
15
  import redis
16
16
  import logging
17
17
  import os
18
- import click
19
18
  from typing import Optional, Dict, Any
20
19
  from repositories.database_manager import DatabaseManager
21
20
  from injector import Binder, singleton, Injector
@@ -252,14 +251,12 @@ class IAToolkit:
252
251
 
253
252
  def _bind_repositories(self, binder: Binder):
254
253
  from repositories.document_repo import DocumentRepo
255
- from repositories.document_type_repo import DocumentTypeRepo
256
254
  from repositories.profile_repo import ProfileRepo
257
255
  from repositories.llm_query_repo import LLMQueryRepo
258
256
  from repositories.vs_repo import VSRepo
259
257
  from repositories.tasks_repo import TaskRepo
260
258
 
261
259
  binder.bind(DocumentRepo, to=DocumentRepo)
262
- binder.bind(DocumentTypeRepo, to=DocumentTypeRepo)
263
260
  binder.bind(ProfileRepo, to=ProfileRepo)
264
261
  binder.bind(LLMQueryRepo, to=LLMQueryRepo)
265
262
  binder.bind(VSRepo, to=VSRepo)
@@ -321,52 +318,29 @@ class IAToolkit:
321
318
  Bcrypt(self.app)
322
319
 
323
320
  def _setup_cli_commands(self):
324
- """⌨️ Configura comandos CLI básicos"""
321
+ from iatoolkit.cli_commands import register_core_commands
325
322
  from services.dispatcher_service import Dispatcher
326
- from services.profile_service import ProfileService
323
+ from iatoolkit.company_registry import get_company_registry
324
+
325
+ # 1. Register core commands
326
+ register_core_commands(self.app)
327
+ logging.info("✅ Comandos CLI del núcleo registrados.")
327
328
 
328
- @self.app.cli.command("init-db")
329
- def init_db():
330
- """🗄️ Inicializa la base de datos del sistema"""
331
- try:
332
- dispatcher = self.get_injector().get(Dispatcher)
333
-
334
- click.echo("🚀 Inicializando base de datos...")
335
- dispatcher.setup_all_companies()
336
- click.echo("✅ Base de datos inicializada correctamente")
337
-
338
- except Exception as e:
339
- logging.exception(e)
340
- click.echo(f"❌ Error: {e}")
341
-
342
-
343
- @self.app.cli.command("setup-company")
344
- @click.argument("company_short_name")
345
- def setup_company(company_short_name: str):
346
- """⚙️ Ejecuta el proceso de configuración para una nueva empresa."""
347
- try:
348
- # step 1: init the database
349
- dispatcher = self.get_injector().get(Dispatcher)
350
- click.echo("🚀 step 1 of 2: init companies in the database...")
351
- dispatcher.setup_all_companies()
352
- click.echo("✅ database is ready.")
353
-
354
- # step 2: generate the api key
355
- profile_service = self.get_injector().get(ProfileService)
356
- click.echo(f"🔑 step 2 of 2: generating api-key for use in '{company_short_name}'...")
357
- result = profile_service.new_api_key(company_short_name)
358
-
359
- if 'error' in result:
360
- click.echo(f"❌ Error in step 2: {result['error']}")
361
- click.echo("👉 Make sure company name is correct and it's initialized in your app.")
362
- else:
363
- click.echo("Configuration es ready, add this variable to your environment")
364
- click.echo(f"IATOOLKIT_API_KEY={result['api-key']}")
365
-
366
- except Exception as e:
367
- logging.exception(e)
368
- click.echo(f"❌ Ocurrió un error inesperado durante la configuración: {e}")
329
+ # 2. Register company-specific commands
330
+ try:
331
+ # Get the dispatcher, which holds the company instances
332
+ dispatcher = self.get_injector().get(Dispatcher)
333
+ registry = get_company_registry()
369
334
 
335
+ # Iterate through the registered company names
336
+ for company_name in registry.get_registered_companies():
337
+ company_instance = dispatcher.get_company_instance(company_name)
338
+ if company_instance:
339
+ company_instance.register_cli_commands(self.app)
340
+ logging.info(f"✅ Comandos CLI para la compañía '{company_name}' registrados.")
341
+
342
+ except Exception as e:
343
+ logging.error(f"❌ Error durante el registro de comandos de compañías: {e}")
370
344
 
371
345
  def _setup_context_processors(self):
372
346
  # Configura context processors para templates
@@ -0,0 +1,32 @@
1
+ Mi app es un chatbot multiempresa, que se conecta
2
+ con datos empresariales por sql y llamadas a endpoints.
3
+
4
+ Es principalmente un backend, pero tambien puede actuar como frontend.
5
+ Cuando actua como backend, los usuarios se identifican como external_user_id
6
+ y el front lo informa en la vista external_login_view.
7
+
8
+ Cuando actua como front y back,
9
+ existe un acceso que simula un usuario externo a través de
10
+ public_chat_view.
11
+
12
+ El otro mecanismo de acceso es con el sistema de usuarios
13
+ integrado a mi app, con tablas propias: user, company, user_company
14
+ y el acceso se hace a través de profile_service.
15
+
16
+ Estos accesos sirven para autentificar las consultas.
17
+ Cada vez que hay un acceso en estos puntos, se llama a query_service
18
+ para que construya el contexto de la empresa y se lo envie al
19
+ llm.
20
+
21
+ Se guarda en session de redis, el response.id generado
22
+ para linkearlo con las consultas futuras (responses api de openai).
23
+
24
+ Por otra parte, las queries ingresan a través de llm_query_view,
25
+ el que autentifica al solicitante y la dirige a query_service.
26
+
27
+ Query_service gestiona los prompts que pueden ser solicitados
28
+ y llama a invoke en openai_client, quien se encarga
29
+ de interactuar con el llm.
30
+
31
+ Una vez terminada la interacción, se graba un registro en la
32
+ tabla llm_queries.
@@ -0,0 +1,97 @@
1
+ ## **Formato y Estilos de Salida (Reglas Generales)**
2
+
3
+ Las siguientes reglas son **obligatorias** y se deben aplicar a **TODA** la salida, sin excepción.
4
+
5
+ - **Uso de Secciones y Títulos (`<h4>`):**
6
+ Este formato solo debe aplicarse cuando el prompt específico lo pida,
7
+ o cuando la respuesta sea compleja y contenga
8
+ **múltiples puntos distintos** que necesiten ser agrupados bajo
9
+ títulos diferentes.
10
+
11
+ - **IMPORTANTE: Cómo manejar respuestas simples:** Para una respuesta que sea una sola frase o un párrafo corto y directo, **NO** generes una sección.
12
+
13
+ **Ejemplo de lo que NO se debe hacer:**
14
+ `<div class="categoria"><h4>Respuesta</h4>El cliente no tiene ofertas.</div>`
15
+
16
+ **Ejemplo de lo que SÍ se debe hacer:**
17
+ `El cliente no tiene ofertas.`
18
+
19
+ ### **Formatos Específicos (Usar solo bajo demanda explícita)**
20
+
21
+ Las siguientes reglas describen el formato a utilizar
22
+ **SÓLO SI el prompt específico te pide generar uno de estos elementos**.
23
+ Si no se te pide una tabla o una lista, ignora estas instrucciones.
24
+
25
+ ## Formato para Tablas:**
26
+ Cuando un prompt te solicite explícitamente generar una tabla**,
27
+ debes aplicar el siguiente formato base:
28
+ - Usa las clases `table table-striped table-hover` de Bootstrap como mínimo.
29
+ - cuando generes tablas si la columna es un rut, fecha, un id certificado
30
+ siempre usa la clase **nowrap** en el <th>
31
+ - debes usar class="text-center" o class="text-right"
32
+ si te piden que alguna columna debe centrarse o alinearse a la derecha.
33
+ - Cuando generes una tabla con columnas centradas o alineadas
34
+ a la derecha , **agrega `class="text-center"` o
35
+ `class="text-right"` también en cada `<td>` correspondiente,
36
+ no solo en el `<th>`.
37
+
38
+ Ejemplo:
39
+ <table class="table table-striped table-hover">
40
+ <thead>
41
+ <tr>
42
+ <th class="nowrap">RUT</th>
43
+ <th>Nombre</th>
44
+ <th class="text-center">Estado</th>
45
+ <th class="text-right">Monto</th>
46
+ </tr>
47
+ </thead>
48
+ <tbody>
49
+ <tr>
50
+ <td class="nowrap">12.345.678-9</td>
51
+ <td>ACME Ltda.</td>
52
+ <td class="text-center">Activo</td>
53
+ <td class="text-right">$1.000.000</td>
54
+ </tr>
55
+ </tbody>
56
+ </table>
57
+
58
+
59
+ ## Formato para Listas Clave-Valor:**
60
+ Cuando un prompt te solicite explícitamente mostrar datos como una lista de "clave-valor"**,
61
+ debes usar el siguiente formato:
62
+ - La clave (el texto antes de los dos puntos) SIEMPRE debe estar
63
+ envuelta en una etiqueta `<strong>`.
64
+ - Ejemplo:
65
+ `<ul>
66
+ <li><strong>Nombre:</strong> Juan Pérez</li>
67
+ <li><strong>ID de Cliente:</strong> 84321</li>
68
+ </ul>
69
+ `
70
+
71
+ ## Formato de Datos dentro de Elementos:**
72
+ Cuando generes tablas o listas que contengan fechas o montos**,
73
+ aplica estas reglas de formato:
74
+ - **Fechas:** `dd-mm-aaaa`
75
+ - **Montos:** Punto (`.`) como separador de miles y coma (`,`)
76
+ como separador decimal.
77
+ si son pesos chilenos coloca el signo $ delante
78
+ si son pesos chilenos ($), nunca pongas decimales
79
+
80
+ ## HTML para descarga de archivos
81
+ Siempre que generes un archivo Excel con la función generate_excel,
82
+ debes retornar al usuario un bloque HTML exacto con el enlace de descarga.
83
+
84
+ Usa esta plantilla reemplaza:
85
+ 1.{filename} por el nombre real del archivo generado
86
+ 2.{download_link} por el link para descargar el archivo generado
87
+ <p>✅ Tu archivo {filename} ha sido generado:</p>
88
+ <a href="{download_link}" download>
89
+ 📥 Descargar
90
+ </a>
91
+
92
+ ## HTML para envio de emails
93
+ Firma con "Saludos, IAToolkit".
94
+ ⚠️ No incluyas ningún enlace de suscripción, texto de "Unsubscribe",
95
+ ni frases sobre administrar preferencias de correo.
96
+
97
+
@@ -0,0 +1,67 @@
1
+ Eres un asistente que responde preguntas o ejecuta tareas según el contexto de la empresa.
2
+
3
+ ### **Nombre de la empresa**
4
+ ## Nombre: {{company.name}}
5
+
6
+ ### ** Información del usuario que esta consultando este chat**
7
+ Contexto del usuario:
8
+ - Nombre: {{ user_fullname }}
9
+ - Email: {{ user_email }}
10
+ - Tipo de usuario: {% if user_is_local %}Interno{% else %}Externo{% endif %}
11
+ - Empresa: {{ company_name }}
12
+
13
+ {% if user_name %}
14
+ El usuario que consulta se identifica con la variable `user_name` y tiene el
15
+ siguiente valor: {{ user_name }}.
16
+
17
+ Este usuario tiene el rol: {{ user_rol }} en el producto {{ user_product }}.
18
+
19
+ {% else %}
20
+ El usuario que consulta se identifica como: {{ user_id }}
21
+ {% endif %}
22
+
23
+ ## Servicios de datos (function calls) disponibles en {{company.name}}:
24
+ {% for service in service_list %}
25
+ # servicio {{ loop.index }}: {{ service.name }}
26
+ {% endfor %}
27
+
28
+ Eres un asistente que responde preguntas sobre empresas y sus clientes.
29
+
30
+ **Reglas obligatorias de contexto:**
31
+ 1. Cada vez que el usuario consulte por un cliente (ya sea por RUT o nombre),
32
+ debes memorizarlo y usarlo como cliente de contexto.
33
+ 2. Si el usuario hace una pregunta **sin especificar un cliente** (sin RUT ni nombre),
34
+ siempre debes asumir que la pregunta se refiere al **último cliente identificado** en la conversación.
35
+ 3. Nunca cambies de cliente de contexto a menos que el usuario especifique uno nuevo.
36
+ 4. Si el usuario pregunta por un cliente que no está en tus registros, responde indicando que no tienes información, pero **no borres el contexto anterior**.
37
+ 5. No respondas con “no se encontró información del cliente” salvo que nunca se haya identificado ningún cliente antes en la conversación.
38
+ 6. No debes incluir explicaciones, comentarios o texto adicional.
39
+
40
+ **IMPORTANTE:**
41
+ Si el usuario no menciona explícitamente nombre ni RUT en la pregunta, SIEMPRE responde usando el **último cliente** del que se obtuvo información.
42
+
43
+ No respondas nunca sobre un cliente anterior si ya se identificó uno nuevo, y nunca pierdas el contexto salvo que el usuario lo cambie explícitamente.
44
+
45
+ ### **Instrucciones**
46
+ 1. Devuelve siempre la respuesta en formato JSON.
47
+ 2. Usa la información de contexto para responder la consulta del usuario de forma clara y concisa.
48
+ Si el contexto o los adjuntos no te proveen información para la respuesta utiliza tu conocimiento propio.
49
+ 3. Genera una única `"answer"` que integre la información de todos los servicios relevantes.
50
+ 4. En `"aditional_data"`, a menos que se te indique lo contrario retorna un diccionario vacio: {}
51
+ 5. El JSON de salida solo puede tener dos llaves: `"answer"` y `"aditional_data"`.
52
+ 6. `"answer"` siempre debe contener un unico string con tu respuesta
53
+ 7. Devuelve **únicamente** la respuesta en JSON válido, sin texto adicional.
54
+ 7. **NO devuelvas el JSON como un string ni como texto escapado.**
55
+ 9. **NO incluyas delimitadores como \`\`\`, \`\`\`json, ni comillas alrededor del objeto JSON.**
56
+ 10. **NO escribas ninguna explicación ni texto fuera del JSON. Devuelve solo el objeto JSON.**
57
+
58
+
59
+ ### **Ejemplo de salida esperada**
60
+ {
61
+ "answer": "El iPhone 15 Pro está disponible por 1099 USD y tu ticket de soporte está en proceso.",
62
+ "aditional_data": {}
63
+ }
64
+
65
+
66
+
67
+