pbesa 4.0.5__tar.gz → 4.0.7__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.5 → pbesa-4.0.7}/PKG-INFO +3 -2
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/cognitive.py +326 -187
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/res/__pycache__/__init__.cpython-39.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/util.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/mas.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/models.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/social/delegator.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/social/dialog.py +10 -2
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/social/dispatcher_team.py +1 -1
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/social/prompts.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/social/templates.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/social/worker.py +3 -3
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa.egg-info/PKG-INFO +3 -2
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa.egg-info/requires.txt +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/setup.py +1 -1
- {pbesa-4.0.5 → pbesa-4.0.7}/.gitignore +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/LICENSE +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/LICENSE.txt +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/MANIFEST +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/README.md +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/db.sqlite3 +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/helloworld/__init__.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/helloworld/__pycache__/__init__.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/helloworld/__pycache__/pbesa.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/helloworld/__pycache__/settings.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/helloworld/__pycache__/urls.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/helloworld/__pycache__/wsgi.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/helloworld/asgi.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/helloworld/pbesa.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/helloworld/settings.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/helloworld/urls.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/helloworld/wsgi.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/manage.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/__init__.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/__pycache__/__init__.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/__pycache__/admin.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/__pycache__/apps.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/__pycache__/models.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/__pycache__/urls.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/__pycache__/views.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/admin.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/apps.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/mas/controller/__pycache__/translatecontroller.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/mas/controller/__pycache__/translatedelegate.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/mas/controller/__pycache__/translateresponse.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/mas/controller/translatecontroller.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/mas/controller/translatedelegate.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/mas/controller/translateresponse.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/mas/worker/__pycache__/translatetask.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/mas/worker/__pycache__/workeragent.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/mas/worker/translatetask.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/mas/worker/workeragent.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/migrations/__init__.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/migrations/__pycache__/__init__.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/models.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/tests.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/urls.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/views.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/controller/__pycache__/countercontroller.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/controller/__pycache__/counterdelegate.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/controller/__pycache__/counterresponse.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/controller/__pycache__/translatecontroller.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/controller/__pycache__/translatedelegate.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/controller/__pycache__/translateresponse.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/controller/countercontroller.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/controller/counterdelegate.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/controller/counterresponse.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/worker/__pycache__/counteragent.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/worker/__pycache__/countertask.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/worker/__pycache__/translatetask.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/worker/__pycache__/workeragent.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/worker/counteragent.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/worker/countertask.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/remote_a.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/remote_b.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/remote_c.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/__init__.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/__init__.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/adapter.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/agent.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/io/__init__.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/io/system_file.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/io/tcp_server.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/res/__init__.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/res/__pycache__/__init__.cpython-36.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/res/__pycache__/__init__.cpython-37.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/res/__pycache__/__init__.cpython-38.pyc +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/res/conf.json +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/kernel/world.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/remote/__init__.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/remote/adm_listener.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/remote/adm_listener_handler.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/remote/exceptions.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/remote/remote_adm.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/remote/remote_adm_handler.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/social/__init__.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/social/collaborative_team.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/social/delegator_team.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa/social/selected_dispatcher_team.py +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa.egg-info/SOURCES.txt +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa.egg-info/dependency_links.txt +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/pbesa.egg-info/top_level.txt +0 -0
- {pbesa-4.0.5 → pbesa-4.0.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: pbesa
|
3
|
-
Version: 4.0.
|
3
|
+
Version: 4.0.7
|
4
4
|
License-File: LICENSE
|
5
5
|
License-File: LICENSE.txt
|
6
6
|
Requires-Dist: pymongo==4.6.3
|
@@ -8,3 +8,4 @@ Requires-Dist: requests==2.32.3
|
|
8
8
|
Requires-Dist: azure-ai-projects==1.0.0b6
|
9
9
|
Requires-Dist: azure-ai-inference==1.0.0b9
|
10
10
|
Requires-Dist: azure-identity==1.20.0
|
11
|
+
Dynamic: requires-dist
|
@@ -13,6 +13,7 @@
|
|
13
13
|
# Define resources
|
14
14
|
# --------------------------------------------------------
|
15
15
|
|
16
|
+
import uuid
|
16
17
|
import json
|
17
18
|
import logging
|
18
19
|
import traceback
|
@@ -20,13 +21,16 @@ from pydantic import BaseModel
|
|
20
21
|
from typing import List, Optional
|
21
22
|
from abc import ABC, abstractmethod
|
22
23
|
from pbesa.models import AIFoundry, AzureInference, GPTService, ServiceProvider
|
23
|
-
from pbesa.social.dialog import
|
24
|
+
from pbesa.social.dialog import (
|
25
|
+
DialogState, imprimir_grafo, recorrer_interacciones, extraer_diccionario_nodos,
|
26
|
+
ActionNode, DeclarativeNode, GotoNode) #, TerminalNode
|
24
27
|
from pbesa.social.prompts import CLASSIFICATION_PROMPT, DERIVE_PROMPT, RECOVERY_PROMPT
|
25
28
|
# --------------------------------------------------------
|
26
29
|
# Define DTOs
|
27
30
|
# --------------------------------------------------------
|
28
31
|
|
29
32
|
class InteraccionDTO(BaseModel):
|
33
|
+
id: str
|
30
34
|
tipo: str
|
31
35
|
texto: str
|
32
36
|
actor: str
|
@@ -38,6 +42,7 @@ def interaccion_serializer(interaccion):
|
|
38
42
|
if isinstance(interaccion, list):
|
39
43
|
return [interaccion_serializer(i) for i in interaccion]
|
40
44
|
return {
|
45
|
+
"id": interaccion["id"] if "id" in interaccion and interaccion["id"] else str(uuid.uuid4()),
|
41
46
|
"tipo": interaccion["tipo"],
|
42
47
|
"texto": interaccion["texto"],
|
43
48
|
"actor": interaccion["actor"],
|
@@ -468,6 +473,19 @@ class Dialog(ABC):
|
|
468
473
|
self.__deep_limit = 3
|
469
474
|
# Define knowledge
|
470
475
|
self.knowledge = None
|
476
|
+
# Define point recovery
|
477
|
+
self.__recovery = {
|
478
|
+
"owner": "Web",
|
479
|
+
"team": "Web",
|
480
|
+
"performative": DialogState.START,
|
481
|
+
"counter": 0
|
482
|
+
}
|
483
|
+
# Define recovery message
|
484
|
+
self.RECOVERY_MSG = "Lo lamento, puedes darme más detalles o reformular"
|
485
|
+
# Define vertices list
|
486
|
+
self.__vertices = []
|
487
|
+
# Define visited nodes
|
488
|
+
self.__visited_nodes = 0
|
471
489
|
|
472
490
|
def setup_world(self):
|
473
491
|
""" Set up model method """
|
@@ -561,223 +579,344 @@ class Dialog(ABC):
|
|
561
579
|
self.__work_memory = []
|
562
580
|
# Set up model
|
563
581
|
self.setup_world()
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
582
|
+
# Reset dialog state
|
583
|
+
self.__recovery = {
|
584
|
+
"owner": "Web",
|
585
|
+
"team": "Web",
|
586
|
+
"performative": DialogState.START,
|
587
|
+
"counter": 0
|
588
|
+
}
|
589
|
+
|
590
|
+
def notify(self, text):
|
591
|
+
try:
|
592
|
+
canales = self.state['canales']
|
593
|
+
canal = canales.get("Webhook")
|
594
|
+
session_id = self.state['session_id']
|
570
595
|
dto = {
|
571
|
-
"
|
572
|
-
|
573
|
-
'operation': operation
|
574
|
-
},
|
596
|
+
"session_id": session_id,
|
597
|
+
"text": text[0:100]
|
575
598
|
}
|
576
|
-
|
577
|
-
if
|
578
|
-
logging.info(f"
|
579
|
-
logging.info(f"------------RESET--------------- {data}")
|
580
|
-
self.reset()
|
581
|
-
return data
|
582
|
-
|
599
|
+
response = canal.post("notify", dto)
|
600
|
+
if response['status']:
|
601
|
+
logging.info(f"Notificación enviada: {text}")
|
583
602
|
else:
|
603
|
+
logging.warning(f"Notificación no enviada: {text}")
|
604
|
+
except Exception as e:
|
605
|
+
logging.error(f"Error al enviar notificación: {text}")
|
606
|
+
logging.erro(f"Error: {e}")
|
607
|
+
|
608
|
+
def team_inquiry(self, team, data, operation, session_flag) -> str:
|
609
|
+
try:
|
610
|
+
dto = None
|
611
|
+
canales = self.state['canales']
|
612
|
+
canal = canales.get(team)
|
613
|
+
if session_flag:
|
584
614
|
dto = {
|
585
615
|
"data": {
|
586
|
-
'text': data
|
616
|
+
'text': data,
|
617
|
+
'session_id': self.state['session_id'],
|
587
618
|
},
|
588
619
|
}
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
620
|
+
# Evia mensaje a los agentes remotos
|
621
|
+
logging.info('>>>> Call remote agent: team.\n')
|
622
|
+
response = canal.post(team.lower(), dto)
|
623
|
+
if response and not response['status']:
|
624
|
+
logging.error(f'No se pudo establecer la comunicación con el agente remoto')
|
625
|
+
return None
|
626
|
+
logging.info(f'>>>> Response: {response}')
|
627
|
+
return response['message']['response']
|
628
|
+
else:
|
629
|
+
if operation:
|
630
|
+
dto = {
|
631
|
+
"data": {
|
632
|
+
'text': data,
|
633
|
+
'operation': operation
|
634
|
+
},
|
635
|
+
}
|
636
|
+
else:
|
637
|
+
dto = {
|
638
|
+
"data": {
|
639
|
+
'text': data
|
640
|
+
},
|
641
|
+
}
|
642
|
+
response = canal.post(team.lower(), dto)
|
643
|
+
if response['status']:
|
644
|
+
return response['message']['response']
|
645
|
+
else:
|
646
|
+
return None
|
647
|
+
logging.info("END: team_inquiry")
|
648
|
+
except Exception as e:
|
649
|
+
logging.error(f"Error al consultar al equipo: {team}")
|
650
|
+
return None
|
606
651
|
|
607
|
-
|
608
|
-
if
|
609
|
-
|
610
|
-
|
652
|
+
def get_text(self, mensaje) -> str:
|
653
|
+
if mensaje:
|
654
|
+
mensaje_limpio = mensaje.replace("<|im_start|>user<|im_sep|>", "").replace("<|im_start|>system<|im_sep|>", "") \
|
655
|
+
.replace("<|im_start|>", "").replace("<|im_sep|>", "").replace("<|im_end|>", "") \
|
656
|
+
.replace("[Usuario]: ", "").replace("[Sistema]: ", "") \
|
657
|
+
.replace("<|user|>", "").replace("<|system|>", "")
|
658
|
+
return mensaje_limpio.strip()
|
659
|
+
else:
|
660
|
+
return ""
|
661
|
+
|
662
|
+
def recovery(self, query):
|
663
|
+
try:
|
611
664
|
prompt = RECOVERY_PROMPT % query
|
612
|
-
temp_work_memory = [{"role": "user", "content":
|
665
|
+
temp_work_memory = [{"role": "user", "content": prompt}]
|
613
666
|
res = self.__ai_service.generate(temp_work_memory)
|
614
667
|
res = self.get_text(res)
|
615
|
-
|
668
|
+
if res and not res == "":
|
669
|
+
return self.__recovery['owner'], self.__recovery['performative'], res, self.__recovery['team']
|
670
|
+
else:
|
671
|
+
logging.info(f"------------RESET---------------")
|
672
|
+
logging.info(f"Recovery from: {self.__recovery['performative']}")
|
673
|
+
self.reset()
|
674
|
+
self.notify("STOP")
|
675
|
+
return "Web", DialogState.START, self.RECOVERY_MSG, "Web"
|
676
|
+
except Exception as e:
|
677
|
+
return self.RECOVERY_MSG
|
616
678
|
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
self.
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
679
|
+
def transition(self, owner, dialog_state, query, team_source=False) -> str:
|
680
|
+
try:
|
681
|
+
res = ""
|
682
|
+
logging.info(f"TOPTQ: {owner} - {dialog_state} - {team_source} - {query}")
|
683
|
+
# Punto de restauración
|
684
|
+
counter = (self.__recovery['counter'] + 1) if self.__recovery['performative'] == dialog_state else self.__recovery['counter']
|
685
|
+
self.__recovery = {
|
686
|
+
"owner": owner,
|
687
|
+
"team": owner,
|
688
|
+
"performative": dialog_state,
|
689
|
+
"counter": counter
|
690
|
+
}
|
691
|
+
logging.info(f"Recovery attemps: {self.__recovery["counter"]}")
|
692
|
+
# Verifica si se ha alcanzado el límite de recuperación
|
693
|
+
if self.__recovery['counter'] <= 3 and self.__visited_nodes < 5:
|
694
|
+
self.notify("identificando concepto...")
|
695
|
+
#--------------------------
|
696
|
+
# Verifica que exista la performativa
|
697
|
+
if not dialog_state in self.__dfa:
|
698
|
+
self.notify("concepto no encontrado")
|
699
|
+
return self.recovery(query)
|
700
|
+
# Performativa encontrada
|
701
|
+
children = None
|
702
|
+
self.notify("concepto encontrado")
|
703
|
+
#--------------------------
|
704
|
+
# Obtiene los hijos del nodo
|
705
|
+
node = self.__dfa[dialog_state]
|
706
|
+
if not isinstance(node, list):
|
707
|
+
children = node.children
|
708
|
+
else:
|
709
|
+
# Es una lista de nodos
|
710
|
+
children = node
|
711
|
+
#--------------------------
|
712
|
+
# Flujo de selección
|
713
|
+
select_node = None
|
714
|
+
self.notify("flujo de seleccion...")
|
715
|
+
if children and len(children)> 1:
|
716
|
+
logging.info(f"--> Más de una opción.")
|
717
|
+
options = ""
|
718
|
+
cont = 1
|
719
|
+
for item in children:
|
720
|
+
options += f"{cont}) {item.text}\n"
|
721
|
+
cont += 1
|
722
|
+
prompt = CLASSIFICATION_PROMPT % (query, options)
|
723
|
+
logging.info(f"Query: {query},\n Options:\n{options}")
|
724
|
+
self.__meta_work_memory.append({"role": "user", "content": prompt})
|
725
|
+
res = self.__ai_service.generate(self.__meta_work_memory)
|
726
|
+
logging.info(f"Thought: {res}")
|
727
|
+
self.__meta_work_memory = []
|
728
|
+
res = self.get_text(res)
|
729
|
+
for option in range(1, cont):
|
730
|
+
if str(option) in res:
|
731
|
+
select_node = children[option-1]
|
732
|
+
logging.info(f"Select node: {select_node.text}")
|
733
|
+
break
|
734
|
+
if not select_node:
|
735
|
+
logging.info("=> No se seleccionó ninguna opción")
|
736
|
+
select_node = children[0]
|
737
|
+
logging.info(f"=> Selecciona el primer nodo: {select_node.text}")
|
738
|
+
elif children and len(children) == 1:
|
739
|
+
logging.info(f"--> Una opción.")
|
740
|
+
select_node = children[0]
|
741
|
+
else:
|
742
|
+
logging.info("???> Es un nodo terminal o iniciador")
|
743
|
+
return self.recovery(query)
|
744
|
+
#--------------------------
|
745
|
+
# Verifica si es un nodo
|
746
|
+
# que ya fue recorrido
|
747
|
+
if select_node.performative in self.__vertices:
|
748
|
+
logging.info(f"-> nodo ya recorrido: {select_node.text}")
|
749
|
+
self.__visited_nodes += 1
|
750
|
+
else:
|
751
|
+
logging.info(f"-> nodo no recorrido: {select_node.text}")
|
752
|
+
self.__visited_nodes = 0
|
753
|
+
# Maraca el nodo como visitado
|
754
|
+
self.__vertices.append(select_node.performative)
|
755
|
+
#---------------------------
|
756
|
+
# Verifica si el nodo es un
|
757
|
+
# nodo de salto
|
758
|
+
if isinstance(select_node, GotoNode):
|
759
|
+
logging.info(f"-> node de salto: {select_node.text}")
|
760
|
+
performative = select_node.text.replace("Salta a:", "").strip()
|
761
|
+
select_node = self.__dfa[performative]
|
762
|
+
logging.info(f"-> Salto: {select_node.text}, performativa: {select_node.performative}")
|
763
|
+
#---------------------------
|
764
|
+
# Efectua transicion
|
765
|
+
if select_node:
|
766
|
+
res = select_node.text
|
767
|
+
node = select_node.children[0]
|
768
|
+
logging.info(f"Flujo normal: {res}")
|
769
|
+
self.notify(res)
|
770
|
+
#---------------------------
|
771
|
+
# Verifica si el nuevo nodo
|
772
|
+
# es un nodo de salto
|
773
|
+
if isinstance(node, GotoNode):
|
774
|
+
logging.info(f"-> node de salto: {node.text}")
|
775
|
+
performative = node.text.replace("Salta a:", "").strip()
|
776
|
+
node = self.__dfa[performative]
|
777
|
+
logging.info(f"-> Salto: {node.text}, performativa: {node.performative}")
|
778
|
+
#---------------------------
|
779
|
+
# Actualiza la memoria de
|
780
|
+
# trabajo
|
781
|
+
if team_source:
|
782
|
+
self.__work_memory.append({"role": "user", "content": res})
|
783
|
+
else:
|
784
|
+
self.__work_memory.append({"role": "user", "content": query})
|
785
|
+
self.__work_memory.append({"role": "user", "content": res})
|
786
|
+
#---------------------------
|
787
|
+
# Efectua inferencia
|
788
|
+
new_owner, new_dialog_state, res, team = self.do_transition(owner, node, query)
|
789
|
+
res = self.get_text(res)
|
790
|
+
return new_owner, new_dialog_state, res, team
|
791
|
+
else:
|
792
|
+
logging.info(f"------------RESET---------------")
|
793
|
+
self.notify("STOP")
|
794
|
+
self.reset()
|
795
|
+
return "Web", DialogState.START, self.RECOVERY_MSG, "Web"
|
796
|
+
logging.info("END: do_transition")
|
797
|
+
except Exception as e:
|
798
|
+
traceback.print_exc()
|
652
799
|
logging.info(f"------------RESET---------------")
|
653
800
|
self.reset()
|
654
|
-
|
655
|
-
|
656
|
-
return owner, DialogState.START, text, owner
|
657
|
-
|
658
|
-
# Flujo normal
|
659
|
-
logging.info(f"Flujo normal: {select_node.text}")
|
660
|
-
if team_source:
|
661
|
-
self.__work_memory.append({"role": "user", "content": select_node.text})
|
662
|
-
else:
|
663
|
-
self.__work_memory.append({"role": "user", "content": query})
|
664
|
-
self.__work_memory.append({"role": "user", "content": select_node.text})
|
665
|
-
new_owner, new_dialog_state, text, team = self.do_transition(owner, select_node.children[0], query)
|
666
|
-
text = self.get_text(text)
|
667
|
-
return new_owner, new_dialog_state, text, team
|
801
|
+
self.notify("STOP")
|
802
|
+
return owner, DialogState.START, "Lo lamento, no puedo responder en este momento", owner
|
668
803
|
|
669
804
|
def do_transition(self, owner, node, query) -> str:
|
670
805
|
""" Generate method
|
671
806
|
:return: str
|
672
|
-
"""
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
elif isinstance(node, ActionNode):
|
677
|
-
|
678
|
-
#------------------------------
|
679
|
-
# Accion
|
680
|
-
#------------------------------
|
681
|
-
|
682
|
-
logging.info(f"-> node action: {node.action}")
|
683
|
-
if node.tool and not node.tool == "Ninguno":
|
684
|
-
logging.info(f"-> node tool: {node.tool}")
|
685
|
-
self.__work_memory.append({"role": "user", "content": node.text})
|
686
|
-
logging.info("-----")
|
687
|
-
logging.info("\n%s", json.dumps(self.__work_memory, indent=4))
|
688
|
-
logging.info("-----")
|
689
|
-
res = self.__ai_service.generate(self.__work_memory)
|
690
|
-
logging.info(f"-> node tool: {res}")
|
691
|
-
res = self.get_text(res)
|
692
|
-
# Check if res is empty
|
693
|
-
if not res or res == "":
|
694
|
-
logging.info("1 -> res vacio")
|
695
|
-
res = "Lo lamento, no puedo responder en este momento"
|
696
|
-
|
697
|
-
logging.info(f"------------RESET---------------")
|
698
|
-
self.reset()
|
699
|
-
|
700
|
-
return owner, DialogState.START, res, owner
|
701
|
-
logging.info(f"-> node tool: envia -> {res}")
|
702
|
-
text = self.team_inquiry(node.team, res, node.tool, False)
|
703
|
-
else:
|
807
|
+
"""
|
808
|
+
try:
|
809
|
+
if isinstance(node, ActionNode):
|
810
|
+
self.notify("realizando acción...")
|
704
811
|
#------------------------------
|
705
|
-
#
|
812
|
+
# Accion
|
706
813
|
#------------------------------
|
707
|
-
logging.info(f"-> node
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
logging.info(f"-> node team -> es terminal -> {node.text}")
|
814
|
+
logging.info(f"-> node action: {node.action}")
|
815
|
+
if node.tool and not node.tool == "Ninguno":
|
816
|
+
self.notify("aplicando herramienta...")
|
817
|
+
logging.info(f"-> node tool: {node.tool}")
|
712
818
|
self.__work_memory.append({"role": "user", "content": node.text})
|
713
819
|
logging.info("-----")
|
714
820
|
logging.info("\n%s", json.dumps(self.__work_memory, indent=4))
|
715
|
-
logging.info("-----")
|
821
|
+
logging.info("-----")
|
716
822
|
res = self.__ai_service.generate(self.__work_memory)
|
823
|
+
logging.info(f"-> node tool: {res}")
|
717
824
|
res = self.get_text(res)
|
718
|
-
self.__work_memory.append({"role": "system", "content": res})
|
719
825
|
# Check if res is empty
|
720
826
|
if not res or res == "":
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
return owner, DialogState.START, res, owner
|
726
|
-
logging.info(f"-> node team -> envia: {res}")
|
727
|
-
text = self.team_inquiry(node.team, res, None, True)
|
728
|
-
return node.team, DialogState.START, res, node.team
|
827
|
+
self.notify("no pude hacer uso de la herramienta")
|
828
|
+
return self.recovery(query)
|
829
|
+
logging.info(f"-> node tool: envia -> {res}")
|
830
|
+
res = self.team_inquiry(node.team, res, node.tool, False)
|
729
831
|
else:
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
832
|
+
self.notify("realizando llamada...")
|
833
|
+
#------------------------------
|
834
|
+
# Lllamada
|
835
|
+
#------------------------------
|
836
|
+
logging.info(f"-> node team: {node.team}")
|
837
|
+
# Verifica si el nodo es termianl ya que significa
|
838
|
+
# que el dialogo cambia de agente
|
839
|
+
if node.is_terminal:
|
840
|
+
logging.info(f"-> node team -> es terminal -> {node.text}")
|
841
|
+
self.__work_memory.append({"role": "user", "content": node.text})
|
842
|
+
logging.info("-----")
|
843
|
+
logging.info("\n%s", json.dumps(self.__work_memory, indent=4))
|
844
|
+
logging.info("-----")
|
845
|
+
res = self.__ai_service.generate(self.__work_memory)
|
846
|
+
res = self.get_text(res)
|
847
|
+
self.__work_memory.append({"role": "system", "content": res})
|
848
|
+
# Check if res is empty
|
849
|
+
if not res or res == "":
|
850
|
+
self.notify(f"no pude contactar al agente: {node.team}")
|
851
|
+
return self.recovery(query)
|
852
|
+
self.notify(f"le envio al agente {node.team}: {res}")
|
853
|
+
logging.info(f"-> node team -> envia: {res}")
|
854
|
+
res = self.team_inquiry(node.team, res, None, True)
|
855
|
+
if res and not res == "ERROR" and not res == "":
|
856
|
+
logging.info(f"------------RESET---------------")
|
857
|
+
self.reset()
|
858
|
+
self.notify("STOP")
|
859
|
+
return node.team, DialogState.START, res, node.team
|
860
|
+
else:
|
861
|
+
return self.recovery(query)
|
862
|
+
else:
|
863
|
+
logging.info("-> node team -> continua")
|
864
|
+
self.__work_memory.append({"role": "user", "content": node.text})
|
865
|
+
logging.info(f"-> node team -> envia: {query}")
|
866
|
+
res = self.team_inquiry(node.team, query, node.tool, False)
|
867
|
+
self.notify(f"continuando con díalogo")
|
868
|
+
# Adiciona el texto al work memory
|
869
|
+
if res and not res == "ERROR" and not res == "":
|
870
|
+
logging.info(f"-> Adicion WM node team -> text: {res}")
|
871
|
+
self.__work_memory.append({"role": "system", "content": res})
|
872
|
+
else:
|
873
|
+
return self.recovery(query)
|
874
|
+
logging.info("#########> Procesa respuesta del equipo en profundidad")
|
875
|
+
# Verifica si se alcanzó el límite de profundidad
|
876
|
+
self.__deep_count += 1
|
877
|
+
if self.__deep_count < self.__deep_limit:
|
878
|
+
self.notify("efectuando inferencia en profundidad")
|
879
|
+
return self.transition(owner, node.performative, res, True)
|
880
|
+
else:
|
881
|
+
self.notify(f"se alcanzó el límite de profundidad: {self.__deep_count}")
|
882
|
+
self.__deep_count = 0
|
883
|
+
logging.info("-> node team -> deep limit")
|
884
|
+
logging.info(f"------------RESET---------------")
|
885
|
+
self.notify("STOP")
|
886
|
+
self.reset()
|
887
|
+
return "Web", DialogState.START, self.RECOVERY_MSG, "Web"
|
741
888
|
else:
|
742
|
-
self.
|
743
|
-
logging.info("-> node
|
744
|
-
|
745
|
-
logging.info(f"
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
889
|
+
self.notify("efectuando inferencia...")
|
890
|
+
logging.info(f"D -> node: {node.text}")
|
891
|
+
self.__work_memory.append({"role": "user", "content": node.text})
|
892
|
+
logging.info(f"=> !!!!: {query}")
|
893
|
+
logging.info("-----")
|
894
|
+
logging.info("\n%s", json.dumps(self.__work_memory, indent=4))
|
895
|
+
logging.info("-----")
|
896
|
+
res = self.__ai_service.generate(self.__work_memory)
|
897
|
+
res = self.get_text(res)
|
898
|
+
self.__work_memory.append({"role": "system", "content": res})
|
899
|
+
# Check if res is empty
|
900
|
+
if not res or res == "":
|
901
|
+
return self.recovery(query)
|
902
|
+
logging.info(f"=> Thought DEEP: {res}")
|
903
|
+
self.notify(f"Realizando inferencia: {res}")
|
904
|
+
new_dialog_state = node.performative
|
905
|
+
if not node.is_terminal:
|
906
|
+
self.notify(f"continuando díalogo de inferencia")
|
907
|
+
return owner, new_dialog_state, res, owner
|
908
|
+
self.notify(f"finalizando díalogo de inferencia")
|
909
|
+
logging.info(f"Tipe node: {type(node)}")
|
910
|
+
logging.info(f"$$$> new_owner: {owner} new_dialog_state: {new_dialog_state}")
|
911
|
+
self.notify("STOP")
|
912
|
+
return owner, new_dialog_state, res, owner
|
913
|
+
logging.info("END: do_transition")
|
914
|
+
except Exception as e:
|
915
|
+
traceback.print_exc()
|
764
916
|
logging.info(f"------------RESET---------------")
|
765
917
|
self.reset()
|
766
|
-
|
767
|
-
|
768
|
-
return owner, DialogState.START, res, owner
|
769
|
-
|
770
|
-
logging.info(f"=> Thought DEEP: {res}")
|
771
|
-
|
772
|
-
new_dialog_state = node.performative
|
773
|
-
if not node.is_terminal:
|
774
|
-
logging.info(f"=> new_owner: {owner} new_dialog_state: {new_dialog_state}")
|
775
|
-
return owner, new_dialog_state, res, owner
|
776
|
-
|
777
|
-
logging.info(f"Tipe node: {type(node)}")
|
778
|
-
|
779
|
-
logging.info(f"$$$> new_owner: {owner} new_dialog_state: {new_dialog_state}")
|
780
|
-
return owner, new_dialog_state, res, owner
|
918
|
+
self.notify("STOP")
|
919
|
+
return owner, DialogState.START, "Lo lamento, no puedo responder en este momento", owner
|
781
920
|
|
782
921
|
def set_knowledge(self, knowledge) -> str:
|
783
922
|
""" Set knowledge method
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -37,7 +37,7 @@ class DialogState:
|
|
37
37
|
class Node:
|
38
38
|
def __init__(self, actor, performative, text=None, is_terminal=False):
|
39
39
|
self.actor = actor
|
40
|
-
self.performative = performative
|
40
|
+
self.performative = performative
|
41
41
|
self.text = text
|
42
42
|
self.is_terminal = is_terminal
|
43
43
|
self.children = []
|
@@ -57,6 +57,10 @@ class ResponseNode(Node):
|
|
57
57
|
def __init__(self, actor, performative, text, is_terminal=False):
|
58
58
|
super().__init__(actor, performative=performative, text=text, is_terminal=is_terminal)
|
59
59
|
|
60
|
+
class GotoNode(Node):
|
61
|
+
def __init__(self, actor, performative, text, is_terminal=False):
|
62
|
+
super().__init__(actor, performative=performative, text=text, is_terminal=is_terminal)
|
63
|
+
|
60
64
|
#------------------------------------------
|
61
65
|
# Define functions
|
62
66
|
#------------------------------------------
|
@@ -125,7 +129,7 @@ def recorrer_interacciones(obj):
|
|
125
129
|
# Si tiene la clave "tipo", creamos un nodo (según su valor) y procesamos sus hijos
|
126
130
|
if "tipo" in obj:
|
127
131
|
# Se usa el id del diccionario convertido a cadena para 'performative'
|
128
|
-
current_id = str(id(obj))
|
132
|
+
current_id = obj["id"] if "id" in obj else str(id(obj))
|
129
133
|
texto = obj.get("texto")
|
130
134
|
|
131
135
|
# Procesamos los nodos hijos buscando en las claves "interacciones" y "Interacciones"
|
@@ -150,6 +154,8 @@ def recorrer_interacciones(obj):
|
|
150
154
|
nuevo_nodo = ActionNode(actor=obj["actor"], performative=current_id, text=texto, action=tipo, team=obj["equipo"], tool=obj["herramienta"], is_terminal=is_terminal)
|
151
155
|
elif tipo == "respuesta de equipo":
|
152
156
|
nuevo_nodo = ResponseNode(actor=obj["actor"], performative=current_id, text=texto, is_terminal=is_terminal)
|
157
|
+
elif tipo == "salta a":
|
158
|
+
nuevo_nodo = GotoNode(actor=obj["actor"], performative=current_id, text=texto, is_terminal=is_terminal)
|
153
159
|
else:
|
154
160
|
nuevo_nodo = Node(actor=obj["actor"], performative=current_id, text=texto, is_terminal=is_terminal)
|
155
161
|
|
@@ -168,8 +174,10 @@ def recorrer_interacciones(obj):
|
|
168
174
|
nodos.extend(resultado)
|
169
175
|
else:
|
170
176
|
nodos.append(resultado)
|
177
|
+
# Devolvemos la lista de nodos encontrados
|
171
178
|
return nodos if nodos else None
|
172
179
|
else:
|
180
|
+
# Si no es ni lista ni diccionario, devolvemos None
|
173
181
|
return None
|
174
182
|
except Exception as e:
|
175
183
|
print(f"Error al recorrer interacciones: {e}")
|
File without changes
|
File without changes
|
@@ -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
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: pbesa
|
3
|
-
Version: 4.0.
|
3
|
+
Version: 4.0.7
|
4
4
|
License-File: LICENSE
|
5
5
|
License-File: LICENSE.txt
|
6
6
|
Requires-Dist: pymongo==4.6.3
|
@@ -8,3 +8,4 @@ Requires-Dist: requests==2.32.3
|
|
8
8
|
Requires-Dist: azure-ai-projects==1.0.0b6
|
9
9
|
Requires-Dist: azure-ai-inference==1.0.0b9
|
10
10
|
Requires-Dist: azure-identity==1.20.0
|
11
|
+
Dynamic: requires-dist
|
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.5 → pbesa-4.0.7}/examples/django/helloworld/helloworld/__pycache__/pbesa.cpython-36.pyc
RENAMED
File without changes
|
File without changes
|
{pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/helloworld/__pycache__/urls.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.5 → pbesa-4.0.7}/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.5 → pbesa-4.0.7}/examples/django/helloworld/translate/__pycache__/__init__.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/__pycache__/admin.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/__pycache__/apps.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/__pycache__/models.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/__pycache__/urls.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.5 → pbesa-4.0.7}/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.5 → pbesa-4.0.7}/examples/django/helloworld/translate/mas/controller/translatedelegate.py
RENAMED
File without changes
|
{pbesa-4.0.5 → pbesa-4.0.7}/examples/django/helloworld/translate/mas/controller/translateresponse.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{pbesa-4.0.5 → pbesa-4.0.7}/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.5 → pbesa-4.0.7}/examples/remote/mas/worker/__pycache__/counteragent.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/worker/__pycache__/countertask.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.5 → pbesa-4.0.7}/examples/remote/mas/worker/__pycache__/translatetask.cpython-36.pyc
RENAMED
File without changes
|
{pbesa-4.0.5 → pbesa-4.0.7}/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
|