kbot-py-client 2.0.4__tar.gz → 2.0.8__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.
- {kbot_py_client-2.0.4 → kbot_py_client-2.0.8}/LICENSE +3 -1
- {kbot_py_client-2.0.4 → kbot_py_client-2.0.8}/PKG-INFO +80 -13
- kbot_py_client-2.0.4/kbot_py_client.egg-info/PKG-INFO → kbot_py_client-2.0.8/README.md +71 -21
- {kbot_py_client-2.0.4 → kbot_py_client-2.0.8}/kbot_client/chat_client.py +54 -12
- kbot_py_client-2.0.8/pyproject.toml +119 -0
- kbot_py_client-2.0.4/.gitignore +0 -50
- kbot_py_client-2.0.4/README.md +0 -182
- kbot_py_client-2.0.4/kbot_client/client.recording.py +0 -316
- kbot_py_client-2.0.4/kbot_py_client.egg-info/SOURCES.txt +0 -16
- kbot_py_client-2.0.4/kbot_py_client.egg-info/dependency_links.txt +0 -1
- kbot_py_client-2.0.4/kbot_py_client.egg-info/requires.txt +0 -1
- kbot_py_client-2.0.4/kbot_py_client.egg-info/top_level.txt +0 -1
- kbot_py_client-2.0.4/setup.cfg +0 -4
- kbot_py_client-2.0.4/setup.py +0 -23
- {kbot_py_client-2.0.4 → kbot_py_client-2.0.8}/kbot_client/__init__.py +0 -0
- {kbot_py_client-2.0.4 → kbot_py_client-2.0.8}/kbot_client/client.py +4 -4
- {kbot_py_client-2.0.4 → kbot_py_client-2.0.8}/kbot_client/client_group.py +0 -0
- {kbot_py_client-2.0.4 → kbot_py_client-2.0.8}/kbot_client/folder_sync.py +0 -0
- {kbot_py_client-2.0.4 → kbot_py_client-2.0.8}/kbot_client/test.py +0 -0
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kbot-py-client
|
|
3
|
-
Version: 2.0.
|
|
4
|
-
Summary: Client for Konverso Kbot
|
|
5
|
-
|
|
3
|
+
Version: 2.0.8
|
|
4
|
+
Summary: Client for Konverso Kbot.
|
|
5
|
+
License: MIT
|
|
6
6
|
Author: Konverso
|
|
7
7
|
Author-email: contact@konverso.ai
|
|
8
|
-
|
|
8
|
+
Requires-Python: >=3.12,<4.0
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
|
-
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Requires-Dist: requests (>=2.27.1)
|
|
14
|
+
Project-URL: Homepage, https://www.konverso.ai/
|
|
15
|
+
Project-URL: Source, https://github.com/konverso-ai/kbot-py-client
|
|
12
16
|
Description-Content-Type: text/markdown
|
|
13
|
-
License-File: LICENSE
|
|
14
|
-
Requires-Dist: requests>=2.27.1
|
|
15
17
|
|
|
16
|
-
#
|
|
18
|
+
# kbot-py-client #
|
|
17
19
|
This package contains utilities which you may use to easily interact with Konverso Kbot application.
|
|
18
20
|
|
|
19
21
|
In particular, you may:
|
|
@@ -30,13 +32,19 @@ You may contact us:
|
|
|
30
32
|
* For any commercial inquiry: contact@konverso.ai
|
|
31
33
|
* For support: https://konverso.atlassian.net/servicedesk/customer/portals
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
## License
|
|
36
|
+
This client code is open source such that you can freely view it, modify it, create your own version.
|
|
37
|
+
We of course appreciate any contribution and sharing of enhancements made.
|
|
38
|
+
|
|
39
|
+
# Installation #
|
|
34
40
|
|
|
35
41
|
You may use pip3 to install the software on your Kbot instance:
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
pip3 install kbot-py-client
|
|
44
|
+
|
|
45
|
+
Alternatively, you can invoke a more explicity pip installation request, with the URL path:
|
|
38
46
|
|
|
39
|
-
pip3 install -e git+https://
|
|
47
|
+
pip3 install -e git+https://github.com/konverso-ai/kbot-py-client.git#egg=kbot-py-client
|
|
40
48
|
|
|
41
49
|
# Usage #
|
|
42
50
|
|
|
@@ -146,8 +154,30 @@ In this example, we create a conversation between the logged in user and the bot
|
|
|
146
154
|
else:
|
|
147
155
|
print("Could not create conversation due to: ", r.text)
|
|
148
156
|
|
|
149
|
-
## Uploading one file to kbot
|
|
150
157
|
|
|
158
|
+
## Command line chatbot
|
|
159
|
+
The following sample is a complete example you may use to kick off a chat for a given user with the kbot chatbot.
|
|
160
|
+
Here there is no user authentication, but impersonation instead, and user/lookup. This example is a good starting point if you are looking at implementing your own chat interface on top of Kbot.
|
|
161
|
+
```
|
|
162
|
+
# Create a session using an API key with strong privileges
|
|
163
|
+
cli = Client("mytenant.konverso.ai", api_key="34exxxxxxxxxxxxxa7bfb6aeb")
|
|
164
|
+
|
|
165
|
+
# Lookup create the user in kbot
|
|
166
|
+
response = cli.post("user/lookup_create", data={
|
|
167
|
+
"user_name": user_email,
|
|
168
|
+
"account_name": user_email,
|
|
169
|
+
"account_type": "local",
|
|
170
|
+
"external_auth": "nice-in-connect",
|
|
171
|
+
})
|
|
172
|
+
response.raise_for_status()
|
|
173
|
+
|
|
174
|
+
cli.impersonate(user_email, 'local', 'nice-in-connect') #, application_uuid="b0d91200-833f-466c-b5fd-1b0402cc3a36")
|
|
175
|
+
|
|
176
|
+
# Start the client (loop… ends with Ctrl D)
|
|
177
|
+
AsyncChatClient(cli)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Uploading one file to kbot
|
|
151
181
|
|
|
152
182
|
### Prerequisites
|
|
153
183
|
* A local file that you wish to send
|
|
@@ -186,7 +216,7 @@ In this example, we create a conversation between the logged in user and the bot
|
|
|
186
216
|
|
|
187
217
|
### Code sample
|
|
188
218
|
In this example, we simply upload the content of a directory to a folder in the file manager.
|
|
189
|
-
|
|
219
|
+
```
|
|
190
220
|
from kbot_client import Client
|
|
191
221
|
from kbot_client.folder_sync import FolderSync
|
|
192
222
|
|
|
@@ -195,3 +225,40 @@ In this example, we simply upload the content of a directory to a folder in the
|
|
|
195
225
|
syncer = FolderSync(client)
|
|
196
226
|
syncer.sync("/tmp/my_source_folder/", "1831f-XXXXXXXXXXXXXXXXXXXXXXX")
|
|
197
227
|
print("Syncing is done :)")
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## A command line chatbot
|
|
231
|
+
We provide a sample command line chatbot, available in Sync or Async mode
|
|
232
|
+
|
|
233
|
+
### Code sample
|
|
234
|
+
In this example, we simply initialize a chatbot client after creating a relevant User and Impersonating to it.
|
|
235
|
+
This is a good example of implementation of a custom Client for the Kbot product.
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
# Create your client
|
|
239
|
+
import json
|
|
240
|
+
from kbot_client import Client
|
|
241
|
+
from kbot_client.chat_client import AsyncChatClient, SyncChatClient
|
|
242
|
+
|
|
243
|
+
cli = Client("mytenant.konverso.ai", api_key="mykey")
|
|
244
|
+
|
|
245
|
+
external_auth = "cmd-line-chatbot"
|
|
246
|
+
user_email = "a-user-name"
|
|
247
|
+
assistant = "225d9647-0b41-473d-b097-ef4be363958a"
|
|
248
|
+
|
|
249
|
+
# Create your target user
|
|
250
|
+
response = cli.post("user/lookup_create", data={
|
|
251
|
+
"user_name": user_email,
|
|
252
|
+
"account_name": user_email,
|
|
253
|
+
"account_type": "local",
|
|
254
|
+
"external_auth": external_auth,
|
|
255
|
+
})
|
|
256
|
+
response.raise_for_status()
|
|
257
|
+
|
|
258
|
+
# Impersonate accordingly
|
|
259
|
+
cli.impersonate(user_email, 'local', external_auth)
|
|
260
|
+
|
|
261
|
+
# Start the chat
|
|
262
|
+
AsyncChatClient(cli, assistant=assistant, convert_html_to_text=True)
|
|
263
|
+
|
|
264
|
+
```
|
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Name: kbot-py-client
|
|
3
|
-
Version: 2.0.4
|
|
4
|
-
Summary: Client for Konverso Kbot
|
|
5
|
-
Home-page: https://www.konverso.ai
|
|
6
|
-
Author: Konverso
|
|
7
|
-
Author-email: contact@konverso.ai
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Requires-Python: >=3.6
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
License-File: LICENSE
|
|
14
|
-
Requires-Dist: requests>=2.27.1
|
|
15
|
-
|
|
16
|
-
# README #
|
|
1
|
+
# kbot-py-client #
|
|
17
2
|
This package contains utilities which you may use to easily interact with Konverso Kbot application.
|
|
18
3
|
|
|
19
4
|
In particular, you may:
|
|
@@ -30,13 +15,19 @@ You may contact us:
|
|
|
30
15
|
* For any commercial inquiry: contact@konverso.ai
|
|
31
16
|
* For support: https://konverso.atlassian.net/servicedesk/customer/portals
|
|
32
17
|
|
|
33
|
-
|
|
18
|
+
## License
|
|
19
|
+
This client code is open source such that you can freely view it, modify it, create your own version.
|
|
20
|
+
We of course appreciate any contribution and sharing of enhancements made.
|
|
21
|
+
|
|
22
|
+
# Installation #
|
|
34
23
|
|
|
35
24
|
You may use pip3 to install the software on your Kbot instance:
|
|
36
25
|
|
|
37
|
-
|
|
26
|
+
pip3 install kbot-py-client
|
|
27
|
+
|
|
28
|
+
Alternatively, you can invoke a more explicity pip installation request, with the URL path:
|
|
38
29
|
|
|
39
|
-
pip3 install -e git+https://
|
|
30
|
+
pip3 install -e git+https://github.com/konverso-ai/kbot-py-client.git#egg=kbot-py-client
|
|
40
31
|
|
|
41
32
|
# Usage #
|
|
42
33
|
|
|
@@ -146,8 +137,30 @@ In this example, we create a conversation between the logged in user and the bot
|
|
|
146
137
|
else:
|
|
147
138
|
print("Could not create conversation due to: ", r.text)
|
|
148
139
|
|
|
149
|
-
## Uploading one file to kbot
|
|
150
140
|
|
|
141
|
+
## Command line chatbot
|
|
142
|
+
The following sample is a complete example you may use to kick off a chat for a given user with the kbot chatbot.
|
|
143
|
+
Here there is no user authentication, but impersonation instead, and user/lookup. This example is a good starting point if you are looking at implementing your own chat interface on top of Kbot.
|
|
144
|
+
```
|
|
145
|
+
# Create a session using an API key with strong privileges
|
|
146
|
+
cli = Client("mytenant.konverso.ai", api_key="34exxxxxxxxxxxxxa7bfb6aeb")
|
|
147
|
+
|
|
148
|
+
# Lookup create the user in kbot
|
|
149
|
+
response = cli.post("user/lookup_create", data={
|
|
150
|
+
"user_name": user_email,
|
|
151
|
+
"account_name": user_email,
|
|
152
|
+
"account_type": "local",
|
|
153
|
+
"external_auth": "nice-in-connect",
|
|
154
|
+
})
|
|
155
|
+
response.raise_for_status()
|
|
156
|
+
|
|
157
|
+
cli.impersonate(user_email, 'local', 'nice-in-connect') #, application_uuid="b0d91200-833f-466c-b5fd-1b0402cc3a36")
|
|
158
|
+
|
|
159
|
+
# Start the client (loop… ends with Ctrl D)
|
|
160
|
+
AsyncChatClient(cli)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Uploading one file to kbot
|
|
151
164
|
|
|
152
165
|
### Prerequisites
|
|
153
166
|
* A local file that you wish to send
|
|
@@ -186,7 +199,7 @@ In this example, we create a conversation between the logged in user and the bot
|
|
|
186
199
|
|
|
187
200
|
### Code sample
|
|
188
201
|
In this example, we simply upload the content of a directory to a folder in the file manager.
|
|
189
|
-
|
|
202
|
+
```
|
|
190
203
|
from kbot_client import Client
|
|
191
204
|
from kbot_client.folder_sync import FolderSync
|
|
192
205
|
|
|
@@ -195,3 +208,40 @@ In this example, we simply upload the content of a directory to a folder in the
|
|
|
195
208
|
syncer = FolderSync(client)
|
|
196
209
|
syncer.sync("/tmp/my_source_folder/", "1831f-XXXXXXXXXXXXXXXXXXXXXXX")
|
|
197
210
|
print("Syncing is done :)")
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## A command line chatbot
|
|
214
|
+
We provide a sample command line chatbot, available in Sync or Async mode
|
|
215
|
+
|
|
216
|
+
### Code sample
|
|
217
|
+
In this example, we simply initialize a chatbot client after creating a relevant User and Impersonating to it.
|
|
218
|
+
This is a good example of implementation of a custom Client for the Kbot product.
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
# Create your client
|
|
222
|
+
import json
|
|
223
|
+
from kbot_client import Client
|
|
224
|
+
from kbot_client.chat_client import AsyncChatClient, SyncChatClient
|
|
225
|
+
|
|
226
|
+
cli = Client("mytenant.konverso.ai", api_key="mykey")
|
|
227
|
+
|
|
228
|
+
external_auth = "cmd-line-chatbot"
|
|
229
|
+
user_email = "a-user-name"
|
|
230
|
+
assistant = "225d9647-0b41-473d-b097-ef4be363958a"
|
|
231
|
+
|
|
232
|
+
# Create your target user
|
|
233
|
+
response = cli.post("user/lookup_create", data={
|
|
234
|
+
"user_name": user_email,
|
|
235
|
+
"account_name": user_email,
|
|
236
|
+
"account_type": "local",
|
|
237
|
+
"external_auth": external_auth,
|
|
238
|
+
})
|
|
239
|
+
response.raise_for_status()
|
|
240
|
+
|
|
241
|
+
# Impersonate accordingly
|
|
242
|
+
cli.impersonate(user_email, 'local', external_auth)
|
|
243
|
+
|
|
244
|
+
# Start the chat
|
|
245
|
+
AsyncChatClient(cli, assistant=assistant, convert_html_to_text=True)
|
|
246
|
+
|
|
247
|
+
```
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
import uuid
|
|
1
2
|
import time
|
|
2
3
|
from abc import ABC, abstractmethod
|
|
3
4
|
|
|
4
5
|
class ChatClient(ABC):
|
|
5
6
|
def __init__(self, client,
|
|
6
|
-
render_types=("text", "html"),
|
|
7
|
+
render_types=("text", "html", "markdown"),
|
|
7
8
|
prompt="{sender}> {message}",
|
|
8
9
|
user_prompt = "User> ",
|
|
9
10
|
bye = "Bye, hope to see you again soon",
|
|
10
|
-
|
|
11
|
+
assistant = None,
|
|
12
|
+
exit_commands = ("stop", "exit"),
|
|
13
|
+
convert_html_to_text=False):
|
|
11
14
|
|
|
12
15
|
self._client = client
|
|
13
16
|
self._render_types = render_types
|
|
@@ -15,10 +18,15 @@ class ChatClient(ABC):
|
|
|
15
18
|
self._user_prompt = user_prompt
|
|
16
19
|
self._bye = bye
|
|
17
20
|
self._exit_commands = exit_commands
|
|
21
|
+
self._assistant = assistant
|
|
22
|
+
self._convert_html_to_text = convert_html_to_text
|
|
18
23
|
|
|
19
24
|
# Create a new conversation
|
|
20
25
|
#
|
|
21
|
-
response = self._client.post("conversation",
|
|
26
|
+
response = self._client.post("conversation",
|
|
27
|
+
data={
|
|
28
|
+
"username": "bot",
|
|
29
|
+
"assistant": assistant})
|
|
22
30
|
response.raise_for_status()
|
|
23
31
|
|
|
24
32
|
# Print the first welcome message
|
|
@@ -74,14 +82,21 @@ class ChatClient(ABC):
|
|
|
74
82
|
self._display_response(message_content)
|
|
75
83
|
|
|
76
84
|
elif context == "response":
|
|
77
|
-
|
|
85
|
+
|
|
86
|
+
stop = not messages_json.get("dialog_in_progress")
|
|
78
87
|
|
|
79
88
|
for one_message in messages_json.get("messages"):
|
|
80
|
-
|
|
89
|
+
|
|
81
90
|
message_type = one_message.get("type")
|
|
82
91
|
|
|
83
|
-
if message_type == "
|
|
84
|
-
|
|
92
|
+
if message_type == "typing":
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
self._sender = one_message.get("sender").get("name")
|
|
96
|
+
|
|
97
|
+
# Deprecated. Applicable only for old kbot versions (before 2024.01)
|
|
98
|
+
#if message_type == "stop_topic":
|
|
99
|
+
# stop = True
|
|
85
100
|
|
|
86
101
|
if one_message.get("type") != "message":
|
|
87
102
|
continue
|
|
@@ -99,7 +114,33 @@ class ChatClient(ABC):
|
|
|
99
114
|
"value": "Hello John",
|
|
100
115
|
...
|
|
101
116
|
"""
|
|
102
|
-
|
|
117
|
+
|
|
118
|
+
# Potentially need to convert the message:
|
|
119
|
+
message_format = message.get('format')
|
|
120
|
+
|
|
121
|
+
if message_format == "text":
|
|
122
|
+
message_value = message.get('value')
|
|
123
|
+
|
|
124
|
+
elif message_format == "html":
|
|
125
|
+
if self._convert_html_to_text:
|
|
126
|
+
try:
|
|
127
|
+
from bs4 import BeautifulSoup
|
|
128
|
+
message_value = BeautifulSoup(message.get('value'), 'html.parser').get_text()
|
|
129
|
+
except:
|
|
130
|
+
print("ERROR: HTML to Text conversion requested, but beautifull soup is not installed. Use: ")
|
|
131
|
+
print("pip3 install BeautifulSoup4")
|
|
132
|
+
message_value = message.get('value')
|
|
133
|
+
else:
|
|
134
|
+
message_value = message.get('value')
|
|
135
|
+
elif message_format == "markdown":
|
|
136
|
+
message_value = message.get('value')
|
|
137
|
+
|
|
138
|
+
else:
|
|
139
|
+
print("Unsupported format:", message.get('format'))
|
|
140
|
+
message_value = message.get('value')
|
|
141
|
+
|
|
142
|
+
# finally display the result to the user
|
|
143
|
+
print(self._prompt.format(sender=self._sender, message=message_value))
|
|
103
144
|
|
|
104
145
|
def _start_question_answer_loop(self, conversation_id):
|
|
105
146
|
|
|
@@ -130,10 +171,11 @@ class ChatClient(ABC):
|
|
|
130
171
|
# Send the user question to Kbot.
|
|
131
172
|
#
|
|
132
173
|
data = {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
174
|
+
'conversation_id': conversation_id,
|
|
175
|
+
'type': 'message',
|
|
176
|
+
'message': question,
|
|
177
|
+
'message_id': str(uuid.uuid4()),
|
|
178
|
+
'assistant': self._assistant
|
|
137
179
|
}
|
|
138
180
|
|
|
139
181
|
response = self._client.post(f"conversation/{conversation_id}/message", data=data)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "kbot-py-client"
|
|
3
|
+
version = "2.0.8"
|
|
4
|
+
description = "Client for Konverso Kbot."
|
|
5
|
+
authors = ["Konverso <contact@konverso.ai>"]
|
|
6
|
+
classifiers=[
|
|
7
|
+
"Programming Language :: Python :: 3",
|
|
8
|
+
"License :: OSI Approved :: MIT License",
|
|
9
|
+
"Operating System :: OS Independent",
|
|
10
|
+
]
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
license = "MIT"
|
|
13
|
+
packages = [
|
|
14
|
+
{ include = "kbot_client" },
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[tool.poetry.urls]
|
|
18
|
+
Homepage = "https://www.konverso.ai/"
|
|
19
|
+
Source = "https://github.com/konverso-ai/kbot-py-client"
|
|
20
|
+
|
|
21
|
+
[tool.poetry.dependencies]
|
|
22
|
+
python = "^3.12"
|
|
23
|
+
requests = ">=2.27.1"
|
|
24
|
+
|
|
25
|
+
[tool.poetry.group.dev.dependencies]
|
|
26
|
+
ruff = "0.4.4"
|
|
27
|
+
pytest = "8.2.1"
|
|
28
|
+
pytest-env = "1.1.3"
|
|
29
|
+
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["poetry-core"]
|
|
32
|
+
build-backend = "poetry.core.masonry.api"
|
|
33
|
+
|
|
34
|
+
[tool.ruff]
|
|
35
|
+
# Exclude a variety of commonly ignored directories.
|
|
36
|
+
exclude = [
|
|
37
|
+
".bzr",
|
|
38
|
+
".direnv",
|
|
39
|
+
".eggs",
|
|
40
|
+
".git",
|
|
41
|
+
".git-rewrite",
|
|
42
|
+
".hg",
|
|
43
|
+
".ipynb_checkpoints",
|
|
44
|
+
".mypy_cache",
|
|
45
|
+
".nox",
|
|
46
|
+
".pants.d",
|
|
47
|
+
".pyenv",
|
|
48
|
+
".pytest_cache",
|
|
49
|
+
".pytype",
|
|
50
|
+
".ruff_cache",
|
|
51
|
+
".svn",
|
|
52
|
+
".tox",
|
|
53
|
+
".venv",
|
|
54
|
+
".vscode",
|
|
55
|
+
"__pypackages__",
|
|
56
|
+
"_build",
|
|
57
|
+
"buck-out",
|
|
58
|
+
"build",
|
|
59
|
+
"dist",
|
|
60
|
+
"node_modules",
|
|
61
|
+
"site-packages",
|
|
62
|
+
"venv",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
# Same as Black.
|
|
66
|
+
line-length = 88
|
|
67
|
+
indent-width = 4
|
|
68
|
+
|
|
69
|
+
# Assume Python 3.10
|
|
70
|
+
target-version = "py310"
|
|
71
|
+
|
|
72
|
+
[tool.pytest]
|
|
73
|
+
testpaths = "tests"
|
|
74
|
+
addopts = "-vv"
|
|
75
|
+
python_files = "test_*.py"
|
|
76
|
+
|
|
77
|
+
# [tool.pytest.ini_options]
|
|
78
|
+
# asyncio_mode = "auto"
|
|
79
|
+
|
|
80
|
+
[tool.ruff.lint]
|
|
81
|
+
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
|
82
|
+
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
|
|
83
|
+
# McCabe complexity (`C901`) by default.
|
|
84
|
+
select = ["E4", "E7", "E9", "F"]
|
|
85
|
+
ignore = ["F401", "E722"]
|
|
86
|
+
|
|
87
|
+
# Allow fix for all enabled rules (when `--fix`) is provided.
|
|
88
|
+
fixable = ["ALL"]
|
|
89
|
+
unfixable = []
|
|
90
|
+
|
|
91
|
+
# Allow unused variables when underscore-prefixed.
|
|
92
|
+
# dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
93
|
+
|
|
94
|
+
[tool.ruff.format]
|
|
95
|
+
# Like Black, use double quotes for strings.
|
|
96
|
+
quote-style = "double"
|
|
97
|
+
|
|
98
|
+
# Like Black, indent with spaces, rather than tabs.
|
|
99
|
+
indent-style = "space"
|
|
100
|
+
|
|
101
|
+
# Like Black, respect magic trailing commas.
|
|
102
|
+
skip-magic-trailing-comma = false
|
|
103
|
+
|
|
104
|
+
# Like Black, automatically detect the appropriate line ending.
|
|
105
|
+
line-ending = "auto"
|
|
106
|
+
|
|
107
|
+
# Enable auto-formatting of code examples in docstrings. Markdown,
|
|
108
|
+
# reStructuredText code/literal blocks and doctests are all supported.
|
|
109
|
+
#
|
|
110
|
+
# This is currently disabled by default, but it is planned for this
|
|
111
|
+
# to be opt-out in the future.
|
|
112
|
+
docstring-code-format = false
|
|
113
|
+
|
|
114
|
+
# Set the line length limit used when formatting code snippets in
|
|
115
|
+
# docstrings.
|
|
116
|
+
#
|
|
117
|
+
# This only has an effect when the `docstring-code-format` setting is
|
|
118
|
+
# enabled.
|
|
119
|
+
docstring-code-line-length = "dynamic"
|
kbot_py_client-2.0.4/.gitignore
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# These are some examples of commonly ignored file patterns.
|
|
2
|
-
# You should customize this list as applicable to your project.
|
|
3
|
-
# Learn more about .gitignore:
|
|
4
|
-
# https://www.atlassian.com/git/tutorials/saving-changes/gitignore
|
|
5
|
-
|
|
6
|
-
# Node artifact files
|
|
7
|
-
node_modules/
|
|
8
|
-
dist/
|
|
9
|
-
|
|
10
|
-
# Compiled Java class files
|
|
11
|
-
*.class
|
|
12
|
-
|
|
13
|
-
# Compiled Python bytecode
|
|
14
|
-
*.py[cod]
|
|
15
|
-
|
|
16
|
-
# Log files
|
|
17
|
-
*.log
|
|
18
|
-
|
|
19
|
-
# Package files
|
|
20
|
-
*.jar
|
|
21
|
-
|
|
22
|
-
# Maven
|
|
23
|
-
target/
|
|
24
|
-
dist/
|
|
25
|
-
|
|
26
|
-
# JetBrains IDE
|
|
27
|
-
.idea/
|
|
28
|
-
|
|
29
|
-
# Unit test reports
|
|
30
|
-
TEST*.xml
|
|
31
|
-
|
|
32
|
-
# Generated by MacOS
|
|
33
|
-
.DS_Store
|
|
34
|
-
|
|
35
|
-
# Generated by Windows
|
|
36
|
-
Thumbs.db
|
|
37
|
-
|
|
38
|
-
# Applications
|
|
39
|
-
*.app
|
|
40
|
-
*.exe
|
|
41
|
-
*.war
|
|
42
|
-
|
|
43
|
-
# Large media files
|
|
44
|
-
*.mp4
|
|
45
|
-
*.tiff
|
|
46
|
-
*.avi
|
|
47
|
-
*.flv
|
|
48
|
-
*.mov
|
|
49
|
-
*.wmv
|
|
50
|
-
|
kbot_py_client-2.0.4/README.md
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
# README #
|
|
2
|
-
This package contains utilities which you may use to easily interact with Konverso Kbot application.
|
|
3
|
-
|
|
4
|
-
In particular, you may:
|
|
5
|
-
|
|
6
|
-
* Invoke some of the APIs to view / update / create Kbot configuration objects such as Intents, Message, etc.
|
|
7
|
-
* Collect metrics showing how the bot is performing.
|
|
8
|
-
* Create a Conversation and interact with it, sending message and getting responses
|
|
9
|
-
|
|
10
|
-
# See also #
|
|
11
|
-
|
|
12
|
-
## Access Konverso Support
|
|
13
|
-
You may contact us:
|
|
14
|
-
|
|
15
|
-
* For any commercial inquiry: contact@konverso.ai
|
|
16
|
-
* For support: https://konverso.atlassian.net/servicedesk/customer/portals
|
|
17
|
-
|
|
18
|
-
### Installation ###
|
|
19
|
-
|
|
20
|
-
You may use pip3 to install the software on your Kbot instance:
|
|
21
|
-
|
|
22
|
-
First Navigate to your work-area and then invoke:
|
|
23
|
-
|
|
24
|
-
pip3 install -e git+https://konverso@bitbucket.org/konversoai/kbot-py-client.git#egg=kbot-py-client
|
|
25
|
-
|
|
26
|
-
# Usage #
|
|
27
|
-
|
|
28
|
-
You would typically first need to login and then invoke some of the API wrapped methods.
|
|
29
|
-
|
|
30
|
-
## Login ##
|
|
31
|
-
|
|
32
|
-
import json
|
|
33
|
-
|
|
34
|
-
from kbot_client import Client
|
|
35
|
-
|
|
36
|
-
### Using user / password ###
|
|
37
|
-
|
|
38
|
-
cli = Client("mybot.konverso.ai")
|
|
39
|
-
cli.login("myuser", "mysecretpassword")
|
|
40
|
-
|
|
41
|
-
The API Key may be created for a given user, with relevant permissions, from the Configuration / Users & Roles / Users / Accounts panel. Create an account of type "Local"
|
|
42
|
-
|
|
43
|
-
### Using and api key ###
|
|
44
|
-
|
|
45
|
-
cli = Client("mybot.konverso.ai", api_key="xxxxxxxxxxxxxxxxxxx")
|
|
46
|
-
|
|
47
|
-
The API Key may be created for a given user, with relevant permissions, from the Configuration / Users & Roles / Users / Accounts panel. Create an account of type "API Key"
|
|
48
|
-
|
|
49
|
-
## Collect metrics
|
|
50
|
-
Once authenticated, you can for example retrieve useful usage metrics, these can be used by a Monitoring application or for some business intelligence rendering:
|
|
51
|
-
|
|
52
|
-
metrics = cli.metric().json()
|
|
53
|
-
print("Collected metrics:")
|
|
54
|
-
print(json.dumps(metrics, indent=4))
|
|
55
|
-
|
|
56
|
-
## Invoke a Workflow and retrieve result
|
|
57
|
-
This is the most powerful mechanism, that let you invoke any business and processing logic in a workflow, leveraging all the capabilities of our platform, and retrieve the result
|
|
58
|
-
|
|
59
|
-
r = client.request("post",
|
|
60
|
-
"workflow/execute",
|
|
61
|
-
{
|
|
62
|
-
"name": "Test API Workflow",
|
|
63
|
-
"keywords": {
|
|
64
|
-
"var1": "abc"
|
|
65
|
-
},
|
|
66
|
-
"result": ["response"]
|
|
67
|
-
}
|
|
68
|
-
)
|
|
69
|
-
print(r.json())
|
|
70
|
-
|
|
71
|
-
## Invoke a search
|
|
72
|
-
You may invoke a search on any of the configured Search Contexts
|
|
73
|
-
Note the search context UUID passed in the URL, which you may retrieve from our Search Context portal.
|
|
74
|
-
|
|
75
|
-
r = client.request("post", "searchcontext/234923-235-sjdhfs-kdjf/search",
|
|
76
|
-
{
|
|
77
|
-
"sentence": "how to create a classifier",
|
|
78
|
-
"num_results": 20,
|
|
79
|
-
#"variables": {'kbs': {'value': ['Confluence_Security_KSEC']}}
|
|
80
|
-
}
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
## Retrieve object details
|
|
85
|
-
You may retrieve list of defined objects. Note that only objects visibled to the logged in users will be returned.
|
|
86
|
-
|
|
87
|
-
Here is a sample code that simply checks for a few objects existance:
|
|
88
|
-
|
|
89
|
-
## Get list of objects and check if object with name is present in response
|
|
90
|
-
for unit, name in (('intention' ,'Create ticket'),
|
|
91
|
-
('knowledge_base', 'faq'),
|
|
92
|
-
('workflow', 'Transfer to Agent')):
|
|
93
|
-
print(f"Get list of '{unit}'")
|
|
94
|
-
objs = cli.unit(unit)
|
|
95
|
-
if objs:
|
|
96
|
-
# Create dict with
|
|
97
|
-
# - key : object name
|
|
98
|
-
# - value : object json data
|
|
99
|
-
data = {obj['name']: obj for obj in objs}
|
|
100
|
-
if name in data:
|
|
101
|
-
print(f"'{name}' is present")
|
|
102
|
-
else:
|
|
103
|
-
print(f"'{name}' is not present")
|
|
104
|
-
else:
|
|
105
|
-
print("Get no data")
|
|
106
|
-
|
|
107
|
-
## Conversation test
|
|
108
|
-
In this example, we create a conversation between the logged in user and the bot and then sends a sentence, and check if we get some expected text in the response. This could for example be the basis of automated testing of the bot
|
|
109
|
-
|
|
110
|
-
r = cli.conversation(username='bot')
|
|
111
|
-
if r.status_code == 201:
|
|
112
|
-
cid = r.json().get('id')
|
|
113
|
-
print("Created conversation with id '%s'" %(cid,))
|
|
114
|
-
|
|
115
|
-
response = cli.message(cid, 'hello')
|
|
116
|
-
|
|
117
|
-
# Process bot response
|
|
118
|
-
if response:
|
|
119
|
-
for resp in response:
|
|
120
|
-
for message in resp.get('message', []):
|
|
121
|
-
responses.append(message.get('value', '')) # dict {message type: message value}
|
|
122
|
-
resp_message = '\n'.join(responses)
|
|
123
|
-
print("Received response: ", resp_message)
|
|
124
|
-
if 'I am kbot' in resp_message:
|
|
125
|
-
print("Excepted response found")
|
|
126
|
-
break
|
|
127
|
-
else:
|
|
128
|
-
print("Did not receive the expected response")
|
|
129
|
-
else:
|
|
130
|
-
print("Did not receive any response")
|
|
131
|
-
else:
|
|
132
|
-
print("Could not create conversation due to: ", r.text)
|
|
133
|
-
|
|
134
|
-
## Uploading one file to kbot
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
### Prerequisites
|
|
138
|
-
* A local file that you wish to send
|
|
139
|
-
* The UUID of the folder that will receive the files you want to upload
|
|
140
|
-
|
|
141
|
-
### Code sample
|
|
142
|
-
```
|
|
143
|
-
current_top_folder = "uuid-of-a-folder"
|
|
144
|
-
filepath = "/tmp/test.pdf"
|
|
145
|
-
filename = os.path.basename(filepath)
|
|
146
|
-
|
|
147
|
-
# Upload to remote filemanager
|
|
148
|
-
with open(filepath, "rb") as fd:
|
|
149
|
-
files = {
|
|
150
|
-
"upload_files": fd
|
|
151
|
-
}
|
|
152
|
-
params= {
|
|
153
|
-
"override": False
|
|
154
|
-
}
|
|
155
|
-
data = {
|
|
156
|
-
"folder": current_top_folder,
|
|
157
|
-
"name": file_name,
|
|
158
|
-
}
|
|
159
|
-
response = self.client.post_file("attachment",
|
|
160
|
-
data=data,
|
|
161
|
-
params=params,
|
|
162
|
-
files=files)
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
## Uploading a batch of files to the file manager
|
|
166
|
-
|
|
167
|
-
### Prerequisites
|
|
168
|
-
|
|
169
|
-
* An API key
|
|
170
|
-
* The UUID of the folder that will receive the files you want to upload
|
|
171
|
-
|
|
172
|
-
### Code sample
|
|
173
|
-
In this example, we simply upload the content of a directory to a folder in the file manager.
|
|
174
|
-
|
|
175
|
-
from kbot_client import Client
|
|
176
|
-
from kbot_client.folder_sync import FolderSync
|
|
177
|
-
|
|
178
|
-
client = Client("mybot.konverso.ai", api_key="17ebXXXXXXXXXXXXXXXXXXXXX")
|
|
179
|
-
|
|
180
|
-
syncer = FolderSync(client)
|
|
181
|
-
syncer.sync("/tmp/my_source_folder/", "1831f-XXXXXXXXXXXXXXXXXXXXXXX")
|
|
182
|
-
print("Syncing is done :)")
|
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
"""Dialog tester"""
|
|
2
|
-
import json
|
|
3
|
-
import time
|
|
4
|
-
|
|
5
|
-
import requests
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Client:
|
|
9
|
-
"""Base representation of Kbot instance"""
|
|
10
|
-
|
|
11
|
-
def __init__(self, server, port=443, proto='https', api_key=None, verify=True):
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Arguments:
|
|
16
|
-
- server: an Host or Ip Address
|
|
17
|
-
- port : default to 443
|
|
18
|
-
- proto: HTTP or HTTPS
|
|
19
|
-
- api_key: A Kbot valid API Key. If provided all requests will be sent with this API Key
|
|
20
|
-
and it is not required to invoke the login method
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
self.host = server
|
|
24
|
-
self.verify = verify
|
|
25
|
-
|
|
26
|
-
if str(port).endswith('443'):
|
|
27
|
-
proto = 'https'
|
|
28
|
-
else:
|
|
29
|
-
proto = 'http'
|
|
30
|
-
|
|
31
|
-
if port in (80, 443):
|
|
32
|
-
self.url = "%s://%s" % (proto, server)
|
|
33
|
-
else:
|
|
34
|
-
self.url = "%s://%s:%s" % (proto, server, port)
|
|
35
|
-
|
|
36
|
-
self._login = False
|
|
37
|
-
self._headers = {}
|
|
38
|
-
|
|
39
|
-
self._recording_fd = None
|
|
40
|
-
|
|
41
|
-
if api_key:
|
|
42
|
-
self._headers = {
|
|
43
|
-
'Content-Type': 'application/json; charset=utf-8',
|
|
44
|
-
'X-API-KEY': api_key
|
|
45
|
-
}
|
|
46
|
-
self.schema()
|
|
47
|
-
|
|
48
|
-
# Variable populated by the login:
|
|
49
|
-
self._user_id = None
|
|
50
|
-
|
|
51
|
-
# Variable populated by the conversation
|
|
52
|
-
self._cid = None
|
|
53
|
-
|
|
54
|
-
# The refresh token may be used in case
|
|
55
|
-
# the access token as expired
|
|
56
|
-
self.__refresh_token = None
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def admin_url(self):
|
|
60
|
-
"""Returns the URL of the Kbot Administration view"""
|
|
61
|
-
return "%s/admin" % self.url
|
|
62
|
-
|
|
63
|
-
@property
|
|
64
|
-
def chat_url(self):
|
|
65
|
-
"""Returns the default URL of the Kbot chat view"""
|
|
66
|
-
return "%s" % self.url
|
|
67
|
-
|
|
68
|
-
@property
|
|
69
|
-
def avatar_url(self):
|
|
70
|
-
return "%s/images/kbot_avatar.png" % self.url
|
|
71
|
-
|
|
72
|
-
def record_start(self, file_descriptor):
|
|
73
|
-
self._recording_fd = file_descriptor
|
|
74
|
-
self._recording_fd.write("import requests\n\n")
|
|
75
|
-
|
|
76
|
-
def record_stop(self):
|
|
77
|
-
self._recording_fd = None
|
|
78
|
-
|
|
79
|
-
def login(self, username, password=None, timeout=5):
|
|
80
|
-
"""Login to local channel"""
|
|
81
|
-
data = {}
|
|
82
|
-
data['username'] = username
|
|
83
|
-
data['usertype'] = 'local'
|
|
84
|
-
if password is not None:
|
|
85
|
-
data['password'] = password
|
|
86
|
-
else:
|
|
87
|
-
print("WARNING!!! Password is not set!!! Trying to use username...")
|
|
88
|
-
data['password'] = username
|
|
89
|
-
|
|
90
|
-
headers = {}
|
|
91
|
-
headers['Content-Type'] = 'application/json; charset=utf-8'
|
|
92
|
-
|
|
93
|
-
url = self.url + '/api/login'
|
|
94
|
-
r = requests.post(url, headers=headers, data=json.dumps(data), timeout=timeout, verify=self.verify)
|
|
95
|
-
|
|
96
|
-
r.raise_for_status()
|
|
97
|
-
|
|
98
|
-
self.__reset_headers(r.json())
|
|
99
|
-
self.schema()
|
|
100
|
-
|
|
101
|
-
def impersonate(self, username, usertype='local', external_auth='', application_uuid='', timeout=5):
|
|
102
|
-
"""Impersonate the given user"""
|
|
103
|
-
|
|
104
|
-
r = self.request("post", "user/impersonate", data={
|
|
105
|
-
"username": username,
|
|
106
|
-
"im_type": usertype,
|
|
107
|
-
"external_auth": external_auth,
|
|
108
|
-
"application_uuid": application_uuid})
|
|
109
|
-
|
|
110
|
-
r.raise_for_status()
|
|
111
|
-
|
|
112
|
-
self.__reset_headers(r.json())
|
|
113
|
-
# After we impersonnate, we need to remove the original API KEY
|
|
114
|
-
if "X-API-KEY" in self._headers:
|
|
115
|
-
del self._headers["X-API-KEY"]
|
|
116
|
-
|
|
117
|
-
self.schema()
|
|
118
|
-
|
|
119
|
-
def schema(self):
|
|
120
|
-
"""Retrieve the public schema of kbot and builds python method for each of the defined end points"""
|
|
121
|
-
r = self.get('schema')
|
|
122
|
-
r.raise_for_status()
|
|
123
|
-
|
|
124
|
-
j = r.json()
|
|
125
|
-
for epoint in j.get('endpoints', []):
|
|
126
|
-
if epoint['name'] != 'schema':
|
|
127
|
-
self.__add_method(epoint['method'], epoint['name'], epoint['path'], epoint['params'], epoint['data'], epoint.get('description', ''))
|
|
128
|
-
|
|
129
|
-
def __reset_headers(self, js):
|
|
130
|
-
"""
|
|
131
|
-
Called when a login or impersonate end point is called. Then expecting to get a json with:
|
|
132
|
-
- access_token
|
|
133
|
-
- refresh_token
|
|
134
|
-
Or in the case of a refresh call. Then we only get a json with:
|
|
135
|
-
- access_token
|
|
136
|
-
|
|
137
|
-
The header content is ajusted with the token information from the json
|
|
138
|
-
"""
|
|
139
|
-
if not self._headers:
|
|
140
|
-
self._headers = {}
|
|
141
|
-
self._headers['Content-Type'] = 'application/json; charset=utf-8'
|
|
142
|
-
|
|
143
|
-
if js.get('access_token'):
|
|
144
|
-
self._headers['Authorization'] = js['access_token']
|
|
145
|
-
|
|
146
|
-
# The refresh token will be used by the refresh_token method later if the access token expires
|
|
147
|
-
if js.get('refresh_token'):
|
|
148
|
-
self.__refresh_token = js['refresh_token']
|
|
149
|
-
|
|
150
|
-
self._login = True
|
|
151
|
-
if js.get("user_id"):
|
|
152
|
-
self._user_id = js['user_id']
|
|
153
|
-
|
|
154
|
-
def __add_method(self, method: str, name: str, path: str, params: list, data: list, description: str):
|
|
155
|
-
"""Method to add a new dynamic method, based on the schema. This provides "python method" wrapper
|
|
156
|
-
on top of the kbot APIs
|
|
157
|
-
"""
|
|
158
|
-
def endpoint(*args, **kwargs):
|
|
159
|
-
"""Invoke request to endpoint"""
|
|
160
|
-
rargs = {}
|
|
161
|
-
for aname, values in (('params', params), ('data', data)):
|
|
162
|
-
rargs[aname] = {}
|
|
163
|
-
for value in values:
|
|
164
|
-
if value['mandatory'] and value['name'] not in kwargs:
|
|
165
|
-
raise RuntimeError("Missed attribute '%s' in '%s'" % (value['name'], aname))
|
|
166
|
-
# pylint: disable=eval-used
|
|
167
|
-
if value['name'] in kwargs:
|
|
168
|
-
if not isinstance(kwargs[value['name']], eval(value['type'])):
|
|
169
|
-
raise RuntimeError("Invalid type of attribute '%s'" % (value['name']))
|
|
170
|
-
v = kwargs[value['name']]
|
|
171
|
-
elif value['name'] not in kwargs and value['default'] is not None:
|
|
172
|
-
v = value['default']
|
|
173
|
-
else:
|
|
174
|
-
continue
|
|
175
|
-
rargs[aname][value['name']] = v
|
|
176
|
-
return self.__request(method, uri=path % args, **rargs)
|
|
177
|
-
endpoint.__doc__ = description
|
|
178
|
-
endpoint.__name__ = name
|
|
179
|
-
setattr(self, name, endpoint)
|
|
180
|
-
|
|
181
|
-
def __refresh(self):
|
|
182
|
-
r = requests.post(self.url + "/api/refresh",
|
|
183
|
-
data=json.dumps({"refresh_token": self.__refresh_token}),
|
|
184
|
-
headers=self._headers)
|
|
185
|
-
r.raise_for_status()
|
|
186
|
-
self.__reset_headers(r.json())
|
|
187
|
-
|
|
188
|
-
def __request(self, method: str, uri : str = None, data: dict = None, params: dict = None, files: dict = None, attempt=0):
|
|
189
|
-
dump_data = json.dumps(data or {})
|
|
190
|
-
|
|
191
|
-
if files:
|
|
192
|
-
headers = self._headers.copy()
|
|
193
|
-
del headers['Content-Type']
|
|
194
|
-
headers['Accept'] = "*/*"
|
|
195
|
-
else:
|
|
196
|
-
headers = self._headers
|
|
197
|
-
|
|
198
|
-
r = requests.request(method.upper(), self.url + '/api/%s/' % (uri), params=params, data=dump_data, headers=headers, verify=self.verify)
|
|
199
|
-
|
|
200
|
-
# The recording if configured to do so
|
|
201
|
-
if self._recording_fd:
|
|
202
|
-
self._recording_fd.write("\n## {uri}".format(uri='/api/%s/' % (uri)))
|
|
203
|
-
self._recording_fd.write("\nheaders = {headers}".format(headers=json.dumps(headers)))
|
|
204
|
-
self._recording_fd.write("\nparams = {params}".format(params=json.dumps(params)))
|
|
205
|
-
self._recording_fd.write("\ndata = {data}".format(data=json.dumps(data)))
|
|
206
|
-
self._recording_fd.write("\nr = requests.request({method}, {url}, params=params, data=data)".format(
|
|
207
|
-
method=method.upper(), url=self.url + '/api/%s/' % (uri)))
|
|
208
|
-
self._recording_fd.write("\nr.raise_for_status()")
|
|
209
|
-
|
|
210
|
-
if r.status_code == 401:
|
|
211
|
-
# Refresh the token
|
|
212
|
-
try:
|
|
213
|
-
self.__refresh()
|
|
214
|
-
except Exception as e:
|
|
215
|
-
|
|
216
|
-
# We try 3 times at the most
|
|
217
|
-
if attempt == 3:
|
|
218
|
-
raise e
|
|
219
|
-
|
|
220
|
-
time.sleep(3)
|
|
221
|
-
|
|
222
|
-
# Re-invoke the request
|
|
223
|
-
r = self.__request(method, uri=uri, data=data, params=params, attempt=attempt+1)
|
|
224
|
-
|
|
225
|
-
return r
|
|
226
|
-
|
|
227
|
-
def request(self, method: str, uri: str, data: dict = None, params: dict = None):
|
|
228
|
-
return self.__request(method, uri=uri, data=data, params=params)
|
|
229
|
-
|
|
230
|
-
def unit(self, name: str, params=None) -> dict:
|
|
231
|
-
r = self.get(name, params)
|
|
232
|
-
if r:
|
|
233
|
-
return r.json()
|
|
234
|
-
return None
|
|
235
|
-
|
|
236
|
-
def message(self, cid: int, message: str, timeout: int=60) -> list:
|
|
237
|
-
response = []
|
|
238
|
-
|
|
239
|
-
data = {'type': 'message', 'message': message}
|
|
240
|
-
r = self.request("post", f'conversation/{cid}/message', data)
|
|
241
|
-
|
|
242
|
-
r.raise_for_status()
|
|
243
|
-
|
|
244
|
-
curtime = time.time()
|
|
245
|
-
while timeout > 0:
|
|
246
|
-
r = self.get(f'conversation/{cid}')
|
|
247
|
-
|
|
248
|
-
r.raise_for_status()
|
|
249
|
-
|
|
250
|
-
j = r.json()
|
|
251
|
-
for resp in j:
|
|
252
|
-
if resp['type'] == 'message':
|
|
253
|
-
# It's possible that bot will send several message to one input
|
|
254
|
-
response.append(resp)
|
|
255
|
-
elif resp['type'] in ('stop_topic', 'wait_user_input'):
|
|
256
|
-
# Bot stop to process
|
|
257
|
-
# - stop_topic : bot stop to process message and ready for new topic
|
|
258
|
-
# - wait_user_input : bot asked the question and wait for user answer
|
|
259
|
-
timeout = 0
|
|
260
|
-
timeout = timeout + curtime - time.time()
|
|
261
|
-
return response
|
|
262
|
-
|
|
263
|
-
def logout(self):
|
|
264
|
-
"""Logout from local channel"""
|
|
265
|
-
if self._login:
|
|
266
|
-
|
|
267
|
-
# If we have an open conversation, close it.
|
|
268
|
-
# if self._cid:
|
|
269
|
-
# What for?
|
|
270
|
-
# self._process('logout', 1)
|
|
271
|
-
|
|
272
|
-
# Logout from the APIs
|
|
273
|
-
requests.post(self.url + '/api/logout', headers=self._headers, verify=self.verify)
|
|
274
|
-
|
|
275
|
-
#
|
|
276
|
-
# In addition to the Generated and built in API methods, we have the classic base REST methods
|
|
277
|
-
#
|
|
278
|
-
def get(self, unit, params=None):
|
|
279
|
-
return self.__request("get", unit, params=params)
|
|
280
|
-
|
|
281
|
-
def put(self, unit, data=None):
|
|
282
|
-
return self.__request("put", unit, data=data)
|
|
283
|
-
|
|
284
|
-
def post(self, unit, data=None):
|
|
285
|
-
return self.__request("post", unit, data=data)
|
|
286
|
-
|
|
287
|
-
def delete(self, unit, params=None):
|
|
288
|
-
return self.__request("delete", unit, params=params)
|
|
289
|
-
|
|
290
|
-
def post_file(self, unit, data, params=None, files=None):
|
|
291
|
-
"""Attach the given files.
|
|
292
|
-
Sample parameter values:
|
|
293
|
-
unit = "attachment"
|
|
294
|
-
files = {
|
|
295
|
-
"upload_files": (f, fd, "application/pdf")
|
|
296
|
-
}
|
|
297
|
-
params= {
|
|
298
|
-
"override": False
|
|
299
|
-
}
|
|
300
|
-
data = {
|
|
301
|
-
"folder": current_top_folder,
|
|
302
|
-
"name": f,
|
|
303
|
-
}
|
|
304
|
-
"""
|
|
305
|
-
return self.__request("post", unit, params=params, data=data, files=files)
|
|
306
|
-
|
|
307
|
-
class UpKbotClient(Client):
|
|
308
|
-
"""Represents a currently reachable Kbot instance"""
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
class DownKbotClient(Client):
|
|
312
|
-
"""Represents a currently not reachable Kbot instance"""
|
|
313
|
-
|
|
314
|
-
def __init__(self, server, port=443, proto='http', error=''):
|
|
315
|
-
super().__init__(server, port=port, proto=proto)
|
|
316
|
-
self.error = error
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
.gitignore
|
|
2
|
-
LICENSE
|
|
3
|
-
README.md
|
|
4
|
-
setup.py
|
|
5
|
-
kbot_client/__init__.py
|
|
6
|
-
kbot_client/chat_client.py
|
|
7
|
-
kbot_client/client.py
|
|
8
|
-
kbot_client/client.recording.py
|
|
9
|
-
kbot_client/client_group.py
|
|
10
|
-
kbot_client/folder_sync.py
|
|
11
|
-
kbot_client/test.py
|
|
12
|
-
kbot_py_client.egg-info/PKG-INFO
|
|
13
|
-
kbot_py_client.egg-info/SOURCES.txt
|
|
14
|
-
kbot_py_client.egg-info/dependency_links.txt
|
|
15
|
-
kbot_py_client.egg-info/requires.txt
|
|
16
|
-
kbot_py_client.egg-info/top_level.txt
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
requests>=2.27.1
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
kbot_client
|
kbot_py_client-2.0.4/setup.cfg
DELETED
kbot_py_client-2.0.4/setup.py
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import setuptools
|
|
2
|
-
|
|
3
|
-
with open("README.md", mode="r", encoding="utf-8") as fh:
|
|
4
|
-
long_description = fh.read()
|
|
5
|
-
|
|
6
|
-
setuptools.setup(
|
|
7
|
-
name="kbot-py-client",
|
|
8
|
-
version="2.0.4",
|
|
9
|
-
author="Konverso",
|
|
10
|
-
author_email="contact@konverso.ai",
|
|
11
|
-
description="Client for Konverso Kbot",
|
|
12
|
-
long_description=long_description,
|
|
13
|
-
long_description_content_type="text/markdown",
|
|
14
|
-
url="https://www.konverso.ai",
|
|
15
|
-
packages=setuptools.find_packages(),
|
|
16
|
-
install_requires=["requests>=2.27.1"],
|
|
17
|
-
classifiers=[
|
|
18
|
-
"Programming Language :: Python :: 3",
|
|
19
|
-
"License :: OSI Approved :: MIT License",
|
|
20
|
-
"Operating System :: OS Independent",
|
|
21
|
-
],
|
|
22
|
-
python_requires='>=3.6',
|
|
23
|
-
)
|
|
File without changes
|
|
@@ -36,6 +36,10 @@ class Client:
|
|
|
36
36
|
self._login = False
|
|
37
37
|
self._headers = {}
|
|
38
38
|
|
|
39
|
+
# The refresh token may be used in case
|
|
40
|
+
# the access token as expired
|
|
41
|
+
self.__refresh_token = None
|
|
42
|
+
|
|
39
43
|
if api_key:
|
|
40
44
|
self._headers = {
|
|
41
45
|
'Content-Type': 'application/json; charset=utf-8',
|
|
@@ -49,10 +53,6 @@ class Client:
|
|
|
49
53
|
# Variable populated by the conversation
|
|
50
54
|
self._cid = None
|
|
51
55
|
|
|
52
|
-
# The refresh token may be used in case
|
|
53
|
-
# the access token as expired
|
|
54
|
-
self.__refresh_token = None
|
|
55
|
-
|
|
56
56
|
@property
|
|
57
57
|
def admin_url(self):
|
|
58
58
|
"""Returns the URL of the Kbot Administration view"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|