Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ def _classify_error(
error: Exception,
context: InterceptorContext[Input, Output, $1T, $2T | None]
) -> RetryErrorInfo:
logger.debug("Classifying error: %s", error)
""", transportRequest, transportResponse);
writer.indent();

Expand Down Expand Up @@ -229,7 +230,7 @@ async def _handle_execution(
event_response_deserializer: DeserializeableShape | None = None,
${/hasEventStream}
) -> Output:
logger.debug(f"Making request for operation {operation_name} with parameters: {input}")
logger.debug('Making request for operation "%s" with parameters: %s', operation_name, input)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example:

DEBUG:bedrock_runtime.client:Making request for operation "Converse" with parameters: ConverseInput(model_id='amazon.titan-text-express-v1', messages=[Message(role='user', content=[ContentBlockText(value='Create a list of 3 best songs from the 1990s')])], system=None, inference_config=None, tool_config=None, guardrail_config=None, additional_model_request_fields=None, prompt_variables=None, additional_model_response_field_paths=None, request_metadata=None, performance_config=None)

context: InterceptorContext[Input, None, None, None] = InterceptorContext(
request=input,
response=None,
Expand Down Expand Up @@ -276,9 +277,11 @@ async def _handle_execution(
context_with_transport_request = cast(
InterceptorContext[Input, None, $2T, None], context
)
logger.debug("Serializing request for: %s", context_with_transport_request.request)
context_with_transport_request._transport_request = await serialize(
context_with_transport_request.request, config
)
logger.debug("Serialization complete. Transport request: %s", context_with_transport_request._transport_request)

# Step 5: Invoke read_after_serialization
for interceptor in interceptors:
Expand Down Expand Up @@ -326,6 +329,11 @@ async def _handle_execution(
)
except SmithyRetryException:
raise context_with_response.response
logger.debug(
"Retry needed. Attempting request #%s in %.4f seconds.",
retry_token.retry_count + 1,
retry_token.retry_delay
)
await sleep(retry_token.retry_delay)
current_body = context_with_transport_request.transport_request.body
if (seek := getattr(current_body, "seek", None)) is not None:
Expand All @@ -336,7 +344,7 @@ await seek(0)
break
except Exception as e:
if context.response is not None:
logger.exception(f"Exception occurred while handling: {context.response}")
logger.exception("Exception occurred while handling: %s", context.response)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What gets logged with this? Do we have an example response payload?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, here is an example response:

ERROR:bedrock_runtime.client:Exception occurred while handling: Missing Authentication Token

pass
context._response = e

Expand Down Expand Up @@ -443,10 +451,12 @@ async def _handle_attempt(
raise $1T(
"No endpoint_uri found on the operation config."
)

endpoint_resolver_parameters = StaticEndpointParams(uri=config.endpoint_uri)
logger.debug("Calling endpoint resolver with parameters: %s", endpoint_resolver_parameters)
Comment on lines +454 to +455
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may need to rationalize this against Alex's other Endpoint work. I hope we don't need to add different log statements for each endpoint resolver.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - I'm planning on doing a small refactor of the endpoint generator so that this code wouldn't be duplicated between them. (IE - the only thing that should be different between different endpoint generators is the parameter classes and how they are constructed)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See refactoring: 2a4e6f0

This should play nicely with the logging above.

endpoint = await config.endpoint_resolver.resolve_endpoint(
StaticEndpointParams(uri=config.endpoint_uri)
endpoint_resolver_parameters
)
logger.debug("Endpoint resolver result: %s", endpoint)
if not endpoint.uri.path:
path = ""
elif endpoint.uri.path.endswith("/"):
Expand Down Expand Up @@ -484,11 +494,17 @@ async def _handle_attempt(
writer.write("""
# Step 7i: sign the request
if auth_option and signer:
logger.debug("HTTP request to sign: %s", context.transport_request)
logger.debug(
"Signer properties: %s",
auth_option.signer_properties
)
Comment on lines +497 to +501
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can build this to check but if you have a quick answer, what does the output of these look like? I haven't checked our repr's for them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The signer_properties value would print like a normal python dictionary.

auth_option is either HTTPAuthOption | None and at this point we've checked auth_option isn't None. I've copied over the HTTPAuthOption class below for convenience:

@dataclass(kw_only=True)
class HTTPAuthOption:
    """Auth scheme used for signing and identity resolution."""

    # The ID of the scheme to use. This string matches the one returned by
    # HttpAuthScheme.scheme_id
    scheme_id: str

    # Parameters to pass to IdentityResolver.get_identity.
    identity_properties: dict[str, Any]

    # Parameters to pass to HttpSigner.sign.
    signer_properties: dict[str, Any]

context._transport_request = await signer.sign(
http_request=context.transport_request,
identity=identity,
signing_properties=auth_option.signer_properties,
)
logger.debug("Signed HTTP request: %s", context._transport_request)
""");
}
writer.popState();
Expand Down Expand Up @@ -518,10 +534,13 @@ async def _handle_attempt(
context_with_response = cast(
InterceptorContext[Input, None, $1T, $2T], context
)
logger.debug("HTTP request config: %s", request_config)
logger.debug("Sending HTTP request: %s", context_with_response.transport_request)
Comment on lines +537 to +538
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example:

DEBUG:bedrock_runtime.client:HTTP request config: HTTPRequestConfiguration(read_timeout=None)
DEBUG:bedrock_runtime.client:Sending HTTP request: HTTPRequest(destination=URI(scheme='https', username=None, password=None, host='bedrock-runtime.us-east-1.amazonaws.com', port=None, path='/model/amazon.titan-text-express-v1/converse', query='', fragment=None), method='POST', fields=Fields(OrderedDict({'content-type': Field(name='Content-Type', value=['application/json'], kind=<FieldPosition.HEADER: 0>), 'content-length': Field(name='Content-Length', value=['98'], kind=<FieldPosition.HEADER: 0>)})))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may need to tweak our reprs for fields and URIs so they're more readable. What we have now isn't great for logging.

Copy link
Contributor Author

@jonathan343 jonathan343 Mar 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that seems reasonable. Out of curiosity, why modify the __repr__ and not __str__? Based off the docs for __repr__, what we have now is preferred:

If at all possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment).
...
This is typically used for debugging, so it is important that the representation is information-rich and unambiguous.

Seems like __str__ is what we want to modify:

Called by str(object), the default format() implementation, and the built-in function print(), to compute the “informal” or nicely printable string representation of an object. The return value must be a str object.

This method differs from object.repr() in that there is no expectation that str() return a valid Python expression: a more convenient or concise representation can be used.

context_with_response._transport_response = await config.http_client.send(
request=context_with_response.transport_request,
request_config=request_config,
)
logger.debug("Received HTTP response: %s", context_with_response.transport_response)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example:

DEBUG:bedrock_runtime.client:Received HTTP response: HTTPResponse(status=403, fields=Fields(OrderedDict({'date': Field(name='Date', value=['Fri, 28 Feb 2025 18:54:22 GMT'], kind=<FieldPosition.HEADER: 0>), 'content-type': Field(name='Content-Type', value=['application/json'], kind=<FieldPosition.HEADER: 0>), 'content-length': Field(name='Content-Length', value=['42'], kind=<FieldPosition.HEADER: 0>), 'connection': Field(name='Connection', value=['keep-alive'], kind=<FieldPosition.HEADER: 0>), 'x-amzn-requestid': Field(name='x-amzn-RequestId', value=['af2aa5a0-aeae-406b-8082-c131293508e3'], kind=<FieldPosition.HEADER: 0>), 'x-amzn-errortype': Field(name='x-amzn-ErrorType', value=['MissingAuthenticationTokenException:http://internal.amazon.com/coral/com.amazon.coral.service/'], kind=<FieldPosition.HEADER: 0>)})), reason='Forbidden')


""", transportRequest, transportResponse);
}
Expand All @@ -547,16 +566,18 @@ async def _handle_attempt(
InterceptorContext[Input, Output, $1T, $2T],
context_with_response,
)
logger.debug("Deserializing transport response: %s", context_with_output._transport_response)
context_with_output._response = await deserialize(
context_with_output._transport_response, config
)
logger.debug("Deserialization complete. Response: %s", context_with_output._response)

# Step 7r: Invoke read_after_deserialization
for interceptor in interceptors:
interceptor.read_after_deserialization(context_with_output)
except Exception as e:
if context.response is not None:
logger.exception(f"Exception occurred while handling: {context.response}")
logger.exception("Exception occurred while handling: %s", context.response)
pass
context._response = e

Expand All @@ -582,7 +603,7 @@ async def _finalize_attempt(
)
except Exception as e:
if context.response is not None:
logger.exception(f"Exception occurred while handling: {context.response}")
logger.exception("Exception occurred while handling: %s", context.response)
pass
context._response = e

Expand All @@ -592,7 +613,7 @@ async def _finalize_attempt(
interceptor.read_after_attempt(context)
except Exception as e:
if context.response is not None:
logger.exception(f"Exception occurred while handling: {context.response}")
logger.exception("Exception occurred while handling: %s", context.response)
pass
context._response = e

Expand All @@ -613,11 +634,11 @@ async def _finalize_execution(
pass
except Exception as e:
# log and ignore exceptions
logger.exception(f"Exception occurred while dispatching trace events: {e}")
logger.exception("Exception occurred while dispatching trace events: %s", e)
pass
except Exception as e:
if context.response is not None:
logger.exception(f"Exception occurred while handling: {context.response}")
logger.exception("Exception occurred while handling: %s", context.response)
pass
context._response = e

Expand All @@ -627,7 +648,7 @@ async def _finalize_execution(
interceptor.read_after_execution(context)
except Exception as e:
if context.response is not None:
logger.exception(f"Exception occurred while handling: {context.response}")
logger.exception("Exception occurred while handling: %s", context.response)
pass
context._response = e

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,16 @@ class $1L($2T):
private void writeProperties() {
for (MemberShape member : requiredMembers) {
writer.pushState();
writer.putContext("sensitive", false);
if (member.hasTrait(SensitiveTrait.class)) {
var target = model.expectShape(member.getTarget());

if (target.hasTrait(SensitiveTrait.class)) {
writer.addStdlibImport("dataclasses", "field");
writer.putContext("sensitive", true);
} else {
writer.putContext("sensitive", false);
}

var memberName = symbolProvider.toMemberName(member);
var target = model.expectShape(member.getTarget());
writer.putContext("quote", recursiveShapes.contains(target) ? "'" : "");
writer.write("""
$L: ${quote:L}$T${quote:L}\
Expand All @@ -178,9 +180,10 @@ private void writeProperties() {

for (MemberShape member : optionalMembers) {
writer.pushState();
var target = model.expectShape(member.getTarget());

var requiresField = false;
if (member.hasTrait(SensitiveTrait.class)) {
if (target.hasTrait(SensitiveTrait.class)) {
writer.putContext("sensitive", true);
writer.addStdlibImport("dataclasses", "field");
requiresField = true;
Expand All @@ -191,7 +194,7 @@ private void writeProperties() {
var defaultValue = "None";
var defaultKey = "default";
if (member.hasTrait(DefaultTrait.class)) {
var target = model.expectShape(member.getTarget());

defaultValue = getDefaultValue(writer, member);
if (target.isDocumentShape() || Set.of("list", "dict").contains(defaultValue)) {
writer.addStdlibImport("dataclasses", "field");
Expand All @@ -206,7 +209,6 @@ private void writeProperties() {
writer.putContext("defaultValue", defaultValue);
writer.putContext("useField", requiresField);

var target = model.expectShape(member.getTarget());
writer.putContext("quote", recursiveShapes.contains(target) ? "'" : "");

var memberName = symbolProvider.toMemberName(member);
Expand Down Expand Up @@ -412,7 +414,7 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None:
match schema.expect_member_index():
${C|}
case _:
logger.debug(f"Unexpected member schema: {schema}")
logger.debug("Unexpected member schema: %s", schema)

deserializer.read_struct($T, consumer=_consumer)
return kwargs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def _consumer(self, schema: Schema, de: ShapeDeserializer) -> None:
match schema.expect_member_index():
${4C|}
case _:
logger.debug(f"Unexpected member schema: {schema}")
logger.debug("Unexpected member schema: %s", schema)

def _set_result(self, value: $2T) -> None:
if self._result is not None:
Expand Down
Loading