kbot-py-client 2.1.2__tar.gz → 2.1.4__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.1.2/README.md → kbot_py_client-2.1.4/PKG-INFO +72 -0
- kbot_py_client-2.1.2/PKG-INFO → kbot_py_client-2.1.4/README.md +55 -20
- {kbot_py_client-2.1.2 → kbot_py_client-2.1.4}/kbot_client/__init__.py +1 -0
- {kbot_py_client-2.1.2 → kbot_py_client-2.1.4}/kbot_client/callback_chat.py +3 -2
- {kbot_py_client-2.1.2 → kbot_py_client-2.1.4}/kbot_client/chatbot_client/chat_client.py +2 -1
- {kbot_py_client-2.1.2 → kbot_py_client-2.1.4}/kbot_client/chatbot_client/chat_client_2.py +2 -1
- {kbot_py_client-2.1.2 → kbot_py_client-2.1.4}/kbot_client/client.py +38 -7
- kbot_py_client-2.1.4/kbot_client/recorder.py +69 -0
- kbot_py_client-2.1.4/pyproject.toml +58 -0
- kbot_py_client-2.1.4/pyproject.toml.orig +64 -0
- kbot_py_client-2.1.2/LICENSE +0 -21
- kbot_py_client-2.1.2/kbot_client/test.py +0 -26
- kbot_py_client-2.1.2/pyproject.toml +0 -283
- {kbot_py_client-2.1.2 → kbot_py_client-2.1.4}/kbot_client/chatbot_client/__init__.py +0 -0
- {kbot_py_client-2.1.2 → kbot_py_client-2.1.4}/kbot_client/client_group.py +0 -0
- {kbot_py_client-2.1.2 → kbot_py_client-2.1.4}/kbot_client/folder_sync.py +0 -0
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kbot-py-client
|
|
3
|
+
Version: 2.1.4
|
|
4
|
+
Summary: Client for Konverso Kbot.
|
|
5
|
+
Author: Konverso
|
|
6
|
+
Author-email: Konverso <contact@konverso.ai>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Dist: requests>=2.32.5
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Project-URL: Homepage, https://www.konverso.ai/
|
|
14
|
+
Project-URL: Source, https://github.com/konverso-ai/kbot-py-client
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
1
17
|
# kbot-py-client #
|
|
2
18
|
This package contains utilities which you may use to easily interact with Konverso Kbot application.
|
|
3
19
|
|
|
@@ -225,6 +241,33 @@ syncer.sync("/tmp/my_source_folder/", "1831f-XXXXXXXXXXXXXXXXXXXXXXX")
|
|
|
225
241
|
print("Syncing is done :)")
|
|
226
242
|
````
|
|
227
243
|
|
|
244
|
+
## Recording API activity
|
|
245
|
+
You may attach a Recorder to a client to trace every API call it makes. This is useful for debugging or for building your own logging.
|
|
246
|
+
|
|
247
|
+
### Printing each request and response
|
|
248
|
+
```python
|
|
249
|
+
from kbot_client import Client, Printer
|
|
250
|
+
|
|
251
|
+
cli = Client("mybot.konverso.ai", api_key="xxxxxxxxxxxxxxxxxxx", recorder=Printer())
|
|
252
|
+
cli.metric()
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Printing the equivalent curl command
|
|
256
|
+
This is handy when you want to reproduce a call outside of Python, for example to share it with support or run it from a terminal.
|
|
257
|
+
```python
|
|
258
|
+
from kbot_client import Client, CurlPrinter
|
|
259
|
+
|
|
260
|
+
cli = Client("mybot.konverso.ai", api_key="xxxxxxxxxxxxxxxxxxx", recorder=CurlPrinter())
|
|
261
|
+
cli.metric()
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
You can also set or change the recorder after the client is created:
|
|
265
|
+
```python
|
|
266
|
+
cli.recorder = CurlPrinter()
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
If you need a different behavior, such as writing the activity to a file or a logger, subclass `Recorder` and override its `record` method.
|
|
270
|
+
|
|
228
271
|
## A command line chatbot
|
|
229
272
|
We provide a sample command line chatbot, available in Sync or Async mode
|
|
230
273
|
|
|
@@ -271,3 +314,32 @@ from kbot_client import chatbot_client
|
|
|
271
314
|
...
|
|
272
315
|
chatbot_client.run(mode="synchronous", client=cli, assistant=assistant, convert_html_to_text=True)
|
|
273
316
|
```
|
|
317
|
+
|
|
318
|
+
# Development #
|
|
319
|
+
|
|
320
|
+
## Running the tests
|
|
321
|
+
The test suite lives in `tests/` and runs against a local fake Kbot HTTP server, so no Kbot instance is needed:
|
|
322
|
+
```bash
|
|
323
|
+
uv run --group test pytest
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Live tests
|
|
327
|
+
`tests/live/` runs against a real Kbot instance. These tests are skipped unless the following environment variables are set (never commit the API key):
|
|
328
|
+
|
|
329
|
+
* `KBOT_LIVE_HOST`: the Kbot host, such as `mybot.konverso.ai`
|
|
330
|
+
* `KBOT_LIVE_API_KEY`: an API key allowed to create and impersonate users. The tests create (once) and impersonate the end user `kbot-py-client-tests@konverso.ai`
|
|
331
|
+
* `KBOT_LIVE_FOLDER_UUID` (optional): a File Manager folder. File tests create a timestamped sub folder in it and delete it afterwards
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
KBOT_LIVE_HOST=mybot.konverso.ai KBOT_LIVE_API_KEY=xxxx KBOT_LIVE_FOLDER_UUID=yyyy uv run --group test pytest -m live
|
|
335
|
+
```
|
|
336
|
+
Use `-m "not live"` to run only the offline tests.
|
|
337
|
+
|
|
338
|
+
## Linting and type checking
|
|
339
|
+
The CI runs the following checks, which must pass:
|
|
340
|
+
```bash
|
|
341
|
+
uv run ruff check --select F,E9,B,PLE kbot_client examples
|
|
342
|
+
uv run pylint --errors-only kbot_client examples tests
|
|
343
|
+
uv run ty check kbot_client examples tests
|
|
344
|
+
```
|
|
345
|
+
`ruff.toml` selects all Ruff rules: this is the target, not yet met by the codebase (`uv run ruff check` still reports style findings).
|
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: kbot-py-client
|
|
3
|
-
Version: 2.1.2
|
|
4
|
-
Summary: Client for Konverso Kbot.
|
|
5
|
-
License: MIT
|
|
6
|
-
Author: Konverso
|
|
7
|
-
Author-email: contact@konverso.ai
|
|
8
|
-
Requires-Python: >=3.10
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
Requires-Dist: requests (>=2.27.1)
|
|
17
|
-
Project-URL: Homepage, https://www.konverso.ai/
|
|
18
|
-
Project-URL: Source, https://github.com/konverso-ai/kbot-py-client
|
|
19
|
-
Description-Content-Type: text/markdown
|
|
20
|
-
|
|
21
1
|
# kbot-py-client #
|
|
22
2
|
This package contains utilities which you may use to easily interact with Konverso Kbot application.
|
|
23
3
|
|
|
@@ -245,6 +225,33 @@ syncer.sync("/tmp/my_source_folder/", "1831f-XXXXXXXXXXXXXXXXXXXXXXX")
|
|
|
245
225
|
print("Syncing is done :)")
|
|
246
226
|
````
|
|
247
227
|
|
|
228
|
+
## Recording API activity
|
|
229
|
+
You may attach a Recorder to a client to trace every API call it makes. This is useful for debugging or for building your own logging.
|
|
230
|
+
|
|
231
|
+
### Printing each request and response
|
|
232
|
+
```python
|
|
233
|
+
from kbot_client import Client, Printer
|
|
234
|
+
|
|
235
|
+
cli = Client("mybot.konverso.ai", api_key="xxxxxxxxxxxxxxxxxxx", recorder=Printer())
|
|
236
|
+
cli.metric()
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Printing the equivalent curl command
|
|
240
|
+
This is handy when you want to reproduce a call outside of Python, for example to share it with support or run it from a terminal.
|
|
241
|
+
```python
|
|
242
|
+
from kbot_client import Client, CurlPrinter
|
|
243
|
+
|
|
244
|
+
cli = Client("mybot.konverso.ai", api_key="xxxxxxxxxxxxxxxxxxx", recorder=CurlPrinter())
|
|
245
|
+
cli.metric()
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
You can also set or change the recorder after the client is created:
|
|
249
|
+
```python
|
|
250
|
+
cli.recorder = CurlPrinter()
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
If you need a different behavior, such as writing the activity to a file or a logger, subclass `Recorder` and override its `record` method.
|
|
254
|
+
|
|
248
255
|
## A command line chatbot
|
|
249
256
|
We provide a sample command line chatbot, available in Sync or Async mode
|
|
250
257
|
|
|
@@ -292,3 +299,31 @@ from kbot_client import chatbot_client
|
|
|
292
299
|
chatbot_client.run(mode="synchronous", client=cli, assistant=assistant, convert_html_to_text=True)
|
|
293
300
|
```
|
|
294
301
|
|
|
302
|
+
# Development #
|
|
303
|
+
|
|
304
|
+
## Running the tests
|
|
305
|
+
The test suite lives in `tests/` and runs against a local fake Kbot HTTP server, so no Kbot instance is needed:
|
|
306
|
+
```bash
|
|
307
|
+
uv run --group test pytest
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Live tests
|
|
311
|
+
`tests/live/` runs against a real Kbot instance. These tests are skipped unless the following environment variables are set (never commit the API key):
|
|
312
|
+
|
|
313
|
+
* `KBOT_LIVE_HOST`: the Kbot host, such as `mybot.konverso.ai`
|
|
314
|
+
* `KBOT_LIVE_API_KEY`: an API key allowed to create and impersonate users. The tests create (once) and impersonate the end user `kbot-py-client-tests@konverso.ai`
|
|
315
|
+
* `KBOT_LIVE_FOLDER_UUID` (optional): a File Manager folder. File tests create a timestamped sub folder in it and delete it afterwards
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
KBOT_LIVE_HOST=mybot.konverso.ai KBOT_LIVE_API_KEY=xxxx KBOT_LIVE_FOLDER_UUID=yyyy uv run --group test pytest -m live
|
|
319
|
+
```
|
|
320
|
+
Use `-m "not live"` to run only the offline tests.
|
|
321
|
+
|
|
322
|
+
## Linting and type checking
|
|
323
|
+
The CI runs the following checks, which must pass:
|
|
324
|
+
```bash
|
|
325
|
+
uv run ruff check --select F,E9,B,PLE kbot_client examples
|
|
326
|
+
uv run pylint --errors-only kbot_client examples tests
|
|
327
|
+
uv run ty check kbot_client examples tests
|
|
328
|
+
```
|
|
329
|
+
`ruff.toml` selects all Ruff rules: this is the target, not yet met by the codebase (`uv run ruff check` still reports style findings).
|
|
@@ -85,7 +85,7 @@ class AsyncCallbackChatClient:
|
|
|
85
85
|
|
|
86
86
|
self._conversation_uuid = messages_json.get("id")
|
|
87
87
|
|
|
88
|
-
if display_intro:
|
|
88
|
+
if display_intro and self._callback:
|
|
89
89
|
self._callback(greeting_response.json())
|
|
90
90
|
|
|
91
91
|
@property
|
|
@@ -126,7 +126,8 @@ class AsyncCallbackChatClient:
|
|
|
126
126
|
Returns a tuple of two elements: sender, messages
|
|
127
127
|
"""
|
|
128
128
|
stop = not messages_json.get("dialog_in_progress")
|
|
129
|
-
self._callback
|
|
129
|
+
if self._callback:
|
|
130
|
+
self._callback(messages_json)
|
|
130
131
|
return stop
|
|
131
132
|
|
|
132
133
|
def send(self, question):
|
|
@@ -131,7 +131,8 @@ class ChatClient(ABC):
|
|
|
131
131
|
elif message_format == "html":
|
|
132
132
|
if self._convert_html_to_text:
|
|
133
133
|
try:
|
|
134
|
-
|
|
134
|
+
# Optional dependency, not installed by default
|
|
135
|
+
from bs4 import BeautifulSoup # pylint: disable=import-error # ty: ignore[unresolved-import]
|
|
135
136
|
message_value = BeautifulSoup(message.get('value'), 'html.parser').get_text()
|
|
136
137
|
except:
|
|
137
138
|
print("ERROR: HTML to Text conversion requested, but beautifull soup is not installed. Use: ")
|
|
@@ -146,7 +146,8 @@ class ChatClient(ABC):
|
|
|
146
146
|
elif message_format == "html":
|
|
147
147
|
if self._convert_html_to_text:
|
|
148
148
|
try:
|
|
149
|
-
|
|
149
|
+
# Optional dependency, not installed by default
|
|
150
|
+
from bs4 import BeautifulSoup # pylint: disable=import-error # ty: ignore[unresolved-import]
|
|
150
151
|
message_value = BeautifulSoup(message.get('value'), 'html.parser').get_text()
|
|
151
152
|
except:
|
|
152
153
|
print("ERROR: HTML to Text conversion requested, but beautifull soup is not installed. Use: ")
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""Dialog tester"""
|
|
2
2
|
import json
|
|
3
3
|
import time
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
4
6
|
|
|
5
7
|
import requests
|
|
6
8
|
|
|
@@ -8,7 +10,7 @@ import requests
|
|
|
8
10
|
class Client:
|
|
9
11
|
"""Base representation of Kbot instance"""
|
|
10
12
|
|
|
11
|
-
def __init__(self, server, port=443, proto='https', api_key=None, verify=True):
|
|
13
|
+
def __init__(self, server, port=443, proto='https', api_key=None, verify=True, recorder=None):
|
|
12
14
|
"""
|
|
13
15
|
|
|
14
16
|
|
|
@@ -18,10 +20,12 @@ class Client:
|
|
|
18
20
|
- proto: HTTP or HTTPS
|
|
19
21
|
- api_key: A Kbot valid API Key. If provided all requests will be sent with this API Key
|
|
20
22
|
and it is not required to invoke the login method
|
|
23
|
+
- recorder: an optional Recorder instance used to trace every API call made by this client
|
|
21
24
|
"""
|
|
22
25
|
|
|
23
26
|
self.host = server
|
|
24
27
|
self.verify = verify
|
|
28
|
+
self.recorder = recorder
|
|
25
29
|
|
|
26
30
|
# A version, in the formation "YEAR-V", such as 2024.02
|
|
27
31
|
self.version = None
|
|
@@ -177,6 +181,12 @@ class Client:
|
|
|
177
181
|
endpoint.__name__ = name
|
|
178
182
|
setattr(self, name, endpoint)
|
|
179
183
|
|
|
184
|
+
if TYPE_CHECKING:
|
|
185
|
+
# Endpoint methods (metric, get_dashboard, ...) are added at runtime from
|
|
186
|
+
# the server schema: tell static checkers that they exist. This is
|
|
187
|
+
# not defined at runtime, so unknown attributes still raise AttributeError.
|
|
188
|
+
def __getattr__(self, name: str) -> Callable[..., requests.Response]: ...
|
|
189
|
+
|
|
180
190
|
def __refresh(self):
|
|
181
191
|
r = requests.post(self.url + "/api/refresh",
|
|
182
192
|
data=json.dumps({"refresh_token": self.__refresh_token}),
|
|
@@ -184,7 +194,7 @@ class Client:
|
|
|
184
194
|
r.raise_for_status()
|
|
185
195
|
self.__reset_headers(r.json())
|
|
186
196
|
|
|
187
|
-
def __request(self, method: str, uri
|
|
197
|
+
def __request(self, method: str, uri: str | None = None, data: dict | None = None, params: dict | None = None, files: dict | None = None, attempt=0, timeout=None):
|
|
188
198
|
if files:
|
|
189
199
|
# For file upload, the data must be a dictionnary
|
|
190
200
|
dump_data = data
|
|
@@ -198,9 +208,18 @@ class Client:
|
|
|
198
208
|
else:
|
|
199
209
|
headers = self._headers
|
|
200
210
|
|
|
211
|
+
# requests reads upload files to their end: remember where they start so
|
|
212
|
+
# that a replay after a token refresh sends the same content.
|
|
213
|
+
file_positions = [(fileobj, fileobj.tell()) for fileobj in _file_objects(files)]
|
|
214
|
+
|
|
201
215
|
r = requests.request(method.upper(), self.url + '/api/%s/' % (
|
|
202
216
|
uri), params=params, data=dump_data, headers=headers, files=files, verify=self.verify, timeout=timeout)
|
|
203
217
|
|
|
218
|
+
if self.recorder:
|
|
219
|
+
# Give the recorder a snapshot: the client headers change on token
|
|
220
|
+
# refresh, and a recorder must not be able to alter them.
|
|
221
|
+
self.recorder.record(method, r.url, headers=dict(headers), data=data, files=files, response=r)
|
|
222
|
+
|
|
204
223
|
if r.status_code == 401:
|
|
205
224
|
# Refresh the token
|
|
206
225
|
try:
|
|
@@ -214,20 +233,22 @@ class Client:
|
|
|
214
233
|
time.sleep(3)
|
|
215
234
|
|
|
216
235
|
# Re-invoke the request
|
|
217
|
-
|
|
236
|
+
for fileobj, position in file_positions:
|
|
237
|
+
fileobj.seek(position)
|
|
238
|
+
r = self.__request(method, uri=uri, data=data, params=params, files=files, attempt=attempt+1, timeout=timeout)
|
|
218
239
|
|
|
219
240
|
return r
|
|
220
241
|
|
|
221
|
-
def request(self, method: str, uri: str, data: dict = None, params: dict = None, files: dict = None, timeout=None):
|
|
242
|
+
def request(self, method: str, uri: str, data: dict | None = None, params: dict | None = None, files: dict | None = None, timeout=None):
|
|
222
243
|
return self.__request(method, uri=uri, data=data, params=params, files=files, timeout=timeout)
|
|
223
244
|
|
|
224
|
-
def unit(self, name: str, params=None, timeout=None) -> dict:
|
|
245
|
+
def unit(self, name: str, params=None, timeout=None) -> dict | None:
|
|
225
246
|
r = self.get(name, params, timeout=timeout)
|
|
226
247
|
if r:
|
|
227
248
|
return r.json()
|
|
228
249
|
return None
|
|
229
250
|
|
|
230
|
-
def message(self, cid: int, message: str, timeout:
|
|
251
|
+
def message(self, cid: int, message: str, timeout: float = 60) -> list:
|
|
231
252
|
response = []
|
|
232
253
|
|
|
233
254
|
data = {'type': 'message', 'message': message}
|
|
@@ -264,7 +285,7 @@ class Client:
|
|
|
264
285
|
# self._process('logout', 1)
|
|
265
286
|
|
|
266
287
|
# Logout from the APIs
|
|
267
|
-
|
|
288
|
+
requests.post(self.url + '/api/logout', headers=self._headers, verify=self.verify, timeout=timeout)
|
|
268
289
|
|
|
269
290
|
#
|
|
270
291
|
# In addition to the Generated and built in API methods, we have the classic base REST methods
|
|
@@ -298,6 +319,16 @@ class Client:
|
|
|
298
319
|
"""
|
|
299
320
|
return self.__request("post", unit, params=params, data=data, files=files, timeout=timeout)
|
|
300
321
|
|
|
322
|
+
|
|
323
|
+
def _file_objects(files: dict | None = None):
|
|
324
|
+
"""Yields the seekable file objects of a requests `files` mapping, whose values
|
|
325
|
+
are either a file object or a (filename, file object, ...) tuple"""
|
|
326
|
+
for value in (files or {}).values():
|
|
327
|
+
fileobj = value[1] if isinstance(value, (tuple, list)) else value
|
|
328
|
+
if hasattr(fileobj, "seek") and hasattr(fileobj, "tell"):
|
|
329
|
+
yield fileobj
|
|
330
|
+
|
|
331
|
+
|
|
301
332
|
class UpKbotClient(Client):
|
|
302
333
|
"""Represents a currently reachable Kbot instance"""
|
|
303
334
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Recorders used to trace the API activity of a Client"""
|
|
2
|
+
import json
|
|
3
|
+
import shlex
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Recorder:
|
|
7
|
+
"""Base class for recording the API calls made by a Client.
|
|
8
|
+
|
|
9
|
+
Subclasses should override `record`, which is invoked once per HTTP
|
|
10
|
+
call performed by `Client.__request`, after the response has been
|
|
11
|
+
received.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def record(self, method: str, url: str, headers: dict | None = None, data=None, files: dict | None = None, response=None):
|
|
15
|
+
"""Called after each API request is made.
|
|
16
|
+
|
|
17
|
+
Arguments:
|
|
18
|
+
- method: the HTTP method, such as "GET" or "POST"
|
|
19
|
+
- url: the full URL that was called, including query string
|
|
20
|
+
- headers: the headers sent with the request
|
|
21
|
+
- data: the request payload, as passed to the client (before serialization)
|
|
22
|
+
- files: the files sent with the request, if any
|
|
23
|
+
- response: the requests.Response object returned by the call
|
|
24
|
+
"""
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Printer(Recorder):
|
|
29
|
+
"""Recorder that prints the URL, protocol, payload and response of each API call"""
|
|
30
|
+
|
|
31
|
+
def record(self, method: str, url: str, headers: dict | None = None, data=None, files: dict | None = None, response=None):
|
|
32
|
+
proto = url.split(':', 1)[0]
|
|
33
|
+
print("%s %s (%s)" % (method.upper(), url, proto))
|
|
34
|
+
if headers:
|
|
35
|
+
print(" headers: %s" % headers)
|
|
36
|
+
if data:
|
|
37
|
+
print(" payload: %s" % data)
|
|
38
|
+
if files:
|
|
39
|
+
print(" files: %s" % list(files.keys()))
|
|
40
|
+
if response is not None:
|
|
41
|
+
print(" -> %s %s" % (response.status_code, response.text))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class CurlPrinter(Recorder):
|
|
45
|
+
"""Recorder that prints the equivalent curl command of each API call"""
|
|
46
|
+
|
|
47
|
+
def record(self, method: str, url: str, headers: dict | None = None, data=None, files: dict | None = None, response=None):
|
|
48
|
+
print(self.to_curl(method, url, headers=headers, data=data, files=files))
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def to_curl(method: str, url: str, headers: dict | None = None, data=None, files: dict | None = None) -> str:
|
|
52
|
+
"""Builds the curl command line equivalent to the given request"""
|
|
53
|
+
parts = ["curl", "-X", method.upper()]
|
|
54
|
+
|
|
55
|
+
for name, value in (headers or {}).items():
|
|
56
|
+
parts.append("-H %s" % shlex.quote("%s: %s" % (name, value)))
|
|
57
|
+
|
|
58
|
+
if files:
|
|
59
|
+
for name in files:
|
|
60
|
+
parts.append("-F %s" % shlex.quote("%s=@<file>" % name))
|
|
61
|
+
for name, value in (data or {}).items():
|
|
62
|
+
parts.append("-F %s" % shlex.quote("%s=%s" % (name, value)))
|
|
63
|
+
elif data:
|
|
64
|
+
body = data if isinstance(data, str) else json.dumps(data)
|
|
65
|
+
parts.append("-d %s" % shlex.quote(body))
|
|
66
|
+
|
|
67
|
+
parts.append(shlex.quote(url))
|
|
68
|
+
|
|
69
|
+
return " ".join(parts)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "kbot-py-client"
|
|
3
|
+
version = "2.1.4"
|
|
4
|
+
requires-python = ">=3.10"
|
|
5
|
+
description = "Client for Konverso Kbot."
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
classifiers = [
|
|
9
|
+
"Programming Language :: Python :: 3",
|
|
10
|
+
"License :: OSI Approved :: MIT License",
|
|
11
|
+
"Operating System :: OS Independent",
|
|
12
|
+
]
|
|
13
|
+
dependencies = ["requests>=2.32.5"]
|
|
14
|
+
|
|
15
|
+
[[project.authors]]
|
|
16
|
+
name = "Konverso"
|
|
17
|
+
email = "contact@konverso.ai"
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Homepage = "https://www.konverso.ai/"
|
|
21
|
+
Source = "https://github.com/konverso-ai/kbot-py-client"
|
|
22
|
+
|
|
23
|
+
[build-system]
|
|
24
|
+
requires = ["uv_build>=0.9.4,<0.10.0"]
|
|
25
|
+
build-backend = "uv_build"
|
|
26
|
+
|
|
27
|
+
[tool.uv.build-backend]
|
|
28
|
+
module-name = "kbot_client"
|
|
29
|
+
module-root = ""
|
|
30
|
+
|
|
31
|
+
[tool.deptry]
|
|
32
|
+
extend_exclude = ["(^|.*/)tests/"]
|
|
33
|
+
known_first_party = ["kbot_client"]
|
|
34
|
+
|
|
35
|
+
[dependency-groups]
|
|
36
|
+
deps = ["deptry"]
|
|
37
|
+
lint = [
|
|
38
|
+
"pylint",
|
|
39
|
+
"ruff",
|
|
40
|
+
]
|
|
41
|
+
test = [
|
|
42
|
+
"pytest",
|
|
43
|
+
"pytest-asyncio",
|
|
44
|
+
"pytest-cov",
|
|
45
|
+
]
|
|
46
|
+
typecheck = ["ty"]
|
|
47
|
+
|
|
48
|
+
[[dependency-groups.dev]]
|
|
49
|
+
include-group = "deps"
|
|
50
|
+
|
|
51
|
+
[[dependency-groups.dev]]
|
|
52
|
+
include-group = "lint"
|
|
53
|
+
|
|
54
|
+
[[dependency-groups.dev]]
|
|
55
|
+
include-group = "typecheck"
|
|
56
|
+
|
|
57
|
+
[[dependency-groups.dev]]
|
|
58
|
+
include-group = "test"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "kbot-py-client"
|
|
3
|
+
version = "2.1.4"
|
|
4
|
+
requires-python = ">=3.10"
|
|
5
|
+
description = "Client for Konverso Kbot."
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Konverso", email = "contact@konverso.ai" }
|
|
10
|
+
]
|
|
11
|
+
classifiers=[
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
]
|
|
16
|
+
dependencies = [
|
|
17
|
+
"requests>=2.32.5",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://www.konverso.ai/"
|
|
22
|
+
Source = "https://github.com/konverso-ai/kbot-py-client"
|
|
23
|
+
|
|
24
|
+
[build-system]
|
|
25
|
+
requires = ["uv_build>=0.9.4,<0.10.0"]
|
|
26
|
+
build-backend = "uv_build"
|
|
27
|
+
|
|
28
|
+
[tool.uv.build-backend]
|
|
29
|
+
module-name = "kbot_client"
|
|
30
|
+
module-root = ""
|
|
31
|
+
|
|
32
|
+
[tool.deptry]
|
|
33
|
+
# The default "tests" exclude only matches a top-level tests/ folder (re.match
|
|
34
|
+
# anchors at the start), so nested test dirs like core/.../tests/ are missed,
|
|
35
|
+
# making deptry flag pytest (a dev dependency) as used in "non-dev" code.
|
|
36
|
+
extend_exclude = [
|
|
37
|
+
"(^|.*/)tests/",
|
|
38
|
+
]
|
|
39
|
+
known_first_party = [
|
|
40
|
+
"kbot_client",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[dependency-groups]
|
|
44
|
+
deps = [
|
|
45
|
+
"deptry",
|
|
46
|
+
]
|
|
47
|
+
lint = [
|
|
48
|
+
"pylint",
|
|
49
|
+
"ruff",
|
|
50
|
+
]
|
|
51
|
+
test = [
|
|
52
|
+
"pytest",
|
|
53
|
+
"pytest-asyncio",
|
|
54
|
+
"pytest-cov",
|
|
55
|
+
]
|
|
56
|
+
typecheck = [
|
|
57
|
+
"ty",
|
|
58
|
+
]
|
|
59
|
+
dev = [
|
|
60
|
+
{ include-group = "deps" },
|
|
61
|
+
{ include-group = "lint" },
|
|
62
|
+
{ include-group = "typecheck" },
|
|
63
|
+
{ include-group = "test" },
|
|
64
|
+
]
|
kbot_py_client-2.1.2/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Konverso
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import json
|
|
3
|
-
|
|
4
|
-
from kbot_client import Client
|
|
5
|
-
|
|
6
|
-
# Option 1: using an API Key
|
|
7
|
-
client = Client("myhost.konverso.ai", api_key="18460555-9012-4a43-bf68-fc55325b96af")
|
|
8
|
-
|
|
9
|
-
# Option 2: using a user login
|
|
10
|
-
client = Client("myhost.konverso.ai")
|
|
11
|
-
client.login("myuser", "mypassword")
|
|
12
|
-
|
|
13
|
-
metrics = client.metric()
|
|
14
|
-
print("Collected metrics (%s):" % (metrics))
|
|
15
|
-
print(metrics.text)
|
|
16
|
-
print(json.dumps(metrics.json(), indent=4))
|
|
17
|
-
|
|
18
|
-
r = client.conversation(username='bot')
|
|
19
|
-
print("Post conversation (%s):" % (r))
|
|
20
|
-
print(r.text)
|
|
21
|
-
|
|
22
|
-
r = client.get_dashboard(1)
|
|
23
|
-
print("Get dashboard (%s):" % (r))
|
|
24
|
-
print(r.text)
|
|
25
|
-
|
|
26
|
-
client.logout()
|
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
[tool.poetry]
|
|
2
|
-
name = "kbot-py-client"
|
|
3
|
-
version = "2.1.2"
|
|
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.10"
|
|
23
|
-
requests = ">=2.27.1"
|
|
24
|
-
|
|
25
|
-
[tool.poetry.group.dev.dependencies]
|
|
26
|
-
pylint = "^3.3.4"
|
|
27
|
-
ruff = "^0.9.4"
|
|
28
|
-
|
|
29
|
-
[build-system]
|
|
30
|
-
requires = ["poetry-core"]
|
|
31
|
-
build-backend = "poetry.core.masonry.api"
|
|
32
|
-
|
|
33
|
-
[tool.pylint]
|
|
34
|
-
# Master section
|
|
35
|
-
extension-pkg-whitelist = []
|
|
36
|
-
ignore = ["CVS", ".git", ".ruff_cache", ".venv"]
|
|
37
|
-
ignore-patterns = []
|
|
38
|
-
jobs = 1
|
|
39
|
-
load-plugins = ["pylint.extensions.no_self_use"]
|
|
40
|
-
persistent = true
|
|
41
|
-
unsafe-load-any-extension = false
|
|
42
|
-
|
|
43
|
-
[tool.pylint.messages_control]
|
|
44
|
-
confidence = []
|
|
45
|
-
disable = [
|
|
46
|
-
"invalid-name",
|
|
47
|
-
"fixme",
|
|
48
|
-
"too-many-arguments",
|
|
49
|
-
"too-many-locals",
|
|
50
|
-
"too-many-branches",
|
|
51
|
-
"bare-except",
|
|
52
|
-
"broad-except",
|
|
53
|
-
"no-self-use",
|
|
54
|
-
"too-few-public-methods",
|
|
55
|
-
"too-many-public-methods",
|
|
56
|
-
"too-many-instance-attributes",
|
|
57
|
-
"too-many-nested-blocks",
|
|
58
|
-
"too-many-statements",
|
|
59
|
-
"too-many-return-statements",
|
|
60
|
-
"unused-argument",
|
|
61
|
-
"too-many-ancestors",
|
|
62
|
-
"wildcard-import",
|
|
63
|
-
"abstract-method",
|
|
64
|
-
"missing-docstring"
|
|
65
|
-
]
|
|
66
|
-
enable = []
|
|
67
|
-
|
|
68
|
-
[tool.pylint.reports]
|
|
69
|
-
evaluation = "10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)"
|
|
70
|
-
msg-template = "{path}:{module}:{line}: [{msg_id}({symbol}), {obj}] {msg}"
|
|
71
|
-
output-format = "colorized"
|
|
72
|
-
reports = true
|
|
73
|
-
score = true
|
|
74
|
-
|
|
75
|
-
[tool.pylint.refactoring]
|
|
76
|
-
max-nested-blocks = 5
|
|
77
|
-
|
|
78
|
-
[tool.pylint.basic]
|
|
79
|
-
argument-rgx = "(([a-z][A-Za-z0-9_]{2,30})|(_[a-z0-9_]*))$"
|
|
80
|
-
attr-rgx = "(([a-z][A-Za-z0-9_]{2,30})|(_[a-z0-9_]*))$"
|
|
81
|
-
bad-names = ["foo", "bar", "baz", "toto", "tutu", "tata"]
|
|
82
|
-
class-attribute-rgx = "([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$"
|
|
83
|
-
class-rgx = "[A-Z_][a-zA-Z0-9]+$"
|
|
84
|
-
const-rgx = "(([A-Za-z_][A-Za-z0-9_]*)|(__.*__))$"
|
|
85
|
-
docstring-min-length = -1
|
|
86
|
-
function-rgx = "(([a-zA-Z][A-Za-z0-9_]{2,30})|(_[a-zA-Z0-9_]*))$"
|
|
87
|
-
good-names = ["i", "j", "k", "ex", "Run", "_"]
|
|
88
|
-
include-naming-hint = false
|
|
89
|
-
inlinevar-rgx = "[A-Za-z_][A-Za-z0-9_]*$"
|
|
90
|
-
method-rgx = "(([a-zA-Z][a-zA-Z0-9_]{2,40})|(_[a-zA-Z0-9_]*))$"
|
|
91
|
-
module-rgx = "(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$"
|
|
92
|
-
name-group = []
|
|
93
|
-
no-docstring-rgx = "^_"
|
|
94
|
-
property-classes = ["abc.abstractproperty"]
|
|
95
|
-
variable-rgx = "(([a-z][A-Za-z0-9_]{0,30})|(_[a-z0-9_]*))$"
|
|
96
|
-
|
|
97
|
-
[tool.pylint.format]
|
|
98
|
-
expected-line-ending-format = "LF"
|
|
99
|
-
ignore-long-lines = "^\\s*(# )?<?https?://\\S+>?$"
|
|
100
|
-
indent-after-paren = 4
|
|
101
|
-
indent-string = " "
|
|
102
|
-
max-line-length = 120
|
|
103
|
-
max-module-lines = 1000
|
|
104
|
-
single-line-class-stmt = false
|
|
105
|
-
single-line-if-stmt = false
|
|
106
|
-
|
|
107
|
-
[tool.pylint.spelling]
|
|
108
|
-
spelling-dict = ""
|
|
109
|
-
spelling-ignore-words = []
|
|
110
|
-
spelling-private-dict-file = ""
|
|
111
|
-
spelling-store-unknown-words = false
|
|
112
|
-
|
|
113
|
-
[tool.pylint.miscellaneous]
|
|
114
|
-
notes = ["FIXME", "XXX", "TODO"]
|
|
115
|
-
|
|
116
|
-
[tool.pylint.similarities]
|
|
117
|
-
ignore-comments = true
|
|
118
|
-
ignore-docstrings = true
|
|
119
|
-
ignore-imports = false
|
|
120
|
-
min-similarity-lines = 4
|
|
121
|
-
|
|
122
|
-
[tool.pylint.variables]
|
|
123
|
-
additional-builtins = []
|
|
124
|
-
allow-global-unused-variables = true
|
|
125
|
-
callbacks = ["cb_", "_cb"]
|
|
126
|
-
dummy-variables-rgx = "_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy"
|
|
127
|
-
ignored-argument-names = "_.*"
|
|
128
|
-
init-import = false
|
|
129
|
-
redefining-builtins-modules = ["six.moves", "future.builtins"]
|
|
130
|
-
|
|
131
|
-
[tool.pylint.typecheck]
|
|
132
|
-
contextmanager-decorators = ["contextlib.contextmanager"]
|
|
133
|
-
generated-members = []
|
|
134
|
-
ignore-mixin-members = true
|
|
135
|
-
ignored-classes = ["optparse.Values", "thread._local", "_thread._local"]
|
|
136
|
-
ignored-modules = []
|
|
137
|
-
missing-member-hint = true
|
|
138
|
-
missing-member-hint-distance = 1
|
|
139
|
-
missing-member-max-choices = 1
|
|
140
|
-
|
|
141
|
-
[tool.pylint.logging]
|
|
142
|
-
logging-modules = ["logging"]
|
|
143
|
-
|
|
144
|
-
[tool.pylint.imports]
|
|
145
|
-
allow-wildcard-with-all = false
|
|
146
|
-
analyse-fallback-blocks = false
|
|
147
|
-
deprecated-modules = ["optparse", "tkinter.tix"]
|
|
148
|
-
ext-import-graph = ""
|
|
149
|
-
import-graph = ""
|
|
150
|
-
int-import-graph = ""
|
|
151
|
-
known-standard-library = []
|
|
152
|
-
known-third-party = ["enchant"]
|
|
153
|
-
|
|
154
|
-
[tool.pylint.design]
|
|
155
|
-
max-args = 5
|
|
156
|
-
max-attributes = 7
|
|
157
|
-
max-bool-expr = 5
|
|
158
|
-
max-branches = 12
|
|
159
|
-
max-locals = 15
|
|
160
|
-
max-parents = 7
|
|
161
|
-
max-public-methods = 20
|
|
162
|
-
max-returns = 6
|
|
163
|
-
max-statements = 50
|
|
164
|
-
min-public-methods = 2
|
|
165
|
-
|
|
166
|
-
[tool.pylint.classes]
|
|
167
|
-
defining-attr-methods = ["__init__", "__new__", "setUp"]
|
|
168
|
-
exclude-protected = ["_asdict", "_fields", "_replace", "_source", "_make"]
|
|
169
|
-
valid-classmethod-first-arg = ["cls"]
|
|
170
|
-
valid-metaclass-classmethod-first-arg = ["mcs"]
|
|
171
|
-
|
|
172
|
-
[tool.pylint.exceptions]
|
|
173
|
-
overgeneral-exceptions = ["builtins.Exception"]
|
|
174
|
-
|
|
175
|
-
[tool.pylint.'MASTER']
|
|
176
|
-
ignore-patterns = "test*.py"
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
[tool.pytest]
|
|
180
|
-
testpaths = "tests"
|
|
181
|
-
addopts = "-vv"
|
|
182
|
-
python_files = "test_*.py"
|
|
183
|
-
|
|
184
|
-
[tool.pytest.ini_options]
|
|
185
|
-
asyncio_mode = "auto"
|
|
186
|
-
|
|
187
|
-
[tool.ruff]
|
|
188
|
-
# Exclude a variety of commonly ignored directories.
|
|
189
|
-
exclude = [
|
|
190
|
-
".bzr",
|
|
191
|
-
".direnv",
|
|
192
|
-
".eggs",
|
|
193
|
-
".git",
|
|
194
|
-
".git-rewrite",
|
|
195
|
-
".hg",
|
|
196
|
-
".ipynb_checkpoints",
|
|
197
|
-
".mypy_cache",
|
|
198
|
-
".nox",
|
|
199
|
-
".pants.d",
|
|
200
|
-
".pyenv",
|
|
201
|
-
".pytest_cache",
|
|
202
|
-
".pytype",
|
|
203
|
-
".ruff_cache",
|
|
204
|
-
".svn",
|
|
205
|
-
".tox",
|
|
206
|
-
".venv",
|
|
207
|
-
".vscode",
|
|
208
|
-
"__pypackages__",
|
|
209
|
-
"_build",
|
|
210
|
-
"buck-out",
|
|
211
|
-
"build",
|
|
212
|
-
"dist",
|
|
213
|
-
"node_modules",
|
|
214
|
-
"site-packages",
|
|
215
|
-
"venv",
|
|
216
|
-
]
|
|
217
|
-
|
|
218
|
-
line-length = 120
|
|
219
|
-
indent-width = 4
|
|
220
|
-
|
|
221
|
-
# Assume Python 3.12
|
|
222
|
-
target-version = "py312"
|
|
223
|
-
|
|
224
|
-
output-format = "grouped"
|
|
225
|
-
|
|
226
|
-
[tool.ruff.lint]
|
|
227
|
-
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
|
228
|
-
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
|
|
229
|
-
# McCabe complexity (`C901`) by default.
|
|
230
|
-
select = ["ALL"]
|
|
231
|
-
ignore = [
|
|
232
|
-
"D100",
|
|
233
|
-
"D106",
|
|
234
|
-
"D203",
|
|
235
|
-
"D213",
|
|
236
|
-
"ARG002",
|
|
237
|
-
"ANN002",
|
|
238
|
-
"ANN003",
|
|
239
|
-
"S101",
|
|
240
|
-
"FBT001",
|
|
241
|
-
"FBT002",
|
|
242
|
-
]
|
|
243
|
-
|
|
244
|
-
# Allow fix for all enabled rules (when `--fix`) is provided.
|
|
245
|
-
fixable = ["ALL"]
|
|
246
|
-
unfixable = []
|
|
247
|
-
|
|
248
|
-
# Allow unused variables when underscore-prefixed.
|
|
249
|
-
# dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
250
|
-
|
|
251
|
-
[tool.ruff.lint.extend-per-file-ignores]
|
|
252
|
-
# Also ignore `E402` in all `__init__.py` files.
|
|
253
|
-
"__init__.py" = ["D104", "E402", "F401"]
|
|
254
|
-
|
|
255
|
-
[tool.ruff.format]
|
|
256
|
-
# Like Black, use double quotes for strings.
|
|
257
|
-
quote-style = "double"
|
|
258
|
-
|
|
259
|
-
# Like Black, indent with spaces, rather than tabs.
|
|
260
|
-
indent-style = "space"
|
|
261
|
-
|
|
262
|
-
# Like Black, respect magic trailing commas.
|
|
263
|
-
skip-magic-trailing-comma = false
|
|
264
|
-
|
|
265
|
-
# Like Black, automatically detect the appropriate line ending.
|
|
266
|
-
line-ending = "auto"
|
|
267
|
-
|
|
268
|
-
# Enable auto-formatting of code examples in docstrings. Markdown,
|
|
269
|
-
# reStructuredText code/literal blocks and doctests are all supported.
|
|
270
|
-
#
|
|
271
|
-
# This is currently disabled by default, but it is planned for this
|
|
272
|
-
# to be opt-out in the future.
|
|
273
|
-
docstring-code-format = false
|
|
274
|
-
|
|
275
|
-
# Set the line length limit used when formatting code snippets in
|
|
276
|
-
# docstrings.
|
|
277
|
-
#
|
|
278
|
-
# This only has an effect when the `docstring-code-format` setting is
|
|
279
|
-
# enabled.
|
|
280
|
-
docstring-code-line-length = "dynamic"
|
|
281
|
-
|
|
282
|
-
[tool.ruff.lint.isort]
|
|
283
|
-
known-first-party = ["kbot_client"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|