pbesa 4.0.12__tar.gz → 4.0.13__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.
Files changed (111) hide show
  1. {pbesa-4.0.12 → pbesa-4.0.13}/PKG-INFO +1 -1
  2. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/celulas/celula_casos.py +2 -2
  3. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/celulas/celula_consultas.py +2 -2
  4. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/celulas/celula_datos_identificables.py +2 -2
  5. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/celulas/celula_generar_documento.py +2 -2
  6. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/celulas/celula_saludos.py +2 -2
  7. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/cognitive.py +48 -17
  8. pbesa-4.0.13/pbesa/models.py +314 -0
  9. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/social/dispatcher_team.py +152 -6
  10. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/social/worker.py +4 -2
  11. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa.egg-info/PKG-INFO +1 -1
  12. {pbesa-4.0.12 → pbesa-4.0.13}/setup.py +1 -1
  13. pbesa-4.0.12/pbesa/models.py +0 -149
  14. {pbesa-4.0.12 → pbesa-4.0.13}/.gitignore +0 -0
  15. {pbesa-4.0.12 → pbesa-4.0.13}/LICENSE +0 -0
  16. {pbesa-4.0.12 → pbesa-4.0.13}/LICENSE.txt +0 -0
  17. {pbesa-4.0.12 → pbesa-4.0.13}/MANIFEST +0 -0
  18. {pbesa-4.0.12 → pbesa-4.0.13}/README.md +0 -0
  19. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/db.sqlite3 +0 -0
  20. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/helloworld/__init__.py +0 -0
  21. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/helloworld/__pycache__/__init__.cpython-36.pyc +0 -0
  22. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/helloworld/__pycache__/pbesa.cpython-36.pyc +0 -0
  23. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/helloworld/__pycache__/settings.cpython-36.pyc +0 -0
  24. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/helloworld/__pycache__/urls.cpython-36.pyc +0 -0
  25. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/helloworld/__pycache__/wsgi.cpython-36.pyc +0 -0
  26. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/helloworld/asgi.py +0 -0
  27. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/helloworld/pbesa.py +0 -0
  28. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/helloworld/settings.py +0 -0
  29. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/helloworld/urls.py +0 -0
  30. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/helloworld/wsgi.py +0 -0
  31. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/manage.py +0 -0
  32. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/__init__.py +0 -0
  33. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/__pycache__/__init__.cpython-36.pyc +0 -0
  34. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/__pycache__/admin.cpython-36.pyc +0 -0
  35. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/__pycache__/apps.cpython-36.pyc +0 -0
  36. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/__pycache__/models.cpython-36.pyc +0 -0
  37. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/__pycache__/urls.cpython-36.pyc +0 -0
  38. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/__pycache__/views.cpython-36.pyc +0 -0
  39. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/admin.py +0 -0
  40. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/apps.py +0 -0
  41. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/mas/controller/__pycache__/translatecontroller.cpython-36.pyc +0 -0
  42. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/mas/controller/__pycache__/translatedelegate.cpython-36.pyc +0 -0
  43. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/mas/controller/__pycache__/translateresponse.cpython-36.pyc +0 -0
  44. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/mas/controller/translatecontroller.py +0 -0
  45. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/mas/controller/translatedelegate.py +0 -0
  46. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/mas/controller/translateresponse.py +0 -0
  47. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/mas/worker/__pycache__/translatetask.cpython-36.pyc +0 -0
  48. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/mas/worker/__pycache__/workeragent.cpython-36.pyc +0 -0
  49. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/mas/worker/translatetask.py +0 -0
  50. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/mas/worker/workeragent.py +0 -0
  51. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/migrations/__init__.py +0 -0
  52. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/migrations/__pycache__/__init__.cpython-36.pyc +0 -0
  53. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/models.py +0 -0
  54. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/tests.py +0 -0
  55. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/urls.py +0 -0
  56. {pbesa-4.0.12 → pbesa-4.0.13}/examples/django/helloworld/translate/views.py +0 -0
  57. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/controller/__pycache__/countercontroller.cpython-36.pyc +0 -0
  58. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/controller/__pycache__/counterdelegate.cpython-36.pyc +0 -0
  59. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/controller/__pycache__/counterresponse.cpython-36.pyc +0 -0
  60. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/controller/__pycache__/translatecontroller.cpython-36.pyc +0 -0
  61. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/controller/__pycache__/translatedelegate.cpython-36.pyc +0 -0
  62. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/controller/__pycache__/translateresponse.cpython-36.pyc +0 -0
  63. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/controller/countercontroller.py +0 -0
  64. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/controller/counterdelegate.py +0 -0
  65. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/controller/counterresponse.py +0 -0
  66. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/worker/__pycache__/counteragent.cpython-36.pyc +0 -0
  67. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/worker/__pycache__/countertask.cpython-36.pyc +0 -0
  68. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/worker/__pycache__/translatetask.cpython-36.pyc +0 -0
  69. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/worker/__pycache__/workeragent.cpython-36.pyc +0 -0
  70. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/worker/counteragent.py +0 -0
  71. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/mas/worker/countertask.py +0 -0
  72. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/remote_a.py +0 -0
  73. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/remote_b.py +0 -0
  74. {pbesa-4.0.12 → pbesa-4.0.13}/examples/remote/remote_c.py +0 -0
  75. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/__init__.py +0 -0
  76. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/celulas/__init__.py +0 -0
  77. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/celulas/web.py +0 -0
  78. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/__init__.py +0 -0
  79. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/adapter.py +0 -0
  80. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/agent.py +0 -0
  81. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/io/__init__.py +0 -0
  82. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/io/system_file.py +0 -0
  83. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/io/tcp_server.py +0 -0
  84. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/res/__init__.py +0 -0
  85. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/res/__pycache__/__init__.cpython-36.pyc +0 -0
  86. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/res/__pycache__/__init__.cpython-37.pyc +0 -0
  87. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/res/__pycache__/__init__.cpython-38.pyc +0 -0
  88. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/res/__pycache__/__init__.cpython-39.pyc +0 -0
  89. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/res/conf.json +0 -0
  90. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/util.py +0 -0
  91. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/kernel/world.py +0 -0
  92. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/mas.py +0 -0
  93. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/remote/__init__.py +0 -0
  94. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/remote/adm_listener.py +0 -0
  95. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/remote/adm_listener_handler.py +0 -0
  96. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/remote/exceptions.py +0 -0
  97. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/remote/remote_adm.py +0 -0
  98. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/remote/remote_adm_handler.py +0 -0
  99. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/social/__init__.py +0 -0
  100. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/social/collaborative_team.py +0 -0
  101. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/social/delegator.py +0 -0
  102. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/social/delegator_team.py +0 -0
  103. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/social/dialog.py +0 -0
  104. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/social/prompts.py +0 -0
  105. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/social/selected_dispatcher_team.py +0 -0
  106. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa/social/templates.py +0 -0
  107. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa.egg-info/SOURCES.txt +0 -0
  108. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa.egg-info/dependency_links.txt +0 -0
  109. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa.egg-info/requires.txt +0 -0
  110. {pbesa-4.0.12 → pbesa-4.0.13}/pbesa.egg-info/top_level.txt +0 -0
  111. {pbesa-4.0.12 → pbesa-4.0.13}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pbesa
3
- Version: 4.0.12
3
+ Version: 4.0.13
4
4
  License-File: LICENSE
5
5
  License-File: LICENSE.txt
6
6
  Requires-Dist: pymongo>=4.6.3
@@ -42,13 +42,13 @@ Clasificación:
42
42
  """
43
43
 
44
44
  # Efectua la inferencia del modelo.
45
- def derive(service, text) -> any:
45
+ def derive(service, text, max_tkns=4096) -> any:
46
46
  try:
47
47
  logging.info(f"Procesando: {text}")
48
48
  tmp_work_memory = []
49
49
  prompt = PROMPT % text
50
50
  tmp_work_memory.append({"role": "user", "content": prompt})
51
- res = service.generate(tmp_work_memory)
51
+ res = service.generate(tmp_work_memory, max_tokens=max_tkns)
52
52
  logging.info(f"Respuesta: {res}")
53
53
  if not res or res == "":
54
54
  res = text
@@ -27,13 +27,13 @@ Clasificación:
27
27
  """
28
28
 
29
29
  # Efectua la inferencia del modelo.
30
- def derive(service, text) -> any:
30
+ def derive(service, text, max_tkns=4096) -> any:
31
31
  try:
32
32
  logging.info(f"Procesando: {text}")
33
33
  tmp_work_memory = []
34
34
  prompt = PROMPT % text
35
35
  tmp_work_memory.append({"role": "user", "content": prompt})
36
- res = service.generate(tmp_work_memory)
36
+ res = service.generate(tmp_work_memory, max_tokens=max_tkns)
37
37
  logging.info(f"Respuesta: {res}")
38
38
  if not res or res == "":
39
39
  res = text
@@ -35,12 +35,12 @@ Clasificación:
35
35
  """
36
36
 
37
37
  # Efectua la inferencia del modelo.
38
- def derive(service, text) -> any:
38
+ def derive(service, text, max_tkns=4096) -> any:
39
39
  try:
40
40
  tmp_work_memory = []
41
41
  prompt = PROMPT % text
42
42
  tmp_work_memory.append({"role": "user", "content": prompt})
43
- res = service.generate(tmp_work_memory)
43
+ res = service.generate(tmp_work_memory, max_tokens=max_tkns)
44
44
  logging.info(f"Procesando: {text}")
45
45
  logging.info(f"Respuesta: {res}")
46
46
  if not res or res == "":
@@ -26,13 +26,13 @@ DEMANDA FORMATEADA:
26
26
  """
27
27
 
28
28
  # Efectua la inferencia del modelo.
29
- def derive(service, formato, text) -> any:
29
+ def derive(service, formato, text, max_tkns=4096) -> any:
30
30
  try:
31
31
  logging.info(f"Procesando: {text}")
32
32
  tmp_work_memory = []
33
33
  prompt = PROMPT % (formato, text)
34
34
  tmp_work_memory.append({"role": "user", "content": prompt})
35
- res = service.generate(tmp_work_memory)
35
+ res = service.generate(tmp_work_memory, max_tokens=max_tkns)
36
36
  logging.info(f"Respuesta: {res}")
37
37
  if not res or res == "":
38
38
  res = text
@@ -23,13 +23,13 @@ Clasificación:
23
23
  """
24
24
 
25
25
  # Efectua la inferencia del modelo.
26
- def derive(service, text) -> any:
26
+ def derive(service, text, max_tkns=4096) -> any:
27
27
  try:
28
28
  logging.info(f"Procesando: {text}")
29
29
  tmp_work_memory = []
30
30
  prompt = PROMPT % text
31
31
  tmp_work_memory.append({"role": "user", "content": prompt})
32
- res = service.generate(tmp_work_memory)
32
+ res = service.generate(tmp_work_memory, max_tokens=max_tkns)
33
33
  logging.info(f"Respuesta: {res}")
34
34
  if not res or res == "":
35
35
  res = text
@@ -12,7 +12,7 @@
12
12
  # --------------------------------------------------------
13
13
  # Define resources
14
14
  # --------------------------------------------------------
15
-
15
+ import re
16
16
  import uuid
17
17
  import json
18
18
  import logging
@@ -21,13 +21,14 @@ from .mas import Adm
21
21
  from pydantic import BaseModel
22
22
  from typing import List, Optional
23
23
  from abc import ABC, abstractmethod
24
- from pbesa.models import AIFoundry, AzureInference, GPTService, ServiceProvider
24
+ from pbesa.models import AIFoundry, AzureInference, AzureOpenAIInference, GPTService, ServiceProvider
25
25
  from pbesa.social.dialog import (
26
26
  DialogState, imprimir_grafo, recorrer_interacciones, extraer_diccionario_nodos,
27
27
  ActionNode, DeclarativeNode, GotoNode)
28
28
  from .celulas import (celula_casos, celula_consultas, celula_saludos, celula_datos_identificables,
29
29
  celula_generar_documento)
30
30
  from pbesa.social.prompts import CLASSIFICATION_PROMPT, DERIVE_PROMPT, RECOVERY_PROMPT, ADAPT_PROMPT
31
+
31
32
  # --------------------------------------------------------
32
33
  # Define DTOs
33
34
  # --------------------------------------------------------
@@ -124,6 +125,9 @@ def define_service_provider(provider, ai_service=None) -> None:
124
125
  elif "AZURE_INFERENCE" in provider:
125
126
  service = AzureInference()
126
127
  service_provider.register("AZURE_INFERENCE", service)
128
+ elif "AZURE_OPEN_AI_INFERENCE" in provider:
129
+ service = AzureOpenAIInference()
130
+ service_provider.register("AZURE_INFERENCE", service)
127
131
  return service_provider, service
128
132
 
129
133
  # --------------------------------------------------------
@@ -703,11 +707,13 @@ class Dialog(ABC):
703
707
  logging.info(f'>>>> Response: {response}')
704
708
  return response['message']['response']
705
709
  else:
706
- if operation:
710
+ if operation and not operation == "Ninguno":
707
711
  dto = {
708
712
  "data": {
709
713
  'text': data,
710
- 'operation': operation
714
+ 'operation': operation,
715
+ 'id_conversacion': self.state['id_conversacion'],
716
+ 'session_id': self.state['session_id'],
711
717
  },
712
718
  }
713
719
  else:
@@ -740,7 +746,7 @@ class Dialog(ABC):
740
746
  try:
741
747
  prompt = RECOVERY_PROMPT % query
742
748
  temp_work_memory = [{"role": "user", "content": prompt}]
743
- res = self.__ai_service.generate(temp_work_memory)
749
+ res = self.__ai_service.generate(temp_work_memory, max_tokens=500)
744
750
  res = self.get_text(res)
745
751
  if res and not res == "":
746
752
  return self.__recovery['owner'], self.__recovery['performative'], res, self.__recovery['team']
@@ -777,14 +783,16 @@ class Dialog(ABC):
777
783
  logging.info(f"------------Flujo de excepcion---------------")
778
784
  self.notify("identificando intencion...")
779
785
  # Obtiene discriminadores
780
- caso = celula_casos.derive(self.__ai_service, query)
781
- consulta = celula_consultas.derive(self.__ai_service, query)
782
- saludo = celula_saludos.derive(self.__ai_service, query)
786
+ caso = celula_casos.derive(self.__ai_service, query, max_tkns=10)
787
+ consulta = celula_consultas.derive(self.__ai_service, query, max_tkns=10)
788
+ saludo = celula_saludos.derive(self.__ai_service, query, max_tkns=10)
783
789
  # Verifica si es un saludo
784
790
  es_saludo = ("SALUDO" in saludo) and ("NO_PREGUNTA" in consulta) and ("NO_QUEJA_DEMANDA" in caso) and not ("NO_SALUDO" in saludo)
785
791
  es_consulta = ("PREGUNTA_O_SOLICITUD" in consulta) and ("NO_QUEJA_DEMANDA" in caso) and ("NO_SALUDO" in saludo) and not ("NO_PREGUNTA" in consulta)
786
792
  es_caso = ("QUEJA_DEMANDA" in caso) and ("NO_PREGUNTA" in consulta) and ("NO_SALUDO" in saludo) and not ("NO_QUEJA_DEMANDA" in caso)
787
- logging.info(f"==> Saludo: {saludo}, Consulta: {consulta}, Caso: {caso}")
793
+
794
+ print("\n--- Clase ---")
795
+ logging.info(f"==> Saludo: {saludo}, Consulta: {consulta}, Caso: {caso}")
788
796
  logging.info(f"==> Es saludo: {es_saludo}, Es consulta: {es_consulta}, Es caso: {es_caso}")
789
797
  # Verifica los casos
790
798
  dicriminador = "Ninguno"
@@ -806,6 +814,10 @@ class Dialog(ABC):
806
814
  dicriminador = "consulta"
807
815
  elif saludo == "SALUDO":
808
816
  dicriminador = "saluda"
817
+ logging.info(f"==> Discriminador: {dicriminador}")
818
+ if dicriminador == "Ninguno":
819
+ dicriminador = "consulta"
820
+ print("---------------------\n")
809
821
  #--------------------------
810
822
  # Obtiene los hijos del
811
823
  # nodo
@@ -865,7 +877,7 @@ class Dialog(ABC):
865
877
  prompt = CLASSIFICATION_PROMPT % (query, options)
866
878
  logging.info(f"Query: {query},\n Options:\n{options}")
867
879
  self.__meta_work_memory.append({"role": "user", "content": prompt})
868
- res = self.__ai_service.generate(self.__meta_work_memory)
880
+ res = self.__ai_service.generate(self.__meta_work_memory, max_tokens=10)
869
881
  logging.info(f"Thought: {res}")
870
882
  self.__meta_work_memory = []
871
883
  res = self.get_text(res)
@@ -883,9 +895,17 @@ class Dialog(ABC):
883
895
  select_node = children[0]
884
896
  else:
885
897
  logging.info("???> Es un nodo terminal o iniciador")
886
- logging.warning(f"???> Es un nodo terminal o iniciador: {node.text}")
887
- logging.warning(f"???> Es un nodo terminal o iniciador: {node.performative}")
898
+ logging.warning(f"???> Es un nodo terminal o iniciador: {node}")
899
+ logging.warning(f"???> Es un nodo terminal o iniciador: {node}")
888
900
  return self.recovery(query)
901
+
902
+ # Verifica si el nodo fue seleccionado
903
+ if not select_node:
904
+ logging.warning(f"???> No se seleccionó ningún nodo")
905
+ logging.info(f"------------RESET---------------")
906
+ self.notify("STOP")
907
+ self.reset()
908
+ return "Web", DialogState.START, self.RECOVERY_MSG, "Web"
889
909
 
890
910
  #--------------------------
891
911
  # Verifica si es un nodo
@@ -987,7 +1007,7 @@ class Dialog(ABC):
987
1007
  logging.info("-----")
988
1008
  logging.info("\n%s", json.dumps(self.__work_memory, indent=4))
989
1009
  logging.info("-----")
990
- res = self.__ai_service.generate(self.__work_memory)
1010
+ res = self.__ai_service.generate(self.__work_memory, max_tokens=100)
991
1011
  logging.info(f"-> node tool: {res}")
992
1012
  res = self.get_text(res)
993
1013
  # Check if res is empty
@@ -1021,7 +1041,7 @@ class Dialog(ABC):
1021
1041
  logging.info("-----")
1022
1042
  logging.info("\n%s", json.dumps(self.__work_memory, indent=4))
1023
1043
  logging.info("-----")
1024
- res = self.__ai_service.generate(self.__work_memory)
1044
+ res = self.__ai_service.generate(self.__work_memory, max_tokens=1000)
1025
1045
  res = self.get_text(res)
1026
1046
  self.__work_memory.append({"role": "system", "content": res})
1027
1047
  # Check if res is empty
@@ -1088,7 +1108,7 @@ class Dialog(ABC):
1088
1108
  logging.info("-----")
1089
1109
  logging.info("\n%s", json.dumps(self.__work_memory, indent=4))
1090
1110
  logging.info("-----")
1091
- res = self.__ai_service.generate(self.__work_memory)
1111
+ res = self.__ai_service.generate(self.__work_memory, max_tokens=1000)
1092
1112
  res = self.get_text(res)
1093
1113
  self.__work_memory.append({"role": "system", "content": res})
1094
1114
  # Check if res is empty
@@ -1190,6 +1210,8 @@ class SpecialDispatch():
1190
1210
  # Get the role
1191
1211
  role = agent.get_role()
1192
1212
  self.__options_dict[agent_id] = role.description
1213
+ # Log
1214
+ logging.info(f"Agentes disponibles: {self.__options_dict.keys()}")
1193
1215
 
1194
1216
  def get_text(self, mensaje) -> str:
1195
1217
  if mensaje:
@@ -1218,13 +1240,22 @@ class SpecialDispatch():
1218
1240
  prompt = CLASSIFICATION_PROMPT % (query, options)
1219
1241
  logging.info(f"Query: {query},\n Options:\n{options}")
1220
1242
  self.__meta_work_memory.append({"role": "user", "content": prompt})
1221
- res = self.__ai_service.generate(self.__meta_work_memory)
1243
+ res = self.__ai_service.generate(self.__meta_work_memory, max_tokens=10)
1222
1244
  logging.info(f"Thought: {res}")
1223
1245
  self.__meta_work_memory = []
1224
1246
  res = self.get_text(res)
1225
1247
  select_agent = None
1248
+ compare = re.findall(r'\d+', res)
1249
+ print(f"Compare: {compare}")
1250
+ if len(compare) > 0:
1251
+ compare = compare[0]
1252
+ else:
1253
+ compare = res
1254
+ compare = compare.strip()
1255
+ print(f"Compare: {compare}")
1226
1256
  for option in range(1, cont+1):
1227
- if str(option) in res:
1257
+ print(f"Option: {option} - Compare: {compare}")
1258
+ if str(option) == compare:
1228
1259
  select_agent = agent_options[option]
1229
1260
  logging.info(f"Descripcion del agente seleccionado: {select_agent}")
1230
1261
  break
@@ -0,0 +1,314 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ ----------------------------------------------------------
4
+ -------------------------- PBESA -------------------------
5
+ ----------------------------------------------------------
6
+
7
+ @autor AKEN
8
+ @version 4.0.0
9
+ @date 08/08/24
10
+ """
11
+
12
+ # --------------------------------------------------------
13
+ # Define resources
14
+ # --------------------------------------------------------
15
+
16
+ import traceback
17
+ from openai import AzureOpenAI
18
+ from abc import ABC, abstractmethod
19
+ from azure.ai.projects import AIProjectClient
20
+ from azure.identity import DefaultAzureCredential
21
+ from azure.ai.inference import ChatCompletionsClient
22
+ from azure.core.credentials import AzureKeyCredential
23
+
24
+ import time
25
+ from openai import AzureOpenAI, RateLimitError, APIStatusError
26
+ from tenacity import (
27
+ retry,
28
+ stop_after_attempt,
29
+ wait_exponential,
30
+ retry_if_exception_type,
31
+ wait_fixed
32
+ )
33
+
34
+ # --------------------------------------------------------
35
+ # Define classes
36
+ # --------------------------------------------------------
37
+
38
+ class AIService(ABC):
39
+ """Abstract class for AI services."""
40
+
41
+ def __init__(self) -> None:
42
+ self.model:any = None
43
+ self.model_conf:dict = None
44
+
45
+ @abstractmethod
46
+ def setup(self, config: dict) -> None:
47
+ """Method to setup the AI service."""
48
+ raise NotImplementedError("Method 'setup' must be implemented.")
49
+
50
+ @abstractmethod
51
+ def generate(self, work_memory, max_tokens=4096, temperature=0, top_p=0.9) -> str:
52
+ """Method to generate a response based on user input."""
53
+ raise NotImplementedError("Method 'setup' must be implemented.")
54
+
55
+ class GPTService(AIService):
56
+
57
+ def __init__(self) -> None:
58
+ super().__init__()
59
+
60
+ def setup(self, config: dict, work_memory) -> None:
61
+ try:
62
+ self.model:any = config['model']
63
+ self.model_conf:dict = config
64
+ self.__work_memory:list = work_memory
65
+ except Exception as e:
66
+ raise Exception("Could not setup GPTService: check the configuration.")
67
+
68
+ def generate(self) -> str:
69
+ """ Generate method
70
+ :return: str
71
+ """
72
+ try:
73
+ # Genera texto con OpenAI.
74
+ self.model.api_key = self.model_conf['API_KEY']
75
+ engine = self.model_conf['OPENAI_ENGINE']
76
+ response = self.model.ChatCompletion.create(
77
+ model = engine,
78
+ messages = self.__work_memory
79
+ )
80
+ # Verifica si se obtuvo respuesta.
81
+ if response['choices'][0]['finish_reason'] == 'completed' or response['choices'][0]['finish_reason'] == 'stop':
82
+ res = response['choices'][0]['message']['content']
83
+ try:
84
+ if not res or res == 'null' or res == 'N/A' or 'N/A' in res:
85
+ #self.log.warning("OpenAI response not completed", extra={'log_data': {'gpt_response': response}})
86
+ print("OpenAI response not completed")
87
+ return None
88
+ #self.log.info("OpenAI response completed", extra={'log_data': {'gpt_response': response}})
89
+ print("OpenAI response completed")
90
+ self.__work_memory.append({"role": "assistant", "content": res})
91
+ return res
92
+ except:
93
+ #self.log.warning("OpenAI response not completed", extra={'log_data': {'gpt_response': response}})
94
+ print("OpenAI response not completed")
95
+ return None
96
+ else:
97
+ #self.log.warning("OpenAI response not completed", extra={'log_data': {'gpt_response': response}})
98
+ print("OpenAI response not completed")
99
+ return None
100
+ except Exception as e:
101
+ trace_err = traceback.format_exc()
102
+ err = str(e) + " - " + trace_err
103
+ print(err)
104
+ return None
105
+
106
+ class AIFoundry(AIService):
107
+
108
+ def __init__(self) -> None:
109
+ super().__init__()
110
+
111
+ def setup(self, config: dict, work_memory) -> None:
112
+ self.model_conf:dict = config
113
+ self.__work_memory:list = work_memory
114
+ project = AIProjectClient.from_connection_string(
115
+ conn_str=config['PROJECT_CONNECTION_STRING'], credential=DefaultAzureCredential()
116
+ )
117
+ self.model:any = project.inference.get_chat_completions_client()
118
+
119
+ def generate(self) -> str:
120
+ response = self.model.complete(
121
+ model=self.model_conf['AIFOUNDRY_MODEL'],
122
+ messages=self.__work_memory,
123
+ )
124
+ return response.choices[0].message.content
125
+
126
+ class AzureInference(AIService):
127
+
128
+ def __init__(self) -> None:
129
+ super().__init__()
130
+
131
+ def setup(self, config: dict) -> None:
132
+ self.model_conf:dict = config
133
+ self.model:any = ChatCompletionsClient(
134
+ endpoint=config['AZURE_INFERENCE_SDK_ENDPOINT'],
135
+ credential=AzureKeyCredential(config['AZURE_INFERENCE_SDK_KEY'])
136
+ )
137
+
138
+ def generate(self, work_memory) -> str:
139
+ response = self.model.complete(
140
+ messages= work_memory,
141
+ model =self.model_conf['DEPLOYMENT_NAME'],
142
+ max_tokens=self.model_conf['MAX_TOKENS']
143
+ )
144
+ return response.choices[0].message.content
145
+
146
+ # Función auxiliar para determinar la espera basada en el error
147
+ def wait_strategy_for_rate_limit(retry_state):
148
+ """
149
+ Determina la estrategia de espera.
150
+ Si es un RateLimitError y hay un header 'Retry-After', lo usa.
151
+ De lo contrario, usa un backoff exponencial.
152
+ """
153
+ exception = retry_state.outcome.exception()
154
+ if isinstance(exception, (RateLimitError, APIStatusError)):
155
+ if hasattr(exception, 'response') and exception.response:
156
+ headers = exception.response.headers
157
+ retry_after_seconds = headers.get("Retry-After")
158
+ if retry_after_seconds:
159
+ try:
160
+ wait_time = int(retry_after_seconds)
161
+ print(f"Rate limit: Respetando header Retry-After: esperando {wait_time} segundos.")
162
+ return wait_fixed(wait_time)(retry_state) # Usa wait_fixed para el tiempo específico
163
+ except ValueError:
164
+ print(f"Rate limit: Retry-After header no es un entero ({retry_after_seconds}). Usando backoff exponencial.")
165
+ else: # No hay Retry-After, usar backoff exponencial
166
+ print("Rate limit: No se encontró header Retry-After. Usando backoff exponencial.")
167
+ else: # No hay objeto response, usar backoff exponencial
168
+ print("Rate limit: No se encontró objeto response en la excepción. Usando backoff exponencial.")
169
+
170
+ # Fallback a backoff exponencial para otros casos o si Retry-After falla
171
+ return wait_exponential(multiplier=1, min=4, max=60)(retry_state)
172
+
173
+ class AzureOpenAIInference(AIService):
174
+
175
+ def __init__(self) -> None:
176
+ super().__init__()
177
+
178
+ def setup(self, config: dict) -> None:
179
+ self.model_conf:dict = config
180
+ self.model:any = AzureOpenAI(
181
+ api_version=config['API_VERSION'],
182
+ azure_endpoint=config['AZURE_INFERENCE_SDK_ENDPOINT'],
183
+ api_key=config['AZURE_INFERENCE_SDK_KEY'],
184
+ )
185
+ self.current_time = time.time()
186
+ self.total_tokens = 0
187
+
188
+ def _log_attempt_number(self, retry_state):
189
+ """Función para registrar el número de intento."""
190
+ print(f"Intento {retry_state.attempt_number} fallido. Reintentando...")
191
+
192
+ # Usamos retry_if_exception_type para especificar qué excepciones deben activar un reintento.
193
+ # wait_strategy_for_rate_limit determinará dinámicamente el tiempo de espera.
194
+ @retry(
195
+ retry=retry_if_exception_type((RateLimitError, APIStatusError)), # Reintentar en estos errores
196
+ wait=wait_strategy_for_rate_limit, # Estrategia de espera personalizada
197
+ stop=stop_after_attempt(5), # Número máximo de intentos (además del original)
198
+ before_sleep=_log_attempt_number # Función a llamar antes de esperar/dormir
199
+ )
200
+ def generate(self, work_memory, max_tokens=4096, temperature=0, top_p=0.9) -> str:
201
+ print(f"Generando completion para el modelo: {self.model_conf['DEPLOYMENT_NAME']}")
202
+ try:
203
+
204
+ # Espera 10 segubdos antes de la primera llamada
205
+ print("Esperando 3 segundos antes de la primera llamada...")
206
+ time.sleep(3)
207
+ print("Llamada a la API de OpenAI...")
208
+
209
+
210
+ response = self.model.chat.completions.create(
211
+ messages=work_memory,
212
+ max_tokens=max_tokens,
213
+ temperature=temperature,
214
+ top_p=top_p,
215
+ model=self.model_conf['DEPLOYMENT_NAME'],
216
+ )
217
+
218
+ # --- Agrega estas líneas para imprimir el uso de tokens ---
219
+ if hasattr(response, 'usage') and response.usage is not None:
220
+ print("\n--- Uso de Tokens ---")
221
+ print(f"Tokens enviados (prompt): {response.usage.prompt_tokens}")
222
+ print(f"Tokens recibidos (completion): {response.usage.completion_tokens}")
223
+ print(f"Tokens totales: {response.usage.total_tokens}")
224
+ self.total_tokens += response.usage.total_tokens
225
+ current_t = time.time()
226
+ elapsed_time = current_t - self.current_time
227
+ print(f"Tiempo transcurrido para la generación: {elapsed_time:.2f} segundos")
228
+ # En minutos
229
+ elapsed_time_minutes = elapsed_time / 60
230
+ print(f"Tiempo transcurrido para la generación: {elapsed_time_minutes:.2f} minutos")
231
+ print(f"Total de tokens generados hasta ahora: {self.total_tokens}")
232
+
233
+
234
+ if elapsed_time >= 60:
235
+ print("Reiniciando el contador de tiempo y tokens...")
236
+ self.total_tokens = 0
237
+
238
+ print("---------------------\n")
239
+ else:
240
+ print("\n--- Uso de Tokens no disponible en la respuesta ---")
241
+ # -------------------------------------------------------
242
+
243
+
244
+
245
+ return response.choices[0].message.content
246
+ except RateLimitError as e:
247
+ # Capturada específicamente para poder inspeccionarla si es necesario,
248
+ # pero tenacity se encargará del reintento si esta función la vuelve a lanzar.
249
+ print(f"Error de Límite de Tasa detectado (RateLimitError): {e.message}")
250
+ # Aquí podríamos añadir lógica específica si el error persiste después de los reintentos de tenacity
251
+ # o si queremos hacer algo antes de que tenacity lo maneje (aunque `before_sleep` es mejor para eso)
252
+ raise # Re-lanzar para que tenacity la maneje
253
+ except APIStatusError as e:
254
+ # Similar a RateLimitError, pero más general para errores de API con código de estado.
255
+ # La lógica de reintento de tenacity ya verifica el tipo, pero podemos ser explícitos.
256
+ print(f"Error de API detectado (APIStatusError): Código {e.status_code}, Mensaje: {e.message}")
257
+ if e.status_code == 429:
258
+ # Ya cubierto por retry_if_exception_type y wait_strategy_for_rate_limit
259
+ pass # Tenacity lo manejará
260
+ else:
261
+ # Si es otro APIStatusError que no queremos reintentar con esta política,
262
+ # podríamos manejarlo aquí o dejar que se propague si no está en retry_if_exception_type.
263
+ print(f"Error de API no relacionado con límite de tasa: {e.status_code}")
264
+ raise # Re-lanzar para que tenacity (si aplica) o el llamador lo maneje
265
+ except Exception as e:
266
+ print(f"Ocurrió un error inesperado durante la generación: {e}")
267
+ raise # Re-lanzar otras excepciones
268
+
269
+ # --- Ejemplo de Uso ---
270
+ if __name__ == '__main__':
271
+ # Simula una configuración
272
+ mock_config = {
273
+ 'API_VERSION': '2024-02-01', # Reemplaza con tu API version real
274
+ 'AZURE_INFERENCE_SDK_ENDPOINT': 'TU_ENDPOINT_AZURE_OPENAI', # Reemplaza
275
+ 'AZURE_INFERENCE_SDK_KEY': 'TU_API_KEY', # Reemplaza
276
+ 'DEPLOYMENT_NAME': 'gpt-4o' # O tu nombre de despliegue
277
+ }
278
+
279
+ # Reemplaza con tu endpoint y key reales para probar.
280
+ # ¡CUIDADO! Las siguientes líneas HARÁN LLAMADAS REALES si descomentas y configuras.
281
+ # Por ahora, para evitar llamadas reales en este ejemplo, el cliente no se usará activamente
282
+ # a menos que descomentes las líneas de llamada.
283
+
284
+ # client = LLMClient(mock_config)
285
+ # sample_work_memory = [{"role": "user", "content": "Hola, ¿cómo estás?"}]
286
+
287
+ # try:
288
+ # # Para probar el RateLimitError, necesitarías hacer muchas llamadas muy rápido
289
+ # # o simular que la API devuelve un RateLimitError.
290
+ # # Por ejemplo, podrías mockear `client.model.chat.completions.create`
291
+ # # para que lance un RateLimitError la primera vez.
292
+ # print("Intentando generar contenido...")
293
+ # # response_content = client.generate(sample_work_memory)
294
+ # # print("\nRespuesta del modelo:")
295
+ # # print(response_content)
296
+ # print("\nEjemplo de simulación (sin llamada real):")
297
+ # print("Si esto fuera una llamada real y ocurriera un error 429,")
298
+ # print("verías los mensajes de reintento de tenacity.")
299
+
300
+ class ServiceProvider:
301
+ _services = {}
302
+
303
+ @classmethod
304
+ def register(cls, name: str, service):
305
+ """Register a service with a unique name."""
306
+ cls._services[name] = service
307
+
308
+ @classmethod
309
+ def get(cls, name: str):
310
+ """Retrieve a registered service."""
311
+ service = cls._services.get(name)
312
+ if not service:
313
+ raise ValueError(f"Service '{name}' not found!")
314
+ return service