magic_hour 0.8.0__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 magic_hour might be problematic. Click here for more details.

Files changed (131) hide show
  1. magic_hour/__init__.py +6 -0
  2. magic_hour/client.py +61 -0
  3. magic_hour/core/__init__.py +53 -0
  4. magic_hour/core/api_error.py +48 -0
  5. magic_hour/core/auth.py +314 -0
  6. magic_hour/core/base_client.py +600 -0
  7. magic_hour/core/binary_response.py +23 -0
  8. magic_hour/core/request.py +158 -0
  9. magic_hour/core/response.py +293 -0
  10. magic_hour/core/type_utils.py +28 -0
  11. magic_hour/core/utils.py +38 -0
  12. magic_hour/environment.py +6 -0
  13. magic_hour/resources/v1/__init__.py +4 -0
  14. magic_hour/resources/v1/ai_clothes_changer/README.md +41 -0
  15. magic_hour/resources/v1/ai_clothes_changer/__init__.py +4 -0
  16. magic_hour/resources/v1/ai_clothes_changer/client.py +129 -0
  17. magic_hour/resources/v1/ai_headshot_generator/README.md +31 -0
  18. magic_hour/resources/v1/ai_headshot_generator/__init__.py +4 -0
  19. magic_hour/resources/v1/ai_headshot_generator/client.py +119 -0
  20. magic_hour/resources/v1/ai_image_generator/README.md +37 -0
  21. magic_hour/resources/v1/ai_image_generator/__init__.py +4 -0
  22. magic_hour/resources/v1/ai_image_generator/client.py +144 -0
  23. magic_hour/resources/v1/ai_image_upscaler/README.md +37 -0
  24. magic_hour/resources/v1/ai_image_upscaler/__init__.py +4 -0
  25. magic_hour/resources/v1/ai_image_upscaler/client.py +143 -0
  26. magic_hour/resources/v1/ai_photo_editor/README.md +53 -0
  27. magic_hour/resources/v1/ai_photo_editor/__init__.py +4 -0
  28. magic_hour/resources/v1/ai_photo_editor/client.py +167 -0
  29. magic_hour/resources/v1/ai_qr_code_generator/README.md +35 -0
  30. magic_hour/resources/v1/ai_qr_code_generator/__init__.py +4 -0
  31. magic_hour/resources/v1/ai_qr_code_generator/client.py +127 -0
  32. magic_hour/resources/v1/animation/README.md +63 -0
  33. magic_hour/resources/v1/animation/__init__.py +4 -0
  34. magic_hour/resources/v1/animation/client.py +179 -0
  35. magic_hour/resources/v1/client.py +153 -0
  36. magic_hour/resources/v1/face_swap/README.md +52 -0
  37. magic_hour/resources/v1/face_swap/__init__.py +4 -0
  38. magic_hour/resources/v1/face_swap/client.py +165 -0
  39. magic_hour/resources/v1/face_swap_photo/README.md +39 -0
  40. magic_hour/resources/v1/face_swap_photo/__init__.py +4 -0
  41. magic_hour/resources/v1/face_swap_photo/client.py +127 -0
  42. magic_hour/resources/v1/files/__init__.py +4 -0
  43. magic_hour/resources/v1/files/client.py +19 -0
  44. magic_hour/resources/v1/files/upload_urls/README.md +56 -0
  45. magic_hour/resources/v1/files/upload_urls/__init__.py +4 -0
  46. magic_hour/resources/v1/files/upload_urls/client.py +142 -0
  47. magic_hour/resources/v1/image_background_remover/README.md +31 -0
  48. magic_hour/resources/v1/image_background_remover/__init__.py +4 -0
  49. magic_hour/resources/v1/image_background_remover/client.py +121 -0
  50. magic_hour/resources/v1/image_projects/README.md +63 -0
  51. magic_hour/resources/v1/image_projects/__init__.py +4 -0
  52. magic_hour/resources/v1/image_projects/client.py +177 -0
  53. magic_hour/resources/v1/image_to_video/README.md +44 -0
  54. magic_hour/resources/v1/image_to_video/__init__.py +4 -0
  55. magic_hour/resources/v1/image_to_video/client.py +165 -0
  56. magic_hour/resources/v1/lip_sync/README.md +54 -0
  57. magic_hour/resources/v1/lip_sync/__init__.py +4 -0
  58. magic_hour/resources/v1/lip_sync/client.py +177 -0
  59. magic_hour/resources/v1/text_to_video/README.md +40 -0
  60. magic_hour/resources/v1/text_to_video/__init__.py +4 -0
  61. magic_hour/resources/v1/text_to_video/client.py +150 -0
  62. magic_hour/resources/v1/video_projects/README.md +63 -0
  63. magic_hour/resources/v1/video_projects/__init__.py +4 -0
  64. magic_hour/resources/v1/video_projects/client.py +177 -0
  65. magic_hour/resources/v1/video_to_video/README.md +60 -0
  66. magic_hour/resources/v1/video_to_video/__init__.py +4 -0
  67. magic_hour/resources/v1/video_to_video/client.py +204 -0
  68. magic_hour/types/models/__init__.py +60 -0
  69. magic_hour/types/models/get_v1_image_projects_id_response.py +82 -0
  70. magic_hour/types/models/get_v1_image_projects_id_response_downloads_item.py +19 -0
  71. magic_hour/types/models/get_v1_image_projects_id_response_error.py +25 -0
  72. magic_hour/types/models/get_v1_video_projects_id_response.py +114 -0
  73. magic_hour/types/models/get_v1_video_projects_id_response_download.py +19 -0
  74. magic_hour/types/models/get_v1_video_projects_id_response_downloads_item.py +19 -0
  75. magic_hour/types/models/get_v1_video_projects_id_response_error.py +25 -0
  76. magic_hour/types/models/post_v1_ai_clothes_changer_response.py +25 -0
  77. magic_hour/types/models/post_v1_ai_headshot_generator_response.py +25 -0
  78. magic_hour/types/models/post_v1_ai_image_generator_response.py +25 -0
  79. magic_hour/types/models/post_v1_ai_image_upscaler_response.py +25 -0
  80. magic_hour/types/models/post_v1_ai_photo_editor_response.py +25 -0
  81. magic_hour/types/models/post_v1_ai_qr_code_generator_response.py +25 -0
  82. magic_hour/types/models/post_v1_animation_response.py +25 -0
  83. magic_hour/types/models/post_v1_face_swap_photo_response.py +25 -0
  84. magic_hour/types/models/post_v1_face_swap_response.py +25 -0
  85. magic_hour/types/models/post_v1_files_upload_urls_response.py +21 -0
  86. magic_hour/types/models/post_v1_files_upload_urls_response_items_item.py +31 -0
  87. magic_hour/types/models/post_v1_image_background_remover_response.py +25 -0
  88. magic_hour/types/models/post_v1_image_to_video_response.py +25 -0
  89. magic_hour/types/models/post_v1_lip_sync_response.py +25 -0
  90. magic_hour/types/models/post_v1_text_to_video_response.py +25 -0
  91. magic_hour/types/models/post_v1_video_to_video_response.py +25 -0
  92. magic_hour/types/params/__init__.py +205 -0
  93. magic_hour/types/params/post_v1_ai_clothes_changer_body.py +40 -0
  94. magic_hour/types/params/post_v1_ai_clothes_changer_body_assets.py +45 -0
  95. magic_hour/types/params/post_v1_ai_headshot_generator_body.py +40 -0
  96. magic_hour/types/params/post_v1_ai_headshot_generator_body_assets.py +28 -0
  97. magic_hour/types/params/post_v1_ai_image_generator_body.py +54 -0
  98. magic_hour/types/params/post_v1_ai_image_generator_body_style.py +28 -0
  99. magic_hour/types/params/post_v1_ai_image_upscaler_body.py +54 -0
  100. magic_hour/types/params/post_v1_ai_image_upscaler_body_assets.py +28 -0
  101. magic_hour/types/params/post_v1_ai_image_upscaler_body_style.py +36 -0
  102. magic_hour/types/params/post_v1_ai_photo_editor_body.py +63 -0
  103. magic_hour/types/params/post_v1_ai_photo_editor_body_assets.py +28 -0
  104. magic_hour/types/params/post_v1_ai_photo_editor_body_style.py +67 -0
  105. magic_hour/types/params/post_v1_ai_qr_code_generator_body.py +45 -0
  106. magic_hour/types/params/post_v1_ai_qr_code_generator_body_style.py +28 -0
  107. magic_hour/types/params/post_v1_animation_body.py +84 -0
  108. magic_hour/types/params/post_v1_animation_body_assets.py +55 -0
  109. magic_hour/types/params/post_v1_animation_body_style.py +279 -0
  110. magic_hour/types/params/post_v1_face_swap_body.py +72 -0
  111. magic_hour/types/params/post_v1_face_swap_body_assets.py +52 -0
  112. magic_hour/types/params/post_v1_face_swap_photo_body.py +40 -0
  113. magic_hour/types/params/post_v1_face_swap_photo_body_assets.py +36 -0
  114. magic_hour/types/params/post_v1_files_upload_urls_body.py +31 -0
  115. magic_hour/types/params/post_v1_files_upload_urls_body_items_item.py +38 -0
  116. magic_hour/types/params/post_v1_image_background_remover_body.py +40 -0
  117. magic_hour/types/params/post_v1_image_background_remover_body_assets.py +28 -0
  118. magic_hour/types/params/post_v1_image_to_video_body.py +73 -0
  119. magic_hour/types/params/post_v1_image_to_video_body_assets.py +28 -0
  120. magic_hour/types/params/post_v1_image_to_video_body_style.py +29 -0
  121. magic_hour/types/params/post_v1_lip_sync_body.py +80 -0
  122. magic_hour/types/params/post_v1_lip_sync_body_assets.py +52 -0
  123. magic_hour/types/params/post_v1_text_to_video_body.py +57 -0
  124. magic_hour/types/params/post_v1_text_to_video_body_style.py +28 -0
  125. magic_hour/types/params/post_v1_video_to_video_body.py +93 -0
  126. magic_hour/types/params/post_v1_video_to_video_body_assets.py +44 -0
  127. magic_hour/types/params/post_v1_video_to_video_body_style.py +199 -0
  128. magic_hour-0.8.0.dist-info/LICENSE +21 -0
  129. magic_hour-0.8.0.dist-info/METADATA +138 -0
  130. magic_hour-0.8.0.dist-info/RECORD +131 -0
  131. magic_hour-0.8.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,600 @@
1
+ from json import JSONDecodeError
2
+ from typing import (
3
+ Any,
4
+ List,
5
+ TypeVar,
6
+ Dict,
7
+ Optional,
8
+ Type,
9
+ Union,
10
+ cast,
11
+ )
12
+
13
+ import httpx
14
+ from pydantic import BaseModel
15
+
16
+ from .api_error import ApiError
17
+ from .auth import AuthProvider
18
+ from .request import RequestConfig, RequestOptions, default_request_options, QueryParams
19
+ from .response import from_encodable, AsyncStreamResponse, StreamResponse
20
+ from .utils import is_binary_content_type, get_content_type
21
+ from .binary_response import BinaryResponse
22
+
23
+ NoneType = type(None)
24
+ T = TypeVar(
25
+ "T",
26
+ bound=Union[object, None, str, "BaseModel", List[Any], Dict[str, Any], Any],
27
+ )
28
+
29
+
30
+ class BaseClient:
31
+ """Base client class providing core HTTP client functionality.
32
+
33
+ Handles authentication, request building, and response processing for HTTP API clients.
34
+ Serves as the foundation for both synchronous and asynchronous client implementations.
35
+
36
+ Attributes:
37
+ _base_url: Base URL for the API endpoint
38
+ _auths: Dictionary mapping auth provider IDs to AuthProvider instances
39
+ """
40
+
41
+ def __init__(
42
+ self,
43
+ *,
44
+ base_url: str,
45
+ ):
46
+ """Initialize the base client.
47
+
48
+ Args:
49
+ base_url: Base URL for the API endpoint
50
+ """
51
+ self._base_url = base_url
52
+ self._auths: Dict[str, AuthProvider] = {}
53
+
54
+ def register_auth(self, auth_id: str, provider: AuthProvider):
55
+ """Register an authentication provider.
56
+
57
+ Args:
58
+ auth_id: Unique identifier for the auth provider
59
+ provider: AuthProvider instance to handle authentication
60
+ """
61
+ self._auths[auth_id] = provider
62
+
63
+ def default_headers(self) -> Dict[str, str]:
64
+ """Get default headers for requests.
65
+
66
+ Returns:
67
+ Dictionary of default headers
68
+ """
69
+ headers: Dict[str, str] = {
70
+ "x-sideko-sdk-language": "Python",
71
+ }
72
+ return headers
73
+
74
+ def get_base_url(self) -> str:
75
+ """Get the base URL for the API endpoint.
76
+
77
+ Returns:
78
+ Base URL string
79
+ """
80
+ return self._base_url
81
+
82
+ def build_url(self, path: str) -> str:
83
+ """Build a complete URL by combining base URL and path.
84
+
85
+ Args:
86
+ path: API endpoint path
87
+
88
+ Returns:
89
+ Complete URL string
90
+ """
91
+ base = self._base_url
92
+ if base.endswith("/"):
93
+ base = base[:-1]
94
+ if path.startswith("/"):
95
+ path = path[1:]
96
+
97
+ return f"{base}/{path}"
98
+
99
+ def _apply_auth(
100
+ self, *, cfg: RequestConfig, auth_names: List[str]
101
+ ) -> RequestConfig:
102
+ """Apply authentication to the request configuration.
103
+
104
+ Args:
105
+ cfg: Request configuration to modify
106
+ auth_names: List of auth provider IDs to apply
107
+
108
+ Returns:
109
+ Modified request configuration
110
+ """
111
+ for auth_name in auth_names:
112
+ auth_provider = self._auths.get(auth_name)
113
+ if auth_provider is not None:
114
+ cfg = auth_provider.add_to_request(cfg)
115
+
116
+ return cfg
117
+
118
+ def _apply_headers(
119
+ self,
120
+ *,
121
+ cfg: RequestConfig,
122
+ opts: RequestOptions,
123
+ content_type: Optional[str] = None,
124
+ explicit_headers: Optional[Dict[str, str]] = None,
125
+ ) -> RequestConfig:
126
+ """Apply headers to the request configuration.
127
+
128
+ Args:
129
+ cfg: Request configuration to modify
130
+ opts: Request options containing additional headers
131
+ content_type: Optional content type header
132
+ explicit_headers: Optional explicitly specified headers
133
+
134
+ Returns:
135
+ Modified request configuration
136
+ """
137
+ headers = cfg.get("headers", {})
138
+ headers.update(self.default_headers())
139
+
140
+ if content_type is not None:
141
+ headers["content-type"] = content_type
142
+
143
+ if explicit_headers is not None:
144
+ headers.update(explicit_headers)
145
+
146
+ additional_headers = opts.get("additional_headers", None)
147
+ if additional_headers is not None:
148
+ headers.update(additional_headers)
149
+
150
+ if len(headers) > 0:
151
+ cfg["headers"] = headers
152
+
153
+ return cfg
154
+
155
+ def _apply_query_params(
156
+ self,
157
+ *,
158
+ cfg: RequestConfig,
159
+ opts: RequestOptions,
160
+ query_params: Optional[QueryParams] = None,
161
+ ) -> RequestConfig:
162
+ """Apply query parameters to the request configuration.
163
+
164
+ Args:
165
+ cfg: Request configuration to modify
166
+ opts: Request options containing additional parameters
167
+ query_params: Optional query parameters to add
168
+
169
+ Returns:
170
+ Modified request configuration
171
+ """
172
+ params = cfg.get("params", {})
173
+
174
+ if query_params is not None:
175
+ params.update(query_params)
176
+
177
+ additional_params = opts.get("additional_params", None)
178
+ if additional_params is not None:
179
+ params.update(additional_params)
180
+
181
+ if len(params) > 0:
182
+ cfg["params"] = params
183
+
184
+ return cfg
185
+
186
+ def _apply_timeout(
187
+ self,
188
+ *,
189
+ cfg: RequestConfig,
190
+ opts: RequestOptions,
191
+ ) -> RequestConfig:
192
+ """Apply timeout settings to the request configuration.
193
+
194
+ Args:
195
+ cfg: Request configuration to modify
196
+ opts: Request options containing timeout settings
197
+
198
+ Returns:
199
+ Modified request configuration
200
+ """
201
+ timeout = opts.get("timeout", None)
202
+
203
+ if timeout is not None:
204
+ cfg["timeout"] = timeout
205
+
206
+ return cfg
207
+
208
+ def _apply_body(
209
+ self,
210
+ *,
211
+ cfg: RequestConfig,
212
+ data: Optional[httpx._types.RequestData] = None,
213
+ files: Optional[httpx._types.RequestFiles] = None,
214
+ json: Optional[Any] = None,
215
+ content: Optional[httpx._types.RequestContent] = None,
216
+ ) -> RequestConfig:
217
+ """Apply request body content to the request configuration.
218
+
219
+ Args:
220
+ cfg: Request configuration to modify
221
+ data: Optional form data
222
+ files: Optional files to upload
223
+ json: Optional JSON data
224
+ content: Optional raw content
225
+
226
+ Returns:
227
+ Modified request configuration
228
+ """
229
+ if data is not None:
230
+ cfg["data"] = data
231
+
232
+ if files is not None:
233
+ cfg["files"] = files
234
+
235
+ if json is not None:
236
+ cfg["json"] = json
237
+
238
+ if content is not None:
239
+ cfg["content"] = content
240
+
241
+ return cfg
242
+
243
+ def build_request(
244
+ self,
245
+ *,
246
+ method: str,
247
+ path: str,
248
+ auth_names: Optional[List[str]] = None,
249
+ query_params: Optional[QueryParams] = None,
250
+ headers: Optional[Dict[str, str]] = None,
251
+ data: Optional[httpx._types.RequestData] = None,
252
+ files: Optional[httpx._types.RequestFiles] = None,
253
+ json: Optional[Any] = None,
254
+ content_type: Optional[str] = None,
255
+ content: Optional[httpx._types.RequestContent] = None,
256
+ request_options: Optional[RequestOptions] = None,
257
+ ) -> RequestConfig:
258
+ """Build a complete request configuration.
259
+
260
+ Args:
261
+ method: HTTP method
262
+ path: API endpoint path
263
+ auth_names: List of auth provider IDs
264
+ query_params: Query parameters
265
+ headers: Request headers
266
+ data: Form data
267
+ files: Files to upload
268
+ json: JSON data
269
+ content_type: Content type header
270
+ content: Raw content
271
+ request_options: Additional request options
272
+
273
+ Returns:
274
+ Complete request configuration
275
+ """
276
+ opts = request_options or default_request_options()
277
+ req_cfg: RequestConfig = {"method": method, "url": self.build_url(path)}
278
+ req_cfg = self._apply_auth(cfg=req_cfg, auth_names=auth_names or [])
279
+ req_cfg = self._apply_headers(
280
+ cfg=req_cfg, opts=opts, content_type=content_type, explicit_headers=headers
281
+ )
282
+ req_cfg = self._apply_query_params(
283
+ cfg=req_cfg, opts=opts, query_params=query_params
284
+ )
285
+ req_cfg = self._apply_body(
286
+ cfg=req_cfg, data=data, files=files, json=json, content=content
287
+ )
288
+ req_cfg = self._apply_timeout(cfg=req_cfg, opts=opts)
289
+
290
+ return req_cfg
291
+
292
+ def process_response(
293
+ self,
294
+ *,
295
+ response=httpx.Response,
296
+ cast_to: Union[Type[T], Any],
297
+ ) -> T:
298
+ """Process an HTTP response and convert it to the desired type.
299
+
300
+ Args:
301
+ response: HTTP response to process
302
+ cast_to: Type to cast the response data to
303
+
304
+ Returns:
305
+ Processed response data of the specified type
306
+
307
+ Raises:
308
+ ApiError: If the response indicates an error
309
+ """
310
+ if 200 <= response.status_code < 300:
311
+ content_type = get_content_type(response.headers)
312
+ if response.status_code == 204 or cast_to == NoneType:
313
+ return cast(T, None)
314
+ elif cast_to == BinaryResponse:
315
+ return cast(
316
+ T,
317
+ BinaryResponse(content=response.content, headers=response.headers),
318
+ )
319
+ else:
320
+ if "json" in content_type or "form" in content_type:
321
+ if cast_to is type(Any):
322
+ return response.json()
323
+ return from_encodable(data=response.json(), load_with=cast_to)
324
+ elif is_binary_content_type(content_type):
325
+ return cast(
326
+ T,
327
+ BinaryResponse(
328
+ content=response.content, headers=response.headers
329
+ ),
330
+ )
331
+ else:
332
+ return from_encodable(data=response.content, load_with=cast_to)
333
+ else:
334
+ try:
335
+ response_json = response.json()
336
+ except JSONDecodeError:
337
+ raise ApiError(status_code=response.status_code, body=response.text)
338
+ raise ApiError(status_code=response.status_code, body=response_json)
339
+
340
+
341
+ class SyncBaseClient(BaseClient):
342
+ """Synchronous HTTP client implementation.
343
+
344
+ Provides synchronous HTTP request capabilities building on the base client functionality.
345
+ """
346
+
347
+ def __init__(
348
+ self,
349
+ *,
350
+ base_url: str,
351
+ httpx_client: httpx.Client,
352
+ ):
353
+ """Initialize the synchronous client.
354
+
355
+ Args:
356
+ base_url: Base URL for the API endpoint
357
+ httpx_client: Synchronous HTTPX client instance
358
+ """
359
+ super().__init__(base_url=base_url)
360
+ self.httpx_client = httpx_client
361
+
362
+ def request(
363
+ self,
364
+ *,
365
+ method: str,
366
+ path: str,
367
+ cast_to: Union[Type[T], Any],
368
+ auth_names: Optional[List[str]] = None,
369
+ query_params: Optional[QueryParams] = None,
370
+ headers: Optional[Dict[str, str]] = None,
371
+ data: Optional[httpx._types.RequestData] = None,
372
+ files: Optional[httpx._types.RequestFiles] = None,
373
+ json: Optional[Any] = None,
374
+ content_type: Optional[str] = None,
375
+ content: Optional[httpx._types.RequestContent] = None,
376
+ request_options: Optional[RequestOptions] = None,
377
+ ) -> T:
378
+ """Make a synchronous HTTP request.
379
+
380
+ Args:
381
+ method: HTTP method
382
+ path: API endpoint path
383
+ cast_to: Type to cast the response to
384
+ auth_names: List of auth provider IDs
385
+ query_params: Query parameters
386
+ headers: Request headers
387
+ data: Form data
388
+ files: Files to upload
389
+ json: JSON data
390
+ content_type: Content type header
391
+ content: Raw content
392
+ request_options: Additional request options
393
+
394
+ Returns:
395
+ Response data of the specified type
396
+
397
+ Raises:
398
+ ApiError: If the request fails
399
+ """
400
+ req_cfg = self.build_request(
401
+ method=method,
402
+ path=path,
403
+ auth_names=auth_names,
404
+ query_params=query_params,
405
+ headers=headers,
406
+ data=data,
407
+ files=files,
408
+ json=json,
409
+ content_type=content_type,
410
+ content=content,
411
+ request_options=request_options,
412
+ )
413
+ response = self.httpx_client.request(**req_cfg)
414
+ return self.process_response(response=response, cast_to=cast_to)
415
+
416
+ def stream_request(
417
+ self,
418
+ *,
419
+ method: str,
420
+ path: str,
421
+ cast_to: Union[Type[T], Any],
422
+ auth_names: Optional[List[str]] = None,
423
+ query_params: Optional[QueryParams] = None,
424
+ headers: Optional[Dict[str, str]] = None,
425
+ data: Optional[httpx._types.RequestData] = None,
426
+ files: Optional[httpx._types.RequestFiles] = None,
427
+ json: Optional[Any] = None,
428
+ content_type: Optional[str] = None,
429
+ content: Optional[httpx._types.RequestContent] = None,
430
+ request_options: Optional[RequestOptions] = None,
431
+ ) -> StreamResponse[T]:
432
+ """Make a streaming synchronous HTTP request.
433
+
434
+ Args:
435
+ method: HTTP method
436
+ path: API endpoint path
437
+ cast_to: Type to cast the response to
438
+ auth_names: List of auth provider IDs
439
+ query_params: Query parameters
440
+ headers: Request headers
441
+ data: Form data
442
+ files: Files to upload
443
+ json: JSON data
444
+ content_type: Content type header
445
+ content: Raw content
446
+ request_options: Additional request options
447
+
448
+ Returns:
449
+ StreamResponse containing the streaming response
450
+
451
+ Raises:
452
+ ApiError: If the request fails
453
+ """
454
+ req_cfg = self.build_request(
455
+ method=method,
456
+ path=path,
457
+ auth_names=auth_names,
458
+ query_params=query_params,
459
+ headers=headers,
460
+ data=data,
461
+ files=files,
462
+ json=json,
463
+ content_type=content_type,
464
+ content=content,
465
+ request_options=request_options,
466
+ )
467
+ context = self.httpx_client.stream(**req_cfg)
468
+ response = context.__enter__()
469
+ return StreamResponse(response, context, cast_to)
470
+
471
+
472
+ class AsyncBaseClient(BaseClient):
473
+ """Asynchronous HTTP client implementation.
474
+
475
+ Provides asynchronous HTTP request capabilities building on the base client functionality.
476
+ """
477
+
478
+ def __init__(
479
+ self,
480
+ *,
481
+ base_url: str,
482
+ httpx_client: httpx.AsyncClient,
483
+ ):
484
+ """Initialize the asynchronous client.
485
+
486
+ Args:
487
+ base_url: Base URL for the API endpoint
488
+ httpx_client: Asynchronous HTTPX client instance
489
+ """
490
+ super().__init__(base_url=base_url)
491
+ self.httpx_client = httpx_client
492
+
493
+ async def request(
494
+ self,
495
+ *,
496
+ method: str,
497
+ path: str,
498
+ cast_to: Union[Type[T], Any],
499
+ auth_names: Optional[List[str]] = None,
500
+ query_params: Optional[QueryParams] = None,
501
+ headers: Optional[Dict[str, str]] = None,
502
+ data: Optional[httpx._types.RequestData] = None,
503
+ files: Optional[httpx._types.RequestFiles] = None,
504
+ json: Optional[Any] = None,
505
+ content_type: Optional[str] = None,
506
+ content: Optional[httpx._types.RequestContent] = None,
507
+ request_options: Optional[RequestOptions] = None,
508
+ ) -> T:
509
+ """Make an asynchronous HTTP request.
510
+
511
+ Args:
512
+ method: HTTP method
513
+ path: API endpoint path
514
+ cast_to: Type to cast the response to
515
+ auth_names: List of auth provider IDs
516
+ query_params: Query parameters
517
+ headers: Request headers
518
+ data: Form data
519
+ files: Files to upload
520
+ json: JSON data
521
+ content_type: Content type header
522
+ content: Raw content
523
+ request_options: Additional request options
524
+
525
+ Returns:
526
+ Response data of the specified type
527
+
528
+ Raises:
529
+ ApiError: If the request fails
530
+ """
531
+ req_cfg = self.build_request(
532
+ method=method,
533
+ path=path,
534
+ auth_names=auth_names,
535
+ query_params=query_params,
536
+ headers=headers,
537
+ data=data,
538
+ files=files,
539
+ json=json,
540
+ content_type=content_type,
541
+ content=content,
542
+ request_options=request_options,
543
+ )
544
+ response = await self.httpx_client.request(**req_cfg)
545
+ return self.process_response(response=response, cast_to=cast_to)
546
+
547
+ async def stream_request(
548
+ self,
549
+ *,
550
+ method: str,
551
+ path: str,
552
+ cast_to: Union[Type[T], Any],
553
+ auth_names: Optional[List[str]] = None,
554
+ query_params: Optional[QueryParams] = None,
555
+ headers: Optional[Dict[str, str]] = None,
556
+ data: Optional[httpx._types.RequestData] = None,
557
+ files: Optional[httpx._types.RequestFiles] = None,
558
+ json: Optional[Any] = None,
559
+ content_type: Optional[str] = None,
560
+ content: Optional[httpx._types.RequestContent] = None,
561
+ request_options: Optional[RequestOptions] = None,
562
+ ) -> AsyncStreamResponse[T]:
563
+ """Make a streaming asynchronous HTTP request.
564
+
565
+ Args:
566
+ method: HTTP method
567
+ path: API endpoint path
568
+ cast_to: Type to cast the response to
569
+ auth_names: List of auth provider IDs
570
+ query_params: Query parameters
571
+ headers: Request headers
572
+ data: Form data
573
+ files: Files to upload
574
+ json: JSON data
575
+ content_type: Content type header
576
+ content: Raw content
577
+ request_options: Additional request options
578
+
579
+ Returns:
580
+ AsyncStreamResponse containing the streaming response
581
+
582
+ Raises:
583
+ ApiError: If the request fails
584
+ """
585
+ req_cfg = self.build_request(
586
+ method=method,
587
+ path=path,
588
+ auth_names=auth_names,
589
+ query_params=query_params,
590
+ headers=headers,
591
+ data=data,
592
+ files=files,
593
+ json=json,
594
+ content_type=content_type,
595
+ content=content,
596
+ request_options=request_options,
597
+ )
598
+ context = self.httpx_client.stream(**req_cfg)
599
+ response = await context.__aenter__()
600
+ return AsyncStreamResponse(response, context, cast_to)
@@ -0,0 +1,23 @@
1
+ from httpx._models import Headers
2
+
3
+
4
+ class BinaryResponse:
5
+ """
6
+ Represents a binary HTTP response.
7
+
8
+ A lightweight wrapper for binary content and its associated HTTP headers,
9
+ typically used for handling file downloads or raw binary data from HTTP requests.
10
+ """
11
+
12
+ content: bytes
13
+ headers: Headers
14
+
15
+ def __init__(self, content: bytes, headers: Headers) -> None:
16
+ """
17
+ Initialize a binary response with content and headers.
18
+
19
+ The content represents the raw binary data received in the response,
20
+ while headers contain the associated HTTP response headers.
21
+ """
22
+ self.content = content
23
+ self.headers = headers