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.
@@ -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).
@@ -1,2 +1,3 @@
1
1
  from .client import Client, DownKbotClient, UpKbotClient
2
2
  from .client_group import ClientGroup
3
+ from .recorder import Recorder, Printer, CurlPrinter
@@ -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(messages_json)
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
- from bs4 import BeautifulSoup
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
- from bs4 import BeautifulSoup
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 : str = None, data: dict = None, params: dict = None, files: dict = None, attempt=0, timeout=None):
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
- r = self.__request(method, uri=uri, data=data, params=params, attempt=attempt+1)
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: int=60) -> list:
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
- self.request("post", self.url + '/api/logout', timeout=timeout)
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
+ ]
@@ -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"]