vibe-client 0.1.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.
- vibe_client/__init__.py +3 -0
- vibe_client/_private/__init__.py +1 -0
- vibe_client/_private/schemas.py +352 -0
- vibe_client/client.py +498 -0
- vibe_client/config.py +74 -0
- vibe_client/deserialize.py +115 -0
- vibe_client/models.py +710 -0
- vibe_client/serialize.py +53 -0
- vibe_client-0.1.0.dist-info/METADATA +128 -0
- vibe_client-0.1.0.dist-info/RECORD +11 -0
- vibe_client-0.1.0.dist-info/WHEEL +4 -0
vibe_client/client.py
ADDED
@@ -0,0 +1,498 @@
|
|
1
|
+
# Code generated by smithy-python-codegen DO NOT EDIT.
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
from asyncio import Future, iscoroutine, sleep
|
5
|
+
from copy import copy, deepcopy
|
6
|
+
from dataclasses import replace
|
7
|
+
import logging
|
8
|
+
from typing import Any, Awaitable, Callable, cast
|
9
|
+
|
10
|
+
from smithy_core import URI
|
11
|
+
from smithy_core.deserializers import DeserializeableShape
|
12
|
+
from smithy_core.endpoints import EndpointResolverParams
|
13
|
+
from smithy_core.exceptions import SmithyRetryException
|
14
|
+
from smithy_core.interceptors import (
|
15
|
+
InputContext,
|
16
|
+
Interceptor,
|
17
|
+
InterceptorChain,
|
18
|
+
OutputContext,
|
19
|
+
RequestContext,
|
20
|
+
ResponseContext,
|
21
|
+
)
|
22
|
+
from smithy_core.interfaces.exceptions import HasFault
|
23
|
+
from smithy_core.interfaces.retries import RetryErrorInfo, RetryErrorType
|
24
|
+
from smithy_core.schemas import APIOperation
|
25
|
+
from smithy_core.serializers import SerializeableShape
|
26
|
+
from smithy_core.types import TypedProperties
|
27
|
+
from smithy_http.aio.interfaces import HTTPRequest, HTTPResponse
|
28
|
+
from smithy_http.interfaces import HTTPRequestConfiguration
|
29
|
+
from smithy_http.plugins import user_agent_plugin
|
30
|
+
|
31
|
+
from .config import Config, Plugin
|
32
|
+
from .deserialize import _deserialize_query_agent
|
33
|
+
from .models import QUERY_AGENT, QueryAgentInput, QueryAgentOutput, ServiceError
|
34
|
+
from .serialize import _serialize_query_agent
|
35
|
+
from smithy_http import Field
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
logger = logging.getLogger(__name__)
|
40
|
+
|
41
|
+
class Vibe:
|
42
|
+
"""
|
43
|
+
Client for Vibe
|
44
|
+
|
45
|
+
:param config: Optional configuration for the client. Here you can set things like the
|
46
|
+
endpoint for HTTP services or auth credentials.
|
47
|
+
|
48
|
+
:param plugins: A list of callables that modify the configuration dynamically. These
|
49
|
+
can be used to set defaults, for example.
|
50
|
+
"""
|
51
|
+
def __init__(self, config: Config | None = None, plugins: list[Plugin] | None = None):
|
52
|
+
self._config = config or Config()
|
53
|
+
|
54
|
+
client_plugins: list[Plugin] = [
|
55
|
+
user_agent_plugin,
|
56
|
+
]
|
57
|
+
if plugins:
|
58
|
+
client_plugins.extend(plugins)
|
59
|
+
|
60
|
+
for plugin in client_plugins:
|
61
|
+
plugin(self._config)
|
62
|
+
|
63
|
+
async def query_agent(self, input: QueryAgentInput, plugins: list[Plugin] | None = None) -> QueryAgentOutput:
|
64
|
+
"""
|
65
|
+
Invokes the QueryAgent operation.
|
66
|
+
|
67
|
+
:param input: The operation's input.
|
68
|
+
|
69
|
+
:param plugins: A list of callables that modify the configuration dynamically.
|
70
|
+
Changes made by these plugins only apply for the duration of the operation
|
71
|
+
execution and will not affect any other operation invocations.
|
72
|
+
|
73
|
+
"""
|
74
|
+
operation_plugins: list[Plugin] = [
|
75
|
+
|
76
|
+
]
|
77
|
+
if plugins:
|
78
|
+
operation_plugins.extend(plugins)
|
79
|
+
|
80
|
+
return await self._execute_operation(
|
81
|
+
input=input,
|
82
|
+
plugins=operation_plugins,
|
83
|
+
serialize=_serialize_query_agent,
|
84
|
+
deserialize=_deserialize_query_agent,
|
85
|
+
config=self._config,
|
86
|
+
operation=QUERY_AGENT,
|
87
|
+
)
|
88
|
+
|
89
|
+
def _classify_error(
|
90
|
+
self,
|
91
|
+
*,
|
92
|
+
error: Exception,
|
93
|
+
context: ResponseContext[Any, HTTPRequest, HTTPResponse | None]
|
94
|
+
) -> RetryErrorInfo:
|
95
|
+
logger.debug("Classifying error: %s", error)
|
96
|
+
|
97
|
+
if not isinstance(error, HasFault) and not context.transport_response:
|
98
|
+
return RetryErrorInfo(error_type=RetryErrorType.TRANSIENT)
|
99
|
+
|
100
|
+
if context.transport_response:
|
101
|
+
if context.transport_response.status in [429, 503]:
|
102
|
+
retry_after = None
|
103
|
+
retry_header = context.transport_response.fields["retry-after"]
|
104
|
+
if retry_header and retry_header.values:
|
105
|
+
retry_after = float(retry_header.values[0])
|
106
|
+
return RetryErrorInfo(error_type=RetryErrorType.THROTTLING, retry_after_hint=retry_after)
|
107
|
+
|
108
|
+
if context.transport_response.status >= 500:
|
109
|
+
return RetryErrorInfo(error_type=RetryErrorType.SERVER_ERROR)
|
110
|
+
|
111
|
+
error_type = RetryErrorType.CLIENT_ERROR
|
112
|
+
if isinstance(error, HasFault) and error.fault == "server":
|
113
|
+
error_type = RetryErrorType.SERVER_ERROR
|
114
|
+
|
115
|
+
return RetryErrorInfo(error_type=error_type)
|
116
|
+
|
117
|
+
async def _execute_operation[Input: SerializeableShape, Output: DeserializeableShape](
|
118
|
+
self,
|
119
|
+
input: Input,
|
120
|
+
plugins: list[Plugin],
|
121
|
+
serialize: Callable[[Input, Config], Awaitable[HTTPRequest]],
|
122
|
+
deserialize: Callable[[HTTPResponse, Config], Awaitable[Output]],
|
123
|
+
config: Config,
|
124
|
+
operation: APIOperation[Input, Output],
|
125
|
+
request_future: Future[RequestContext[Any, HTTPRequest]] | None = None,
|
126
|
+
response_future: Future[HTTPResponse] | None = None,
|
127
|
+
) -> Output:
|
128
|
+
try:
|
129
|
+
return await self._handle_execution(
|
130
|
+
input, plugins, serialize, deserialize, config, operation,
|
131
|
+
request_future, response_future,
|
132
|
+
)
|
133
|
+
except Exception as e:
|
134
|
+
if request_future is not None and not request_future.done():
|
135
|
+
request_future.set_exception(ServiceError(e))
|
136
|
+
if response_future is not None and not response_future.done():
|
137
|
+
response_future.set_exception(ServiceError(e))
|
138
|
+
|
139
|
+
# Make sure every exception that we throw is an instance of ServiceError so
|
140
|
+
# customers can reliably catch everything we throw.
|
141
|
+
if not isinstance(e, ServiceError):
|
142
|
+
raise ServiceError(e) from e
|
143
|
+
raise
|
144
|
+
|
145
|
+
async def _handle_execution[Input: SerializeableShape, Output: DeserializeableShape](
|
146
|
+
self,
|
147
|
+
input: Input,
|
148
|
+
plugins: list[Plugin],
|
149
|
+
serialize: Callable[[Input, Config], Awaitable[HTTPRequest]],
|
150
|
+
deserialize: Callable[[HTTPResponse, Config], Awaitable[Output]],
|
151
|
+
config: Config,
|
152
|
+
operation: APIOperation[Input, Output],
|
153
|
+
request_future: Future[RequestContext[Any, HTTPRequest]] | None,
|
154
|
+
response_future: Future[HTTPResponse] | None,
|
155
|
+
) -> Output:
|
156
|
+
operation_name = operation.schema.id.name
|
157
|
+
logger.debug('Making request for operation "%s" with parameters: %s', operation_name, input)
|
158
|
+
config = deepcopy(config)
|
159
|
+
for plugin in plugins:
|
160
|
+
plugin(config)
|
161
|
+
|
162
|
+
input_context = InputContext(request=input, properties=TypedProperties({"config": config}))
|
163
|
+
transport_request: HTTPRequest | None = None
|
164
|
+
output_context: OutputContext[Input, Output, HTTPRequest | None, HTTPResponse | None] | None = None
|
165
|
+
|
166
|
+
client_interceptors = cast(
|
167
|
+
list[Interceptor[Input, Output, HTTPRequest, HTTPResponse]], list(config.interceptors)
|
168
|
+
)
|
169
|
+
interceptor_chain = InterceptorChain(client_interceptors)
|
170
|
+
|
171
|
+
try:
|
172
|
+
# Step 1: Invoke read_before_execution
|
173
|
+
interceptor_chain.read_before_execution(input_context)
|
174
|
+
|
175
|
+
# Step 2: Invoke the modify_before_serialization hooks
|
176
|
+
input_context = replace(
|
177
|
+
input_context,
|
178
|
+
request=interceptor_chain.modify_before_serialization(input_context)
|
179
|
+
)
|
180
|
+
|
181
|
+
# Step 3: Invoke the read_before_serialization hooks
|
182
|
+
interceptor_chain.read_before_serialization(input_context)
|
183
|
+
|
184
|
+
# Step 4: Serialize the request
|
185
|
+
logger.debug("Serializing request for: %s", input_context.request)
|
186
|
+
transport_request = await serialize(input_context.request, config)
|
187
|
+
request_context = RequestContext(
|
188
|
+
request=input_context.request,
|
189
|
+
transport_request=transport_request,
|
190
|
+
properties=input_context.properties,
|
191
|
+
)
|
192
|
+
logger.debug("Serialization complete. Transport request: %s", request_context.transport_request)
|
193
|
+
|
194
|
+
# Step 5: Invoke read_after_serialization
|
195
|
+
interceptor_chain.read_after_serialization(request_context)
|
196
|
+
|
197
|
+
# Step 6: Invoke modify_before_retry_loop
|
198
|
+
request_context = replace(
|
199
|
+
request_context,
|
200
|
+
transport_request=interceptor_chain.modify_before_retry_loop(request_context)
|
201
|
+
)
|
202
|
+
|
203
|
+
# Step 7: Acquire the retry token.
|
204
|
+
retry_strategy = config.retry_strategy
|
205
|
+
retry_token = retry_strategy.acquire_initial_retry_token()
|
206
|
+
|
207
|
+
while True:
|
208
|
+
# Make an attempt
|
209
|
+
output_context = await self._handle_attempt(
|
210
|
+
deserialize,
|
211
|
+
interceptor_chain,
|
212
|
+
replace(
|
213
|
+
request_context,
|
214
|
+
transport_request = copy(request_context.transport_request)
|
215
|
+
),
|
216
|
+
config,
|
217
|
+
operation,
|
218
|
+
request_future,
|
219
|
+
)
|
220
|
+
|
221
|
+
if isinstance(output_context.response, Exception):
|
222
|
+
# Step 7u: Reacquire retry token if the attempt failed
|
223
|
+
try:
|
224
|
+
retry_token = retry_strategy.refresh_retry_token_for_retry(
|
225
|
+
token_to_renew=retry_token,
|
226
|
+
error_info=self._classify_error(
|
227
|
+
error=output_context.response,
|
228
|
+
context=output_context,
|
229
|
+
)
|
230
|
+
)
|
231
|
+
except SmithyRetryException:
|
232
|
+
raise output_context.response
|
233
|
+
logger.debug(
|
234
|
+
"Retry needed. Attempting request #%s in %.4f seconds.",
|
235
|
+
retry_token.retry_count + 1,
|
236
|
+
retry_token.retry_delay
|
237
|
+
)
|
238
|
+
await sleep(retry_token.retry_delay)
|
239
|
+
current_body = output_context.transport_request.body
|
240
|
+
if (seek := getattr(current_body, "seek", None)) is not None:
|
241
|
+
if iscoroutine((result := seek(0))):
|
242
|
+
await result
|
243
|
+
else:
|
244
|
+
# Step 8: Invoke record_success
|
245
|
+
retry_strategy.record_success(token=retry_token)
|
246
|
+
if response_future is not None:
|
247
|
+
response_future.set_result(
|
248
|
+
output_context.transport_response # type: ignore
|
249
|
+
)
|
250
|
+
break
|
251
|
+
except Exception as e:
|
252
|
+
if output_context is not None:
|
253
|
+
logger.exception("Exception occurred while handling: %s", output_context.response)
|
254
|
+
output_context = replace(output_context, response=e)
|
255
|
+
else:
|
256
|
+
output_context = OutputContext(
|
257
|
+
request=input_context.request,
|
258
|
+
response=e,
|
259
|
+
transport_request=transport_request,
|
260
|
+
transport_response=None,
|
261
|
+
properties=input_context.properties
|
262
|
+
)
|
263
|
+
|
264
|
+
return await self._finalize_execution(interceptor_chain, output_context)
|
265
|
+
|
266
|
+
async def _handle_attempt[Input: SerializeableShape, Output: DeserializeableShape](
|
267
|
+
self,
|
268
|
+
deserialize: Callable[[HTTPResponse, Config], Awaitable[Output]],
|
269
|
+
interceptor: Interceptor[Input, Output, HTTPRequest, HTTPResponse],
|
270
|
+
context: RequestContext[Input, HTTPRequest],
|
271
|
+
config: Config,
|
272
|
+
operation: APIOperation[Input, Output],
|
273
|
+
request_future: Future[RequestContext[Input, HTTPRequest]] | None,
|
274
|
+
) -> OutputContext[Input, Output, HTTPRequest, HTTPResponse | None]:
|
275
|
+
transport_response: HTTPResponse | None = None
|
276
|
+
try:
|
277
|
+
# Step 7a: Invoke read_before_attempt
|
278
|
+
interceptor.read_before_attempt(context)
|
279
|
+
|
280
|
+
# Step 7f: Invoke endpoint_resolver.resolve_endpoint
|
281
|
+
endpoint_resolver_parameters = EndpointResolverParams(
|
282
|
+
operation=operation,
|
283
|
+
input=context.request,
|
284
|
+
context=context.properties
|
285
|
+
)
|
286
|
+
logger.debug("Calling endpoint resolver with parameters: %s", endpoint_resolver_parameters)
|
287
|
+
endpoint = await config.endpoint_resolver.resolve_endpoint(
|
288
|
+
endpoint_resolver_parameters
|
289
|
+
)
|
290
|
+
logger.debug("Endpoint resolver result: %s", endpoint)
|
291
|
+
if not endpoint.uri.path:
|
292
|
+
path = ""
|
293
|
+
elif endpoint.uri.path.endswith("/"):
|
294
|
+
path = endpoint.uri.path[:-1]
|
295
|
+
else:
|
296
|
+
path = endpoint.uri.path
|
297
|
+
if context.transport_request.destination.path:
|
298
|
+
path += context.transport_request.destination.path
|
299
|
+
context.transport_request.destination = URI(
|
300
|
+
scheme=endpoint.uri.scheme,
|
301
|
+
host=context.transport_request.destination.host + endpoint.uri.host,
|
302
|
+
path=path,
|
303
|
+
port=endpoint.uri.port,
|
304
|
+
query=context.transport_request.destination.query,
|
305
|
+
)
|
306
|
+
|
307
|
+
if (headers := endpoint.properties.get("headers")) is not None:
|
308
|
+
context.transport_request.fields.extend(headers)
|
309
|
+
|
310
|
+
# Step 7g: Invoke modify_before_signing
|
311
|
+
context = replace(
|
312
|
+
context,
|
313
|
+
transport_request=interceptor.modify_before_signing(context)
|
314
|
+
)
|
315
|
+
|
316
|
+
# Step 7h: Invoke read_before_signing
|
317
|
+
interceptor.read_before_signing(context)
|
318
|
+
|
319
|
+
# Step 7j: Invoke read_after_signing
|
320
|
+
interceptor.read_after_signing(context)
|
321
|
+
|
322
|
+
# Step 7k: Invoke modify_before_transmit
|
323
|
+
context = replace(
|
324
|
+
context,
|
325
|
+
transport_request=interceptor.modify_before_transmit(context)
|
326
|
+
)
|
327
|
+
|
328
|
+
# Step 7l: Invoke read_before_transmit
|
329
|
+
interceptor.read_before_transmit(context)
|
330
|
+
|
331
|
+
# Step 7m: Invoke http_client.send
|
332
|
+
request_config = config.http_request_config or HTTPRequestConfiguration()
|
333
|
+
logger.debug("HTTP request config: %s", request_config)
|
334
|
+
logger.debug("Sending HTTP request: %s", context.transport_request)
|
335
|
+
|
336
|
+
if request_future is not None:
|
337
|
+
response_task = asyncio.create_task(config.http_client.send(
|
338
|
+
request=context.transport_request,
|
339
|
+
request_config=request_config,
|
340
|
+
))
|
341
|
+
request_future.set_result(context)
|
342
|
+
transport_response = await response_task
|
343
|
+
else:
|
344
|
+
transport_response = await config.http_client.send(
|
345
|
+
request=context.transport_request,
|
346
|
+
request_config=request_config,
|
347
|
+
)
|
348
|
+
|
349
|
+
response_context = ResponseContext(
|
350
|
+
request=context.request,
|
351
|
+
transport_request=context.transport_request,
|
352
|
+
transport_response=transport_response,
|
353
|
+
properties=context.properties
|
354
|
+
)
|
355
|
+
logger.debug("Received HTTP response: %s", response_context.transport_response)
|
356
|
+
|
357
|
+
# Step 7n: Invoke read_after_transmit
|
358
|
+
interceptor.read_after_transmit(response_context)
|
359
|
+
|
360
|
+
# Step 7o: Invoke modify_before_deserialization
|
361
|
+
response_context = replace(
|
362
|
+
response_context,
|
363
|
+
transport_response=interceptor.modify_before_deserialization(response_context)
|
364
|
+
)
|
365
|
+
|
366
|
+
# Step 7p: Invoke read_before_deserialization
|
367
|
+
interceptor.read_before_deserialization(response_context)
|
368
|
+
|
369
|
+
# Step 7q: deserialize
|
370
|
+
logger.debug("Deserializing transport response: %s", response_context.transport_response)
|
371
|
+
output = await deserialize(
|
372
|
+
response_context.transport_response, config
|
373
|
+
)
|
374
|
+
output_context = OutputContext(
|
375
|
+
request=response_context.request,
|
376
|
+
response=output,
|
377
|
+
transport_request=response_context.transport_request,
|
378
|
+
transport_response=response_context.transport_response,
|
379
|
+
properties=response_context.properties
|
380
|
+
)
|
381
|
+
logger.debug("Deserialization complete. Response: %s", output_context.response)
|
382
|
+
|
383
|
+
# Step 7r: Invoke read_after_deserialization
|
384
|
+
interceptor.read_after_deserialization(output_context)
|
385
|
+
except Exception as e:
|
386
|
+
output_context: OutputContext[Input, Output, HTTPRequest, HTTPResponse] = OutputContext(
|
387
|
+
request=context.request,
|
388
|
+
response=e, # type: ignore
|
389
|
+
transport_request=context.transport_request,
|
390
|
+
transport_response=transport_response,
|
391
|
+
properties=context.properties
|
392
|
+
)
|
393
|
+
|
394
|
+
return await self._finalize_attempt(interceptor, output_context)
|
395
|
+
|
396
|
+
async def _finalize_attempt[Input: SerializeableShape, Output: DeserializeableShape](
|
397
|
+
self,
|
398
|
+
interceptor: Interceptor[Input, Output, HTTPRequest, HTTPResponse],
|
399
|
+
context: OutputContext[Input, Output, HTTPRequest, HTTPResponse | None],
|
400
|
+
) -> OutputContext[Input, Output, HTTPRequest, HTTPResponse | None]:
|
401
|
+
# Step 7s: Invoke modify_before_attempt_completion
|
402
|
+
try:
|
403
|
+
context = replace(
|
404
|
+
context,
|
405
|
+
response=interceptor.modify_before_attempt_completion(context)
|
406
|
+
)
|
407
|
+
except Exception as e:
|
408
|
+
logger.exception("Exception occurred while handling: %s", context.response)
|
409
|
+
context = replace(context, response=e)
|
410
|
+
|
411
|
+
# Step 7t: Invoke read_after_attempt
|
412
|
+
try:
|
413
|
+
interceptor.read_after_attempt(context)
|
414
|
+
except Exception as e:
|
415
|
+
context = replace(context, response=e)
|
416
|
+
|
417
|
+
return context
|
418
|
+
|
419
|
+
async def _finalize_execution[Input: SerializeableShape, Output: DeserializeableShape](
|
420
|
+
self,
|
421
|
+
interceptor: Interceptor[Input, Output, HTTPRequest, HTTPResponse],
|
422
|
+
context: OutputContext[Input, Output, HTTPRequest | None, HTTPResponse | None],
|
423
|
+
) -> Output:
|
424
|
+
try:
|
425
|
+
# Step 9: Invoke modify_before_completion
|
426
|
+
context = replace(
|
427
|
+
context,
|
428
|
+
response=interceptor.modify_before_completion(context)
|
429
|
+
)
|
430
|
+
|
431
|
+
# Step 10: Invoke trace_probe.dispatch_events
|
432
|
+
try:
|
433
|
+
pass
|
434
|
+
except Exception as e:
|
435
|
+
# log and ignore exceptions
|
436
|
+
logger.exception("Exception occurred while dispatching trace events: %s", e)
|
437
|
+
pass
|
438
|
+
except Exception as e:
|
439
|
+
logger.exception("Exception occurred while handling: %s", context.response)
|
440
|
+
context = replace(context, response=e)
|
441
|
+
|
442
|
+
# Step 11: Invoke read_after_execution
|
443
|
+
try:
|
444
|
+
interceptor.read_after_execution(context)
|
445
|
+
except Exception as e:
|
446
|
+
context = replace(context, response=e)
|
447
|
+
|
448
|
+
# Step 12: Return / throw
|
449
|
+
if isinstance(context.response, Exception):
|
450
|
+
raise context.response
|
451
|
+
|
452
|
+
# We may want to add some aspects of this context to the output types so we can
|
453
|
+
# return it to the end-users.
|
454
|
+
return context.response
|
455
|
+
|
456
|
+
|
457
|
+
|
458
|
+
class ApiKeyInterceptor(Interceptor):
|
459
|
+
def __init__(self, api_key: str):
|
460
|
+
self.api_key = api_key
|
461
|
+
|
462
|
+
def modify_before_signing(self, context: RequestContext) -> HTTPRequest:
|
463
|
+
"""Add the x-api-key header to the request before it's signed."""
|
464
|
+
|
465
|
+
request = context.transport_request
|
466
|
+
# Add the x-api-key header directly
|
467
|
+
request.fields.set_field(Field(name="x-api-key", values=[str(self.api_key)]))
|
468
|
+
return context.transport_request
|
469
|
+
|
470
|
+
class VibeClient(Vibe):
|
471
|
+
"""
|
472
|
+
An extension of the Vibe client with built-in API key authentication.
|
473
|
+
"""
|
474
|
+
|
475
|
+
def __init__(self, api_key: str, endpoint: str = "https://us-east-1.vibe.api.astartech.ai/", **kwargs):
|
476
|
+
"""
|
477
|
+
Initialize a Vibe client with API key.
|
478
|
+
|
479
|
+
Args:
|
480
|
+
api_key: The API key for authentication
|
481
|
+
endpoint: The API endpoint URI (default: https://us-east-1.vibe.api.astartech.ai/)
|
482
|
+
**kwargs: Additional arguments to pass to the Vibe constructor
|
483
|
+
|
484
|
+
Raises:
|
485
|
+
ValueError: If api_key is None or empty
|
486
|
+
"""
|
487
|
+
# Check that api_key is not None or empty
|
488
|
+
if not api_key:
|
489
|
+
raise ValueError("api_key cannot be None or empty")
|
490
|
+
|
491
|
+
# Create config with API key interceptor
|
492
|
+
config = Config(
|
493
|
+
endpoint_uri=endpoint,
|
494
|
+
interceptors=[ApiKeyInterceptor(api_key)]
|
495
|
+
)
|
496
|
+
|
497
|
+
# Initialize the parent Vibe class with our config
|
498
|
+
super().__init__(config=config, **kwargs)
|
vibe_client/config.py
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Code generated by smithy-python-codegen DO NOT EDIT.
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from typing import Any, Callable, TypeAlias, Union
|
5
|
+
|
6
|
+
from smithy_core.aio.endpoints import StaticEndpointResolver
|
7
|
+
from smithy_core.aio.interfaces import EndpointResolver as _EndpointResolver
|
8
|
+
from smithy_core.interceptors import Interceptor
|
9
|
+
from smithy_core.interfaces import URI
|
10
|
+
from smithy_core.interfaces.retries import RetryStrategy
|
11
|
+
from smithy_core.retries import SimpleRetryStrategy
|
12
|
+
from smithy_http.aio.aiohttp import AIOHTTPClient
|
13
|
+
from smithy_http.aio.interfaces import HTTPClient
|
14
|
+
from smithy_http.interfaces import HTTPRequestConfiguration
|
15
|
+
|
16
|
+
from .models import QueryAgentInput, QueryAgentOutput
|
17
|
+
|
18
|
+
|
19
|
+
_ServiceInterceptor = Union[Interceptor[QueryAgentInput, QueryAgentOutput, Any, Any]]
|
20
|
+
@dataclass(init=False)
|
21
|
+
class Config:
|
22
|
+
"""Configuration for Vibe."""
|
23
|
+
|
24
|
+
endpoint_resolver: _EndpointResolver
|
25
|
+
endpoint_uri: str | URI | None
|
26
|
+
http_client: HTTPClient
|
27
|
+
http_request_config: HTTPRequestConfiguration | None
|
28
|
+
interceptors: list[_ServiceInterceptor]
|
29
|
+
retry_strategy: RetryStrategy
|
30
|
+
|
31
|
+
def __init__(
|
32
|
+
self,
|
33
|
+
*,
|
34
|
+
endpoint_resolver: _EndpointResolver | None = None,
|
35
|
+
endpoint_uri: str | URI | None = None,
|
36
|
+
http_client: HTTPClient | None = None,
|
37
|
+
http_request_config: HTTPRequestConfiguration | None = None,
|
38
|
+
interceptors: list[_ServiceInterceptor] | None = None,
|
39
|
+
retry_strategy: RetryStrategy | None = None,
|
40
|
+
):
|
41
|
+
"""Constructor.
|
42
|
+
|
43
|
+
:param endpoint_resolver:
|
44
|
+
The endpoint resolver used to resolve the final endpoint per-operation based on
|
45
|
+
the configuration.
|
46
|
+
|
47
|
+
:param endpoint_uri:
|
48
|
+
A static URI to route requests to.
|
49
|
+
|
50
|
+
:param http_client:
|
51
|
+
The HTTP client used to make requests.
|
52
|
+
|
53
|
+
:param http_request_config:
|
54
|
+
Configuration for individual HTTP requests.
|
55
|
+
|
56
|
+
:param interceptors:
|
57
|
+
The list of interceptors, which are hooks that are called during the execution
|
58
|
+
of a request.
|
59
|
+
|
60
|
+
:param retry_strategy:
|
61
|
+
The retry strategy for issuing retry tokens and computing retry delays.
|
62
|
+
|
63
|
+
"""
|
64
|
+
self.endpoint_resolver = endpoint_resolver or StaticEndpointResolver()
|
65
|
+
self.endpoint_uri = endpoint_uri
|
66
|
+
self.http_client = http_client or AIOHTTPClient()
|
67
|
+
self.http_request_config = http_request_config
|
68
|
+
self.interceptors = interceptors or []
|
69
|
+
self.retry_strategy = retry_strategy or SimpleRetryStrategy()
|
70
|
+
|
71
|
+
#
|
72
|
+
# A callable that allows customizing the config object on each request.
|
73
|
+
#
|
74
|
+
Plugin: TypeAlias = Callable[[Config], None]
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# Code generated by smithy-python-codegen DO NOT EDIT.
|
2
|
+
|
3
|
+
import json
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from smithy_core.documents import DocumentValue
|
7
|
+
from smithy_core.types import TimestampFormat
|
8
|
+
from smithy_http.aio.interfaces import HTTPResponse
|
9
|
+
from smithy_http.aio.restjson import parse_rest_json_error_info
|
10
|
+
from smithy_json import JSONCodec
|
11
|
+
|
12
|
+
from .config import Config
|
13
|
+
from .models import (
|
14
|
+
ApiError,
|
15
|
+
QueryAgentOutput,
|
16
|
+
UnauthorizedException,
|
17
|
+
UnknownApiError,
|
18
|
+
ValidationException,
|
19
|
+
VibeValidationException,
|
20
|
+
)
|
21
|
+
|
22
|
+
|
23
|
+
async def _deserialize_query_agent(http_response: HTTPResponse, config: Config) -> QueryAgentOutput:
|
24
|
+
if http_response.status != 200 and http_response.status >= 300:
|
25
|
+
raise await _deserialize_error_query_agent(http_response, config)
|
26
|
+
|
27
|
+
kwargs: dict[str, Any] = {}
|
28
|
+
|
29
|
+
body = await http_response.consume_body_async()
|
30
|
+
if body:
|
31
|
+
codec = JSONCodec(default_timestamp_format=TimestampFormat.EPOCH_SECONDS)
|
32
|
+
deserializer = codec.create_deserializer(body)
|
33
|
+
body_kwargs = QueryAgentOutput.deserialize_kwargs(deserializer)
|
34
|
+
kwargs.update(body_kwargs)
|
35
|
+
|
36
|
+
return QueryAgentOutput(**kwargs)
|
37
|
+
|
38
|
+
async def _deserialize_error_query_agent(http_response: HTTPResponse, config: Config) -> ApiError:
|
39
|
+
code, message, parsed_body = await parse_rest_json_error_info(http_response)
|
40
|
+
|
41
|
+
match code.lower():
|
42
|
+
case "unauthorizedexception":
|
43
|
+
return await _deserialize_error_unauthorized_exception(http_response, config, parsed_body, message)
|
44
|
+
|
45
|
+
case "validationexception":
|
46
|
+
return await _deserialize_error_validation_exception(http_response, config, parsed_body, message)
|
47
|
+
|
48
|
+
case "vibevalidationexception":
|
49
|
+
return await _deserialize_error_vibe_validation_exception(http_response, config, parsed_body, message)
|
50
|
+
|
51
|
+
case _:
|
52
|
+
return UnknownApiError(f"{code}: {message}")
|
53
|
+
|
54
|
+
async def _deserialize_error_validation_exception(
|
55
|
+
http_response: HTTPResponse,
|
56
|
+
config: Config,
|
57
|
+
parsed_body: dict[str, DocumentValue] | None,
|
58
|
+
default_message: str,
|
59
|
+
) -> ValidationException:
|
60
|
+
kwargs: dict[str, Any] = {"message": default_message}
|
61
|
+
|
62
|
+
if parsed_body is None:
|
63
|
+
body = await http_response.consume_body_async()
|
64
|
+
else:
|
65
|
+
body = json.dumps(parsed_body).encode('utf-8')
|
66
|
+
|
67
|
+
if body:
|
68
|
+
codec = JSONCodec(default_timestamp_format=TimestampFormat.EPOCH_SECONDS)
|
69
|
+
deserializer = codec.create_deserializer(body)
|
70
|
+
body_kwargs = ValidationException.deserialize_kwargs(deserializer)
|
71
|
+
kwargs.update(body_kwargs)
|
72
|
+
|
73
|
+
return ValidationException(**kwargs)
|
74
|
+
|
75
|
+
async def _deserialize_error_unauthorized_exception(
|
76
|
+
http_response: HTTPResponse,
|
77
|
+
config: Config,
|
78
|
+
parsed_body: dict[str, DocumentValue] | None,
|
79
|
+
default_message: str,
|
80
|
+
) -> UnauthorizedException:
|
81
|
+
kwargs: dict[str, Any] = {"message": default_message}
|
82
|
+
|
83
|
+
if parsed_body is None:
|
84
|
+
body = await http_response.consume_body_async()
|
85
|
+
else:
|
86
|
+
body = json.dumps(parsed_body).encode('utf-8')
|
87
|
+
|
88
|
+
if body:
|
89
|
+
codec = JSONCodec(default_timestamp_format=TimestampFormat.EPOCH_SECONDS)
|
90
|
+
deserializer = codec.create_deserializer(body)
|
91
|
+
body_kwargs = UnauthorizedException.deserialize_kwargs(deserializer)
|
92
|
+
kwargs.update(body_kwargs)
|
93
|
+
|
94
|
+
return UnauthorizedException(**kwargs)
|
95
|
+
|
96
|
+
async def _deserialize_error_vibe_validation_exception(
|
97
|
+
http_response: HTTPResponse,
|
98
|
+
config: Config,
|
99
|
+
parsed_body: dict[str, DocumentValue] | None,
|
100
|
+
default_message: str,
|
101
|
+
) -> VibeValidationException:
|
102
|
+
kwargs: dict[str, Any] = {"message": default_message}
|
103
|
+
|
104
|
+
if parsed_body is None:
|
105
|
+
body = await http_response.consume_body_async()
|
106
|
+
else:
|
107
|
+
body = json.dumps(parsed_body).encode('utf-8')
|
108
|
+
|
109
|
+
if body:
|
110
|
+
codec = JSONCodec(default_timestamp_format=TimestampFormat.EPOCH_SECONDS)
|
111
|
+
deserializer = codec.create_deserializer(body)
|
112
|
+
body_kwargs = VibeValidationException.deserialize_kwargs(deserializer)
|
113
|
+
kwargs.update(body_kwargs)
|
114
|
+
|
115
|
+
return VibeValidationException(**kwargs)
|