unique_toolkit 1.16.1__py3-none-any.whl → 1.16.2__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.
@@ -280,9 +280,9 @@ if __name__ == "__main__":
280
280
  class CombinedParams(GetUserPathParams, GetUserRequestBody):
281
281
  pass
282
282
 
283
- UserEndpoint = build_api_operation(
283
+ UserApiOperation = build_api_operation(
284
284
  method=EndpointMethods.GET,
285
- url_template=Template("https://api.example.com/users/{user_id}"),
285
+ path_template=Template("/users/{user_id}"),
286
286
  path_params_constructor=GetUserPathParams,
287
287
  payload_constructor=GetUserRequestBody,
288
288
  response_model_type=UserResponse,
@@ -290,7 +290,7 @@ if __name__ == "__main__":
290
290
 
291
291
  human_verification_manager = HumanVerificationManagerForApiCalling(
292
292
  logger=logging.getLogger(__name__),
293
- operation=UserEndpoint,
293
+ operation=UserApiOperation,
294
294
  requestor_type=RequestorType.FAKE,
295
295
  return_value={"id": 100, "name": "John Doe"},
296
296
  )
@@ -4,7 +4,7 @@ the endpoints without having to know the details of the endpoints.
4
4
  """
5
5
 
6
6
  from enum import StrEnum
7
- from string import Formatter, Template
7
+ from string import Template
8
8
  from typing import (
9
9
  Any,
10
10
  Callable,
@@ -16,7 +16,6 @@ from typing import (
16
16
  )
17
17
 
18
18
  from pydantic import BaseModel
19
- from typing_extensions import deprecated
20
19
 
21
20
  # Paramspecs
22
21
  PayloadParamSpec = ParamSpec("PayloadParamSpec")
@@ -67,13 +66,12 @@ class ApiOperationProtocol(
67
66
  def response_model() -> type[ResponseType]: ...
68
67
 
69
68
  @staticmethod
70
- def create_url(
69
+ def create_path(
71
70
  *args: PathParamsSpec.args, **kwargs: PathParamsSpec.kwargs
72
71
  ) -> str: ...
73
72
 
74
73
  @staticmethod
75
- def create_url_from_model(path_params: PathParamsType) -> str: ...
76
-
74
+ def create_path_from_model(path_params: PathParamsType) -> str: ...
77
75
  @staticmethod
78
76
  def create_payload(
79
77
  *args: PayloadParamSpec.args, **kwargs: PayloadParamSpec.kwargs
@@ -98,7 +96,7 @@ class ApiOperationProtocol(
98
96
  def build_api_operation(
99
97
  *,
100
98
  method: HttpMethods,
101
- url_template: Template,
99
+ path_template: Template,
102
100
  path_params_constructor: Callable[PathParamsSpec, PathParamsType],
103
101
  payload_constructor: Callable[PayloadParamSpec, PayloadType],
104
102
  response_model_type: type[ResponseType],
@@ -143,33 +141,19 @@ def build_api_operation(
143
141
  return response_model_type
144
142
 
145
143
  @staticmethod
146
- def create_url(
147
- *args: PathParamsSpec.args,
148
- **kwargs: PathParamsSpec.kwargs,
149
- ) -> str:
150
- """Create URL from path parameters."""
151
- path_model = Operation.path_params_model()(*args, **kwargs)
152
- path_dict = path_model.model_dump(**dump_options)
153
-
154
- # Extract expected path parameters from template
155
- template_params = [
156
- fname
157
- for _, fname, _, _ in Formatter().parse(url_template.template)
158
- if fname is not None
159
- ]
160
-
161
- # Verify all required path parameters are present
162
- missing_params = [
163
- param for param in template_params if param not in path_dict
164
- ]
165
- if missing_params:
166
- raise ValueError(f"Missing path parameters: {missing_params}")
167
-
168
- return url_template.substitute(**path_dict)
144
+ def path_template() -> Template:
145
+ return path_template
169
146
 
170
147
  @staticmethod
171
- def create_url_from_model(path_params: PathParamsType) -> str:
172
- return url_template.substitute(**path_params.model_dump(**dump_options))
148
+ def create_path_from_model(path_params: PathParamsType) -> str:
149
+ return path_template.substitute(**path_params.model_dump(**dump_options))
150
+
151
+ @staticmethod
152
+ def create_path(
153
+ *args: PathParamsSpec.args, **kwargs: PathParamsSpec.kwargs
154
+ ) -> str:
155
+ model = Operation.path_params_model()(*args, **kwargs)
156
+ return Operation.create_path_from_model(model)
173
157
 
174
158
  @staticmethod
175
159
  def create_payload(
@@ -204,81 +188,3 @@ def build_api_operation(
204
188
  return path_params, payload
205
189
 
206
190
  return Operation
207
-
208
-
209
- @deprecated("Use ApiOperationProtocol instead")
210
- class EndpointClassProtocol(ApiOperationProtocol):
211
- pass
212
-
213
-
214
- @deprecated("Use build_api_operation instead")
215
- def build_endpoint_class(
216
- *,
217
- method: HttpMethods,
218
- url_template: Template,
219
- path_params_constructor: Callable[PathParamsSpec, PathParamsType],
220
- payload_constructor: Callable[PayloadParamSpec, PayloadType],
221
- response_model_type: type[ResponseType],
222
- dump_options: dict | None = None,
223
- ) -> type[
224
- ApiOperationProtocol[
225
- PathParamsSpec,
226
- PathParamsType,
227
- PayloadParamSpec,
228
- PayloadType,
229
- ResponseType,
230
- ]
231
- ]:
232
- return build_api_operation(
233
- method=method,
234
- url_template=url_template,
235
- path_params_constructor=path_params_constructor,
236
- payload_constructor=payload_constructor,
237
- response_model_type=response_model_type,
238
- dump_options=dump_options,
239
- )
240
-
241
-
242
- if __name__ == "__main__":
243
- # Example models
244
- class GetUserPathParams(BaseModel):
245
- """Path parameters for the user endpoint."""
246
-
247
- user_id: int
248
-
249
- class GetUserRequestBody(BaseModel):
250
- """Request body/query parameters for the user endpoint."""
251
-
252
- include_profile: bool = False
253
-
254
- class UserResponse(BaseModel):
255
- """Response model for user data."""
256
-
257
- id: int
258
- name: str
259
-
260
- # Example usage of make_endpoint_class
261
- UserOperation = build_endpoint_class(
262
- url_template=Template("/users/${user_id}"),
263
- path_params_constructor=GetUserPathParams,
264
- payload_constructor=GetUserRequestBody,
265
- response_model_type=UserResponse,
266
- method=EndpointMethods.GET,
267
- )
268
-
269
- # Create URL from path parameters
270
- url = UserOperation.create_url(user_id=123)
271
- print(f"URL: {url}")
272
-
273
- # Create payload from request body parameters
274
- payload = UserOperation.create_payload(include_profile=True)
275
- print(f"Payload: {payload}")
276
-
277
- # Create response from endpoint
278
- response = UserOperation.handle_response(
279
- {
280
- "id": 123,
281
- "name": "John Doe",
282
- }
283
- )
284
- print(f"Response: {response}")
@@ -2,7 +2,7 @@ from enum import StrEnum
2
2
  from typing import Any, Callable, Generic, Protocol, TypeVar
3
3
  from urllib.parse import urljoin, urlparse
4
4
 
5
- from pydantic import BaseModel
5
+ from pydantic import BaseModel, Field
6
6
  from typing_extensions import ParamSpec
7
7
 
8
8
  from unique_toolkit._common.endpoint_builder import (
@@ -25,25 +25,15 @@ CombinedParamsType = TypeVar("CombinedParamsType", bound=BaseModel)
25
25
  ResponseT_co = TypeVar("ResponseT_co", bound=BaseModel, covariant=True)
26
26
 
27
27
 
28
- def _construct_full_url(base_url: str, url: str) -> str:
29
- """
30
- Construct full URL from base_url and url.
31
- If base_url is provided and url is absolute, strip the scheme/netloc from url.
32
- """
33
- if not base_url:
34
- return url
35
-
36
- parsed = urlparse(url)
37
- if parsed.scheme:
38
- # URL is absolute, extract only path + query + fragment
39
- url = parsed._replace(scheme="", netloc="").geturl()
28
+ class RequestContext(BaseModel):
29
+ base_url: str
30
+ headers: dict[str, str] = Field(default_factory=dict)
40
31
 
41
- return urljoin(base_url, url)
42
32
 
43
-
44
- class RequestContext(BaseModel):
45
- base_url: str = ""
46
- headers: dict[str, str] | None = None
33
+ def _verify_url(url: str) -> None:
34
+ parse_result = urlparse(url)
35
+ if not (parse_result.netloc and parse_result.scheme):
36
+ raise ValueError("Scheme and netloc are required for url")
47
37
 
48
38
 
49
39
  class EndpointRequestorProtocol(Protocol, Generic[CombinedParamsSpec, ResponseT_co]):
@@ -142,12 +132,15 @@ def build_request_requestor(
142
132
  combined=kwargs
143
133
  )
144
134
 
145
- url = cls._operation.create_url_from_model(path_params)
135
+ path = cls._operation.create_path_from_model(path_params)
136
+ url = urljoin(context.base_url, path)
137
+ _verify_url(url)
138
+
146
139
  payload = cls._operation.create_payload_from_model(payload_model)
147
140
 
148
141
  response = requests.request(
149
142
  method=cls._operation.request_method(),
150
- url=_construct_full_url(context.base_url, url),
143
+ url=url,
151
144
  headers=context.headers,
152
145
  json=payload,
153
146
  )
@@ -198,13 +191,13 @@ def build_httpx_requestor(
198
191
  combined=kwargs
199
192
  )
200
193
 
194
+ path = cls._operation.create_path_from_model(path_params)
195
+ url = urljoin(context.base_url, path)
196
+ _verify_url(url)
201
197
  with httpx.Client() as client:
202
198
  response = client.request(
203
199
  method=cls._operation.request_method(),
204
- url=_construct_full_url(
205
- base_url=context.base_url,
206
- url=cls._operation.create_url_from_model(path_params),
207
- ),
200
+ url=url,
208
201
  headers=headers,
209
202
  json=cls._operation.create_payload_from_model(payload_model),
210
203
  )
@@ -223,13 +216,13 @@ def build_httpx_requestor(
223
216
  combined=kwargs
224
217
  )
225
218
 
219
+ path = cls._operation.create_path_from_model(path_params)
220
+ url = urljoin(context.base_url, path)
221
+ _verify_url(url)
226
222
  async with httpx.AsyncClient() as client:
227
223
  response = await client.request(
228
224
  method=cls._operation.request_method(),
229
- url=_construct_full_url(
230
- base_url=context.base_url,
231
- url=cls._operation.create_url_from_model(path_params),
232
- ),
225
+ url=url,
233
226
  headers=headers,
234
227
  json=cls._operation.create_payload_from_model(payload_model),
235
228
  )
@@ -279,14 +272,14 @@ def build_aiohttp_requestor(
279
272
  path_params, payload_model = cls._operation.models_from_combined(
280
273
  combined=kwargs
281
274
  )
275
+ path = cls._operation.create_path_from_model(path_params)
276
+ url = urljoin(context.base_url, path)
277
+ _verify_url(url)
282
278
 
283
279
  async with aiohttp.ClientSession() as session:
284
280
  response = await session.request(
285
281
  method=cls._operation.request_method(),
286
- url=_construct_full_url(
287
- base_url=context.base_url,
288
- url=cls._operation.create_url_from_model(path_params),
289
- ),
282
+ url=url,
290
283
  headers=headers,
291
284
  json=cls._operation.create_payload_from_model(payload_model),
292
285
  )
@@ -360,7 +353,7 @@ if __name__ == "__main__":
360
353
 
361
354
  UserEndpoint = build_api_operation(
362
355
  method=HttpMethods.GET,
363
- url_template=Template("https://api.example.com/users/{user_id}"),
356
+ path_template=Template("/users/{user_id}"),
364
357
  path_params_constructor=GetUserPathParams,
365
358
  payload_constructor=GetUserRequestBody,
366
359
  response_model_type=UserResponse,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_toolkit
3
- Version: 1.16.1
3
+ Version: 1.16.2
4
4
  Summary:
5
5
  License: Proprietary
6
6
  Author: Cedric Klinkert
@@ -117,6 +117,8 @@ All notable changes to this project will be documented in this file.
117
117
 
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
+ ## [1.16.2] - 2025-10-16
121
+ - Reduce operation dependency on path instead of full url
120
122
 
121
123
  ## [1.16.1] - 2025-10-16
122
124
  - Update debug info for better tool call tracking
@@ -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=ZgWThDHdTNFmgNdE4dwR7L7qPV73nSe5wOLpcB1QbWM,11919
4
+ unique_toolkit/_common/api_calling/human_verification_manager.py,sha256=AeGzaGYlo8RHwEp7jhWM7gdx32dRyLUIioZRfrVbgUI,11905
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,8 +9,8 @@ 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=WzJrJ7azUQhvQRd-vsFFoyj6omJpHiVYrh1UFxNQvVg,8242
13
- unique_toolkit/_common/endpoint_requestor.py,sha256=7rDpeEvmQbLtn3iNW8NftyvAqDkLFGHoYN1h5AFDxho,12319
12
+ unique_toolkit/_common/endpoint_builder.py,sha256=09Y8rC83oNsmxBLAf7WiHqK_OjZmeKb82TPzF0SLSVM,5499
13
+ unique_toolkit/_common/endpoint_requestor.py,sha256=3B7ekfiTmgCEc2h1_m94DsfPwigE2yfiFUmbAAdjuVc,12039
14
14
  unique_toolkit/_common/exception.py,sha256=hwh60UUawHDyPFNs-Wom-Gc6Yb09gPelftAuW1tXE6o,779
15
15
  unique_toolkit/_common/feature_flags/schema.py,sha256=F1NdVJFNU8PKlS7bYzrIPeDu2LxRqHSM9pyw622a1Kk,547
16
16
  unique_toolkit/_common/pydantic/rjsf_tags.py,sha256=T3AZIF8wny3fFov66s258nEl1GqfKevFouTtG6k9PqU,31219
@@ -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.16.1.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
169
- unique_toolkit-1.16.1.dist-info/METADATA,sha256=bgeCiumOrLKNtqV3xQoDz7Yktvb0z_W8EJnv6pTUVc4,37517
170
- unique_toolkit-1.16.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
171
- unique_toolkit-1.16.1.dist-info/RECORD,,
168
+ unique_toolkit-1.16.2.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
169
+ unique_toolkit-1.16.2.dist-info/METADATA,sha256=x7WrizUo_LYUUzuW5XwaOATe89GBQpT0v2_KQUlAXAo,37600
170
+ unique_toolkit-1.16.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
171
+ unique_toolkit-1.16.2.dist-info/RECORD,,