@planqk/planqk-service-sdk 1.9.1 → 2.1.0

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.
Files changed (221) hide show
  1. package/.devcontainer/devcontainer.json +32 -0
  2. package/.devcontainer/post-create.sh +7 -0
  3. package/.env.template +4 -0
  4. package/.gitlab-ci.yml +103 -0
  5. package/.python-version +1 -0
  6. package/.releaserc.json +45 -0
  7. package/LICENSE +201 -0
  8. package/README-node.md +41 -0
  9. package/README-python.md +52 -0
  10. package/README.md +42 -21
  11. package/dist/auth.js +12 -19
  12. package/dist/client.d.ts +2 -12
  13. package/dist/client.js +5 -76
  14. package/dist/sdk/Client.d.ts +4 -3
  15. package/dist/sdk/Client.js +4 -8
  16. package/dist/sdk/api/errors/BadRequestError.d.ts +1 -1
  17. package/dist/sdk/api/errors/BadRequestError.js +18 -8
  18. package/dist/sdk/api/errors/ForbiddenError.d.ts +1 -1
  19. package/dist/sdk/api/errors/ForbiddenError.js +18 -8
  20. package/dist/sdk/api/errors/InternalServerError.d.ts +1 -1
  21. package/dist/sdk/api/errors/InternalServerError.js +18 -8
  22. package/dist/sdk/api/errors/NotFoundError.d.ts +1 -1
  23. package/dist/sdk/api/errors/NotFoundError.js +18 -8
  24. package/dist/sdk/api/errors/UnauthorizedError.d.ts +1 -1
  25. package/dist/sdk/api/errors/UnauthorizedError.js +18 -8
  26. package/dist/sdk/api/resources/index.d.ts +0 -3
  27. package/dist/sdk/api/resources/index.js +18 -11
  28. package/dist/sdk/api/resources/serviceApi/client/Client.d.ts +65 -12
  29. package/dist/sdk/api/resources/serviceApi/client/Client.js +405 -261
  30. package/dist/sdk/api/resources/serviceApi/client/index.d.ts +1 -1
  31. package/dist/sdk/api/resources/serviceApi/client/index.js +0 -15
  32. package/dist/sdk/api/resources/serviceApi/types/GetResultResponse.d.ts +4 -1
  33. package/dist/sdk/api/resources/serviceApi/types/{GetInterimResultsResponse.d.ts → GetResultResponseEmbedded.d.ts} +3 -1
  34. package/dist/sdk/api/resources/serviceApi/types/GetResultResponseLinks.d.ts +7 -0
  35. package/dist/sdk/api/resources/{statusApi → serviceApi}/types/HealthCheckResponse.d.ts +1 -0
  36. package/dist/sdk/api/resources/serviceApi/types/index.d.ts +3 -1
  37. package/dist/sdk/api/resources/serviceApi/types/index.js +3 -1
  38. package/dist/sdk/api/types/HalLink.d.ts +21 -0
  39. package/dist/sdk/api/types/InputData.d.ts +1 -1
  40. package/dist/sdk/api/types/{InputRef.d.ts → InputDataRef.d.ts} +1 -1
  41. package/dist/sdk/api/types/InputParams.d.ts +1 -1
  42. package/dist/sdk/api/types/{Job.d.ts → ServiceExecution.d.ts} +2 -2
  43. package/dist/sdk/api/types/{JobStatus.d.ts → ServiceExecutionStatus.d.ts} +2 -2
  44. package/dist/sdk/api/types/{JobStatus.js → ServiceExecutionStatus.js} +2 -2
  45. package/dist/sdk/api/types/index.d.ts +4 -8
  46. package/dist/sdk/api/types/index.js +4 -8
  47. package/dist/sdk/core/fetcher/APIResponse.d.ts +1 -0
  48. package/dist/sdk/core/fetcher/Fetcher.d.ts +6 -5
  49. package/dist/sdk/core/fetcher/Fetcher.js +68 -112
  50. package/dist/sdk/core/fetcher/Supplier.js +2 -11
  51. package/dist/sdk/core/fetcher/createRequestUrl.d.ts +1 -0
  52. package/dist/sdk/core/fetcher/createRequestUrl.js +12 -0
  53. package/dist/sdk/core/fetcher/getFetchFn.d.ts +4 -0
  54. package/dist/sdk/core/fetcher/getFetchFn.js +57 -0
  55. package/dist/sdk/core/fetcher/getHeader.d.ts +1 -0
  56. package/dist/sdk/core/fetcher/getHeader.js +11 -0
  57. package/dist/sdk/core/fetcher/getRequestBody.d.ts +7 -0
  58. package/dist/sdk/core/fetcher/getRequestBody.js +11 -0
  59. package/dist/sdk/core/fetcher/getResponseBody.d.ts +1 -0
  60. package/dist/sdk/core/fetcher/getResponseBody.js +40 -0
  61. package/dist/sdk/core/fetcher/index.d.ts +1 -0
  62. package/dist/sdk/core/fetcher/index.js +3 -1
  63. package/dist/sdk/core/fetcher/makeRequest.d.ts +1 -0
  64. package/dist/sdk/core/fetcher/makeRequest.js +33 -0
  65. package/dist/sdk/core/fetcher/requestWithRetries.d.ts +1 -0
  66. package/dist/sdk/core/fetcher/requestWithRetries.js +20 -0
  67. package/dist/sdk/core/fetcher/signals.d.ts +11 -0
  68. package/dist/sdk/core/fetcher/signals.js +36 -0
  69. package/dist/sdk/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.d.ts +30 -0
  70. package/dist/sdk/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.js +232 -0
  71. package/dist/sdk/core/fetcher/stream-wrappers/NodePre18StreamWrapper.d.ts +21 -0
  72. package/dist/sdk/core/fetcher/stream-wrappers/NodePre18StreamWrapper.js +91 -0
  73. package/dist/sdk/core/fetcher/stream-wrappers/UndiciStreamWrapper.d.ts +31 -0
  74. package/dist/sdk/core/fetcher/stream-wrappers/UndiciStreamWrapper.js +214 -0
  75. package/dist/sdk/core/fetcher/stream-wrappers/chooseStreamWrapper.d.ts +18 -0
  76. package/dist/sdk/core/fetcher/stream-wrappers/chooseStreamWrapper.js +48 -0
  77. package/dist/sdk/core/index.d.ts +1 -0
  78. package/dist/sdk/core/index.js +1 -0
  79. package/dist/sdk/core/runtime/index.d.ts +1 -0
  80. package/dist/sdk/core/runtime/index.js +5 -0
  81. package/dist/sdk/core/runtime/runtime.d.ts +9 -0
  82. package/dist/sdk/core/runtime/runtime.js +92 -0
  83. package/dist/sdk/errors/PlanqkServiceApiError.js +2 -0
  84. package/dist/sdk/index.js +17 -7
  85. package/eslint.config.mjs +11 -0
  86. package/fern/fern.config.json +4 -0
  87. package/fern/generators.yml +25 -0
  88. package/fern/openapi/openapi.yml +342 -0
  89. package/notebooks/python-sdk.ipynb +280 -0
  90. package/package.json +27 -28
  91. package/planqk/__init__.py +0 -0
  92. package/planqk/service/__init__.py +1 -0
  93. package/planqk/service/_version.py +1 -0
  94. package/planqk/service/auth.py +30 -0
  95. package/planqk/service/client.py +151 -0
  96. package/planqk/service/sdk/__init__.py +48 -0
  97. package/planqk/service/sdk/client.py +152 -0
  98. package/planqk/service/sdk/core/__init__.py +47 -0
  99. package/planqk/service/sdk/core/api_error.py +17 -0
  100. package/planqk/service/sdk/core/client_wrapper.py +74 -0
  101. package/planqk/service/sdk/core/datetime_utils.py +30 -0
  102. package/planqk/service/sdk/core/file.py +70 -0
  103. package/planqk/service/sdk/core/http_client.py +575 -0
  104. package/planqk/service/sdk/core/jsonable_encoder.py +103 -0
  105. package/planqk/service/sdk/core/pydantic_utilities.py +323 -0
  106. package/planqk/service/sdk/core/query_encoder.py +60 -0
  107. package/planqk/service/sdk/core/remove_none_from_dict.py +11 -0
  108. package/planqk/service/sdk/core/request_options.py +35 -0
  109. package/planqk/service/sdk/core/serialization.py +276 -0
  110. package/planqk/service/sdk/environment.py +7 -0
  111. package/planqk/service/sdk/errors/__init__.py +15 -0
  112. package/planqk/service/sdk/errors/bad_request_error.py +9 -0
  113. package/planqk/service/sdk/errors/forbidden_error.py +9 -0
  114. package/planqk/service/sdk/errors/internal_server_error.py +9 -0
  115. package/planqk/service/sdk/errors/not_found_error.py +9 -0
  116. package/planqk/service/sdk/errors/unauthorized_error.py +9 -0
  117. package/planqk/service/sdk/service_api/__init__.py +15 -0
  118. package/planqk/service/sdk/service_api/client.py +1257 -0
  119. package/planqk/service/sdk/service_api/types/__init__.py +13 -0
  120. package/planqk/service/sdk/service_api/types/get_result_response.py +30 -0
  121. package/planqk/service/sdk/service_api/types/get_result_response_embedded.py +22 -0
  122. package/planqk/service/sdk/service_api/types/get_result_response_links.py +22 -0
  123. package/planqk/service/sdk/service_api/types/health_check_response.py +24 -0
  124. package/planqk/service/sdk/types/__init__.py +17 -0
  125. package/planqk/service/sdk/types/hal_link.py +59 -0
  126. package/planqk/service/sdk/types/input_data.py +5 -0
  127. package/planqk/service/sdk/types/input_data_ref.py +27 -0
  128. package/planqk/service/sdk/types/input_params.py +5 -0
  129. package/planqk/service/sdk/types/service_execution.py +34 -0
  130. package/planqk/service/sdk/types/service_execution_status.py +8 -0
  131. package/pyproject.toml +51 -0
  132. package/scripts/update-version.sh +6 -0
  133. package/src/client.ts +4 -78
  134. package/src/index.test.ts +43 -0
  135. package/src/sdk/Client.ts +4 -7
  136. package/src/sdk/api/errors/BadRequestError.ts +1 -1
  137. package/src/sdk/api/errors/ForbiddenError.ts +1 -1
  138. package/src/sdk/api/errors/InternalServerError.ts +1 -1
  139. package/src/sdk/api/errors/NotFoundError.ts +1 -1
  140. package/src/sdk/api/errors/UnauthorizedError.ts +1 -1
  141. package/src/sdk/api/resources/index.ts +0 -3
  142. package/src/sdk/api/resources/serviceApi/client/Client.ts +205 -32
  143. package/src/sdk/api/resources/serviceApi/client/index.ts +1 -1
  144. package/src/sdk/api/resources/serviceApi/types/GetResultResponse.ts +4 -5
  145. package/src/sdk/api/resources/serviceApi/types/GetResultResponseEmbedded.ts +9 -0
  146. package/src/sdk/api/resources/serviceApi/types/GetResultResponseLinks.ts +9 -0
  147. package/src/sdk/api/resources/{statusApi → serviceApi}/types/HealthCheckResponse.ts +1 -0
  148. package/src/sdk/api/resources/serviceApi/types/index.ts +3 -1
  149. package/src/sdk/api/types/HalLink.ts +22 -0
  150. package/src/sdk/api/types/InputData.ts +1 -1
  151. package/src/sdk/api/types/{InputRef.ts → InputDataRef.ts} +1 -1
  152. package/src/sdk/api/types/InputParams.ts +1 -1
  153. package/src/sdk/api/types/{Job.ts → ServiceExecution.ts} +2 -2
  154. package/src/sdk/api/types/{JobStatus.ts → ServiceExecutionStatus.ts} +2 -2
  155. package/src/sdk/api/types/index.ts +4 -8
  156. package/src/sdk/core/fetcher/APIResponse.ts +1 -0
  157. package/src/sdk/core/fetcher/Fetcher.ts +55 -72
  158. package/src/sdk/core/fetcher/createRequestUrl.ts +10 -0
  159. package/src/sdk/core/fetcher/getFetchFn.ts +25 -0
  160. package/src/sdk/core/fetcher/getHeader.ts +8 -0
  161. package/src/sdk/core/fetcher/getRequestBody.ts +14 -0
  162. package/src/sdk/core/fetcher/getResponseBody.ts +32 -0
  163. package/src/sdk/core/fetcher/index.ts +1 -0
  164. package/src/sdk/core/fetcher/makeRequest.ts +44 -0
  165. package/src/sdk/core/fetcher/requestWithRetries.ts +21 -0
  166. package/src/sdk/core/fetcher/signals.ts +38 -0
  167. package/src/sdk/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts +252 -0
  168. package/src/sdk/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts +106 -0
  169. package/src/sdk/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts +239 -0
  170. package/src/sdk/core/fetcher/stream-wrappers/chooseStreamWrapper.ts +33 -0
  171. package/src/sdk/core/index.ts +1 -0
  172. package/src/sdk/core/runtime/index.ts +1 -0
  173. package/src/sdk/core/runtime/runtime.ts +126 -0
  174. package/tsconfig.json +15 -107
  175. package/uv.lock +1109 -0
  176. package/.eslintignore +0 -3
  177. package/.eslintrc +0 -7
  178. package/dist/sdk/api/resources/serviceApi/client/requests/GetInterimResultsRequest.d.ts +0 -9
  179. package/dist/sdk/api/resources/serviceApi/client/requests/StartExecutionRequest.d.ts +0 -13
  180. package/dist/sdk/api/resources/serviceApi/client/requests/index.d.ts +0 -2
  181. package/dist/sdk/api/resources/serviceApi/client/requests/index.js +0 -2
  182. package/dist/sdk/api/resources/statusApi/client/Client.d.ts +0 -28
  183. package/dist/sdk/api/resources/statusApi/client/Client.js +0 -97
  184. package/dist/sdk/api/resources/statusApi/client/index.d.ts +0 -1
  185. package/dist/sdk/api/resources/statusApi/client/index.js +0 -2
  186. package/dist/sdk/api/resources/statusApi/index.d.ts +0 -2
  187. package/dist/sdk/api/resources/statusApi/index.js +0 -18
  188. package/dist/sdk/api/resources/statusApi/types/index.d.ts +0 -1
  189. package/dist/sdk/api/resources/statusApi/types/index.js +0 -17
  190. package/dist/sdk/api/types/ArrayResponse.d.ts +0 -4
  191. package/dist/sdk/api/types/InterimResultResponse.d.ts +0 -4
  192. package/dist/sdk/api/types/InterimResultResponse.js +0 -5
  193. package/dist/sdk/api/types/Job.js +0 -5
  194. package/dist/sdk/api/types/NumberResponse.d.ts +0 -4
  195. package/dist/sdk/api/types/NumberResponse.js +0 -5
  196. package/dist/sdk/api/types/ObjectResponse.d.ts +0 -4
  197. package/dist/sdk/api/types/ObjectResponse.js +0 -5
  198. package/dist/sdk/api/types/StringResponse.d.ts +0 -4
  199. package/dist/sdk/api/types/StringResponse.js +0 -5
  200. package/jest.config.js +0 -12
  201. package/src/sdk/api/resources/serviceApi/client/requests/GetInterimResultsRequest.ts +0 -10
  202. package/src/sdk/api/resources/serviceApi/client/requests/StartExecutionRequest.ts +0 -15
  203. package/src/sdk/api/resources/serviceApi/client/requests/index.ts +0 -2
  204. package/src/sdk/api/resources/serviceApi/types/GetInterimResultsResponse.ts +0 -9
  205. package/src/sdk/api/resources/statusApi/client/Client.ts +0 -75
  206. package/src/sdk/api/resources/statusApi/client/index.ts +0 -1
  207. package/src/sdk/api/resources/statusApi/index.ts +0 -2
  208. package/src/sdk/api/resources/statusApi/types/index.ts +0 -1
  209. package/src/sdk/api/types/ArrayResponse.ts +0 -5
  210. package/src/sdk/api/types/InterimResultResponse.ts +0 -5
  211. package/src/sdk/api/types/NumberResponse.ts +0 -5
  212. package/src/sdk/api/types/ObjectResponse.ts +0 -5
  213. package/src/sdk/api/types/StringResponse.ts +0 -5
  214. package/tests/fixtures/complex-input.ts +0 -477
  215. package/tests/integration.test.ts +0 -92
  216. /package/dist/sdk/api/resources/serviceApi/{client/requests/GetInterimResultsRequest.js → types/GetResultResponseEmbedded.js} +0 -0
  217. /package/dist/sdk/api/resources/serviceApi/{client/requests/StartExecutionRequest.js → types/GetResultResponseLinks.js} +0 -0
  218. /package/dist/sdk/api/resources/{statusApi → serviceApi}/types/HealthCheckResponse.js +0 -0
  219. /package/dist/sdk/api/{resources/serviceApi/types/GetInterimResultsResponse.js → types/HalLink.js} +0 -0
  220. /package/dist/sdk/api/types/{ArrayResponse.js → InputDataRef.js} +0 -0
  221. /package/dist/sdk/api/types/{InputRef.js → ServiceExecution.js} +0 -0
@@ -0,0 +1,575 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import asyncio
4
+ import email.utils
5
+ import json
6
+ import re
7
+ import time
8
+ import typing
9
+ import urllib.parse
10
+ from contextlib import asynccontextmanager, contextmanager
11
+ from random import random
12
+
13
+ import httpx
14
+
15
+ from .file import File, convert_file_dict_to_httpx_tuples
16
+ from .jsonable_encoder import jsonable_encoder
17
+ from .query_encoder import encode_query
18
+ from .remove_none_from_dict import remove_none_from_dict
19
+ from .request_options import RequestOptions
20
+
21
+ INITIAL_RETRY_DELAY_SECONDS = 0.5
22
+ MAX_RETRY_DELAY_SECONDS = 10
23
+ MAX_RETRY_DELAY_SECONDS_FROM_HEADER = 30
24
+
25
+
26
+ def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float]:
27
+ """
28
+ This function parses the `Retry-After` header in a HTTP response and returns the number of seconds to wait.
29
+
30
+ Inspired by the urllib3 retry implementation.
31
+ """
32
+ retry_after_ms = response_headers.get("retry-after-ms")
33
+ if retry_after_ms is not None:
34
+ try:
35
+ return int(retry_after_ms) / 1000 if retry_after_ms > 0 else 0
36
+ except Exception:
37
+ pass
38
+
39
+ retry_after = response_headers.get("retry-after")
40
+ if retry_after is None:
41
+ return None
42
+
43
+ # Attempt to parse the header as an int.
44
+ if re.match(r"^\s*[0-9]+\s*$", retry_after):
45
+ seconds = float(retry_after)
46
+ # Fallback to parsing it as a date.
47
+ else:
48
+ retry_date_tuple = email.utils.parsedate_tz(retry_after)
49
+ if retry_date_tuple is None:
50
+ return None
51
+ if retry_date_tuple[9] is None: # Python 2
52
+ # Assume UTC if no timezone was specified
53
+ # On Python2.7, parsedate_tz returns None for a timezone offset
54
+ # instead of 0 if no timezone is given, where mktime_tz treats
55
+ # a None timezone offset as local time.
56
+ retry_date_tuple = retry_date_tuple[:9] + (0,) + retry_date_tuple[10:]
57
+
58
+ retry_date = email.utils.mktime_tz(retry_date_tuple)
59
+ seconds = retry_date - time.time()
60
+
61
+ if seconds < 0:
62
+ seconds = 0
63
+
64
+ return seconds
65
+
66
+
67
+ def _retry_timeout(response: httpx.Response, retries: int) -> float:
68
+ """
69
+ Determine the amount of time to wait before retrying a request.
70
+ This function begins by trying to parse a retry-after header from the response, and then proceeds to use exponential backoff
71
+ with a jitter to determine the number of seconds to wait.
72
+ """
73
+
74
+ # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says.
75
+ retry_after = _parse_retry_after(response.headers)
76
+ if retry_after is not None and retry_after <= MAX_RETRY_DELAY_SECONDS_FROM_HEADER:
77
+ return retry_after
78
+
79
+ # Apply exponential backoff, capped at MAX_RETRY_DELAY_SECONDS.
80
+ retry_delay = min(
81
+ INITIAL_RETRY_DELAY_SECONDS * pow(2.0, retries), MAX_RETRY_DELAY_SECONDS
82
+ )
83
+
84
+ # Add a randomness / jitter to the retry delay to avoid overwhelming the server with retries.
85
+ timeout = retry_delay * (1 - 0.25 * random())
86
+ return timeout if timeout >= 0 else 0
87
+
88
+
89
+ def _should_retry(response: httpx.Response) -> bool:
90
+ retryable_400s = [429, 408, 409]
91
+ return response.status_code >= 500 or response.status_code in retryable_400s
92
+
93
+
94
+ def remove_omit_from_dict(
95
+ original: typing.Dict[str, typing.Optional[typing.Any]],
96
+ omit: typing.Optional[typing.Any],
97
+ ) -> typing.Dict[str, typing.Any]:
98
+ if omit is None:
99
+ return original
100
+ new: typing.Dict[str, typing.Any] = {}
101
+ for key, value in original.items():
102
+ if value is not omit:
103
+ new[key] = value
104
+ return new
105
+
106
+
107
+ def maybe_filter_request_body(
108
+ data: typing.Optional[typing.Any],
109
+ request_options: typing.Optional[RequestOptions],
110
+ omit: typing.Optional[typing.Any],
111
+ ) -> typing.Optional[typing.Any]:
112
+ if data is None:
113
+ return (
114
+ jsonable_encoder(request_options.get("additional_body_parameters", {}))
115
+ or {}
116
+ if request_options is not None
117
+ else None
118
+ )
119
+ elif not isinstance(data, typing.Mapping):
120
+ data_content = jsonable_encoder(data)
121
+ else:
122
+ data_content = {
123
+ **(jsonable_encoder(remove_omit_from_dict(data, omit))), # type: ignore
124
+ **(
125
+ jsonable_encoder(request_options.get("additional_body_parameters", {}))
126
+ or {}
127
+ if request_options is not None
128
+ else {}
129
+ ),
130
+ }
131
+ return data_content
132
+
133
+
134
+ # Abstracted out for testing purposes
135
+ def get_request_body(
136
+ *,
137
+ json: typing.Optional[typing.Any],
138
+ data: typing.Optional[typing.Any],
139
+ request_options: typing.Optional[RequestOptions],
140
+ omit: typing.Optional[typing.Any],
141
+ ) -> typing.Tuple[typing.Optional[typing.Any], typing.Optional[typing.Any]]:
142
+ json_body = None
143
+ data_body = None
144
+ if data is not None:
145
+ data_body = maybe_filter_request_body(data, request_options, omit)
146
+ else:
147
+ # If both data and json are None, we send json data in the event extra properties are specified
148
+ json_body = maybe_filter_request_body(json, request_options, omit)
149
+
150
+ # If you have an empty JSON body, you should just send None
151
+ return (
152
+ json_body if json_body != {} else None
153
+ ), data_body if data_body != {} else None
154
+
155
+
156
+ class HttpClient:
157
+ def __init__(
158
+ self,
159
+ *,
160
+ httpx_client: httpx.Client,
161
+ base_timeout: typing.Callable[[], typing.Optional[float]],
162
+ base_headers: typing.Callable[[], typing.Dict[str, str]],
163
+ base_url: typing.Optional[typing.Callable[[], str]] = None,
164
+ ):
165
+ self.base_url = base_url
166
+ self.base_timeout = base_timeout
167
+ self.base_headers = base_headers
168
+ self.httpx_client = httpx_client
169
+
170
+ def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str:
171
+ base_url = maybe_base_url
172
+ if self.base_url is not None and base_url is None:
173
+ base_url = self.base_url()
174
+
175
+ if base_url is None:
176
+ raise ValueError(
177
+ "A base_url is required to make this request, please provide one and try again."
178
+ )
179
+ return base_url
180
+
181
+ def request(
182
+ self,
183
+ path: typing.Optional[str] = None,
184
+ *,
185
+ method: str,
186
+ base_url: typing.Optional[str] = None,
187
+ params: typing.Optional[typing.Dict[str, typing.Any]] = None,
188
+ json: typing.Optional[typing.Any] = None,
189
+ data: typing.Optional[typing.Any] = None,
190
+ content: typing.Optional[
191
+ typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]
192
+ ] = None,
193
+ files: typing.Optional[
194
+ typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]]
195
+ ] = None,
196
+ headers: typing.Optional[typing.Dict[str, typing.Any]] = None,
197
+ request_options: typing.Optional[RequestOptions] = None,
198
+ retries: int = 2,
199
+ omit: typing.Optional[typing.Any] = None,
200
+ ) -> httpx.Response:
201
+ base_url = self.get_base_url(base_url)
202
+ timeout = (
203
+ request_options.get("timeout_in_seconds")
204
+ if request_options is not None
205
+ and request_options.get("timeout_in_seconds") is not None
206
+ else self.base_timeout()
207
+ )
208
+
209
+ json_body, data_body = get_request_body(
210
+ json=json, data=data, request_options=request_options, omit=omit
211
+ )
212
+
213
+ response = self.httpx_client.request(
214
+ method=method,
215
+ url=urllib.parse.urljoin(f"{base_url}/", path),
216
+ headers=jsonable_encoder(
217
+ remove_none_from_dict(
218
+ {
219
+ **self.base_headers(),
220
+ **(headers if headers is not None else {}),
221
+ **(
222
+ request_options.get("additional_headers", {}) or {}
223
+ if request_options is not None
224
+ else {}
225
+ ),
226
+ }
227
+ )
228
+ ),
229
+ params=encode_query(
230
+ jsonable_encoder(
231
+ remove_none_from_dict(
232
+ remove_omit_from_dict(
233
+ {
234
+ **(params if params is not None else {}),
235
+ **(
236
+ request_options.get(
237
+ "additional_query_parameters", {}
238
+ )
239
+ or {}
240
+ if request_options is not None
241
+ else {}
242
+ ),
243
+ },
244
+ omit,
245
+ )
246
+ )
247
+ )
248
+ ),
249
+ json=json_body,
250
+ data=data_body,
251
+ content=content,
252
+ files=(
253
+ convert_file_dict_to_httpx_tuples(
254
+ remove_omit_from_dict(remove_none_from_dict(files), omit)
255
+ )
256
+ if (files is not None and files is not omit)
257
+ else None
258
+ ),
259
+ timeout=timeout,
260
+ )
261
+
262
+ max_retries: int = (
263
+ request_options.get("max_retries", 0) if request_options is not None else 0
264
+ )
265
+ if _should_retry(response=response):
266
+ if max_retries > retries:
267
+ time.sleep(_retry_timeout(response=response, retries=retries))
268
+ return self.request(
269
+ path=path,
270
+ method=method,
271
+ base_url=base_url,
272
+ params=params,
273
+ json=json,
274
+ content=content,
275
+ files=files,
276
+ headers=headers,
277
+ request_options=request_options,
278
+ retries=retries + 1,
279
+ omit=omit,
280
+ )
281
+
282
+ return response
283
+
284
+ @contextmanager
285
+ def stream(
286
+ self,
287
+ path: typing.Optional[str] = None,
288
+ *,
289
+ method: str,
290
+ base_url: typing.Optional[str] = None,
291
+ params: typing.Optional[typing.Dict[str, typing.Any]] = None,
292
+ json: typing.Optional[typing.Any] = None,
293
+ data: typing.Optional[typing.Any] = None,
294
+ content: typing.Optional[
295
+ typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]
296
+ ] = None,
297
+ files: typing.Optional[
298
+ typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]]
299
+ ] = None,
300
+ headers: typing.Optional[typing.Dict[str, typing.Any]] = None,
301
+ request_options: typing.Optional[RequestOptions] = None,
302
+ retries: int = 2,
303
+ omit: typing.Optional[typing.Any] = None,
304
+ ) -> typing.Iterator[httpx.Response]:
305
+ base_url = self.get_base_url(base_url)
306
+ timeout = (
307
+ request_options.get("timeout_in_seconds")
308
+ if request_options is not None
309
+ and request_options.get("timeout_in_seconds") is not None
310
+ else self.base_timeout()
311
+ )
312
+
313
+ json_body, data_body = get_request_body(
314
+ json=json, data=data, request_options=request_options, omit=omit
315
+ )
316
+
317
+ with self.httpx_client.stream(
318
+ method=method,
319
+ url=urllib.parse.urljoin(f"{base_url}/", path),
320
+ headers=jsonable_encoder(
321
+ remove_none_from_dict(
322
+ {
323
+ **self.base_headers(),
324
+ **(headers if headers is not None else {}),
325
+ **(
326
+ request_options.get("additional_headers", {})
327
+ if request_options is not None
328
+ else {}
329
+ ),
330
+ }
331
+ )
332
+ ),
333
+ params=encode_query(
334
+ jsonable_encoder(
335
+ remove_none_from_dict(
336
+ remove_omit_from_dict(
337
+ {
338
+ **(params if params is not None else {}),
339
+ **(
340
+ request_options.get(
341
+ "additional_query_parameters", {}
342
+ )
343
+ if request_options is not None
344
+ else {}
345
+ ),
346
+ },
347
+ omit,
348
+ )
349
+ )
350
+ )
351
+ ),
352
+ json=json_body,
353
+ data=data_body,
354
+ content=content,
355
+ files=(
356
+ convert_file_dict_to_httpx_tuples(
357
+ remove_omit_from_dict(remove_none_from_dict(files), omit)
358
+ )
359
+ if (files is not None and files is not omit)
360
+ else None
361
+ ),
362
+ timeout=timeout,
363
+ ) as stream:
364
+ yield stream
365
+
366
+
367
+ class AsyncHttpClient:
368
+ def __init__(
369
+ self,
370
+ *,
371
+ httpx_client: httpx.AsyncClient,
372
+ base_timeout: typing.Callable[[], typing.Optional[float]],
373
+ base_headers: typing.Callable[[], typing.Dict[str, str]],
374
+ base_url: typing.Optional[typing.Callable[[], str]] = None,
375
+ ):
376
+ self.base_url = base_url
377
+ self.base_timeout = base_timeout
378
+ self.base_headers = base_headers
379
+ self.httpx_client = httpx_client
380
+
381
+ def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str:
382
+ base_url = maybe_base_url
383
+ if self.base_url is not None and base_url is None:
384
+ base_url = self.base_url()
385
+
386
+ if base_url is None:
387
+ raise ValueError(
388
+ "A base_url is required to make this request, please provide one and try again."
389
+ )
390
+ return base_url
391
+
392
+ async def request(
393
+ self,
394
+ path: typing.Optional[str] = None,
395
+ *,
396
+ method: str,
397
+ base_url: typing.Optional[str] = None,
398
+ params: typing.Optional[typing.Dict[str, typing.Any]] = None,
399
+ json: typing.Optional[typing.Any] = None,
400
+ data: typing.Optional[typing.Any] = None,
401
+ content: typing.Optional[
402
+ typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]
403
+ ] = None,
404
+ files: typing.Optional[
405
+ typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]]
406
+ ] = None,
407
+ headers: typing.Optional[typing.Dict[str, typing.Any]] = None,
408
+ request_options: typing.Optional[RequestOptions] = None,
409
+ retries: int = 2,
410
+ omit: typing.Optional[typing.Any] = None,
411
+ ) -> httpx.Response:
412
+ base_url = self.get_base_url(base_url)
413
+ timeout = (
414
+ request_options.get("timeout_in_seconds")
415
+ if request_options is not None
416
+ and request_options.get("timeout_in_seconds") is not None
417
+ else self.base_timeout()
418
+ )
419
+
420
+ json_body, data_body = get_request_body(
421
+ json=json, data=data, request_options=request_options, omit=omit
422
+ )
423
+
424
+ # Add the input to each of these and do None-safety checks
425
+ response = await self.httpx_client.request(
426
+ method=method,
427
+ url=urllib.parse.urljoin(f"{base_url}/", path),
428
+ headers=jsonable_encoder(
429
+ remove_none_from_dict(
430
+ {
431
+ **self.base_headers(),
432
+ **(headers if headers is not None else {}),
433
+ **(
434
+ request_options.get("additional_headers", {}) or {}
435
+ if request_options is not None
436
+ else {}
437
+ ),
438
+ }
439
+ )
440
+ ),
441
+ params=encode_query(
442
+ jsonable_encoder(
443
+ remove_none_from_dict(
444
+ remove_omit_from_dict(
445
+ {
446
+ **(params if params is not None else {}),
447
+ **(
448
+ request_options.get(
449
+ "additional_query_parameters", {}
450
+ )
451
+ or {}
452
+ if request_options is not None
453
+ else {}
454
+ ),
455
+ },
456
+ omit,
457
+ )
458
+ )
459
+ )
460
+ ),
461
+ json=json_body,
462
+ data=data_body,
463
+ content=content,
464
+ files=(
465
+ convert_file_dict_to_httpx_tuples(
466
+ remove_omit_from_dict(remove_none_from_dict(files), omit)
467
+ )
468
+ if files is not None
469
+ else None
470
+ ),
471
+ timeout=timeout,
472
+ )
473
+
474
+ max_retries: int = (
475
+ request_options.get("max_retries", 0) if request_options is not None else 0
476
+ )
477
+ if _should_retry(response=response):
478
+ if max_retries > retries:
479
+ await asyncio.sleep(_retry_timeout(response=response, retries=retries))
480
+ return await self.request(
481
+ path=path,
482
+ method=method,
483
+ base_url=base_url,
484
+ params=params,
485
+ json=json,
486
+ content=content,
487
+ files=files,
488
+ headers=headers,
489
+ request_options=request_options,
490
+ retries=retries + 1,
491
+ omit=omit,
492
+ )
493
+ return response
494
+
495
+ @asynccontextmanager
496
+ async def stream(
497
+ self,
498
+ path: typing.Optional[str] = None,
499
+ *,
500
+ method: str,
501
+ base_url: typing.Optional[str] = None,
502
+ params: typing.Optional[typing.Dict[str, typing.Any]] = None,
503
+ json: typing.Optional[typing.Any] = None,
504
+ data: typing.Optional[typing.Any] = None,
505
+ content: typing.Optional[
506
+ typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]
507
+ ] = None,
508
+ files: typing.Optional[
509
+ typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]]
510
+ ] = None,
511
+ headers: typing.Optional[typing.Dict[str, typing.Any]] = None,
512
+ request_options: typing.Optional[RequestOptions] = None,
513
+ retries: int = 2,
514
+ omit: typing.Optional[typing.Any] = None,
515
+ ) -> typing.AsyncIterator[httpx.Response]:
516
+ base_url = self.get_base_url(base_url)
517
+ timeout = (
518
+ request_options.get("timeout_in_seconds")
519
+ if request_options is not None
520
+ and request_options.get("timeout_in_seconds") is not None
521
+ else self.base_timeout()
522
+ )
523
+
524
+ json_body, data_body = get_request_body(
525
+ json=json, data=data, request_options=request_options, omit=omit
526
+ )
527
+
528
+ async with self.httpx_client.stream(
529
+ method=method,
530
+ url=urllib.parse.urljoin(f"{base_url}/", path),
531
+ headers=jsonable_encoder(
532
+ remove_none_from_dict(
533
+ {
534
+ **self.base_headers(),
535
+ **(headers if headers is not None else {}),
536
+ **(
537
+ request_options.get("additional_headers", {})
538
+ if request_options is not None
539
+ else {}
540
+ ),
541
+ }
542
+ )
543
+ ),
544
+ params=encode_query(
545
+ jsonable_encoder(
546
+ remove_none_from_dict(
547
+ remove_omit_from_dict(
548
+ {
549
+ **(params if params is not None else {}),
550
+ **(
551
+ request_options.get(
552
+ "additional_query_parameters", {}
553
+ )
554
+ if request_options is not None
555
+ else {}
556
+ ),
557
+ },
558
+ omit=omit,
559
+ )
560
+ )
561
+ )
562
+ ),
563
+ json=json_body,
564
+ data=data_body,
565
+ content=content,
566
+ files=(
567
+ convert_file_dict_to_httpx_tuples(
568
+ remove_omit_from_dict(remove_none_from_dict(files), omit)
569
+ )
570
+ if files is not None
571
+ else None
572
+ ),
573
+ timeout=timeout,
574
+ ) as stream:
575
+ yield stream
@@ -0,0 +1,103 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ """
4
+ jsonable_encoder converts a Python object to a JSON-friendly dict
5
+ (e.g. datetimes to strings, Pydantic models to dicts).
6
+
7
+ Taken from FastAPI, and made a bit simpler
8
+ https://github.com/tiangolo/fastapi/blob/master/fastapi/encoders.py
9
+ """
10
+
11
+ import base64
12
+ import dataclasses
13
+ import datetime as dt
14
+ from enum import Enum
15
+ from pathlib import PurePath
16
+ from types import GeneratorType
17
+ from typing import Any, Callable, Dict, List, Optional, Set, Union
18
+
19
+ import pydantic
20
+
21
+ from .datetime_utils import serialize_datetime
22
+ from .pydantic_utilities import (
23
+ IS_PYDANTIC_V2,
24
+ encode_by_type,
25
+ to_jsonable_with_fallback,
26
+ )
27
+
28
+ SetIntStr = Set[Union[int, str]]
29
+ DictIntStrAny = Dict[Union[int, str], Any]
30
+
31
+
32
+ def jsonable_encoder(
33
+ obj: Any, custom_encoder: Optional[Dict[Any, Callable[[Any], Any]]] = None
34
+ ) -> Any:
35
+ custom_encoder = custom_encoder or {}
36
+ if custom_encoder:
37
+ if type(obj) in custom_encoder:
38
+ return custom_encoder[type(obj)](obj)
39
+ else:
40
+ for encoder_type, encoder_instance in custom_encoder.items():
41
+ if isinstance(obj, encoder_type):
42
+ return encoder_instance(obj)
43
+ if isinstance(obj, pydantic.BaseModel):
44
+ if IS_PYDANTIC_V2:
45
+ encoder = getattr(obj.model_config, "json_encoders", {}) # type: ignore # Pydantic v2
46
+ else:
47
+ encoder = getattr(obj.__config__, "json_encoders", {}) # type: ignore # Pydantic v1
48
+ if custom_encoder:
49
+ encoder.update(custom_encoder)
50
+ obj_dict = obj.dict(by_alias=True)
51
+ if "__root__" in obj_dict:
52
+ obj_dict = obj_dict["__root__"]
53
+ if "root" in obj_dict:
54
+ obj_dict = obj_dict["root"]
55
+ return jsonable_encoder(obj_dict, custom_encoder=encoder)
56
+ if dataclasses.is_dataclass(obj):
57
+ obj_dict = dataclasses.asdict(obj) # type: ignore
58
+ return jsonable_encoder(obj_dict, custom_encoder=custom_encoder)
59
+ if isinstance(obj, bytes):
60
+ return base64.b64encode(obj).decode("utf-8")
61
+ if isinstance(obj, Enum):
62
+ return obj.value
63
+ if isinstance(obj, PurePath):
64
+ return str(obj)
65
+ if isinstance(obj, (str, int, float, type(None))):
66
+ return obj
67
+ if isinstance(obj, dt.datetime):
68
+ return serialize_datetime(obj)
69
+ if isinstance(obj, dt.date):
70
+ return str(obj)
71
+ if isinstance(obj, dict):
72
+ encoded_dict = {}
73
+ allowed_keys = set(obj.keys())
74
+ for key, value in obj.items():
75
+ if key in allowed_keys:
76
+ encoded_key = jsonable_encoder(key, custom_encoder=custom_encoder)
77
+ encoded_value = jsonable_encoder(value, custom_encoder=custom_encoder)
78
+ encoded_dict[encoded_key] = encoded_value
79
+ return encoded_dict
80
+ if isinstance(obj, (list, set, frozenset, GeneratorType, tuple)):
81
+ encoded_list = []
82
+ for item in obj:
83
+ encoded_list.append(jsonable_encoder(item, custom_encoder=custom_encoder))
84
+ return encoded_list
85
+
86
+ def fallback_serializer(o: Any) -> Any:
87
+ attempt_encode = encode_by_type(o)
88
+ if attempt_encode is not None:
89
+ return attempt_encode
90
+
91
+ try:
92
+ data = dict(o)
93
+ except Exception as e:
94
+ errors: List[Exception] = []
95
+ errors.append(e)
96
+ try:
97
+ data = vars(o)
98
+ except Exception as e:
99
+ errors.append(e)
100
+ raise ValueError(errors) from e
101
+ return jsonable_encoder(data, custom_encoder=custom_encoder)
102
+
103
+ return to_jsonable_with_fallback(obj, fallback_serializer)