@planqk/planqk-service-sdk 2.2.1 → 2.3.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 (45) hide show
  1. package/README-node.md +1 -0
  2. package/README-python.md +7 -7
  3. package/README.md +1 -0
  4. package/notebooks/python-sdk.ipynb +33 -113
  5. package/package.json +1 -1
  6. package/planqk/service/_version.py +1 -1
  7. package/planqk/service/client.py +13 -12
  8. package/planqk/service/sdk/__init__.py +4 -30
  9. package/planqk/service/sdk/client.py +20 -19
  10. package/planqk/service/sdk/core/__init__.py +5 -0
  11. package/planqk/service/sdk/core/api_error.py +12 -6
  12. package/planqk/service/sdk/core/client_wrapper.py +12 -4
  13. package/planqk/service/sdk/core/datetime_utils.py +1 -3
  14. package/planqk/service/sdk/core/file.py +2 -5
  15. package/planqk/service/sdk/core/force_multipart.py +16 -0
  16. package/planqk/service/sdk/core/http_client.py +86 -118
  17. package/planqk/service/sdk/core/http_response.py +55 -0
  18. package/planqk/service/sdk/core/jsonable_encoder.py +1 -4
  19. package/planqk/service/sdk/core/pydantic_utilities.py +79 -147
  20. package/planqk/service/sdk/core/query_encoder.py +1 -3
  21. package/planqk/service/sdk/core/serialization.py +10 -10
  22. package/planqk/service/sdk/environment.py +1 -1
  23. package/planqk/service/sdk/service_api/__init__.py +4 -12
  24. package/planqk/service/sdk/service_api/client.py +138 -860
  25. package/planqk/service/sdk/service_api/raw_client.py +606 -0
  26. package/planqk/service/sdk/service_api/types/__init__.py +3 -7
  27. package/planqk/service/sdk/service_api/types/get_result_response.py +7 -11
  28. package/planqk/service/sdk/service_api/types/get_result_response_embedded.py +4 -6
  29. package/planqk/service/sdk/service_api/types/get_result_response_links.py +4 -6
  30. package/planqk/service/sdk/types/__init__.py +3 -11
  31. package/planqk/service/sdk/types/hal_link.py +3 -5
  32. package/planqk/service/sdk/types/service_execution.py +8 -16
  33. package/planqk/service/sdk/types/service_execution_status.py +1 -2
  34. package/pyproject.toml +1 -1
  35. package/uv.lock +488 -494
  36. package/planqk/service/sdk/errors/__init__.py +0 -15
  37. package/planqk/service/sdk/errors/bad_request_error.py +0 -9
  38. package/planqk/service/sdk/errors/forbidden_error.py +0 -9
  39. package/planqk/service/sdk/errors/internal_server_error.py +0 -9
  40. package/planqk/service/sdk/errors/not_found_error.py +0 -9
  41. package/planqk/service/sdk/errors/unauthorized_error.py +0 -9
  42. package/planqk/service/sdk/service_api/types/health_check_response.py +0 -24
  43. package/planqk/service/sdk/types/input_data.py +0 -5
  44. package/planqk/service/sdk/types/input_data_ref.py +0 -27
  45. package/planqk/service/sdk/types/input_params.py +0 -5
package/README-node.md CHANGED
@@ -15,6 +15,7 @@ const serviceEndpoint = "..."
15
15
  const consumerKey = "..."
16
16
  const consumerSecret = "..."
17
17
 
18
+ // Create a new PlanqkServiceClient instance
18
19
  const client = new PlanqkServiceClient(serviceEndpoint, consumerKey, consumerSecret)
19
20
 
20
21
  // List all service executions
package/README-python.md CHANGED
@@ -18,14 +18,14 @@ consumer_key = "..."
18
18
  consumer_secret = "..."
19
19
  service_endpoint = "..."
20
20
 
21
- # Create a client
21
+ # Create a new PlanqkServiceClient instance
22
22
  client = PlanqkServiceClient(service_endpoint, consumer_key, consumer_secret)
23
23
 
24
24
  # Prepare your input data and parameters
25
- data = {"input": {"a": 1, "b": 2}}
26
- params = {"param1": "value1", "param2": "value2"}
25
+ data = {"values": [2]}
26
+ params = {"shots": 100}
27
27
 
28
- # Start a service execution
28
+ # Start a new service execution
29
29
  service_execution = client.run(request={"data": data, "params": params})
30
30
 
31
31
  # Wait for the service execution to finish (blocking)
@@ -35,13 +35,13 @@ status = service_execution.status
35
35
  ended_at = service_execution.ended_at
36
36
  print(f"Service execution finished at '{ended_at}' with status '{status}'")
37
37
 
38
- # Use the client to retrieve a service execution by its id
38
+ # Use the client to retrieve a service execution by its ID
39
39
  service_execution = client.get_service_execution("0030737b-35cb-46a8-88c2-f59d4885484d")
40
40
 
41
- # Retrieve the result
41
+ # Get the results of the service execution
42
42
  result = service_execution.result()
43
43
 
44
- # Retrieve service execution logs
44
+ # Get the logs of the service execution
45
45
  logs = service_execution.logs()
46
46
 
47
47
  # List the result files
package/README.md CHANGED
@@ -15,6 +15,7 @@ const serviceEndpoint = "..."
15
15
  const consumerKey = "..."
16
16
  const consumerSecret = "..."
17
17
 
18
+ // Create a new PlanqkServiceClient instance
18
19
  const client = new PlanqkServiceClient(serviceEndpoint, consumerKey, consumerSecret)
19
20
 
20
21
  // List all service executions
@@ -52,14 +52,28 @@
52
52
  "source": [
53
53
  "from planqk.service.client import PlanqkServiceClient\n",
54
54
  "\n",
55
- "# Create a client\n",
55
+ "# Create a new PlanqkServiceClient instance\n",
56
56
  "client = PlanqkServiceClient(\n",
57
57
  " service_endpoint, consumer_key, consumer_secret, token_endpoint\n",
58
58
  ")\n",
59
59
  "\n",
60
- "# Start a service execution\n",
61
- "data = {\"n_coin_tosses\": 3}\n",
62
- "service_execution = client.run(request={\"data\": data})\n",
60
+ "# List all service executions\n",
61
+ "service_executions = client.get_service_executions()\n",
62
+ "print(f\"Found {len(service_executions)} service executions\")"
63
+ ]
64
+ },
65
+ {
66
+ "cell_type": "code",
67
+ "execution_count": null,
68
+ "metadata": {},
69
+ "outputs": [],
70
+ "source": [
71
+ "# Prepare your input data and parameters\n",
72
+ "data = {\"values\": [2]}\n",
73
+ "params = {\"shots\": 100}\n",
74
+ "\n",
75
+ "# Start a new service execution\n",
76
+ "service_execution = client.run(request={\"data\": data, \"params\": params})\n",
63
77
  "\n",
64
78
  "# Wait for the service execution to finish (blocking)\n",
65
79
  "service_execution.wait_for_final_state()\n",
@@ -71,17 +85,9 @@
71
85
  },
72
86
  {
73
87
  "cell_type": "code",
74
- "execution_count": 3,
88
+ "execution_count": null,
75
89
  "metadata": {},
76
- "outputs": [
77
- {
78
- "name": "stdout",
79
- "output_type": "stream",
80
- "text": [
81
- "Service execution finished at '2025-03-14 06:35:36' with status 'SUCCEEDED'\n"
82
- ]
83
- }
84
- ],
90
+ "outputs": [],
85
91
  "source": [
86
92
  "# You may perform other operations while waiting for the service execution to finish\n",
87
93
  "while not service_execution.has_finished:\n",
@@ -98,20 +104,12 @@
98
104
  },
99
105
  {
100
106
  "cell_type": "code",
101
- "execution_count": 13,
107
+ "execution_count": null,
102
108
  "metadata": {},
103
- "outputs": [
104
- {
105
- "name": "stdout",
106
- "output_type": "stream",
107
- "text": [
108
- "Service execution finished at '2025-03-14 06:35:36' with status 'SUCCEEDED'\n"
109
- ]
110
- }
111
- ],
109
+ "outputs": [],
112
110
  "source": [
113
111
  "# Use the client to retrieve a service execution by its id\n",
114
- "service_execution = client.get_service_execution(\"0030737b-35cb-46a8-88c2-f59d4885484d\")\n",
112
+ "service_execution = client.get_service_execution(\"3063bd47-1b73-414f-96da-95a95d8fdb0c\")\n",
115
113
  "\n",
116
114
  "status = service_execution.status\n",
117
115
  "ended_at = service_execution.ended_at\n",
@@ -120,44 +118,23 @@
120
118
  },
121
119
  {
122
120
  "cell_type": "code",
123
- "execution_count": 14,
121
+ "execution_count": null,
124
122
  "metadata": {},
125
- "outputs": [
126
- {
127
- "name": "stdout",
128
- "output_type": "stream",
129
- "text": [
130
- "{'000': 10, '001': 7, '010': 18, '011': 14, '100': 16, '101': 7, '110': 14, '111': 14}\n"
131
- ]
132
- }
133
- ],
123
+ "outputs": [],
134
124
  "source": [
135
- "# Retrieve the result\n",
125
+ "# Get the results of the service execution\n",
136
126
  "result = service_execution.result()\n",
137
127
  "\n",
138
- "print(result.counts)"
128
+ "print(result)"
139
129
  ]
140
130
  },
141
131
  {
142
132
  "cell_type": "code",
143
- "execution_count": 15,
133
+ "execution_count": null,
144
134
  "metadata": {},
145
- "outputs": [
146
- {
147
- "name": "stdout",
148
- "output_type": "stream",
149
- "text": [
150
- "...\n",
151
- "PlanQK:Job:MultilineResult\n",
152
- "2025-03-14 06:35:35.424 | DEBUG | qsharp.telemetry:callHandlers:1706 - In on_exit handler\n",
153
- "2025-03-14 06:35:35.424 | DEBUG | qsharp.telemetry:callHandlers:1706 - Exiting telemetry thread\n",
154
- "2025-03-14 06:35:35.424 | DEBUG | qsharp.telemetry:callHandlers:1706 - Sending telemetry request: b'[{\"ver\": 1, \"name\": \"Microsoft.ApplicationInsights.Metric\", \"time\": \"2025-03-14T06:35:35.424270Z\", \"sampleRate\": 100.0, \"iKey\": \"95d25b22-8b6d-448e-9677-78ad4047a95a\", \"tags\": {\"ai.device.locale\": \"en_US\", \"ai.device.osVersion\": \"#1 SMP PREEMPT_DYNAMIC Thu Jan 16 17:10:14 UTC 2025\"}, \"data\": {\"baseType\": \"MetricData\", \"baseData\": {\"ver\": 2, \"metrics\": [{\"name\": \"qsharp.import\", \"value\": 1, \"count\": 1}], \"properties\": {\"qsharp.version\": \"1.13.2\"}}}}]'\n",
155
- "2025-03-14 06:35:36.073 | DEBUG | qsharp.telemetry:callHandlers:1706 - Telemetry response: 200\n"
156
- ]
157
- }
158
- ],
135
+ "outputs": [],
159
136
  "source": [
160
- "# Retrieve service execution logs\n",
137
+ "# Get the logs of the service execution\n",
161
138
  "logs = service_execution.logs()\n",
162
139
  "\n",
163
140
  "print(\"...\")\n",
@@ -167,17 +144,9 @@
167
144
  },
168
145
  {
169
146
  "cell_type": "code",
170
- "execution_count": 11,
147
+ "execution_count": null,
171
148
  "metadata": {},
172
- "outputs": [
173
- {
174
- "name": "stdout",
175
- "output_type": "stream",
176
- "text": [
177
- "['hello.jpg', 'hello.txt', 'output.json']\n"
178
- ]
179
- }
180
- ],
149
+ "outputs": [],
181
150
  "source": [
182
151
  "import os\n",
183
152
  "\n",
@@ -205,60 +174,11 @@
205
174
  " for chunk in file_stream:\n",
206
175
  " f.write(chunk)"
207
176
  ]
208
- },
209
- {
210
- "cell_type": "markdown",
211
- "metadata": {},
212
- "source": [
213
- "### Use a Data Pool as Input Data"
214
- ]
215
- },
216
- {
217
- "cell_type": "code",
218
- "execution_count": null,
219
- "metadata": {},
220
- "outputs": [],
221
- "source": [
222
- "from planqk.service.sdk.types.input_data_ref import InputDataRef\n",
223
- "\n",
224
- "# Create a client\n",
225
- "client = PlanqkServiceClient(\n",
226
- " service_endpoint, consumer_key, consumer_secret, token_endpoint\n",
227
- ")\n",
228
- "\n",
229
- "# Use the InputDataRef class to define a reference to a file in a data pool.\n",
230
- "# Create a data pools, and upload a file called 'data.json'. Use the \"Copy File Reference\"\n",
231
- "# button in the PLANQK Data Pool UI to copy the data pool id, data source descriptor id, and file id.\n",
232
- "file_reference = {\n",
233
- " \"dataPoolId\": \"9b943af3-ca78-4d6e-87fd-33129f5330a2\",\n",
234
- " \"dataSourceDescriptorId\": \"a1e4f7ab-82d5-4158-98c3-3562635cedd2\",\n",
235
- " \"fileId\": \"f9b5c9a2-0101-46a8-9958-383670a2cef4\",\n",
236
- "}\n",
237
- "\n",
238
- "data = InputDataRef.model_validate(\n",
239
- " {\n",
240
- " \"data_pool_id\": file_reference[\"dataPoolId\"],\n",
241
- " \"data_source_descriptor_id\": file_reference[\"dataSourceDescriptorId\"],\n",
242
- " \"file_id\": file_reference[\"fileId\"],\n",
243
- " }\n",
244
- ")\n",
245
- "params = {\"some_attribute\": True}\n",
246
- "\n",
247
- "# Start a service execution\n",
248
- "service_execution = client.run({\"dataRef\": data, \"params\": params})\n",
249
- "\n",
250
- "# Wait for the service execution to finish (blocking)\n",
251
- "service_execution.wait_for_final_state()\n",
252
- "\n",
253
- "status = service_execution.status\n",
254
- "ended_at = service_execution.ended_at\n",
255
- "print(f\"Service execution finished at '{ended_at}' with status '{status}'\")"
256
- ]
257
177
  }
258
178
  ],
259
179
  "metadata": {
260
180
  "kernelspec": {
261
- "display_name": ".venv",
181
+ "display_name": "planqk-service-sdk",
262
182
  "language": "python",
263
183
  "name": "python3"
264
184
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planqk/planqk-service-sdk",
3
- "version": "2.2.1",
3
+ "version": "2.3.0",
4
4
  "description": "SDK to interact with PLANQK Managed Services.",
5
5
  "author": "Kipu Quantum GmbH",
6
6
  "contributors": [
@@ -1 +1 @@
1
- __version__ = "2.2.1"
1
+ __version__ = "2.3.0"
@@ -5,7 +5,7 @@ import time
5
5
  import typing
6
6
 
7
7
  from planqk.service.auth import DEFAULT_TOKEN_ENDPOINT, PlanqkServiceAuth
8
- from planqk.service.sdk import PlanqkServiceApi, HealthCheckResponse, ServiceExecution, GetResultResponse, NotFoundError
8
+ from planqk.service.sdk import PlanqkServiceApi, ServiceExecution, GetResultResponse
9
9
  from planqk.service.sdk.service_api.client import ServiceApiClient
10
10
  from planqk.service.sdk.types.service_execution_status import ServiceExecutionStatus
11
11
 
@@ -69,7 +69,7 @@ class PlanqkServiceExecution:
69
69
  try:
70
70
  result = self._client.api.get_result(id=self.id)
71
71
  break # If the operation succeeds, break out of the loop
72
- except NotFoundError as e:
72
+ except Exception as e:
73
73
  time.sleep(delay) # If the operation fails, wait
74
74
  delay *= 2 # Double the delay
75
75
  if delay >= max_delay:
@@ -103,7 +103,7 @@ class PlanqkServiceExecution:
103
103
  f.write(chunk)
104
104
 
105
105
  def cancel(self) -> None:
106
- self._client.api.cancel(id=self.id)
106
+ self._client.api.cancel_execution(id=self.id)
107
107
 
108
108
  def logs(self) -> typing.List[str]:
109
109
  return self._client.api.get_logs(id=self.id)
@@ -111,11 +111,11 @@ class PlanqkServiceExecution:
111
111
 
112
112
  class PlanqkServiceClient:
113
113
  def __init__(
114
- self,
115
- service_endpoint: str,
116
- consumer_key: typing.Union[str, None],
117
- consumer_secret: typing.Union[str, None],
118
- token_endpoint: str = DEFAULT_TOKEN_ENDPOINT,
114
+ self,
115
+ service_endpoint: str,
116
+ consumer_key: typing.Union[str, None],
117
+ consumer_secret: typing.Union[str, None],
118
+ token_endpoint: str = DEFAULT_TOKEN_ENDPOINT,
119
119
  ):
120
120
  self._service_endpoint = service_endpoint
121
121
  self._consumer_key = consumer_key
@@ -139,13 +139,14 @@ class PlanqkServiceClient:
139
139
  def api(self) -> ServiceApiClient:
140
140
  return self._api.service_api
141
141
 
142
- def health_check(self) -> HealthCheckResponse:
143
- return self.api.health_check()
144
-
145
142
  def run(self, request: typing.Dict[str, typing.Dict[str, typing.Optional[typing.Any]]]) -> PlanqkServiceExecution:
146
- service_execution = self.api.execute(request=request)
143
+ service_execution = self.api.start_execution(request=request)
147
144
  return PlanqkServiceExecution(client=self, service_execution=service_execution)
148
145
 
149
146
  def get_service_execution(self, service_execution_id: str) -> PlanqkServiceExecution:
150
147
  service_execution = self.api.get_status(id=service_execution_id)
151
148
  return PlanqkServiceExecution(client=self, service_execution=service_execution)
149
+
150
+ def get_service_executions(self) -> typing.List[PlanqkServiceExecution]:
151
+ service_executions = self.api.get_service_executions()
152
+ return [PlanqkServiceExecution(client=self, service_execution=e) for e in service_executions]
@@ -1,48 +1,22 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
- from .types import (
4
- HalLink,
5
- InputData,
6
- InputDataRef,
7
- InputParams,
8
- ServiceExecution,
9
- ServiceExecutionStatus,
10
- )
11
- from .errors import (
12
- BadRequestError,
13
- ForbiddenError,
14
- InternalServerError,
15
- NotFoundError,
16
- UnauthorizedError,
17
- )
3
+ # isort: skip_file
4
+
5
+ from .types import HalLink, ServiceExecution, ServiceExecutionStatus
18
6
  from . import service_api
19
7
  from .client import AsyncPlanqkServiceApi, PlanqkServiceApi
20
8
  from .environment import PlanqkServiceApiEnvironment
21
- from .service_api import (
22
- GetResultResponse,
23
- GetResultResponseEmbedded,
24
- GetResultResponseLinks,
25
- HealthCheckResponse,
26
- )
9
+ from .service_api import GetResultResponse, GetResultResponseEmbedded, GetResultResponseLinks
27
10
 
28
11
  __all__ = [
29
12
  "AsyncPlanqkServiceApi",
30
- "BadRequestError",
31
- "ForbiddenError",
32
13
  "GetResultResponse",
33
14
  "GetResultResponseEmbedded",
34
15
  "GetResultResponseLinks",
35
16
  "HalLink",
36
- "HealthCheckResponse",
37
- "InputData",
38
- "InputDataRef",
39
- "InputParams",
40
- "InternalServerError",
41
- "NotFoundError",
42
17
  "PlanqkServiceApi",
43
18
  "PlanqkServiceApiEnvironment",
44
19
  "ServiceExecution",
45
20
  "ServiceExecutionStatus",
46
- "UnauthorizedError",
47
21
  "service_api",
48
22
  ]
@@ -1,12 +1,11 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
3
  import typing
4
- from .environment import PlanqkServiceApiEnvironment
4
+
5
5
  import httpx
6
- from .core.client_wrapper import SyncClientWrapper
7
- from .service_api.client import ServiceApiClient
8
- from .core.client_wrapper import AsyncClientWrapper
9
- from .service_api.client import AsyncServiceApiClient
6
+ from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
7
+ from .environment import PlanqkServiceApiEnvironment
8
+ from .service_api.client import AsyncServiceApiClient, ServiceApiClient
10
9
 
11
10
 
12
11
  class PlanqkServiceApi:
@@ -28,6 +27,9 @@ class PlanqkServiceApi:
28
27
 
29
28
 
30
29
  token : typing.Union[str, typing.Callable[[], str]]
30
+ headers : typing.Optional[typing.Dict[str, str]]
31
+ Additional headers to send with every request.
32
+
31
33
  timeout : typing.Optional[float]
32
34
  The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced.
33
35
 
@@ -52,21 +54,21 @@ class PlanqkServiceApi:
52
54
  base_url: typing.Optional[str] = None,
53
55
  environment: PlanqkServiceApiEnvironment = PlanqkServiceApiEnvironment.DEFAULT,
54
56
  token: typing.Union[str, typing.Callable[[], str]],
57
+ headers: typing.Optional[typing.Dict[str, str]] = None,
55
58
  timeout: typing.Optional[float] = None,
56
59
  follow_redirects: typing.Optional[bool] = True,
57
60
  httpx_client: typing.Optional[httpx.Client] = None,
58
61
  ):
59
62
  _defaulted_timeout = (
60
- timeout if timeout is not None else 60 if httpx_client is None else None
63
+ timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read
61
64
  )
62
65
  self._client_wrapper = SyncClientWrapper(
63
66
  base_url=_get_base_url(base_url=base_url, environment=environment),
64
67
  token=token,
68
+ headers=headers,
65
69
  httpx_client=httpx_client
66
70
  if httpx_client is not None
67
- else httpx.Client(
68
- timeout=_defaulted_timeout, follow_redirects=follow_redirects
69
- )
71
+ else httpx.Client(timeout=_defaulted_timeout, follow_redirects=follow_redirects)
70
72
  if follow_redirects is not None
71
73
  else httpx.Client(timeout=_defaulted_timeout),
72
74
  timeout=_defaulted_timeout,
@@ -93,6 +95,9 @@ class AsyncPlanqkServiceApi:
93
95
 
94
96
 
95
97
  token : typing.Union[str, typing.Callable[[], str]]
98
+ headers : typing.Optional[typing.Dict[str, str]]
99
+ Additional headers to send with every request.
100
+
96
101
  timeout : typing.Optional[float]
97
102
  The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced.
98
103
 
@@ -117,21 +122,21 @@ class AsyncPlanqkServiceApi:
117
122
  base_url: typing.Optional[str] = None,
118
123
  environment: PlanqkServiceApiEnvironment = PlanqkServiceApiEnvironment.DEFAULT,
119
124
  token: typing.Union[str, typing.Callable[[], str]],
125
+ headers: typing.Optional[typing.Dict[str, str]] = None,
120
126
  timeout: typing.Optional[float] = None,
121
127
  follow_redirects: typing.Optional[bool] = True,
122
128
  httpx_client: typing.Optional[httpx.AsyncClient] = None,
123
129
  ):
124
130
  _defaulted_timeout = (
125
- timeout if timeout is not None else 60 if httpx_client is None else None
131
+ timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read
126
132
  )
127
133
  self._client_wrapper = AsyncClientWrapper(
128
134
  base_url=_get_base_url(base_url=base_url, environment=environment),
129
135
  token=token,
136
+ headers=headers,
130
137
  httpx_client=httpx_client
131
138
  if httpx_client is not None
132
- else httpx.AsyncClient(
133
- timeout=_defaulted_timeout, follow_redirects=follow_redirects
134
- )
139
+ else httpx.AsyncClient(timeout=_defaulted_timeout, follow_redirects=follow_redirects)
135
140
  if follow_redirects is not None
136
141
  else httpx.AsyncClient(timeout=_defaulted_timeout),
137
142
  timeout=_defaulted_timeout,
@@ -139,14 +144,10 @@ class AsyncPlanqkServiceApi:
139
144
  self.service_api = AsyncServiceApiClient(client_wrapper=self._client_wrapper)
140
145
 
141
146
 
142
- def _get_base_url(
143
- *, base_url: typing.Optional[str] = None, environment: PlanqkServiceApiEnvironment
144
- ) -> str:
147
+ def _get_base_url(*, base_url: typing.Optional[str] = None, environment: PlanqkServiceApiEnvironment) -> str:
145
148
  if base_url is not None:
146
149
  return base_url
147
150
  elif environment is not None:
148
151
  return environment.value
149
152
  else:
150
- raise Exception(
151
- "Please pass in either base_url or environment to construct the client"
152
- )
153
+ raise Exception("Please pass in either base_url or environment to construct the client")
@@ -1,10 +1,13 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
+ # isort: skip_file
4
+
3
5
  from .api_error import ApiError
4
6
  from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper
5
7
  from .datetime_utils import serialize_datetime
6
8
  from .file import File, convert_file_dict_to_httpx_tuples, with_content_type
7
9
  from .http_client import AsyncHttpClient, HttpClient
10
+ from .http_response import AsyncHttpResponse, HttpResponse
8
11
  from .jsonable_encoder import jsonable_encoder
9
12
  from .pydantic_utilities import (
10
13
  IS_PYDANTIC_V2,
@@ -24,10 +27,12 @@ __all__ = [
24
27
  "ApiError",
25
28
  "AsyncClientWrapper",
26
29
  "AsyncHttpClient",
30
+ "AsyncHttpResponse",
27
31
  "BaseClientWrapper",
28
32
  "FieldMetadata",
29
33
  "File",
30
34
  "HttpClient",
35
+ "HttpResponse",
31
36
  "IS_PYDANTIC_V2",
32
37
  "RequestOptions",
33
38
  "SyncClientWrapper",
@@ -1,17 +1,23 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
- import typing
3
+ from typing import Any, Dict, Optional
4
4
 
5
5
 
6
6
  class ApiError(Exception):
7
- status_code: typing.Optional[int]
8
- body: typing.Any
7
+ headers: Optional[Dict[str, str]]
8
+ status_code: Optional[int]
9
+ body: Any
9
10
 
10
11
  def __init__(
11
- self, *, status_code: typing.Optional[int] = None, body: typing.Any = None
12
- ):
12
+ self,
13
+ *,
14
+ headers: Optional[Dict[str, str]] = None,
15
+ status_code: Optional[int] = None,
16
+ body: Any = None,
17
+ ) -> None:
18
+ self.headers = headers
13
19
  self.status_code = status_code
14
20
  self.body = body
15
21
 
16
22
  def __str__(self) -> str:
17
- return f"status_code: {self.status_code}, body: {self.body}"
23
+ return f"headers: {self.headers}, status_code: {self.status_code}, body: {self.body}"
@@ -1,9 +1,9 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
3
  import typing
4
+
4
5
  import httpx
5
- from .http_client import HttpClient
6
- from .http_client import AsyncHttpClient
6
+ from .http_client import AsyncHttpClient, HttpClient
7
7
 
8
8
 
9
9
  class BaseClientWrapper:
@@ -11,16 +11,19 @@ class BaseClientWrapper:
11
11
  self,
12
12
  *,
13
13
  token: typing.Union[str, typing.Callable[[], str]],
14
+ headers: typing.Optional[typing.Dict[str, str]] = None,
14
15
  base_url: str,
15
16
  timeout: typing.Optional[float] = None,
16
17
  ):
17
18
  self._token = token
19
+ self._headers = headers
18
20
  self._base_url = base_url
19
21
  self._timeout = timeout
20
22
 
21
23
  def get_headers(self) -> typing.Dict[str, str]:
22
24
  headers: typing.Dict[str, str] = {
23
25
  "X-Fern-Language": "Python",
26
+ **(self.get_custom_headers() or {}),
24
27
  }
25
28
  headers["Authorization"] = f"Bearer {self._get_token()}"
26
29
  return headers
@@ -31,6 +34,9 @@ class BaseClientWrapper:
31
34
  else:
32
35
  return self._token()
33
36
 
37
+ def get_custom_headers(self) -> typing.Optional[typing.Dict[str, str]]:
38
+ return self._headers
39
+
34
40
  def get_base_url(self) -> str:
35
41
  return self._base_url
36
42
 
@@ -43,11 +49,12 @@ class SyncClientWrapper(BaseClientWrapper):
43
49
  self,
44
50
  *,
45
51
  token: typing.Union[str, typing.Callable[[], str]],
52
+ headers: typing.Optional[typing.Dict[str, str]] = None,
46
53
  base_url: str,
47
54
  timeout: typing.Optional[float] = None,
48
55
  httpx_client: httpx.Client,
49
56
  ):
50
- super().__init__(token=token, base_url=base_url, timeout=timeout)
57
+ super().__init__(token=token, headers=headers, base_url=base_url, timeout=timeout)
51
58
  self.httpx_client = HttpClient(
52
59
  httpx_client=httpx_client,
53
60
  base_headers=self.get_headers,
@@ -61,11 +68,12 @@ class AsyncClientWrapper(BaseClientWrapper):
61
68
  self,
62
69
  *,
63
70
  token: typing.Union[str, typing.Callable[[], str]],
71
+ headers: typing.Optional[typing.Dict[str, str]] = None,
64
72
  base_url: str,
65
73
  timeout: typing.Optional[float] = None,
66
74
  httpx_client: httpx.AsyncClient,
67
75
  ):
68
- super().__init__(token=token, base_url=base_url, timeout=timeout)
76
+ super().__init__(token=token, headers=headers, base_url=base_url, timeout=timeout)
69
77
  self.httpx_client = AsyncHttpClient(
70
78
  httpx_client=httpx_client,
71
79
  base_headers=self.get_headers,
@@ -13,9 +13,7 @@ def serialize_datetime(v: dt.datetime) -> str:
13
13
  """
14
14
 
15
15
  def _serialize_zoned_datetime(v: dt.datetime) -> str:
16
- if v.tzinfo is not None and v.tzinfo.tzname(None) == dt.timezone.utc.tzname(
17
- None
18
- ):
16
+ if v.tzinfo is not None and v.tzinfo.tzname(None) == dt.timezone.utc.tzname(None):
19
17
  # UTC is a special case where we use "Z" at the end instead of "+00:00"
20
18
  return v.isoformat().replace("+00:00", "Z")
21
19
  else:
@@ -53,15 +53,12 @@ def with_content_type(*, file: File, default_content_type: str) -> File:
53
53
  filename, content = cast(Tuple[Optional[str], FileContent], file) # type: ignore
54
54
  return (filename, content, default_content_type)
55
55
  elif len(file) == 3:
56
- filename, content, file_content_type = cast(
57
- Tuple[Optional[str], FileContent, Optional[str]], file
58
- ) # type: ignore
56
+ filename, content, file_content_type = cast(Tuple[Optional[str], FileContent, Optional[str]], file) # type: ignore
59
57
  out_content_type = file_content_type or default_content_type
60
58
  return (filename, content, out_content_type)
61
59
  elif len(file) == 4:
62
60
  filename, content, file_content_type, headers = cast( # type: ignore
63
- Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]],
64
- file,
61
+ Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], file
65
62
  )
66
63
  out_content_type = file_content_type or default_content_type
67
64
  return (filename, content, out_content_type, headers)
@@ -0,0 +1,16 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+
4
+ class ForceMultipartDict(dict):
5
+ """
6
+ A dictionary subclass that always evaluates to True in boolean contexts.
7
+
8
+ This is used to force multipart/form-data encoding in HTTP requests even when
9
+ the dictionary is empty, which would normally evaluate to False.
10
+ """
11
+
12
+ def __bool__(self):
13
+ return True
14
+
15
+
16
+ FORCE_MULTIPART = ForceMultipartDict()