chatgraph 0.1.1__py3-none-any.whl → 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of chatgraph might be problematic. Click here for more details.
- chatgraph/bot/chatbot_model.py +19 -13
- chatgraph/bot/chatbot_router.py +9 -9
- chatgraph/messages/test_message_consumer.py +17 -0
- chatgraph/types/output_state.py +57 -0
- chatgraph/types/route.py +9 -7
- chatgraph/types/user_state.py +24 -4
- {chatgraph-0.1.1.dist-info → chatgraph-0.1.4.dist-info}/METADATA +4 -4
- chatgraph-0.1.4.dist-info/RECORD +17 -0
- chatgraph-0.1.1.dist-info/RECORD +0 -16
- {chatgraph-0.1.1.dist-info → chatgraph-0.1.4.dist-info}/LICENSE +0 -0
- {chatgraph-0.1.1.dist-info → chatgraph-0.1.4.dist-info}/WHEEL +0 -0
chatgraph/bot/chatbot_model.py
CHANGED
|
@@ -6,7 +6,7 @@ from logging import debug
|
|
|
6
6
|
from ..error.chatbot_error import ChatbotError, ChatbotMessageError
|
|
7
7
|
from ..messages.base_message_consumer import MessageConsumer
|
|
8
8
|
from ..types.message_types import Message
|
|
9
|
-
from ..types.output_state import ChatbotResponse, RedirectResponse
|
|
9
|
+
from ..types.output_state import ChatbotResponse, RedirectResponse, EndChatResponse, TransferToHuman
|
|
10
10
|
from ..types.route import Route
|
|
11
11
|
from ..types.user_state import UserState
|
|
12
12
|
from .chatbot_router import ChatbotRouter
|
|
@@ -38,16 +38,16 @@ class ChatbotApp(ABC):
|
|
|
38
38
|
prefix (str): O prefixo a ser adicionado às rotas do roteador.
|
|
39
39
|
|
|
40
40
|
Raises:
|
|
41
|
-
ChatbotError: Se a rota '
|
|
41
|
+
ChatbotError: Se a rota 'start' não for encontrada no roteador.
|
|
42
42
|
"""
|
|
43
|
-
if '
|
|
44
|
-
raise ChatbotError('Erro ao incluir rota,
|
|
43
|
+
if 'start' not in router.routes.keys():
|
|
44
|
+
raise ChatbotError('Erro ao incluir rota, start não encontrado!')
|
|
45
45
|
|
|
46
46
|
prefixed_routes = {
|
|
47
47
|
(
|
|
48
|
-
f'
|
|
49
|
-
if key.
|
|
50
|
-
else f'
|
|
48
|
+
f'start{prefix.lower()}'
|
|
49
|
+
if key.lower() == 'start'
|
|
50
|
+
else f'start{prefix.lower()}{key.lower().replace("start", "")}'
|
|
51
51
|
): value
|
|
52
52
|
for key, value in router.routes.items()
|
|
53
53
|
}
|
|
@@ -63,10 +63,10 @@ class ChatbotApp(ABC):
|
|
|
63
63
|
Returns:
|
|
64
64
|
function: O decorador que adiciona a função à rota especificada.
|
|
65
65
|
"""
|
|
66
|
-
route_name = route_name.strip().
|
|
66
|
+
route_name = route_name.strip().lower()
|
|
67
67
|
|
|
68
|
-
if '
|
|
69
|
-
route_name = f'
|
|
68
|
+
if 'start' not in route_name:
|
|
69
|
+
route_name = f'start{route_name}'
|
|
70
70
|
|
|
71
71
|
def decorator(func):
|
|
72
72
|
params = dict()
|
|
@@ -119,7 +119,7 @@ class ChatbotApp(ABC):
|
|
|
119
119
|
customer_id = message.customer_id
|
|
120
120
|
|
|
121
121
|
menu = self.__user_state.get_menu(customer_id)
|
|
122
|
-
menu = menu.
|
|
122
|
+
menu = menu.lower()
|
|
123
123
|
handler = self.__routes.get(menu, None)
|
|
124
124
|
|
|
125
125
|
if not handler:
|
|
@@ -143,11 +143,17 @@ class ChatbotApp(ABC):
|
|
|
143
143
|
elif type(message_response) == ChatbotResponse:
|
|
144
144
|
route = self.__adjust_route(message_response.route, menu)
|
|
145
145
|
self.__user_state.set_menu(customer_id, route)
|
|
146
|
-
return message_response.
|
|
146
|
+
return message_response.json()
|
|
147
147
|
elif type(message_response) == RedirectResponse:
|
|
148
148
|
route = self.__adjust_route(message_response.route, menu)
|
|
149
149
|
self.__user_state.set_menu(customer_id, route)
|
|
150
150
|
return self.process_message(message)
|
|
151
|
+
elif type(message_response) == EndChatResponse:
|
|
152
|
+
self.__user_state.delete_menu(customer_id)
|
|
153
|
+
return message_response.json()
|
|
154
|
+
elif type(message_response) == TransferToHuman:
|
|
155
|
+
self.__user_state.delete_menu(customer_id)
|
|
156
|
+
return message_response.json()
|
|
151
157
|
else:
|
|
152
158
|
raise ChatbotError('Tipo de retorno inválido!')
|
|
153
159
|
|
|
@@ -165,7 +171,7 @@ class ChatbotApp(ABC):
|
|
|
165
171
|
if not route:
|
|
166
172
|
return absolute_route
|
|
167
173
|
|
|
168
|
-
if '
|
|
174
|
+
if 'start' not in route:
|
|
169
175
|
route = absolute_route + route
|
|
170
176
|
|
|
171
177
|
return route
|
chatgraph/bot/chatbot_router.py
CHANGED
|
@@ -29,8 +29,8 @@ class ChatbotRouter:
|
|
|
29
29
|
Returns:
|
|
30
30
|
function: O decorador que adiciona a função à rota especificada.
|
|
31
31
|
"""
|
|
32
|
-
if '
|
|
33
|
-
route_name = f'
|
|
32
|
+
if 'start' not in route_name:
|
|
33
|
+
route_name = f'start{route_name}'
|
|
34
34
|
|
|
35
35
|
def decorator(func):
|
|
36
36
|
params = dict()
|
|
@@ -46,7 +46,7 @@ class ChatbotRouter:
|
|
|
46
46
|
params[param_type] = name
|
|
47
47
|
debug(f'Parameter: {name}, Type: {param_type}')
|
|
48
48
|
|
|
49
|
-
self.routes[route_name.strip().
|
|
49
|
+
self.routes[route_name.strip().lower()] = {
|
|
50
50
|
'function': func,
|
|
51
51
|
'params': params,
|
|
52
52
|
'return': output_param,
|
|
@@ -69,16 +69,16 @@ class ChatbotRouter:
|
|
|
69
69
|
prefix (str): O prefixo a ser adicionado às rotas do roteador.
|
|
70
70
|
|
|
71
71
|
Raises:
|
|
72
|
-
ChatbotError: Se a rota '
|
|
72
|
+
ChatbotError: Se a rota 'start' não for encontrada no roteador fornecido.
|
|
73
73
|
"""
|
|
74
|
-
if '
|
|
75
|
-
raise ChatbotError('Erro ao incluir rota,
|
|
74
|
+
if 'start' not in router.routes.keys():
|
|
75
|
+
raise ChatbotError('Erro ao incluir rota, start não encontrado!')
|
|
76
76
|
|
|
77
77
|
prefixed_routes = {
|
|
78
78
|
(
|
|
79
|
-
f'{prefix.
|
|
80
|
-
if key.
|
|
81
|
-
else f'
|
|
79
|
+
f'{prefix.lower()}'
|
|
80
|
+
if key.lower() == 'start'
|
|
81
|
+
else f'start{prefix.lower()}{key.lower().replace("start", "")}'
|
|
82
82
|
): value
|
|
83
83
|
for key, value in router.routes.items()
|
|
84
84
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import Any, Callable
|
|
2
|
+
from .base_message_consumer import MessageConsumer
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TestMessageConsumer(MessageConsumer):
|
|
6
|
+
"""
|
|
7
|
+
Classe de consumidor de mensagens de teste para uso em testes unitários.
|
|
8
|
+
"""
|
|
9
|
+
def __init__(self) -> None:
|
|
10
|
+
super().__init__()
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def load_dotenv(cls) -> 'TestMessageConsumer':
|
|
14
|
+
return cls()
|
|
15
|
+
|
|
16
|
+
def start_consume(self, process_message: Callable) -> Any:
|
|
17
|
+
return super().start_consume(process_message)
|
chatgraph/types/output_state.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from typing import Union
|
|
2
|
+
import json
|
|
2
3
|
|
|
3
4
|
messageTypes = Union[str, float, int, None]
|
|
4
5
|
|
|
@@ -22,6 +23,15 @@ class ChatbotResponse:
|
|
|
22
23
|
"""
|
|
23
24
|
self.message = message
|
|
24
25
|
self.route = route
|
|
26
|
+
|
|
27
|
+
def json(self):
|
|
28
|
+
'''
|
|
29
|
+
Retorna o objeto em formato json.
|
|
30
|
+
'''
|
|
31
|
+
return json.dumps({
|
|
32
|
+
'type': 'message',
|
|
33
|
+
'message': self.message,
|
|
34
|
+
})
|
|
25
35
|
|
|
26
36
|
|
|
27
37
|
class RedirectResponse:
|
|
@@ -40,3 +50,50 @@ class RedirectResponse:
|
|
|
40
50
|
route (str): A rota para a qual o chatbot deve redirecionar.
|
|
41
51
|
"""
|
|
42
52
|
self.route = route
|
|
53
|
+
|
|
54
|
+
class EndChatResponse:
|
|
55
|
+
"""
|
|
56
|
+
Representa uma resposta que indica o fim do chatbot.
|
|
57
|
+
|
|
58
|
+
Atributos:
|
|
59
|
+
tabulation_id (str): O ID da tabulação do chatbot.
|
|
60
|
+
observations (str): As observações finais do chatbot.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self, tabulation_id: str, observations:str) -> None:
|
|
64
|
+
'''
|
|
65
|
+
Finzaliza e tabula as informações do chatbot.
|
|
66
|
+
'''
|
|
67
|
+
self.tabulation_id = tabulation_id
|
|
68
|
+
self.observations = observations
|
|
69
|
+
|
|
70
|
+
def json(self):
|
|
71
|
+
'''
|
|
72
|
+
Retorna o objeto em formato json.
|
|
73
|
+
'''
|
|
74
|
+
return json.dumps({
|
|
75
|
+
'type': 'tabulate',
|
|
76
|
+
'tabulation_id': self.tabulation_id,
|
|
77
|
+
'observations': self.observations,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
class TransferToHuman:
|
|
81
|
+
"""
|
|
82
|
+
Representa uma transferencia para um atendente humano.
|
|
83
|
+
"""
|
|
84
|
+
def __init__(self, campaign_id: str, observations:str) -> None:
|
|
85
|
+
'''
|
|
86
|
+
Finzaliza e tabula as informações do chatbot.
|
|
87
|
+
'''
|
|
88
|
+
self.campaign_id = campaign_id
|
|
89
|
+
self.observations = observations
|
|
90
|
+
|
|
91
|
+
def json(self):
|
|
92
|
+
'''
|
|
93
|
+
Retorna o objeto em formato json.
|
|
94
|
+
'''
|
|
95
|
+
return json.dumps({
|
|
96
|
+
'type': 'transfer',
|
|
97
|
+
'campaign_id': self.campaign_id,
|
|
98
|
+
'observations': self.observations,
|
|
99
|
+
})
|
chatgraph/types/route.py
CHANGED
|
@@ -10,31 +10,33 @@ class Route:
|
|
|
10
10
|
routes (list[str]): A lista de todas as rotas disponíveis no fluxo.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
def __init__(self, current: str, routes: list[str]):
|
|
13
|
+
def __init__(self, current: str, routes: list[str], separator:str='.'):
|
|
14
14
|
"""
|
|
15
15
|
Inicializa a rota com a rota atual e a lista de rotas disponíveis.
|
|
16
16
|
|
|
17
17
|
Args:
|
|
18
18
|
current (str): A rota atual.
|
|
19
19
|
routes (list[str]): A lista de todas as rotas disponíveis no fluxo.
|
|
20
|
+
separator (str): O separador de partes de rota. Padrão é '.'.
|
|
20
21
|
"""
|
|
21
22
|
self.current = current
|
|
22
23
|
self.routes = routes
|
|
24
|
+
self.separator = separator
|
|
23
25
|
|
|
24
26
|
def get_previous(self) -> str:
|
|
25
27
|
"""
|
|
26
28
|
Retorna o caminho anterior ao caminho atual.
|
|
27
29
|
|
|
28
30
|
Raises:
|
|
29
|
-
RouteError: Se a rota atual for '
|
|
31
|
+
RouteError: Se a rota atual for 'start', indicando que não há caminho anterior.
|
|
30
32
|
|
|
31
33
|
Returns:
|
|
32
34
|
str: O caminho anterior à rota atual.
|
|
33
35
|
"""
|
|
34
|
-
if self.current == '
|
|
35
|
-
raise RouteError('Não há caminho anterior ao
|
|
36
|
+
if self.current == 'start':
|
|
37
|
+
raise RouteError('Não há caminho anterior ao start')
|
|
36
38
|
|
|
37
|
-
previous_route =
|
|
39
|
+
previous_route = self.separator.join(self.current.split(self.separator)[:-1])
|
|
38
40
|
return previous_route
|
|
39
41
|
|
|
40
42
|
def get_next(self, next_part: str) -> str:
|
|
@@ -50,8 +52,8 @@ class Route:
|
|
|
50
52
|
Returns:
|
|
51
53
|
str: O próximo caminho construído a partir da rota atual e da parte fornecida.
|
|
52
54
|
"""
|
|
53
|
-
next_part = next_part.strip().
|
|
54
|
-
next_route = f"{self.current.rstrip(
|
|
55
|
+
next_part = next_part.strip().lower()
|
|
56
|
+
next_route = f"{self.current.rstrip(self.separator)}{next_part}"
|
|
55
57
|
if next_route not in self.routes:
|
|
56
58
|
raise RouteError(f'Rota não encontrada: {next_route}')
|
|
57
59
|
return next_route
|
chatgraph/types/user_state.py
CHANGED
|
@@ -31,6 +31,16 @@ class UserState(ABC):
|
|
|
31
31
|
menu (str): O menu a ser definido para o cliente.
|
|
32
32
|
"""
|
|
33
33
|
pass
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def delete_menu(self, customer_id: str) -> None:
|
|
37
|
+
"""
|
|
38
|
+
Deleta o menu atual para o ID de cliente fornecido.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
customer_id (str): O ID do cliente.
|
|
42
|
+
"""
|
|
43
|
+
pass
|
|
34
44
|
|
|
35
45
|
|
|
36
46
|
class SimpleUserState(UserState):
|
|
@@ -49,7 +59,7 @@ class SimpleUserState(UserState):
|
|
|
49
59
|
|
|
50
60
|
def get_menu(self, customer_id: str) -> str:
|
|
51
61
|
"""
|
|
52
|
-
Retorna o menu atual para o ID de cliente fornecido. Se o cliente não tiver um menu definido, define '
|
|
62
|
+
Retorna o menu atual para o ID de cliente fornecido. Se o cliente não tiver um menu definido, define 'start' como padrão.
|
|
53
63
|
|
|
54
64
|
Args:
|
|
55
65
|
customer_id (str): O ID do cliente.
|
|
@@ -57,8 +67,8 @@ class SimpleUserState(UserState):
|
|
|
57
67
|
Returns:
|
|
58
68
|
str: O menu atual associado ao cliente.
|
|
59
69
|
"""
|
|
60
|
-
menu = self.states.get(customer_id, '
|
|
61
|
-
if menu == '
|
|
70
|
+
menu = self.states.get(customer_id, 'start')
|
|
71
|
+
if menu == 'start':
|
|
62
72
|
self.set_menu(customer_id, menu)
|
|
63
73
|
return menu
|
|
64
74
|
|
|
@@ -71,4 +81,14 @@ class SimpleUserState(UserState):
|
|
|
71
81
|
menu (str | None): O menu a ser definido para o cliente. Se None, não faz nenhuma alteração.
|
|
72
82
|
"""
|
|
73
83
|
if menu:
|
|
74
|
-
self.states[customer_id] = menu.
|
|
84
|
+
self.states[customer_id] = menu.lower()
|
|
85
|
+
|
|
86
|
+
def delete_menu(self, customer_id: str) -> None:
|
|
87
|
+
"""
|
|
88
|
+
Deleta o menu atual para o ID de cliente fornecido.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
customer_id (str): O ID do cliente.
|
|
92
|
+
"""
|
|
93
|
+
if customer_id in self.states:
|
|
94
|
+
self.states.pop(customer_id)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: chatgraph
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: A user-friendly chatbot library
|
|
5
5
|
Home-page: https://github.com/irissonnlima/chatgraph
|
|
6
6
|
License: MIT
|
|
@@ -108,11 +108,11 @@ from chatgraph import ChatbotApp, ChatbotRouter, SimpleUserState, RabbitMessageC
|
|
|
108
108
|
# Definindo rotas com ChatbotRouter
|
|
109
109
|
router = ChatbotRouter()
|
|
110
110
|
|
|
111
|
-
@router.route("
|
|
111
|
+
@router.route("start")
|
|
112
112
|
def say_hello():
|
|
113
|
-
return ChatbotResponse(message="Hello! How can I assist you today?", route="
|
|
113
|
+
return ChatbotResponse(message="Hello! How can I assist you today?", route=".help")
|
|
114
114
|
|
|
115
|
-
@router.route("
|
|
115
|
+
@router.route(".help")
|
|
116
116
|
def provide_help():
|
|
117
117
|
return ChatbotResponse(message="Here are some things I can help with: ...")
|
|
118
118
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
chatgraph/__init__.py,sha256=qPA9Vw2SZivqVUlB19D-YrRuequMThR6kgO2XsZcwDc,602
|
|
2
|
+
chatgraph/auth/credentials.py,sha256=xsMEpLQnc66novPjL6upocMcnUnvFJ7yxINzUenkxmc,2388
|
|
3
|
+
chatgraph/bot/chatbot_model.py,sha256=6tPoplS9nPHMYK0gSKFGq6cQr3dA2jOiBJTmQVuOb0I,6643
|
|
4
|
+
chatgraph/bot/chatbot_router.py,sha256=kZ_4X5AhhE2mntx4tcHiapaSKC1T4K1jqIVjLp1Pjg0,2817
|
|
5
|
+
chatgraph/error/chatbot_error.py,sha256=4sEcW8vz0eQk2QDmDygucVM4caHliZW5iH7XJvmLBuk,1897
|
|
6
|
+
chatgraph/error/route_error.py,sha256=CY8m82ap7-Sr-DXPsolltRW50TqD__5RyXBmNNJCWr8,793
|
|
7
|
+
chatgraph/messages/base_message_consumer.py,sha256=OSdTT4dHIzawLDOCZ-4hZ06T8UBxoJIosqvXl7gxpM0,1099
|
|
8
|
+
chatgraph/messages/rabbitMQ_message_consumer.py,sha256=mRcJ1Hbd5wPFxC3v6Tj3pYvjd9yAc398Ys6cUox-CTY,7005
|
|
9
|
+
chatgraph/messages/test_message_consumer.py,sha256=9XIkbCHd1S6S8pINRT-SLEvUT0TQWBCsdPhYN6PpZ2s,518
|
|
10
|
+
chatgraph/types/message_types.py,sha256=NBuebmpO2e9ilspBoFgs9O3rY_VQ2AP_Z8Q_nC70nyM,965
|
|
11
|
+
chatgraph/types/output_state.py,sha256=fpJ_hGO4aZ3wr6DV4i9ctnkHytWhK6ZDpKpJ3vSwRyY,3064
|
|
12
|
+
chatgraph/types/route.py,sha256=nKTqzwGl7d_Bu8G6Sr0EmmhuuiZWKEoSozITRrdyi1g,2587
|
|
13
|
+
chatgraph/types/user_state.py,sha256=B17bN300RW_9-WUbI7pWK_BUCvMbdpm6gLQHPqdGly4,2858
|
|
14
|
+
chatgraph-0.1.4.dist-info/LICENSE,sha256=rVJozpRzDlplOpvI8A1GvmfVS0ReYdZvMWc1j2jV0v8,1090
|
|
15
|
+
chatgraph-0.1.4.dist-info/METADATA,sha256=KQyN7GDztKsyFwUM4F_kF9eFE--Bi44ajc7AsaI6-Mg,5829
|
|
16
|
+
chatgraph-0.1.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
17
|
+
chatgraph-0.1.4.dist-info/RECORD,,
|
chatgraph-0.1.1.dist-info/RECORD
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
chatgraph/__init__.py,sha256=qPA9Vw2SZivqVUlB19D-YrRuequMThR6kgO2XsZcwDc,602
|
|
2
|
-
chatgraph/auth/credentials.py,sha256=xsMEpLQnc66novPjL6upocMcnUnvFJ7yxINzUenkxmc,2388
|
|
3
|
-
chatgraph/bot/chatbot_model.py,sha256=D2MGguq3Fnl9xiY0frpk_-DkTNgeBV57q5w3jx8Kba4,6296
|
|
4
|
-
chatgraph/bot/chatbot_router.py,sha256=CQxu7ZiLBZDzXso65f4u3jfLA8BsuTAmp_tonFhFdc0,2817
|
|
5
|
-
chatgraph/error/chatbot_error.py,sha256=4sEcW8vz0eQk2QDmDygucVM4caHliZW5iH7XJvmLBuk,1897
|
|
6
|
-
chatgraph/error/route_error.py,sha256=CY8m82ap7-Sr-DXPsolltRW50TqD__5RyXBmNNJCWr8,793
|
|
7
|
-
chatgraph/messages/base_message_consumer.py,sha256=OSdTT4dHIzawLDOCZ-4hZ06T8UBxoJIosqvXl7gxpM0,1099
|
|
8
|
-
chatgraph/messages/rabbitMQ_message_consumer.py,sha256=mRcJ1Hbd5wPFxC3v6Tj3pYvjd9yAc398Ys6cUox-CTY,7005
|
|
9
|
-
chatgraph/types/message_types.py,sha256=NBuebmpO2e9ilspBoFgs9O3rY_VQ2AP_Z8Q_nC70nyM,965
|
|
10
|
-
chatgraph/types/output_state.py,sha256=ZditFyycd6CEvemrW9c7mThQDP1l6tI_8p19Jgj8pbs,1471
|
|
11
|
-
chatgraph/types/route.py,sha256=iSeVDMsH_1Q1n1sAyeiqFdvg2KWIY8DFdGThswwpUBw,2422
|
|
12
|
-
chatgraph/types/user_state.py,sha256=0n7K5k5DGoP3ug8XNPREqdJHGHXmcOwhPrANpXJSDjU,2321
|
|
13
|
-
chatgraph-0.1.1.dist-info/LICENSE,sha256=rVJozpRzDlplOpvI8A1GvmfVS0ReYdZvMWc1j2jV0v8,1090
|
|
14
|
-
chatgraph-0.1.1.dist-info/METADATA,sha256=yt3nbvNpQX-gCBzx8zP9Uz6ZDPfpCml2Dpd1Z8D8KuU,5828
|
|
15
|
-
chatgraph-0.1.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
16
|
-
chatgraph-0.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|