unique_toolkit 1.19.0__py3-none-any.whl → 1.19.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of unique_toolkit might be problematic. Click here for more details.

@@ -4,7 +4,7 @@ from logging import Logger
4
4
  from typing import Any, Generic
5
5
 
6
6
  import jinja2
7
- from pydantic import BaseModel
7
+ from pydantic import BaseModel, ValidationError
8
8
 
9
9
  from unique_toolkit._common.endpoint_builder import (
10
10
  ApiOperationProtocol,
@@ -41,11 +41,13 @@ NEXT_USER_MESSAGE_JINJA2_TEMPLATE = jinja2.Template("""I confirm the api call wi
41
41
  ```""")
42
42
 
43
43
 
44
- ASSISTANT_CONFIRMATION_MESSAGE_JINJA2_TEMPLATE = jinja2.Template("""I would like to call the api with the following data:
45
-
44
+ ASSISTANT_CONFIRMATION_MESSAGE_JINJA2_TEMPLATE = jinja2.Template(
45
+ """
46
+ \n
46
47
  {{ api_call_as_markdown_table }}
47
-
48
- [{{ button_text }}](https://prompt={{ next_user_message | urlencode }})""")
48
+ \n\n
49
+ [{{ button_text }}](https://prompt={{ next_user_message | urlencode }})"""
50
+ )
49
51
 
50
52
 
51
53
  class HumanVerificationManagerForApiCalling(
@@ -175,7 +177,9 @@ class HumanVerificationManagerForApiCalling(
175
177
  self._environment_payload_params.model_dump()
176
178
  )
177
179
 
178
- return self._operation.payload_model().model_validate(payload_dict)
180
+ return self._operation.payload_model().model_validate(
181
+ payload_dict, by_alias=True, by_name=True
182
+ )
179
183
 
180
184
  except Exception as e:
181
185
  self._logger.error(f"Error detecting api calls from user message: {e}")
@@ -211,7 +215,9 @@ class HumanVerificationManagerForApiCalling(
211
215
  modifiable_dict = payload_dict
212
216
 
213
217
  modifiable_params = self._modifiable_payload_params_model.model_validate(
214
- modifiable_dict
218
+ modifiable_dict,
219
+ by_alias=True,
220
+ by_name=True,
215
221
  )
216
222
  api_call = self._verification_model(
217
223
  modifiable_params=modifiable_params,
@@ -226,10 +232,12 @@ class HumanVerificationManagerForApiCalling(
226
232
  api_call_as_json=api_call.model_dump_json(indent=2)
227
233
  )
228
234
 
229
- def create_assistant_confirmation_message(self, *, payload: PayloadType) -> str:
235
+ def create_assistant_confirmation_message(
236
+ self, *, payload: PayloadType, button_text: str = "Confirm"
237
+ ) -> str:
230
238
  return ASSISTANT_CONFIRMATION_MESSAGE_JINJA2_TEMPLATE.render(
231
239
  api_call_as_markdown_table=dict_to_markdown_table(payload.model_dump()),
232
- button_text="Please confirm the call by pressing this button.",
240
+ button_text=button_text,
233
241
  next_user_message=self._create_next_user_message(payload),
234
242
  )
235
243
 
@@ -255,7 +263,11 @@ class HumanVerificationManagerForApiCalling(
255
263
  context=context,
256
264
  **params,
257
265
  )
258
- return self._operation.handle_response(response)
266
+ try:
267
+ return self._operation.handle_response(response)
268
+ except ValidationError as e:
269
+ self._logger.error(f"Error calling api: {e}. Response: {response}")
270
+ raise e
259
271
 
260
272
 
261
273
  if __name__ == "__main__":
@@ -3,6 +3,7 @@ This module provides a minimal framework for building endpoint classes such that
3
3
  the endpoints without having to know the details of the endpoints.
4
4
  """
5
5
 
6
+ import warnings
6
7
  from enum import StrEnum
7
8
  from string import Template
8
9
  from typing import (
@@ -15,7 +16,7 @@ from typing import (
15
16
  cast,
16
17
  )
17
18
 
18
- from pydantic import BaseModel
19
+ from pydantic import BaseModel, ValidationError
19
20
 
20
21
  # Paramspecs
21
22
  PayloadParamSpec = ParamSpec("PayloadParamSpec")
@@ -46,6 +47,42 @@ class HttpMethods(StrEnum):
46
47
  EndpointMethods = HttpMethods
47
48
 
48
49
 
50
+ class ResponseValidationException(Exception):
51
+ """
52
+ This exception is raised when the response validation fails.
53
+ """
54
+
55
+ def __init__(
56
+ self, operation_name: str, response: dict[str, Any], pydantic_error: str
57
+ ):
58
+ super().__init__(
59
+ f"Response validation failed for operation {operation_name}\n"
60
+ f"Response: {response}\n"
61
+ f"Pydantic error: {pydantic_error}"
62
+ )
63
+ self._operation_name = operation_name
64
+ self._response = response
65
+ self._pydantic_error = pydantic_error
66
+
67
+ @property
68
+ def operation_name(self) -> str:
69
+ return self._operation_name
70
+
71
+ @property
72
+ def response(self) -> dict[str, Any]:
73
+ return self._response
74
+
75
+ @property
76
+ def pydantic_error(self) -> str:
77
+ return self._pydantic_error
78
+
79
+ def __str__(self):
80
+ return (
81
+ f"Response validation failed for {self._operation_name}\n"
82
+ + f"Response: {self._response}"
83
+ )
84
+
85
+
49
86
  class ApiOperationProtocol(
50
87
  Protocol,
51
88
  Generic[
@@ -56,12 +93,21 @@ class ApiOperationProtocol(
56
93
  ResponseType,
57
94
  ],
58
95
  ):
96
+ @staticmethod
97
+ def path_dump_options() -> dict[str, Any]: ...
98
+
59
99
  @staticmethod
60
100
  def path_params_model() -> type[PathParamsType]: ...
61
101
 
102
+ @staticmethod
103
+ def payload_dump_options() -> dict[str, Any]: ...
104
+
62
105
  @staticmethod
63
106
  def payload_model() -> type[PayloadType]: ...
64
107
 
108
+ @staticmethod
109
+ def response_validate_options() -> dict[str, Any]: ...
110
+
65
111
  @staticmethod
66
112
  def response_model() -> type[ResponseType]: ...
67
113
 
@@ -71,17 +117,24 @@ class ApiOperationProtocol(
71
117
  ) -> str: ...
72
118
 
73
119
  @staticmethod
74
- def create_path_from_model(path_params: PathParamsType) -> str: ...
120
+ def create_path_from_model(
121
+ path_params: PathParamsType, *, model_dump_options: dict | None = None
122
+ ) -> str: ...
123
+
75
124
  @staticmethod
76
125
  def create_payload(
77
126
  *args: PayloadParamSpec.args, **kwargs: PayloadParamSpec.kwargs
78
127
  ) -> dict[str, Any]: ...
79
128
 
80
129
  @staticmethod
81
- def create_payload_from_model(payload: PayloadType) -> dict[str, Any]: ...
130
+ def create_payload_from_model(
131
+ payload: PayloadType, *, model_dump_options: dict | None = None
132
+ ) -> dict[str, Any]: ...
82
133
 
83
134
  @staticmethod
84
- def handle_response(response: dict[str, Any]) -> ResponseType: ...
135
+ def handle_response(
136
+ response: dict[str, Any], *, model_validate_options: dict | None = None
137
+ ) -> ResponseType: ...
85
138
 
86
139
  @staticmethod
87
140
  def request_method() -> EndpointMethods: ...
@@ -100,7 +153,10 @@ def build_api_operation(
100
153
  path_params_constructor: Callable[PathParamsSpec, PathParamsType],
101
154
  payload_constructor: Callable[PayloadParamSpec, PayloadType],
102
155
  response_model_type: type[ResponseType],
103
- dump_options: dict | None = None,
156
+ payload_dump_options: dict | None = None,
157
+ path_dump_options: dict | None = None,
158
+ response_validate_options: dict | None = None,
159
+ dump_options: dict | None = None, # Deprecated
104
160
  ) -> type[
105
161
  ApiOperationProtocol[
106
162
  PathParamsSpec,
@@ -126,16 +182,33 @@ def build_api_operation(
126
182
  "by_alias": True,
127
183
  "exclude_defaults": True,
128
184
  }
185
+ else:
186
+ warnings.warn(
187
+ "dump_options is deprecated. Use payload_dump_options instead.",
188
+ DeprecationWarning,
189
+ )
129
190
 
130
191
  class Operation(ApiOperationProtocol):
192
+ @staticmethod
193
+ def path_dump_options() -> dict[str, Any]:
194
+ return path_dump_options or {}
195
+
131
196
  @staticmethod
132
197
  def path_params_model() -> type[PathParamsType]:
133
198
  return cast(type[PathParamsType], path_params_constructor)
134
199
 
200
+ @staticmethod
201
+ def payload_dump_options() -> dict[str, Any]:
202
+ return payload_dump_options or {}
203
+
135
204
  @staticmethod
136
205
  def payload_model() -> type[PayloadType]:
137
206
  return cast(type[PayloadType], payload_constructor)
138
207
 
208
+ @staticmethod
209
+ def response_validate_options() -> dict[str, Any]:
210
+ return response_validate_options or {}
211
+
139
212
  @staticmethod
140
213
  def response_model() -> type[ResponseType]:
141
214
  return response_model_type
@@ -145,8 +218,18 @@ def build_api_operation(
145
218
  return path_template
146
219
 
147
220
  @staticmethod
148
- def create_path_from_model(path_params: PathParamsType) -> str:
149
- return path_template.substitute(**path_params.model_dump(**dump_options))
221
+ def create_path_from_model(
222
+ path_params: PathParamsType, *, model_dump_options: dict | None = None
223
+ ) -> str:
224
+ if model_dump_options is None:
225
+ if path_dump_options is None:
226
+ model_dump_options = dump_options
227
+ else:
228
+ model_dump_options = path_dump_options
229
+
230
+ return path_template.substitute(
231
+ **path_params.model_dump(**model_dump_options)
232
+ )
150
233
 
151
234
  @staticmethod
152
235
  def create_path(
@@ -157,19 +240,51 @@ def build_api_operation(
157
240
 
158
241
  @staticmethod
159
242
  def create_payload(
160
- *args: PayloadParamSpec.args, **kwargs: PayloadParamSpec.kwargs
243
+ *args: PayloadParamSpec.args,
244
+ **kwargs: PayloadParamSpec.kwargs,
161
245
  ) -> dict[str, Any]:
162
246
  """Create request body payload."""
247
+ if payload_dump_options is None:
248
+ model_dump_options = dump_options
249
+ else:
250
+ model_dump_options = payload_dump_options
251
+
163
252
  request_model = Operation.payload_model()(*args, **kwargs)
164
- return request_model.model_dump(**dump_options)
253
+ return request_model.model_dump(**model_dump_options)
165
254
 
166
255
  @staticmethod
167
- def create_payload_from_model(payload: PayloadType) -> dict[str, Any]:
168
- return payload.model_dump(**dump_options)
256
+ def create_payload_from_model(
257
+ payload: PayloadType, *, model_dump_options: dict | None = None
258
+ ) -> dict[str, Any]:
259
+ if model_dump_options is None:
260
+ if payload_dump_options is None:
261
+ model_dump_options = dump_options
262
+ else:
263
+ model_dump_options = payload_dump_options
264
+
265
+ return payload.model_dump(**model_dump_options)
169
266
 
170
267
  @staticmethod
171
- def handle_response(response: dict[str, Any]) -> ResponseType:
172
- return Operation.response_model().model_validate(response)
268
+ def handle_response(
269
+ response: dict[str, Any],
270
+ *,
271
+ model_validate_options: dict[str, Any] | None = None,
272
+ ) -> ResponseType:
273
+ if model_validate_options is None:
274
+ if response_validate_options is None:
275
+ model_validate_options = {}
276
+ else:
277
+ model_validate_options = response_validate_options
278
+ try:
279
+ return Operation.response_model().model_validate(
280
+ response, **model_validate_options
281
+ )
282
+ except ValidationError as e:
283
+ raise ResponseValidationException(
284
+ operation_name=Operation.__name__,
285
+ response=response,
286
+ pydantic_error=str(e),
287
+ ) from e
173
288
 
174
289
  @staticmethod
175
290
  def request_method() -> HttpMethods:
@@ -86,7 +86,10 @@ def build_fake_requestor(
86
86
  f"Invalid parameters passed to combined model {combined_model.__name__}: {e}"
87
87
  )
88
88
 
89
- return cls._operation.handle_response(return_value)
89
+ return cls._operation.handle_response(
90
+ return_value,
91
+ model_validate_options=cls._operation.response_validate_options(),
92
+ )
90
93
 
91
94
  @classmethod
92
95
  async def request_async(
@@ -132,11 +135,15 @@ def build_request_requestor(
132
135
  combined=kwargs
133
136
  )
134
137
 
135
- path = cls._operation.create_path_from_model(path_params)
138
+ path = cls._operation.create_path_from_model(
139
+ path_params, model_dump_options=cls._operation.path_dump_options()
140
+ )
136
141
  url = urljoin(context.base_url, path)
137
142
  _verify_url(url)
138
143
 
139
- payload = cls._operation.create_payload_from_model(payload_model)
144
+ payload = cls._operation.create_payload_from_model(
145
+ payload_model, model_dump_options=cls._operation.payload_dump_options()
146
+ )
140
147
 
141
148
  response = requests.request(
142
149
  method=cls._operation.request_method(),
@@ -144,7 +151,13 @@ def build_request_requestor(
144
151
  headers=context.headers,
145
152
  json=payload,
146
153
  )
147
- return cls._operation.handle_response(response.json())
154
+
155
+ response_json = response.json()
156
+
157
+ return cls._operation.handle_response(
158
+ response_json,
159
+ model_validate_options=cls._operation.response_validate_options(),
160
+ )
148
161
 
149
162
  @classmethod
150
163
  async def request_async(
@@ -191,7 +204,9 @@ def build_httpx_requestor(
191
204
  combined=kwargs
192
205
  )
193
206
 
194
- path = cls._operation.create_path_from_model(path_params)
207
+ path = cls._operation.create_path_from_model(
208
+ path_params, model_dump_options=cls._operation.path_dump_options()
209
+ )
195
210
  url = urljoin(context.base_url, path)
196
211
  _verify_url(url)
197
212
  with httpx.Client() as client:
@@ -199,9 +214,16 @@ def build_httpx_requestor(
199
214
  method=cls._operation.request_method(),
200
215
  url=url,
201
216
  headers=headers,
202
- json=cls._operation.create_payload_from_model(payload_model),
217
+ json=cls._operation.create_payload_from_model(
218
+ payload_model,
219
+ model_dump_options=cls._operation.payload_dump_options(),
220
+ ),
221
+ )
222
+ response_json = response.json()
223
+ return cls._operation.handle_response(
224
+ response_json,
225
+ model_validate_options=cls._operation.response_validate_options(),
203
226
  )
204
- return cls._operation.handle_response(response.json())
205
227
 
206
228
  @classmethod
207
229
  async def request_async(
@@ -216,7 +238,9 @@ def build_httpx_requestor(
216
238
  combined=kwargs
217
239
  )
218
240
 
219
- path = cls._operation.create_path_from_model(path_params)
241
+ path = cls._operation.create_path_from_model(
242
+ path_params, model_dump_options=cls._operation.path_dump_options()
243
+ )
220
244
  url = urljoin(context.base_url, path)
221
245
  _verify_url(url)
222
246
  async with httpx.AsyncClient() as client:
@@ -224,9 +248,16 @@ def build_httpx_requestor(
224
248
  method=cls._operation.request_method(),
225
249
  url=url,
226
250
  headers=headers,
227
- json=cls._operation.create_payload_from_model(payload_model),
251
+ json=cls._operation.create_payload_from_model(
252
+ payload_model,
253
+ model_dump_options=cls._operation.payload_dump_options(),
254
+ ),
255
+ )
256
+ response_json = response.json()
257
+ return cls._operation.handle_response(
258
+ response_json,
259
+ model_validate_options=cls._operation.response_validate_options(),
228
260
  )
229
- return cls._operation.handle_response(response.json())
230
261
 
231
262
  return HttpxRequestor
232
263
 
@@ -272,7 +303,9 @@ def build_aiohttp_requestor(
272
303
  path_params, payload_model = cls._operation.models_from_combined(
273
304
  combined=kwargs
274
305
  )
275
- path = cls._operation.create_path_from_model(path_params)
306
+ path = cls._operation.create_path_from_model(
307
+ path_params, model_dump_options=cls._operation.path_dump_options()
308
+ )
276
309
  url = urljoin(context.base_url, path)
277
310
  _verify_url(url)
278
311
 
@@ -281,9 +314,16 @@ def build_aiohttp_requestor(
281
314
  method=cls._operation.request_method(),
282
315
  url=url,
283
316
  headers=headers,
284
- json=cls._operation.create_payload_from_model(payload_model),
317
+ json=cls._operation.create_payload_from_model(
318
+ payload=payload_model,
319
+ model_dump_options=cls._operation.payload_dump_options(),
320
+ ),
321
+ )
322
+ response_json = await response.json()
323
+ return cls._operation.handle_response(
324
+ response=response_json,
325
+ model_validate_options=cls._operation.response_validate_options(),
285
326
  )
286
- return cls._operation.handle_response(await response.json())
287
327
 
288
328
  return AiohttpRequestor
289
329
 
@@ -367,7 +407,7 @@ if __name__ == "__main__":
367
407
 
368
408
  # Note that the return value is a pydantic UserResponse object
369
409
  response = FakeUserRequestor().request(
370
- context=RequestContext(headers={"a": "b"}),
410
+ context=RequestContext(base_url="https://example.com", headers={"a": "b"}),
371
411
  user_id=123,
372
412
  include_profile=True,
373
413
  )
@@ -379,7 +419,9 @@ if __name__ == "__main__":
379
419
 
380
420
  # Check type hints
381
421
  response = RequestRequestor().request(
382
- context=RequestContext(headers={"a": "b"}), user_id=123, include_profile=True
422
+ context=RequestContext(base_url="https://example.com", headers={"a": "b"}),
423
+ user_id=123,
424
+ include_profile=True,
383
425
  )
384
426
 
385
427
  print(response.model_dump())
@@ -35,3 +35,23 @@ class CommonException(Exception):
35
35
 
36
36
  class ConfigurationException(Exception):
37
37
  pass
38
+
39
+
40
+ class InfoExceptionForAi(Exception):
41
+ """
42
+ This exception is raised as information to the AI.
43
+ Such that it can be used to inform the user about the error.
44
+ In a meaningful way.
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ error_message: str,
50
+ message_for_ai: str,
51
+ ):
52
+ super().__init__(error_message)
53
+ self._message_for_ai = message_for_ai
54
+
55
+ @property
56
+ def message_for_ai(self):
57
+ return self._message_for_ai
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_toolkit
3
- Version: 1.19.0
3
+ Version: 1.19.1
4
4
  Summary:
5
5
  License: Proprietary
6
6
  Author: Cedric Klinkert
@@ -118,6 +118,10 @@ All notable changes to this project will be documented in this file.
118
118
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
119
119
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
120
120
 
121
+ ## [1.19.1] - 2025-10-29
122
+ - Make api operations more flexible
123
+ - Make verification button text adaptable
124
+
121
125
 
122
126
  ## [1.19.0] - 2025-10-28
123
127
  - Enable additional headers on openai and langchain client
@@ -1,7 +1,7 @@
1
1
  unique_toolkit/__init__.py,sha256=qrQ0kgAZnmGR6-UpWOpAL4yd-2ic5Jjwh6s8et-7ZTc,1372
2
2
  unique_toolkit/_common/_base_service.py,sha256=S8H0rAebx7GsOldA7xInLp3aQJt9yEPDQdsGSFRJsGg,276
3
3
  unique_toolkit/_common/_time_utils.py,sha256=ztmTovTvr-3w71Ns2VwXC65OKUUh-sQlzbHdKTQWm-w,135
4
- unique_toolkit/_common/api_calling/human_verification_manager.py,sha256=AeGzaGYlo8RHwEp7jhWM7gdx32dRyLUIioZRfrVbgUI,11905
4
+ unique_toolkit/_common/api_calling/human_verification_manager.py,sha256=cM_sS0BqnKhbgXMw90QGi140JPbTP5P6MsyR92SnLt8,12169
5
5
  unique_toolkit/_common/base_model_type_attribute.py,sha256=7rzVqjXa0deYEixeo_pJSJcQ7nKXpWK_UGpOiEH3yZY,10382
6
6
  unique_toolkit/_common/chunk_relevancy_sorter/config.py,sha256=tHETuMIC4CA_TPwU0oaHbckaKhvMFMYdO_d4lNRKnRc,1806
7
7
  unique_toolkit/_common/chunk_relevancy_sorter/exception.py,sha256=1mY4zjbvnXsd5oIxwiVsma09bS2XRnHrxW8KJBGtgCM,126
@@ -9,9 +9,9 @@ unique_toolkit/_common/chunk_relevancy_sorter/schemas.py,sha256=YAyvXzVk8h5q6FEs
9
9
  unique_toolkit/_common/chunk_relevancy_sorter/service.py,sha256=ZX1pxcy53zh3Ha0_pN6yYIbMX1acRxcvqKTPTKpGKwA,13938
10
10
  unique_toolkit/_common/chunk_relevancy_sorter/tests/test_service.py,sha256=giD9b5W8A0xP18dZCcrLUruoGi38BeBvPnO1phY7Sp0,8892
11
11
  unique_toolkit/_common/default_language_model.py,sha256=XCZu6n270QkxEeTpj5NZJda6Ok_IR-GcS8w30DU21aI,343
12
- unique_toolkit/_common/endpoint_builder.py,sha256=09Y8rC83oNsmxBLAf7WiHqK_OjZmeKb82TPzF0SLSVM,5499
13
- unique_toolkit/_common/endpoint_requestor.py,sha256=3B7ekfiTmgCEc2h1_m94DsfPwigE2yfiFUmbAAdjuVc,12039
14
- unique_toolkit/_common/exception.py,sha256=hwh60UUawHDyPFNs-Wom-Gc6Yb09gPelftAuW1tXE6o,779
12
+ unique_toolkit/_common/endpoint_builder.py,sha256=Ge83hyipUbh4fhxzA5XVeg3cj6T4JU4dL8Ue9MWElcw,9100
13
+ unique_toolkit/_common/endpoint_requestor.py,sha256=pY2SlRaHmHftNpPybmzxRsn4OBZCj_7niCMehL8I5O0,13744
14
+ unique_toolkit/_common/exception.py,sha256=ho0uBcPeZXU2w15IrSBhO5w7KUgxp1HcKAQrf2uin-w,1243
15
15
  unique_toolkit/_common/feature_flags/schema.py,sha256=F1NdVJFNU8PKlS7bYzrIPeDu2LxRqHSM9pyw622a1Kk,547
16
16
  unique_toolkit/_common/pydantic/rjsf_tags.py,sha256=T3AZIF8wny3fFov66s258nEl1GqfKevFouTtG6k9PqU,31219
17
17
  unique_toolkit/_common/pydantic_helpers.py,sha256=1zzg6PlzSkHqPTdX-KoBaDHmBeeG7S5PprBsyMSCEuU,4806
@@ -165,7 +165,7 @@ unique_toolkit/short_term_memory/service.py,sha256=5PeVBu1ZCAfyDb2HLVvlmqSbyzBBu
165
165
  unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
166
166
  unique_toolkit/smart_rules/compile.py,sha256=Ozhh70qCn2yOzRWr9d8WmJeTo7AQurwd3tStgBMPFLA,1246
167
167
  unique_toolkit/test_utilities/events.py,sha256=_mwV2bs5iLjxS1ynDCjaIq-gjjKhXYCK-iy3dRfvO3g,6410
168
- unique_toolkit-1.19.0.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
169
- unique_toolkit-1.19.0.dist-info/METADATA,sha256=vFbZDvCyQYheZ7FvASbBZh5M7PqjI4GoRVqDbGkcAKY,38772
170
- unique_toolkit-1.19.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
171
- unique_toolkit-1.19.0.dist-info/RECORD,,
168
+ unique_toolkit-1.19.1.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
169
+ unique_toolkit-1.19.1.dist-info/METADATA,sha256=Dh7RpQJ3P5uPe9kN7mfhkBY5I0lgSC7aK4uLJVJBsEI,38876
170
+ unique_toolkit-1.19.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
171
+ unique_toolkit-1.19.1.dist-info/RECORD,,