pbesa 4.0.4__tar.gz → 4.0.6__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.
- {pbesa-4.0.4 → pbesa-4.0.6}/PKG-INFO +1 -1
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/cognitive.py +288 -192
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/social/dialog.py +2 -1
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/social/dispatcher_team.py +16 -3
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/social/prompts.py +24 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/social/selected_dispatcher_team.py +1 -1
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/social/worker.py +3 -3
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa.egg-info/PKG-INFO +1 -1
- {pbesa-4.0.4 → pbesa-4.0.6}/setup.py +1 -1
- {pbesa-4.0.4 → pbesa-4.0.6}/.gitignore +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/LICENSE +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/LICENSE.txt +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/MANIFEST +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/README.md +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/db.sqlite3 +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/__init__.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/__pycache__/__init__.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/__pycache__/pbesa.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/__pycache__/settings.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/__pycache__/urls.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/__pycache__/wsgi.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/asgi.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/pbesa.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/settings.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/urls.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/wsgi.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/manage.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/__init__.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/__pycache__/__init__.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/__pycache__/admin.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/__pycache__/apps.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/__pycache__/models.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/__pycache__/urls.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/__pycache__/views.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/admin.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/apps.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/mas/controller/__pycache__/translatecontroller.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/mas/controller/__pycache__/translatedelegate.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/mas/controller/__pycache__/translateresponse.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/mas/controller/translatecontroller.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/mas/controller/translatedelegate.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/mas/controller/translateresponse.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/mas/worker/__pycache__/translatetask.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/mas/worker/__pycache__/workeragent.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/mas/worker/translatetask.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/mas/worker/workeragent.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/migrations/__init__.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/migrations/__pycache__/__init__.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/models.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/tests.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/urls.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/views.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/controller/__pycache__/countercontroller.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/controller/__pycache__/counterdelegate.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/controller/__pycache__/counterresponse.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/controller/__pycache__/translatecontroller.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/controller/__pycache__/translatedelegate.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/controller/__pycache__/translateresponse.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/controller/countercontroller.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/controller/counterdelegate.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/controller/counterresponse.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/worker/__pycache__/counteragent.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/worker/__pycache__/countertask.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/worker/__pycache__/translatetask.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/worker/__pycache__/workeragent.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/worker/counteragent.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/worker/countertask.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/remote_a.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/remote_b.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/remote_c.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/__init__.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/__init__.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/adapter.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/agent.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/io/__init__.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/io/system_file.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/io/tcp_server.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/res/__init__.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/res/__pycache__/__init__.cpython-36.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/res/__pycache__/__init__.cpython-37.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/res/__pycache__/__init__.cpython-38.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/res/__pycache__/__init__.cpython-39.pyc +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/res/conf.json +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/util.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/kernel/world.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/mas.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/models.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/remote/__init__.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/remote/adm_listener.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/remote/adm_listener_handler.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/remote/exceptions.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/remote/remote_adm.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/remote/remote_adm_handler.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/social/__init__.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/social/collaborative_team.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/social/delegator.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/social/delegator_team.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa/social/templates.py +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa.egg-info/SOURCES.txt +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa.egg-info/dependency_links.txt +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa.egg-info/requires.txt +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/pbesa.egg-info/top_level.txt +0 -0
- {pbesa-4.0.4 → pbesa-4.0.6}/setup.cfg +0 -0
@@ -21,7 +21,7 @@ from typing import List, Optional
|
|
21
21
|
from abc import ABC, abstractmethod
|
22
22
|
from pbesa.models import AIFoundry, AzureInference, GPTService, ServiceProvider
|
23
23
|
from pbesa.social.dialog import DialogState, imprimir_grafo, recorrer_interacciones, extraer_diccionario_nodos, ActionNode, DeclarativeNode#, TerminalNode
|
24
|
-
from pbesa.social.prompts import CLASSIFICATION_PROMPT, DERIVE_PROMPT
|
24
|
+
from pbesa.social.prompts import CLASSIFICATION_PROMPT, DERIVE_PROMPT, RECOVERY_PROMPT
|
25
25
|
# --------------------------------------------------------
|
26
26
|
# Define DTOs
|
27
27
|
# --------------------------------------------------------
|
@@ -468,6 +468,15 @@ class Dialog(ABC):
|
|
468
468
|
self.__deep_limit = 3
|
469
469
|
# Define knowledge
|
470
470
|
self.knowledge = None
|
471
|
+
# Define point recovery
|
472
|
+
self.__recovery = {
|
473
|
+
"owner": "Web",
|
474
|
+
"team": "Web",
|
475
|
+
"performative": DialogState.START,
|
476
|
+
"counter": 0
|
477
|
+
}
|
478
|
+
# Define recovery message
|
479
|
+
self.RECOVERY_MSG = "Lo lamento, puedes darme más detalles o reformular"
|
471
480
|
|
472
481
|
def setup_world(self):
|
473
482
|
""" Set up model method """
|
@@ -518,29 +527,29 @@ class Dialog(ABC):
|
|
518
527
|
self.setup_world()
|
519
528
|
interations = agent_metadata.role.interactions
|
520
529
|
grafo = recorrer_interacciones(interations)
|
521
|
-
logging.
|
530
|
+
logging.debug("")
|
522
531
|
# Si el grafo es una lista de nodos, lo imprimimos cada uno
|
523
532
|
if isinstance(grafo, list):
|
524
533
|
for nodo in grafo:
|
525
534
|
imprimir_grafo(nodo)
|
526
535
|
else:
|
527
536
|
imprimir_grafo(grafo)
|
528
|
-
logging.
|
537
|
+
logging.debug("")
|
529
538
|
# Ejemplo de uso:
|
530
539
|
self.__dfa = extraer_diccionario_nodos(grafo)
|
531
540
|
# Mostrar el diccionario
|
532
541
|
for clave, valor in self.__dfa.items():
|
533
|
-
logging.
|
534
|
-
logging.
|
542
|
+
logging.debug(f"{clave}: {valor.text}")
|
543
|
+
logging.debug("")
|
535
544
|
iniciadores = []
|
536
545
|
for item in interations:
|
537
546
|
for clave, valor in self.__dfa.items():
|
538
547
|
if valor.text == item['texto']:
|
539
548
|
iniciadores.append(valor)
|
540
|
-
logging.
|
549
|
+
logging.debug("Iniciadores:")
|
541
550
|
for iniciador in iniciadores:
|
542
|
-
logging.
|
543
|
-
logging.
|
551
|
+
logging.debug(iniciador.text)
|
552
|
+
logging.debug("")
|
544
553
|
# Set dialog state
|
545
554
|
self.__dfa['start'] = iniciadores
|
546
555
|
|
@@ -561,219 +570,306 @@ class Dialog(ABC):
|
|
561
570
|
self.__work_memory = []
|
562
571
|
# Set up model
|
563
572
|
self.setup_world()
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
573
|
+
# Reset dialog state
|
574
|
+
self.__recovery = {
|
575
|
+
"owner": "Web",
|
576
|
+
"team": "Web",
|
577
|
+
"performative": DialogState.START,
|
578
|
+
"counter": 0
|
579
|
+
}
|
580
|
+
|
581
|
+
def notify(self, text):
|
582
|
+
try:
|
583
|
+
canales = self.state['canales']
|
584
|
+
canal = canales.get("Webhook")
|
585
|
+
session_id = self.state['session_id']
|
570
586
|
dto = {
|
571
|
-
"
|
572
|
-
|
573
|
-
'operation': operation
|
574
|
-
},
|
587
|
+
"session_id": session_id,
|
588
|
+
"text": text[0:100]
|
575
589
|
}
|
576
|
-
|
577
|
-
if
|
578
|
-
logging.info(f"
|
579
|
-
logging.info(f"------------RESET--------------- {data}")
|
580
|
-
self.reset()
|
581
|
-
return data
|
582
|
-
|
590
|
+
response = canal.post("notify", dto)
|
591
|
+
if response['status']:
|
592
|
+
logging.info(f"Notificación enviada: {text}")
|
583
593
|
else:
|
594
|
+
logging.warning(f"Notificación no enviada: {text}")
|
595
|
+
except Exception as e:
|
596
|
+
logging.error(f"Error al enviar notificación: {text}")
|
597
|
+
logging.erro(f"Error: {e}")
|
598
|
+
|
599
|
+
def team_inquiry(self, team, data, operation, session_flag) -> str:
|
600
|
+
try:
|
601
|
+
dto = None
|
602
|
+
canales = self.state['canales']
|
603
|
+
canal = canales.get(team)
|
604
|
+
if session_flag:
|
584
605
|
dto = {
|
585
606
|
"data": {
|
586
|
-
'text': data
|
607
|
+
'text': data,
|
608
|
+
'session_id': self.state['session_id'],
|
587
609
|
},
|
588
610
|
}
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
611
|
+
# Evia mensaje a los agentes remotos
|
612
|
+
logging.info('>>>> Call remote agent: team.\n')
|
613
|
+
response = canal.post(team.lower(), dto)
|
614
|
+
if response and not response['status']:
|
615
|
+
logging.error(f'No se pudo establecer la comunicación con el agente remoto')
|
616
|
+
return None
|
617
|
+
logging.info(f'>>>> Response: {response}')
|
618
|
+
return response['message']['response']
|
619
|
+
else:
|
620
|
+
if operation:
|
621
|
+
dto = {
|
622
|
+
"data": {
|
623
|
+
'text': data,
|
624
|
+
'operation': operation
|
625
|
+
},
|
626
|
+
}
|
627
|
+
else:
|
628
|
+
dto = {
|
629
|
+
"data": {
|
630
|
+
'text': data
|
631
|
+
},
|
632
|
+
}
|
633
|
+
response = canal.post(team.lower(), dto)
|
634
|
+
if response['status']:
|
635
|
+
return response['message']['response']
|
636
|
+
else:
|
637
|
+
return None
|
638
|
+
logging.info("END: team_inquiry")
|
639
|
+
except Exception as e:
|
640
|
+
logging.error(f"Error al consultar al equipo: {team}")
|
641
|
+
return None
|
642
|
+
|
597
643
|
def get_text(self, mensaje) -> str:
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
644
|
+
if mensaje:
|
645
|
+
mensaje_limpio = mensaje.replace("<|im_start|>user<|im_sep|>", "").replace("<|im_start|>system<|im_sep|>", "")
|
646
|
+
mensaje_limpio = mensaje_limpio.replace("<|im_start|>", "").replace("<|im_sep|>", "").replace("<|im_end|>", "")
|
647
|
+
mensaje_limpio = mensaje_limpio.replace("[Usuario]: ", "").replace("[Sistema]: ", "")
|
648
|
+
return mensaje_limpio.strip()
|
649
|
+
else:
|
650
|
+
return ""
|
651
|
+
|
652
|
+
def recovery(self, query):
|
653
|
+
try:
|
654
|
+
prompt = RECOVERY_PROMPT % query
|
655
|
+
temp_work_memory = [{"role": "user", "content": prompt}]
|
656
|
+
res = self.__ai_service.generate(temp_work_memory)
|
657
|
+
res = self.get_text(res)
|
658
|
+
if res and not res == "":
|
659
|
+
return self.__recovery['owner'], self.__recovery['performative'], res, self.__recovery['team']
|
660
|
+
else:
|
661
|
+
logging.info(f"------------RESET---------------")
|
662
|
+
logging.info(f"Recovery from: {self.__recovery['performative']}")
|
663
|
+
self.reset()
|
664
|
+
self.notify("STOP")
|
665
|
+
return "Web", DialogState.START, self.RECOVERY_MSG, "Web"
|
666
|
+
except Exception as e:
|
667
|
+
return self.RECOVERY_MSG
|
602
668
|
|
603
669
|
def transition(self, owner, dialog_state, query, team_source=False) -> str:
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
if
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
670
|
+
try:
|
671
|
+
res = ""
|
672
|
+
logging.info(f"TOPTQ: {owner} - {dialog_state} - {team_source} - {query}")
|
673
|
+
# Punto de restauración
|
674
|
+
counter = (self.__recovery['counter'] + 1) if self.__recovery['performative'] == dialog_state else self.__recovery['counter']
|
675
|
+
self.__recovery = {
|
676
|
+
"owner": owner,
|
677
|
+
"team": owner,
|
678
|
+
"performative": dialog_state,
|
679
|
+
"counter": counter
|
680
|
+
}
|
681
|
+
logging.info(f"Recovery attemps: {self.__recovery["counter"]}")
|
682
|
+
# Verifica si se ha alcanzado el límite de recuperación
|
683
|
+
if self.__recovery['counter'] <= 3:
|
684
|
+
self.notify("identificando la performativa...")
|
685
|
+
# Verifica que exista la performativa
|
686
|
+
if not dialog_state in self.__dfa:
|
687
|
+
self.notify("performativa no encontrada")
|
688
|
+
return self.recovery(query)
|
689
|
+
# Performativa encontrada
|
690
|
+
children = None
|
691
|
+
self.notify("performativa encontrada")
|
692
|
+
node = self.__dfa[dialog_state]
|
693
|
+
if not isinstance(node, list):
|
694
|
+
children = node.children
|
695
|
+
else:
|
696
|
+
children = node
|
697
|
+
# Flujo de selección
|
698
|
+
select_node = None
|
699
|
+
self.notify("flujo de seleccion...")
|
700
|
+
if children and len(children)> 1:
|
701
|
+
logging.info(f"--> Más de una opción.")
|
702
|
+
options = ""
|
703
|
+
cont = 1
|
704
|
+
for item in children:
|
705
|
+
options += f"{cont}) {item.text}\n"
|
706
|
+
cont += 1
|
707
|
+
prompt = CLASSIFICATION_PROMPT % (query, options)
|
708
|
+
logging.info(f"Query: {query},\n Options:\n{options}")
|
709
|
+
self.__meta_work_memory.append({"role": "user", "content": prompt})
|
710
|
+
res = self.__ai_service.generate(self.__meta_work_memory)
|
711
|
+
logging.info(f"Thought: {res}")
|
712
|
+
self.__meta_work_memory = []
|
713
|
+
res = self.get_text(res)
|
714
|
+
for option in range(1, cont):
|
715
|
+
if str(option) in res:
|
716
|
+
select_node = children[option-1]
|
717
|
+
logging.info(f"Select node: {select_node.text}")
|
718
|
+
break
|
719
|
+
if not select_node:
|
720
|
+
logging.info("=> No se seleccionó ninguna opción")
|
721
|
+
select_node = children[0]
|
722
|
+
logging.info(f"=> Selecciona el primer nodo: {select_node.text}")
|
723
|
+
elif children and len(children) == 1:
|
724
|
+
logging.info(f"--> Una opción.")
|
725
|
+
select_node = children[0]
|
726
|
+
else:
|
727
|
+
logging.info(f"???????????????????> text: {res} es terminal")
|
728
|
+
# Nuevo nodo
|
729
|
+
if select_node:
|
730
|
+
res = select_node.text
|
731
|
+
node = select_node.children[0]
|
732
|
+
# Nodo seleccionado
|
733
|
+
logging.info(f"Flujo normal: {res}")
|
734
|
+
self.notify(res)
|
735
|
+
if team_source:
|
736
|
+
self.__work_memory.append({"role": "user", "content": res})
|
737
|
+
else:
|
738
|
+
self.__work_memory.append({"role": "user", "content": query})
|
739
|
+
self.__work_memory.append({"role": "user", "content": res})
|
740
|
+
# Efectua inferencia
|
741
|
+
new_owner, new_dialog_state, res, team = self.do_transition(owner, node, query)
|
742
|
+
res = self.get_text(res)
|
743
|
+
return new_owner, new_dialog_state, res, team
|
744
|
+
else:
|
745
|
+
logging.info(f"------------RESET---------------")
|
746
|
+
self.notify("STOP")
|
747
|
+
self.reset()
|
748
|
+
return "Web", DialogState.START, self.RECOVERY_MSG, "Web"
|
749
|
+
logging.info("END: do_transition")
|
750
|
+
except Exception as e:
|
751
|
+
traceback.print_exc()
|
648
752
|
logging.info(f"------------RESET---------------")
|
649
753
|
self.reset()
|
650
|
-
|
651
|
-
|
652
|
-
return owner, DialogState.START, text, owner
|
653
|
-
|
654
|
-
# Flujo normal
|
655
|
-
logging.info(f"Flujo normal: {select_node.text}")
|
656
|
-
if team_source:
|
657
|
-
self.__work_memory.append({"role": "user", "content": select_node.text})
|
658
|
-
else:
|
659
|
-
self.__work_memory.append({"role": "user", "content": query})
|
660
|
-
self.__work_memory.append({"role": "user", "content": select_node.text})
|
661
|
-
new_owner, new_dialog_state, text, team = self.do_transition(owner, select_node.children[0], query)
|
662
|
-
text = self.get_text(text)
|
663
|
-
return new_owner, new_dialog_state, text, team
|
754
|
+
self.notify("STOP")
|
755
|
+
return owner, DialogState.START, "Lo lamento, no puedo responder en este momento", owner
|
664
756
|
|
665
757
|
def do_transition(self, owner, node, query) -> str:
|
666
758
|
""" Generate method
|
667
759
|
:return: str
|
668
|
-
"""
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
elif isinstance(node, ActionNode):
|
673
|
-
|
674
|
-
#------------------------------
|
675
|
-
# Accion
|
676
|
-
#------------------------------
|
677
|
-
|
678
|
-
logging.info(f"-> node action: {node.action}")
|
679
|
-
if node.tool and not node.tool == "Ninguno":
|
680
|
-
logging.info(f"-> node tool: {node.tool}")
|
681
|
-
self.__work_memory.append({"role": "user", "content": node.text})
|
682
|
-
logging.info("-----")
|
683
|
-
logging.info("\n%s", json.dumps(self.__work_memory, indent=4))
|
684
|
-
logging.info("-----")
|
685
|
-
res = self.__ai_service.generate(self.__work_memory)
|
686
|
-
logging.info(f"-> node tool: {res}")
|
687
|
-
res = self.get_text(res)
|
688
|
-
# Check if res is empty
|
689
|
-
if not res or res == "":
|
690
|
-
logging.info("1 -> res vacio")
|
691
|
-
res = "Lo lamento, no puedo responder en este momento"
|
692
|
-
|
693
|
-
logging.info(f"------------RESET---------------")
|
694
|
-
self.reset()
|
695
|
-
|
696
|
-
return owner, DialogState.START, res, owner
|
697
|
-
logging.info(f"-> node tool: envia -> {res}")
|
698
|
-
text = self.team_inquiry(node.team, res, node.tool, False)
|
699
|
-
else:
|
760
|
+
"""
|
761
|
+
try:
|
762
|
+
if isinstance(node, ActionNode):
|
763
|
+
self.notify("realizando acción...")
|
700
764
|
#------------------------------
|
701
|
-
#
|
765
|
+
# Accion
|
702
766
|
#------------------------------
|
703
|
-
logging.info(f"-> node
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
logging.info(f"-> node team -> es terminal -> {node.text}")
|
767
|
+
logging.info(f"-> node action: {node.action}")
|
768
|
+
if node.tool and not node.tool == "Ninguno":
|
769
|
+
self.notify("aplicando herramienta...")
|
770
|
+
logging.info(f"-> node tool: {node.tool}")
|
708
771
|
self.__work_memory.append({"role": "user", "content": node.text})
|
709
772
|
logging.info("-----")
|
710
773
|
logging.info("\n%s", json.dumps(self.__work_memory, indent=4))
|
711
|
-
logging.info("-----")
|
774
|
+
logging.info("-----")
|
712
775
|
res = self.__ai_service.generate(self.__work_memory)
|
776
|
+
logging.info(f"-> node tool: {res}")
|
713
777
|
res = self.get_text(res)
|
714
|
-
self.__work_memory.append({"role": "system", "content": res})
|
715
778
|
# Check if res is empty
|
716
779
|
if not res or res == "":
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
return owner, DialogState.START, res, owner
|
722
|
-
logging.info(f"-> node team -> envia: {res}")
|
723
|
-
text = self.team_inquiry(node.team, res, None, True)
|
724
|
-
return node.team, DialogState.START, res, node.team
|
780
|
+
self.notify("no pude hacer uso de la herramienta")
|
781
|
+
return self.recovery(query)
|
782
|
+
logging.info(f"-> node tool: envia -> {res}")
|
783
|
+
res = self.team_inquiry(node.team, res, node.tool, False)
|
725
784
|
else:
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
785
|
+
self.notify("realizando llamada...")
|
786
|
+
#------------------------------
|
787
|
+
# Lllamada
|
788
|
+
#------------------------------
|
789
|
+
logging.info(f"-> node team: {node.team}")
|
790
|
+
# Verifica si el nodo es termianl ya que significa
|
791
|
+
# que el dialogo cambia de agente
|
792
|
+
if node.is_terminal:
|
793
|
+
logging.info(f"-> node team -> es terminal -> {node.text}")
|
794
|
+
self.__work_memory.append({"role": "user", "content": node.text})
|
795
|
+
logging.info("-----")
|
796
|
+
logging.info("\n%s", json.dumps(self.__work_memory, indent=4))
|
797
|
+
logging.info("-----")
|
798
|
+
res = self.__ai_service.generate(self.__work_memory)
|
799
|
+
res = self.get_text(res)
|
800
|
+
self.__work_memory.append({"role": "system", "content": res})
|
801
|
+
# Check if res is empty
|
802
|
+
if not res or res == "":
|
803
|
+
self.notify(f"no pude contactar al agente: {node.team}")
|
804
|
+
return self.recovery(query)
|
805
|
+
self.notify(f"le envio al agente {node.team}: {res}")
|
806
|
+
logging.info(f"-> node team -> envia: {res}")
|
807
|
+
res = self.team_inquiry(node.team, res, None, True)
|
808
|
+
if res and not res == "ERROR" and not res == "":
|
809
|
+
logging.info(f"------------RESET---------------")
|
810
|
+
self.reset()
|
811
|
+
self.notify("STOP")
|
812
|
+
return node.team, DialogState.START, res, node.team
|
813
|
+
else:
|
814
|
+
return self.recovery(query)
|
815
|
+
else:
|
816
|
+
logging.info("-> node team -> continua")
|
817
|
+
self.__work_memory.append({"role": "user", "content": node.text})
|
818
|
+
logging.info(f"-> node team -> envia: {query}")
|
819
|
+
res = self.team_inquiry(node.team, query, node.tool, False)
|
820
|
+
self.notify(f"continuando con díalogo")
|
821
|
+
# Adiciona el texto al work memory
|
822
|
+
if res and not res == "ERROR" and not res == "":
|
823
|
+
logging.info(f"-> Adicion WM node team -> text: {res}")
|
824
|
+
self.__work_memory.append({"role": "system", "content": res})
|
825
|
+
else:
|
826
|
+
return self.recovery(query)
|
827
|
+
logging.info("#########> Procesa respuesta del equipo en profundidad")
|
828
|
+
# Verifica si se alcanzó el límite de profundidad
|
829
|
+
self.__deep_count += 1
|
830
|
+
if self.__deep_count < self.__deep_limit:
|
831
|
+
self.notify("efectuando inferencia en profundidad")
|
832
|
+
return self.transition(owner, node.performative, res, True)
|
833
|
+
else:
|
834
|
+
self.notify(f"se alcanzó el límite de profundidad: {self.__deep_count}")
|
835
|
+
self.__deep_count = 0
|
836
|
+
logging.info("-> node team -> deep limit")
|
837
|
+
logging.info(f"------------RESET---------------")
|
838
|
+
self.notify("STOP")
|
839
|
+
self.reset()
|
840
|
+
return "Web", DialogState.START, self.RECOVERY_MSG, "Web"
|
737
841
|
else:
|
738
|
-
self.
|
739
|
-
logging.info("-> node
|
740
|
-
|
741
|
-
logging.info(f"
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
842
|
+
self.notify("efectuando inferencia...")
|
843
|
+
logging.info(f"D -> node: {node.text}")
|
844
|
+
self.__work_memory.append({"role": "user", "content": node.text})
|
845
|
+
logging.info(f"=> !!!!: {query}")
|
846
|
+
logging.info("-----")
|
847
|
+
logging.info("\n%s", json.dumps(self.__work_memory, indent=4))
|
848
|
+
logging.info("-----")
|
849
|
+
res = self.__ai_service.generate(self.__work_memory)
|
850
|
+
res = self.get_text(res)
|
851
|
+
self.__work_memory.append({"role": "system", "content": res})
|
852
|
+
# Check if res is empty
|
853
|
+
if not res or res == "":
|
854
|
+
return self.recovery(query)
|
855
|
+
logging.info(f"=> Thought DEEP: {res}")
|
856
|
+
self.notify(f"Realizando inferencia: {res}")
|
857
|
+
new_dialog_state = node.performative
|
858
|
+
if not node.is_terminal:
|
859
|
+
self.notify(f"continuando díalogo de inferencia")
|
860
|
+
return owner, new_dialog_state, res, owner
|
861
|
+
self.notify(f"finalizando díalogo de inferencia")
|
862
|
+
logging.info(f"Tipe node: {type(node)}")
|
863
|
+
logging.info(f"$$$> new_owner: {owner} new_dialog_state: {new_dialog_state}")
|
864
|
+
self.notify("STOP")
|
865
|
+
return owner, new_dialog_state, res, owner
|
866
|
+
logging.info("END: do_transition")
|
867
|
+
except Exception as e:
|
868
|
+
traceback.print_exc()
|
760
869
|
logging.info(f"------------RESET---------------")
|
761
870
|
self.reset()
|
762
|
-
|
763
|
-
|
764
|
-
return owner, DialogState.START, res, owner
|
765
|
-
|
766
|
-
logging.info(f"=> Thought DEEP: {res}")
|
767
|
-
|
768
|
-
new_dialog_state = node.performative
|
769
|
-
if not node.is_terminal:
|
770
|
-
logging.info(f"=> new_owner: {owner} new_dialog_state: {new_dialog_state}")
|
771
|
-
return owner, new_dialog_state, res, owner
|
772
|
-
|
773
|
-
logging.info(f"Tipe node: {type(node)}")
|
774
|
-
|
775
|
-
logging.info(f"$$$> new_owner: {owner} new_dialog_state: {new_dialog_state}")
|
776
|
-
return owner, new_dialog_state, res, owner
|
871
|
+
self.notify("STOP")
|
872
|
+
return owner, DialogState.START, "Lo lamento, no puedo responder en este momento", owner
|
777
873
|
|
778
874
|
def set_knowledge(self, knowledge) -> str:
|
779
875
|
""" Set knowledge method
|
@@ -13,6 +13,7 @@
|
|
13
13
|
# Define resources
|
14
14
|
# --------------------------------------------------------
|
15
15
|
|
16
|
+
import logging
|
16
17
|
import datetime
|
17
18
|
import traceback
|
18
19
|
from .templates import AFIRMATIVE_RESPONSE, GET_DATE
|
@@ -199,6 +200,6 @@ def imprimir_grafo(nodo, nivel=0):
|
|
199
200
|
indent = " " * nivel
|
200
201
|
# Se muestra la clase del nodo y algunos atributos
|
201
202
|
clase = nodo.__class__.__name__
|
202
|
-
|
203
|
+
logging.debug(f"{indent}{clase} (performative: {nodo.performative}, text/action: {nodo.text if hasattr(nodo, 'text') and nodo.text is not None else getattr(nodo, 'action', None)}, terminal: {nodo.is_terminal})")
|
203
204
|
for child in nodo.children:
|
204
205
|
imprimir_grafo(child, nivel + 1)
|
@@ -130,7 +130,7 @@ class ResponseAction(Action):
|
|
130
130
|
class DispatcherController(Agent):
|
131
131
|
""" Represents the agent that delegates the execution of actions to other agents """
|
132
132
|
|
133
|
-
def __init__(self, agent_id:str, buffer_size:int, pool_size:int) -> None:
|
133
|
+
def __init__(self, agent_id:str, buffer_size:int, pool_size:int, delegate=None) -> None:
|
134
134
|
""" Constructor
|
135
135
|
@param agent_id: Agent ID
|
136
136
|
@param type: Pool type
|
@@ -139,15 +139,20 @@ class DispatcherController(Agent):
|
|
139
139
|
"""
|
140
140
|
self.__buffer_size = buffer_size
|
141
141
|
self.__request_dict = {}
|
142
|
+
self.__pool_size = pool_size
|
142
143
|
self.__free_queue = Queue(pool_size)
|
143
144
|
self.__agent_list = []
|
145
|
+
self.__delegate = delegate
|
144
146
|
super().__init__(agent_id)
|
145
147
|
|
146
148
|
def setup(self) -> None:
|
147
149
|
""" Set up method """
|
148
150
|
self._social = True
|
149
151
|
self.add_behavior('Delegate')
|
150
|
-
self.
|
152
|
+
if self.__delegate:
|
153
|
+
self.bind_action('Delegate', 'delegate', self.__delegate())
|
154
|
+
else:
|
155
|
+
self.bind_action('Delegate', 'delegate', Delegate())
|
151
156
|
self.add_behavior('Notify')
|
152
157
|
self.bind_action('Notify', 'notify', NotifyFreeAction())
|
153
158
|
self.add_behavior('Response')
|
@@ -161,6 +166,8 @@ class DispatcherController(Agent):
|
|
161
166
|
self.__agent_list.append(agent.id)
|
162
167
|
agent.set_controller(self.id)
|
163
168
|
agent.set_controller_type('POOL')
|
169
|
+
if len(self.__agent_list) > self.__pool_size:
|
170
|
+
raise DispatcherException('[Warn, suscribeAgent]: The pool is full')
|
164
171
|
self.__free_queue.put(agent.id)
|
165
172
|
actions = agent.get_actions()
|
166
173
|
for action in actions:
|
@@ -181,7 +188,7 @@ class DispatcherController(Agent):
|
|
181
188
|
@param event: Event
|
182
189
|
@param data: Data
|
183
190
|
"""
|
184
|
-
from
|
191
|
+
from ..mas import Adm
|
185
192
|
for agent_id in self.__agent_list:
|
186
193
|
Adm().send_event(agent_id, event, data)
|
187
194
|
|
@@ -190,6 +197,12 @@ class DispatcherController(Agent):
|
|
190
197
|
""" Builds the agent """
|
191
198
|
pass
|
192
199
|
|
200
|
+
def get_agent_list(self) -> list:
|
201
|
+
""" Gets the agent list
|
202
|
+
@return: Agent list
|
203
|
+
"""
|
204
|
+
return self.__agent_list
|
205
|
+
|
193
206
|
def get_free_queue(self) -> Queue:
|
194
207
|
""" Gets the free queue
|
195
208
|
@return: Free queue
|
@@ -36,5 +36,29 @@ Ahora, evalúa el siguiente caso:
|
|
36
36
|
|
37
37
|
Texto: "%s"
|
38
38
|
|
39
|
+
Respuesta:
|
40
|
+
"""
|
41
|
+
|
42
|
+
RECOVERY_PROMPT = """
|
43
|
+
Instrucciones:
|
44
|
+
|
45
|
+
Eres un agente especializado en parafrasear. Tu tarea es analizar un texto principal y parafrasearlo generando opciones al usuario.
|
46
|
+
|
47
|
+
Requisitos:
|
48
|
+
|
49
|
+
- No incluyas explicaciones, razonamientos ni texto adicional.
|
50
|
+
- Genera opciones en lenguaje natural sin involucrar números o listas.
|
51
|
+
- Si no logras parafrasear el texto principal, responde solicitando más información.
|
52
|
+
|
53
|
+
Ejemplo:
|
54
|
+
|
55
|
+
Texto: "Hola mi caso esta relacionado con la Superfinanciera"
|
56
|
+
|
57
|
+
Respuesta: Perdona, tu solicitud ¿Es sobre la Superintendencia Financiera de Colombia o sobre la Superintendencia de Sociedades?
|
58
|
+
|
59
|
+
Ahora, evalúa el siguiente caso:
|
60
|
+
|
61
|
+
Texto: "%s"
|
62
|
+
|
39
63
|
Respuesta:
|
40
64
|
"""
|
@@ -104,7 +104,7 @@ class DelegateAction(Action):
|
|
104
104
|
self.__rewier[ag] = 0
|
105
105
|
exit = True
|
106
106
|
else:
|
107
|
-
logging.info('The agent is not
|
107
|
+
logging.info('The agent is not operational')
|
108
108
|
self.adm.send_event(agent_obj.get_controller(), 'notify', ag)
|
109
109
|
if ag in self.__rewier:
|
110
110
|
self.__rewier[ag] = self.__rewier[ag] + 1
|
@@ -57,18 +57,18 @@ class TimeoutAction(Action):
|
|
57
57
|
""" Execute the action
|
58
58
|
@param data: Event data
|
59
59
|
"""
|
60
|
-
logging.
|
60
|
+
logging.debug(f"[TimeoutAction][{self.agent.id}]: Execute {data}")
|
61
61
|
if data['command'] == 'start':
|
62
62
|
if not self.agent.is_timeout():
|
63
63
|
self.agent.set_timeout(True)
|
64
64
|
self.__timer = Timer(data['time'], self.handler)
|
65
65
|
self.__timer.start()
|
66
|
-
logging.
|
66
|
+
logging.debug(f"[TimeoutAction][{self.agent.id}]: Timer started")
|
67
67
|
else:
|
68
68
|
if self.__timer:
|
69
69
|
self.__timer.cancel()
|
70
70
|
self.__timer = None
|
71
|
-
logging.
|
71
|
+
logging.debug(f"[TimeoutAction][{self.agent.id}]: Timer stopped")
|
72
72
|
|
73
73
|
# --------------------------------------------------------
|
74
74
|
# Define Task Action
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/__pycache__/pbesa.cpython-36.pyc
RENAMED
File without changes
|
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/__pycache__/urls.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/helloworld/__pycache__/wsgi.cpython-36.pyc
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/__pycache__/__init__.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/__pycache__/admin.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/__pycache__/apps.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/__pycache__/models.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/__pycache__/urls.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/__pycache__/views.cpython-36.pyc
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/mas/controller/translatedelegate.py
RENAMED
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/mas/controller/translateresponse.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/django/helloworld/translate/mas/worker/translatetask.py
RENAMED
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
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/worker/__pycache__/counteragent.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/worker/__pycache__/countertask.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/worker/__pycache__/translatetask.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.4 → pbesa-4.0.6}/examples/remote/mas/worker/__pycache__/workeragent.cpython-36.pyc
RENAMED
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
|
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
|