diff --git a/aws_lambda_powertools/event_handler/bedrock_agent.py b/aws_lambda_powertools/event_handler/bedrock_agent.py index 9c65547d9a2..0ce0f3ff725 100644 --- a/aws_lambda_powertools/event_handler/bedrock_agent.py +++ b/aws_lambda_powertools/event_handler/bedrock_agent.py @@ -1,13 +1,15 @@ from re import Match -from typing import Any, Dict +from typing import Any, Callable, Dict, List, Optional from typing_extensions import override from aws_lambda_powertools.event_handler import ApiGatewayResolver from aws_lambda_powertools.event_handler.api_gateway import ( + _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, ProxyEventType, ResponseBuilder, ) +from aws_lambda_powertools.event_handler.openapi.types import OpenAPIResponse from aws_lambda_powertools.utilities.data_classes import BedrockAgentEvent @@ -83,6 +85,166 @@ def __init__(self, debug: bool = False, enable_validation: bool = True): ) self._response_builder_class = BedrockResponseBuilder + # Note: we need ignore[override] because we are making the optional `description` field required. + @override + def get( # type: ignore[override] + self, + rule: str, + description: str, + cors: Optional[bool] = None, + compress: bool = False, + cache_control: Optional[str] = None, + summary: Optional[str] = None, + responses: Optional[Dict[int, OpenAPIResponse]] = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: Optional[List[str]] = None, + operation_id: Optional[str] = None, + include_in_schema: bool = True, + middlewares: Optional[List[Callable[..., Any]]] = None, + ) -> Callable[[Callable[..., Any]], Callable[..., Any]]: + return super(BedrockAgentResolver, self).get( + rule, + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + middlewares, + ) + + # Note: we need ignore[override] because we are making the optional `description` field required. + @override + def post( # type: ignore[override] + self, + rule: str, + description: str, + cors: Optional[bool] = None, + compress: bool = False, + cache_control: Optional[str] = None, + summary: Optional[str] = None, + responses: Optional[Dict[int, OpenAPIResponse]] = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: Optional[List[str]] = None, + operation_id: Optional[str] = None, + include_in_schema: bool = True, + middlewares: Optional[List[Callable[..., Any]]] = None, + ): + return super().post( + rule, + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + middlewares, + ) + + # Note: we need ignore[override] because we are making the optional `description` field required. + @override + def put( # type: ignore[override] + self, + rule: str, + description: str, + cors: Optional[bool] = None, + compress: bool = False, + cache_control: Optional[str] = None, + summary: Optional[str] = None, + responses: Optional[Dict[int, OpenAPIResponse]] = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: Optional[List[str]] = None, + operation_id: Optional[str] = None, + include_in_schema: bool = True, + middlewares: Optional[List[Callable[..., Any]]] = None, + ): + return super().put( + rule, + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + middlewares, + ) + + # Note: we need ignore[override] because we are making the optional `description` field required. + @override + def patch( # type: ignore[override] + self, + rule: str, + description: str, + cors: Optional[bool] = None, + compress: bool = False, + cache_control: Optional[str] = None, + summary: Optional[str] = None, + responses: Optional[Dict[int, OpenAPIResponse]] = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: Optional[List[str]] = None, + operation_id: Optional[str] = None, + include_in_schema: bool = True, + middlewares: Optional[List[Callable]] = None, + ): + return super().patch( + rule, + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + middlewares, + ) + + # Note: we need ignore[override] because we are making the optional `description` field required. + @override + def delete( # type: ignore[override] + self, + rule: str, + description: str, + cors: Optional[bool] = None, + compress: bool = False, + cache_control: Optional[str] = None, + summary: Optional[str] = None, + responses: Optional[Dict[int, OpenAPIResponse]] = None, + response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION, + tags: Optional[List[str]] = None, + operation_id: Optional[str] = None, + include_in_schema: bool = True, + middlewares: Optional[List[Callable[..., Any]]] = None, + ): + return super().delete( + rule, + cors, + compress, + cache_control, + summary, + description, + responses, + response_description, + tags, + operation_id, + include_in_schema, + middlewares, + ) + @override def _convert_matches_into_route_keys(self, match: Match) -> Dict[str, str]: # In Bedrock Agents, all the parameters come inside the "parameters" key, not on the apiPath diff --git a/docs/core/event_handler/_openapi_customization_metadata.md b/docs/core/event_handler/_openapi_customization_metadata.md new file mode 100644 index 00000000000..5a96db582cb --- /dev/null +++ b/docs/core/event_handler/_openapi_customization_metadata.md @@ -0,0 +1,16 @@ + + +Defining and customizing OpenAPI metadata gives detailed, top-level information about your API. Here's the method to set and tailor this metadata: + +| Field Name | Type | Description | +| ------------------ | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `title` | `str` | The title for your API. It should be a concise, specific name that can be used to identify the API in documentation or listings. | +| `version` | `str` | The version of the API you are documenting. This could reflect the release iteration of the API and helps clients understand the evolution of the API. | +| `openapi_version` | `str` | Specifies the version of the OpenAPI Specification on which your API is based. When using Pydantic v1 it defaults to 3.0.3, and when using Pydantic v2, it defaults to 3.1.0. | +| `summary` | `str` | A short and informative summary that can provide an overview of what the API does. This can be the same as or different from the title but should add context or information. | +| `description` | `str` | A verbose description that can include Markdown formatting, providing a full explanation of the API's purpose, functionalities, and general usage instructions. | +| `tags` | `List[str]` | A collection of tags that categorize endpoints for better organization and navigation within the documentation. This can group endpoints by their functionality or other criteria. | +| `servers` | `List[Server]` | An array of Server objects, which specify the URL to the server and a description for its environment (production, staging, development, etc.), providing connectivity information. | +| `terms_of_service` | `str` | A URL that points to the terms of service for your API. This could provide legal information and user responsibilities related to the usage of the API. | +| `contact` | `Contact` | A Contact object containing contact details of the organization or individuals maintaining the API. This may include fields such as name, URL, and email. | +| `license_info` | `License` | A License object providing the license details for the API, typically including the name of the license and the URL to the full license text. | diff --git a/docs/core/event_handler/_openapi_customization_operations.md b/docs/core/event_handler/_openapi_customization_operations.md new file mode 100644 index 00000000000..df842b2b7fc --- /dev/null +++ b/docs/core/event_handler/_openapi_customization_operations.md @@ -0,0 +1,15 @@ + + +Customize your API endpoints by adding metadata to endpoint definitions. + +Here's a breakdown of various customizable fields: + +| Field Name | Type | Description | +| ---------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `summary` | `str` | A concise overview of the main functionality of the endpoint. This brief introduction is usually displayed in autogenerated API documentation and helps consumers quickly understand what the endpoint does. | +| `description` | `str` | A more detailed explanation of the endpoint, which can include information about the operation's behavior, including side effects, error states, and other operational guidelines. | +| `responses` | `Dict[int, Dict[str, OpenAPIResponse]]` | A dictionary that maps each HTTP status code to a Response Object as defined by the [OpenAPI Specification](https://swagger.io/specification/#response-object). This allows you to describe expected responses, including default or error messages, and their corresponding schemas or models for different status codes. | +| `response_description` | `str` | Provides the default textual description of the response sent by the endpoint when the operation is successful. It is intended to give a human-readable understanding of the result. | +| `tags` | `List[str]` | Tags are a way to categorize and group endpoints within the API documentation. They can help organize the operations by resources or other heuristic. | +| `operation_id` | `str` | A unique identifier for the operation, which can be used for referencing this operation in documentation or code. This ID must be unique across all operations described in the API. | +| `include_in_schema` | `bool` | A boolean value that determines whether or not this operation should be included in the OpenAPI schema. Setting it to `False` can hide the endpoint from generated documentation and schema exports, which might be useful for private or experimental endpoints. | diff --git a/docs/core/event_handler/_openapi_customization_parameters.md b/docs/core/event_handler/_openapi_customization_parameters.md new file mode 100644 index 00000000000..6b87ce5c598 --- /dev/null +++ b/docs/core/event_handler/_openapi_customization_parameters.md @@ -0,0 +1,25 @@ + +Whenever you use OpenAPI parameters to validate [query strings](api_gateway.md#validating-query-strings) or [path parameters](api_gateway.md#validating-path-parameters), you can enhance validation and OpenAPI documentation by using any of these parameters: + +| Field name | Type | Description | +|-----------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `alias` | `str` | Alternative name for a field, used when serializing and deserializing data | +| `validation_alias` | `str` | Alternative name for a field during validation (but not serialization) | +| `serialization_alias` | `str` | Alternative name for a field during serialization (but not during validation) | +| `description` | `str` | Human-readable description | +| `gt` | `float` | Greater than. If set, value must be greater than this. Only applicable to numbers | +| `ge` | `float` | Greater than or equal. If set, value must be greater than or equal to this. Only applicable to numbers | +| `lt` | `float` | Less than. If set, value must be less than this. Only applicable to numbers | +| `le` | `float` | Less than or equal. If set, value must be less than or equal to this. Only applicable to numbers | +| `min_length` | `int` | Minimum length for strings | +| `max_length` | `int` | Maximum length for strings | +| `pattern` | `string` | A regular expression that the string must match. | +| `strict` | `bool` | If `True`, strict validation is applied to the field. See [Strict Mode](https://docs.pydantic.dev/latest/concepts/strict_mode/){target"_blank" rel="nofollow"} for details | +| `multiple_of` | `float` | Value must be a multiple of this. Only applicable to numbers | +| `allow_inf_nan` | `bool` | Allow `inf`, `-inf`, `nan`. Only applicable to numbers | +| `max_digits` | `int` | Maximum number of allow digits for strings | +| `decimal_places` | `int` | Maximum number of decimal places allowed for numbers | +| `examples` | `List[Any]` | List of examples of the field | +| `deprecated` | `bool` | Marks the field as deprecated | +| `include_in_schema` | `bool` | If `False` the field will not be part of the exported OpenAPI schema | +| `json_schema_extra` | `JsonDict` | Any additional JSON schema data for the schema property | diff --git a/docs/core/event_handler/api_gateway.md b/docs/core/event_handler/api_gateway.md index 032417ef701..97a1bf3c68e 100644 --- a/docs/core/event_handler/api_gateway.md +++ b/docs/core/event_handler/api_gateway.md @@ -971,46 +971,11 @@ In OpenAPI documentation tools like [SwaggerUI](#enabling-swaggerui), these anno #### Customizing OpenAPI parameters -Whenever you use OpenAPI parameters to validate [query strings](#validating-query-strings) or [path parameters](#validating-path-parameters), you can enhance validation and OpenAPI documentation by using any of these parameters: - -| Field name | Type | Description | -| --------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `alias` | `str` | Alternative name for a field, used when serializing and deserializing data | -| `validation_alias` | `str` | Alternative name for a field during validation (but not serialization) | -| `serialization_alias` | `str` | Alternative name for a field during serialization (but not during validation) | -| `description` | `str` | Human-readable description | -| `gt` | `float` | Greater than. If set, value must be greater than this. Only applicable to numbers | -| `ge` | `float` | Greater than or equal. If set, value must be greater than or equal to this. Only applicable to numbers | -| `lt` | `float` | Less than. If set, value must be less than this. Only applicable to numbers | -| `le` | `float` | Less than or equal. If set, value must be less than or equal to this. Only applicable to numbers | -| `min_length` | `int` | Minimum length for strings | -| `max_length` | `int` | Maximum length for strings | -| `pattern` | `string` | A regular expression that the string must match. | -| `strict` | `bool` | If `True`, strict validation is applied to the field. See [Strict Mode](https://docs.pydantic.dev/latest/concepts/strict_mode/){target"_blank" rel="nofollow"} for details | -| `multiple_of` | `float` | Value must be a multiple of this. Only applicable to numbers | -| `allow_inf_nan` | `bool` | Allow `inf`, `-inf`, `nan`. Only applicable to numbers | -| `max_digits` | `int` | Maximum number of allow digits for strings | -| `decimal_places` | `int` | Maximum number of decimal places allowed for numbers | -| `examples` | `List\[Any\]` | List of examples of the field | -| `deprecated` | `bool` | Marks the field as deprecated | -| `include_in_schema` | `bool` | If `False` the field will not be part of the exported OpenAPI schema | -| `json_schema_extra` | `JsonDict` | Any additional JSON schema data for the schema property | +--8<-- "docs/core/event_handler/_openapi_customization_parameters.md" #### Customizing API operations -Customize your API endpoints by adding metadata to endpoint definitions. This provides descriptive documentation for API consumers and gives extra instructions to the framework. - -Here's a breakdown of various customizable fields: - -| Field Name | Type | Description | -| ---------------------- |-----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `summary` | `str` | A concise overview of the main functionality of the endpoint. This brief introduction is usually displayed in autogenerated API documentation and helps consumers quickly understand what the endpoint does. | -| `description` | `str` | A more detailed explanation of the endpoint, which can include information about the operation's behavior, including side effects, error states, and other operational guidelines. | -| `responses` | `Dict[int, Dict[str, OpenAPIResponse]]` | A dictionary that maps each HTTP status code to a Response Object as defined by the [OpenAPI Specification](https://swagger.io/specification/#response-object). This allows you to describe expected responses, including default or error messages, and their corresponding schemas or models for different status codes. | -| `response_description` | `str` | Provides the default textual description of the response sent by the endpoint when the operation is successful. It is intended to give a human-readable understanding of the result. | -| `tags` | `List[str]` | Tags are a way to categorize and group endpoints within the API documentation. They can help organize the operations by resources or other heuristic. | -| `operation_id` | `str` | A unique identifier for the operation, which can be used for referencing this operation in documentation or code. This ID must be unique across all operations described in the API. | -| `include_in_schema` | `bool` | A boolean value that determines whether or not this operation should be included in the OpenAPI schema. Setting it to `False` can hide the endpoint from generated documentation and schema exports, which might be useful for private or experimental endpoints. | +--8<-- "docs/core/event_handler/_openapi_customization_operations.md" To implement these customizations, include extra parameters when defining your routes: @@ -1043,20 +1008,7 @@ Below is an example configuration for serving Swagger UI from a custom path or C #### Customizing OpenAPI metadata -Defining and customizing OpenAPI metadata gives detailed, top-level information about your API. Here's the method to set and tailor this metadata: - -| Field Name | Type | Description | -| ------------------ | -------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `title` | `str` | The title for your API. It should be a concise, specific name that can be used to identify the API in documentation or listings. | -| `version` | `str` | The version of the API you are documenting. This could reflect the release iteration of the API and helps clients understand the evolution of the API. | -| `openapi_version` | `str` | Specifies the version of the OpenAPI Specification on which your API is based. When using Pydantic v1 it defaults to 3.0.3, and when using Pydantic v2, it defaults to 3.1.0. | -| `summary` | `str` | A short and informative summary that can provide an overview of what the API does. This can be the same as or different from the title but should add context or information. **Not supported when using Pydantic v1** | -| `description` | `str` | A verbose description that can include Markdown formatting, providing a full explanation of the API's purpose, functionalities, and general usage instructions. | -| `tags` | `List[str]` | A collection of tags that categorize endpoints for better organization and navigation within the documentation. This can group endpoints by their functionality or other criteria. | -| `servers` | `List[Server]` | An array of Server objects, which specify the URL to the server and a description for its environment (production, staging, development, etc.), providing connectivity information. | -| `terms_of_service` | `str` | A URL that points to the terms of service for your API. This could provide legal information and user responsibilities related to the usage of the API. | -| `contact` | `Contact` | A Contact object containing contact details of the organization or individuals maintaining the API. This may include fields such as name, URL, and email. | -| `license_info` | `License` | A License object providing the license details for the API, typically including the name of the license and the URL to the full license text. | +--8<-- "docs/core/event_handler/_openapi_customization_metadata.md" Include extra parameters when exporting your OpenAPI specification to apply these customizations: diff --git a/docs/core/event_handler/bedrock_agents.md b/docs/core/event_handler/bedrock_agents.md new file mode 100644 index 00000000000..32aa2835491 --- /dev/null +++ b/docs/core/event_handler/bedrock_agents.md @@ -0,0 +1,330 @@ +--- +title: Agents for Amazon Bedrock +description: Core utility +--- + +Create [Agents for Amazon Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/agents.html#agents-how){target="_blank"} using event handlers and auto generation of OpenAPI schemas. + +
+```mermaid +--8<-- "docs/core/event_handler/bedrock_agents.mermaid" +``` +
+ +## Key features + +* Minimal boilerplate to build Agents for Amazon Bedrock +* Automatic generation of [OpenAPI schemas](https://www.openapis.org/){target="_blank"} from your business logic code +* Built-in data validation for requests and responses +* Similar experience to authoring [REST and HTTP APIs](api_gateway.md){target="_blank"} + +## Terminology + +**Data validation** automatically validates the user input and the response of your AWS Lambda function against a set of constraints defined by you. + +**Event handler** is a Powertools for AWS feature that processes an event, runs data parsing and validation, routes the request to a specific function, and returns a response to the caller in the proper format. + +**[OpenAPI schema](https://www.openapis.org/){target="_blank"}** is an industry standard JSON-serialized string that represents the structure and parameters of your API. + +**Action group** is a collection of two resources where you define the actions that the agent should carry out: an OpenAPI schema to define the APIs that the agent can invoke to carry out its tasks, and a Lambda function to execute those actions. + +**Large Language Models (LLM)** are very large deep learning models that are pre-trained on vast amounts of data, capable of extracting meanings from a sequence of text and understanding the relationship between words and phrases on it. + +**Agent for Amazon Bedrock** is an Amazon Bedrock feature to build and deploy conversational agents that can interact with your customers using Large Language Models (LLM) and AWS Lambda functions. + +## Getting started + +!!! tip "All examples shared in this documentation are available within the [project repository](https://github.com/aws-powertools/powertools-lambda-python/tree/develop/examples)" + +### Install + +!!! info "This is unnecessary if you're installing Powertools for AWS Lambda (Python) via [Lambda Layer/SAR](../../index.md#lambda-layer){target="_blank"}." + +You need to add `pydantic` as a dependency in your preferred tool _e.g., requirements.txt, pyproject.toml_. At this time, we only support Pydantic V1, due to an incompatibility with Pydantic V2 generated schemas and the Agents' API. + +### Required resources + +To build Agents for Amazon Bedrock, you will need: + +| Requirement | Description | SAM Supported | CDK Supported | +|-----------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|:-------------:|:-------------:| +| [Lambda Function](#your-first-agent) | Defines your business logic for the action group | ✅ | ✅ | +| [OpenAPI Schema](#generating-openapi-schemas) | API description, structure, and action group parameters | ❌ | ✅ | +| [Bedrock Service Role](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-permissions.html){target="_blank"} | Allows Amazon Bedrock to invoke foundation models | ✅ | ✅ | +| Agents for Bedrock | The service that will combine all the above to create the conversational agent | ❌ | ✅ | + +=== "Using AWS Serverless Application Model (SAM)" + Using [AWS SAM](https://aws.amazon.com/serverless/sam/){target="_blank"} you can create your Lambda function and the necessary permissions. However, you still have to create your Agent for Amazon Bedrock [using the AWS console](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-create.html){target="_blank"}. + + ```yaml hl_lines="18 26 34 61" + --8<-- "examples/event_handler_bedrock_agents/sam/template.yaml" + ``` + + 1. Amazon Bedrock needs permissions to invoke this Lambda function + 2. Check the [supported foundational models](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-supported.html){target="_blank"} + 3. You need the role ARN when creating the Agent for Amazon Bedrock + +=== "Using AWS Cloud Developer Kit (CDK)" + This example uses the [Generative AI CDK constructs](https://awslabs.github.io/generative-ai-cdk-constructs/src/cdk-lib/bedrock/#agents){target="_blank"} to create your Agent with [AWS CDK](https://aws.amazon.com/cdk/){target="_blank"}. + These constructs abstract the underlying permission setup and code bundling of your Lambda function. + + ```python + --8<-- "examples/event_handler_bedrock_agents/cdk/bedrock_agent_stack.py" + ``` + + 1. The path to your Lambda function handler + 2. The path to the OpenAPI schema describing your API + +### Your first Agent + +To create an agent, use the `BedrockAgentResolver` to annotate your actions. +This is similar to the way [all the other Event Handler](api_gateway.md) resolvers work. + +You are required to add a `description` parameter in each endpoint, doing so will improve Bedrock's understanding of your actions. + +=== "Lambda handler" + + The resolvers used by Agents for Amazon Bedrock are compatible with all Powertools for AWS Lambda [features](../../index.md#features){target="blank"}. + For reference, we use [Logger](../logger.md) and [Tracer](../tracer.md) in this example. + + ```python hl_lines="4 9 12 21" + --8<-- "examples/event_handler_bedrock_agents/src/getting_started.py" + ``` + + 1. `description` is a **required** field that should contain a human readable description of your action + 2. We take care of **parsing**, **validating**, **routing** and **responding** to the request. + +=== "OpenAPI schema" + + Powertools for AWS Lambda [generates this automatically](#generating-openapi-schemas) from the Lambda handler. + + ```json + --8<-- "examples/event_handler_bedrock_agents/src/getting_started_schema.json" + ``` + +=== "Input payload" + + ```json hl_lines="4 6 13" + --8<-- "examples/event_handler_bedrock_agents/src/getting_started.json" + ``` + +=== "Output payload" + + ```json hl_lines="10" + --8<-- "examples/event_handler_bedrock_agents/src/getting_started_output.json" + ``` + +??? note "What happens under the hood?" + Powertools will handle the request from the Agent, parse, validate, and route it to the correct method in your code. + The response is then validated and formatted back to the Agent. + +
+ ```mermaid + --8<-- "docs/core/event_handler/bedrock_agents_getting_started.mermaid" + ``` +
+ +### Validating input and output + +You can define the expected format for incoming data and responses by using type annotations. +Define constraints using standard Python types, [dataclasses](https://docs.python.org/3/library/dataclasses.html) or [Pydantic models](https://docs.pydantic.dev/latest/concepts/models/). +Pydantic is a popular library for data validation using Python type annotations. + +=== "Lambda handler" + This example uses [Pydantic's EmailStr](https://docs.pydantic.dev/2.0/usage/types/string_types/#emailstr){target="_blank"} to validate the email address passed to the `schedule_meeting` function. + The function then returns a boolean indicating if the meeting was successfully scheduled. + + ```python hl_lines="1 2 6 16-18" + --8<-- "examples/event_handler_bedrock_agents/src/getting_started_with_validation.py" + ``` + + 1. No need to add the `enable_validation` parameter, as it's enabled by default. + 2. Describe each input using human-readable descriptions + 3. Add the typing annotations to your parameters and return types, and let the event handler take care of the rest + +=== "OpenAPI schema" + + ```json + --8<-- "examples/event_handler_bedrock_agents/src/getting_started_with_validation_schema.json" + ``` + +=== "Input payload" + + ```json hl_lines="6-13 20" + --8<-- "examples/event_handler_bedrock_agents/src/getting_started_with_validation.json" + ``` + +=== "Output payload" + + ```json hl_lines="10" + --8<-- "examples/event_handler_bedrock_agents/src/getting_started_with_validation_output.json" + ``` + +#### When validation fails + +If the request validation fails, your event handler will not be called, and an error message is returned to Bedrock. +Similarly, if the response fails validation, your handler will abort the response. + +???+ info "What does this mean for my Agent?" + The event handler will always return a response according to the OpenAPI schema. + A validation failure always results in a [422 response](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422). + However, how Amazon Bedrock interprets that failure is non-deterministic, since it depends on the characteristics of the LLM being used. + +=== "Input payload" + + ```json hl_lines="11" + --8<-- "examples/event_handler_bedrock_agents/src/validation_failure_input.json" + ``` + +=== "Output payload" + + ```json hl_lines="10" + --8<-- "examples/event_handler_bedrock_agents/src/validation_failure_output.json" + ``` + +
+```mermaid +--8<-- "docs/core/event_handler/bedrock_agents_validation_sequence_diagram.mermaid" +``` +
+ +### Generating OpenAPI schemas + +Use the `get_openapi_json_schema` function provided by the resolver to produce a JSON-serialized string that represents your OpenAPI schema. +You can print this string or save it to a file. You'll use the file later when creating the Agent. + +You'll need to regenerate the OpenAPI schema and update your Agent everytime your API changes. + +=== "app.py" + + ```python hl_lines="24 25" + --8<-- "examples/event_handler_bedrock_agents/src/generating_openapi_schema.py" + ``` + + 1. This ensures that it's only executed when running the file directly, and not when running on the Lambda runtime. + 2. You can use [additional options](#customizing-openapi-metadata) to customize the OpenAPI schema. + +=== "OpenAPI schema" + + ```json hl_lines="13 16 24" + --8<-- "examples/event_handler_bedrock_agents/src/generating_openapi_schema.json" + ``` + +To get the OpenAPI schema, run the Python script from your terminal. +The script will generate the schema directly to standard output, which you can redirect to a file. + +```sh +python3 app.py > schema.json +``` + +### Crafting effective OpenAPI schemas + +Working with Agents for Amazon Bedrock will introduce [non-deterministic behaviour to your system](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-how.html#agents-rt){target="_blank"}. + +???+ note "Why is that?" + Amazon Bedrock uses LLMs to understand and respond to user input. + These models are trained on vast amounts of data and are capable of extracting meanings from a sequence of text and understanding the relationship between words and phrases on it. + However, this means that the same input can result in different outputs, depending on the characteristics of the LLM being used. + +The OpenAPI schema provides context and semantics to the Agent that will support the decision process for invoking our Lambda function. +Sparse or ambiguous schemas can result in unexpected outcomes. + +We recommend enriching your OpenAPI schema with as many details as possible to help the Agent understand your functions, and make correct invocations. +To achieve that, keep the following suggestions in mind: + +* Always describe your function behaviour using the `description` field in your annotations +* When refactoring, update your description field to match the function outcomes +* Use distinct `description` for each function to have clear separation of semantics + +### Video walkthrough + +To create an Agent for Amazon Bedrock, refer to the [official documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-create.html) provided by AWS. + +The following video demonstrates the end-to-end process: + +
+ +
+ +During the creation process, you should use the schema [previously generated](#generating-openapi-schemas) when prompted for an OpenAPI specification. + +## Advanced + +### Accessing custom request fields + +The event sent by Agents for Amazon Bedrock into your Lambda function contains a [number of extra event fields](#request_fields_table), exposed in the `app.current_event` field. + +???+ note "Why is this useful?" + You can for instance identify new conversations (`session_id`) or store and analyze entire conversations (`input_text`). + +=== "Accessing request fields" + + In this example, we [append correlation data](../logger.md#appending-additional-keys) to all generated logs. + This can be used to aggregate logs by `session_id` and observe the entire conversation between a user and the Agent. + + ```python hl_lines="13-16" + --8<-- "examples/event_handler_bedrock_agents/src/accessing_request_fields.py" + ``` + + +The input event fields are: + +| Name | Type | Description | +|---------------------------|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| message_version | `str` | The version of the message that identifies the format of the event data going into the Lambda function and the expected format of the response from a Lambda function. Amazon Bedrock only supports version 1.0. | +| agent | `BedrockAgentInfo` | Contains information about the name, ID, alias, and version of the agent that the action group belongs to. | +| input_text | `str` | The user input for the conversation turn. | +| session_id | `str` | The unique identifier of the agent session. | +| action_group | `str` | The name of the action group. | +| api_path | `str` | The path to the API operation, as defined in the OpenAPI schema. | +| http_method | `str` | The method of the API operation, as defined in the OpenAPI schema. | +| parameters | `List[BedrockAgentProperty]` | Contains a list of objects. Each object contains the name, type, and value of a parameter in the API operation, as defined in the OpenAPI schema. | +| request_body | `BedrockAgentRequestBody` | Contains the request body and its properties, as defined in the OpenAPI schema. | +| session_attributes | `Dict[str, str]` | Contains session attributes and their values. | +| prompt_session_attributes | `Dict[str, str]` | Contains prompt attributes and their values. | + +### Additional metadata + +To enrich the view that Agents for Amazon Bedrock has of your Lambda functions, +use a combination of [Pydantic Models](https://docs.pydantic.dev/latest/concepts/models/){target="_blank"} and [OpenAPI](https://www.openapis.org/){target="_blank"} type annotations to add constraints to your APIs parameters. + +???+ info "When is this useful?" + Adding constraints to your function parameters can help you to enforce data validation and improve the understanding of your APIs by Amazon Bedrock. + +#### Customizing OpenAPI parameters + +--8<-- "docs/core/event_handler/_openapi_customization_parameters.md" + +To implement these customizations, include extra constraints when defining your parameters: + +```python hl_lines="19" title="customizing_api_parameters.py" title="Customizing API parameters" +--8<-- "examples/event_handler_bedrock_agents/src/customizing_bedrock_api_parameters.py" +``` + +1. Title should not be larger than 200 characters and [strict mode](https://docs.pydantic.dev/latest/concepts/strict_mode/){target="_blank"} is activated + +#### Customizing API operations + +--8<-- "docs/core/event_handler/_openapi_customization_operations.md" + +To implement these customizations, include extra parameters when defining your routes: + +```python hl_lines="13-22" title="customizing_api_operations.py" title="Customzing API operations" +--8<-- "examples/event_handler_bedrock_agents/src/customizing_bedrock_api_operations.py" +``` + +## Testing your code + +Test your routes by passing an [Agent for Amazon Bedrock proxy event](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-lambda.html#agents-lambda-input) request: + +=== "assert_bedrock_agent_response.py" + + ```python hl_lines="21-23 27" + --8<-- "examples/event_handler_bedrock_agents/src/assert_bedrock_agent_response.py" + ``` + +=== "assert_bedrock_agent_response_module.py" + + ```python hl_lines="14-17" + --8<-- "examples/event_handler_bedrock_agents/src/assert_bedrock_agent_response_module.py" + ``` diff --git a/docs/core/event_handler/bedrock_agents.mermaid b/docs/core/event_handler/bedrock_agents.mermaid new file mode 100644 index 00000000000..19ae2270234 --- /dev/null +++ b/docs/core/event_handler/bedrock_agents.mermaid @@ -0,0 +1,28 @@ +flowchart LR + Bedrock[LLM] <-- uses --> Agent + You[User input] --> Agent + Agent -- consults --> OpenAPI + Agent[Agents for Amazon Bedrock] -- invokes --> Lambda + + subgraph OpenAPI + Schema + end + + subgraph Lambda[Lambda Function] + direction TB + Parsing[Parameter Parsing] --> Validation + Validation[Parameter Validation] --> Routing + Routing --> Code[Your code] + Code --> ResponseValidation[Response Validation] + ResponseValidation --> ResponseBuilding[Response Building] + end + + subgraph ActionGroup[Action Group] + OpenAPI -. generated from .-> Lambda + end + + style Code fill:#ffa500,color:black,font-weight:bold,stroke-width:3px + style You stroke:#0F0,stroke-width:2px + + + diff --git a/docs/core/event_handler/bedrock_agents_getting_started.mermaid b/docs/core/event_handler/bedrock_agents_getting_started.mermaid new file mode 100644 index 00000000000..29f3a26e323 --- /dev/null +++ b/docs/core/event_handler/bedrock_agents_getting_started.mermaid @@ -0,0 +1,33 @@ +sequenceDiagram + actor User + + User->>Agent: What is the current time? + Agent->>OpenAPI schema: consults + OpenAPI schema-->>Agent: GET /current_time + Agent-->>Agent: LLM interaction + + box Powertools + participant Lambda + participant Parsing + participant Validation + participant Routing + participant Your Code + end + + Agent->>Lambda: GET /current_time + activate Lambda + Lambda->>Parsing: parses parameters + Parsing->>Validation: validates input + Validation->>Routing: finds method to call + Routing->>Your Code: executes + activate Your Code + Your Code->>Routing: 1709215709 + deactivate Your Code + Routing->>Validation: returns output + Validation->>Parsing: validates output + Parsing->>Lambda: formats response + Lambda->>Agent: 1709215709 + deactivate Lambda + + Agent-->>Agent: LLM interaction + Agent->>User: "The current time is 14:08:29 GMT" diff --git a/docs/core/event_handler/bedrock_agents_validation_sequence_diagram.mermaid b/docs/core/event_handler/bedrock_agents_validation_sequence_diagram.mermaid new file mode 100644 index 00000000000..96158c76786 --- /dev/null +++ b/docs/core/event_handler/bedrock_agents_validation_sequence_diagram.mermaid @@ -0,0 +1,16 @@ +sequenceDiagram + Agent->>Lambda: input payload + activate Lambda + Lambda->>Parsing: parses input parameters + Parsing->>Validation: validates input + Validation-->Validation: failure + box BedrockAgentResolver + participant Lambda + participant Parsing + participant Validation + participant Routing + participant Your Code + end + Note right of Validation: Your code is never called + Validation->>Agent: 422 response + deactivate Lambda diff --git a/examples/event_handler_bedrock_agents/cdk/bedrock_agent_stack.py b/examples/event_handler_bedrock_agents/cdk/bedrock_agent_stack.py new file mode 100644 index 00000000000..ef220209d6d --- /dev/null +++ b/examples/event_handler_bedrock_agents/cdk/bedrock_agent_stack.py @@ -0,0 +1,40 @@ +from aws_cdk import ( + Stack, +) +from aws_cdk.aws_lambda import Runtime +from aws_cdk.aws_lambda_python_alpha import PythonFunction +from cdklabs.generative_ai_cdk_constructs.bedrock import ( + Agent, + ApiSchema, + BedrockFoundationModel, +) +from constructs import Construct + + +class AgentsCdkStack(Stack): + + def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: + super().__init__(scope, construct_id, **kwargs) + + action_group_function = PythonFunction( + self, + "LambdaFunction", + runtime=Runtime.PYTHON_3_12, + entry="./lambda", # (1)! + index="app.py", + handler="lambda_handler", + ) + + agent = Agent( + self, + "Agent", + foundation_model=BedrockFoundationModel.ANTHROPIC_CLAUDE_INSTANT_V1_2, + instruction="You are a helpful and friendly agent that answers questions about insurance claims.", + ) + agent.add_action_group( + action_group_name="InsureClaimsSupport", + description="Use these functions for insurance claims support", + action_group_executor=action_group_function, + action_group_state="ENABLED", + api_schema=ApiSchema.from_asset("./lambda/openapi.json"), # (2)! + ) diff --git a/examples/event_handler_bedrock_agents/sam/template.yaml b/examples/event_handler_bedrock_agents/sam/template.yaml new file mode 100644 index 00000000000..34d4cb25ec7 --- /dev/null +++ b/examples/event_handler_bedrock_agents/sam/template.yaml @@ -0,0 +1,61 @@ +AWSTemplateFormatVersion: "2010-09-09" +Transform: AWS::Serverless-2016-10-31 +Description: > + Agents for Amazon Bedrock example with Powertools for AWS Lambda (Python) + +Globals: # https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-template-anatomy-globals.html + Function: + Timeout: 30 + Runtime: python3.12 + Tracing: Active + Environment: + Variables: + POWERTOOLS_SERVICE_NAME: PowertoolsHelloWorld + POWERTOOLS_LOG_LEVEL: INFO + +Resources: + ApiFunction: + Type: AWS::Serverless::Function + Properties: + Handler: getting_started.lambda_handler + Description: Agent for Amazon Bedrock handler function + CodeUri: ../src + + + BedrockPermission: # (1)! + Type: AWS::Lambda::Permission + Properties: + Action: lambda:InvokeFunction + FunctionName: !GetAtt ApiFunction.Arn + Principal: bedrock.amazonaws.com + SourceAccount: !Sub ${AWS::AccountId} + + BedrockServiceRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Action: + - sts:assumeRole + Service: + - bedrock.amazonaws.com + Policies: + - PolicyName: bedrock + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - bedrock:InvokeModel + Resource: # (2)! + - !Sub arn:aws:${AWS::Region}:region::foundation-model/anthropic.claude-v2 + - !Sub arn:aws:${AWS::Region}:region::foundation-model/anthropic.claude-v2:1 + - !Sub arn:aws:${AWS::Region}:region::foundation-model/anthropic.claude-instant-v1 + +Outputs: + BedrockServiceRole: + Description: The role ARN to be used by Amazon Bedrock + Value: !GetAtt BedrockServiceRole.Arn # (3)! diff --git a/examples/event_handler_bedrock_agents/src/accessing_request_fields.py b/examples/event_handler_bedrock_agents/src/accessing_request_fields.py new file mode 100644 index 00000000000..529c9343702 --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/accessing_request_fields.py @@ -0,0 +1,25 @@ +from time import time + +from aws_lambda_powertools import Logger +from aws_lambda_powertools.event_handler import BedrockAgentResolver +from aws_lambda_powertools.utilities.typing import LambdaContext + +logger = Logger() +app = BedrockAgentResolver() + + +@app.get("/current_time", description="Gets the current time in seconds") # (1)! +def current_time() -> int: + logger.append_keys( + session_id=app.current_event.session_id, + action_group=app.current_event.action_group, + input_text=app.current_event.input_text, + ) + + logger.info("Serving current_time") + return int(time()) + + +@logger.inject_lambda_context +def lambda_handler(event: dict, context: LambdaContext): + return app.resolve(event, context) diff --git a/examples/event_handler_bedrock_agents/src/assert_bedrock_agent_response.py b/examples/event_handler_bedrock_agents/src/assert_bedrock_agent_response.py new file mode 100644 index 00000000000..07f3273961e --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/assert_bedrock_agent_response.py @@ -0,0 +1,29 @@ +from dataclasses import dataclass + +import assert_bedrock_agent_response_module +import pytest + + +@pytest.fixture +def lambda_context(): + @dataclass + class LambdaContext: + function_name: str = "test" + memory_limit_in_mb: int = 128 + invoked_function_arn: str = "arn:aws:lambda:eu-west-1:123456789012:function:test" + aws_request_id: str = "da658bd3-2d6f-4e7b-8ec2-937234644fdc" + + return LambdaContext() + + +def test_lambda_handler(lambda_context): + minimal_event = { + "apiPath": "/current_time", + "httpMethod": "GET", + "inputText": "What is the current time?", + } + # Example of Bedrock Agent API request event: + # https://docs.aws.amazon.com/bedrock/latest/userguide/agents-lambda.html#agents-lambda-input + ret = assert_bedrock_agent_response_module.lambda_handler(minimal_event, lambda_context) + assert ret["response"]["httpStatuScode"] == 200 + assert ret["response"]["responseBody"]["application/json"]["body"] != "" diff --git a/examples/event_handler_bedrock_agents/src/assert_bedrock_agent_response_module.py b/examples/event_handler_bedrock_agents/src/assert_bedrock_agent_response_module.py new file mode 100644 index 00000000000..d197e470595 --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/assert_bedrock_agent_response_module.py @@ -0,0 +1,23 @@ +import time + +from aws_lambda_powertools import Logger, Tracer +from aws_lambda_powertools.event_handler import BedrockAgentResolver +from aws_lambda_powertools.event_handler.openapi.params import Body +from aws_lambda_powertools.shared.types import Annotated +from aws_lambda_powertools.utilities.typing import LambdaContext + +tracer = Tracer() +logger = Logger() +app = BedrockAgentResolver() + + +@app.get("/current_time", description="Gets the current time") +@tracer.capture_method +def current_time() -> Annotated[int, Body(description="Current time in milliseconds")]: + return round(time.time() * 1000) + + +@logger.inject_lambda_context +@tracer.capture_lambda_handler +def lambda_handler(event: dict, context: LambdaContext) -> dict: + return app.resolve(event, context) diff --git a/examples/event_handler_bedrock_agents/src/customizing_bedrock_api_metadata.py b/examples/event_handler_bedrock_agents/src/customizing_bedrock_api_metadata.py new file mode 100644 index 00000000000..f5d9ff6ba3d --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/customizing_bedrock_api_metadata.py @@ -0,0 +1,33 @@ +import requests + +from aws_lambda_powertools.event_handler import BedrockAgentResolver +from aws_lambda_powertools.event_handler.openapi.models import Contact, Server +from aws_lambda_powertools.utilities.typing import LambdaContext + +app = BedrockAgentResolver() + + +@app.get("/todos/", description="Gets a todo title by ID") +def get_todo_title(todo_id: int) -> str: + todo = requests.get(f"https://jsonplaceholder.typicode.com/todos/{todo_id}") + todo.raise_for_status() + + return todo.json()["title"] + + +def lambda_handler(event: dict, context: LambdaContext) -> dict: + return app.resolve(event, context) + + +if __name__ == "__main__": + print( + app.get_openapi_json_schema( + title="TODO's API", + version="1.21.3", + summary="API to manage TODOs", + description="This API implements all the CRUD operations for the TODO app", + tags=["todos"], + servers=[Server(url="https://stg.example.org/orders", description="Staging server")], + contact=Contact(name="John Smith", email="john@smith.com"), + ), + ) diff --git a/examples/event_handler_bedrock_agents/src/customizing_bedrock_api_operations.py b/examples/event_handler_bedrock_agents/src/customizing_bedrock_api_operations.py new file mode 100644 index 00000000000..6eb2393b263 --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/customizing_bedrock_api_operations.py @@ -0,0 +1,34 @@ +import requests +from typing_extensions import Annotated + +from aws_lambda_powertools.event_handler import BedrockAgentResolver +from aws_lambda_powertools.event_handler.openapi.params import Body, Query +from aws_lambda_powertools.utilities.typing import LambdaContext + +app = BedrockAgentResolver() + + +@app.get( + "/todos/", + summary="Retrieves a TODO item, returning it's title", + description="Loads a TODO item identified by the `todo_id`", + response_description="The TODO title", + responses={ + 200: {"description": "TODO item found"}, + 404: { + "description": "TODO not found", + }, + }, + tags=["todos"], +) +def get_todo_title( + todo_id: Annotated[int, Query(description="The ID of the TODO item to get the title from")], +) -> Annotated[str, Body(description="The TODO title")]: + todo = requests.get(f"https://jsonplaceholder.typicode.com/todos/{todo_id}") + todo.raise_for_status() + + return todo.json()["title"] + + +def lambda_handler(event: dict, context: LambdaContext) -> dict: + return app.resolve(event, context) diff --git a/examples/event_handler_bedrock_agents/src/customizing_bedrock_api_parameters.py b/examples/event_handler_bedrock_agents/src/customizing_bedrock_api_parameters.py new file mode 100644 index 00000000000..5834959d15b --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/customizing_bedrock_api_parameters.py @@ -0,0 +1,31 @@ +import requests +from typing_extensions import Annotated + +from aws_lambda_powertools import Logger +from aws_lambda_powertools.event_handler import BedrockAgentResolver +from aws_lambda_powertools.event_handler.openapi.params import Body, Query +from aws_lambda_powertools.utilities.typing import LambdaContext + +app = BedrockAgentResolver() + +logger = Logger() + + +@app.post( + "/todos", + description="Creates a TODO", +) +def create_todo( + title: Annotated[str, Query(max_length=200, strict=True, description="The TODO title")], # (1)! +) -> Annotated[bool, Body(description="Was the TODO created correctly?")]: + todo = requests.post("https://jsonplaceholder.typicode.com/todos", data={"title": title}) + try: + todo.raise_for_status() + return True + except Exception: + logger.exception("Error creating TODO") + return False + + +def lambda_handler(event: dict, context: LambdaContext) -> dict: + return app.resolve(event, context) diff --git a/examples/event_handler_bedrock_agents/src/generating_openapi_schema.json b/examples/event_handler_bedrock_agents/src/generating_openapi_schema.json new file mode 100644 index 00000000000..7e492082d43 --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/generating_openapi_schema.json @@ -0,0 +1,94 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Powertools API", + "version": "1.0.0" + }, + "servers": [ + { + "url": "/" + } + ], + "paths": { + "/current_time": { + "get": { + "summary": "GET /current_time", + "description": "Gets the current time in seconds", + "operationId": "current_time_current_time_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "integer", + "title": "Return" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + } + } + } +} diff --git a/examples/event_handler_bedrock_agents/src/generating_openapi_schema.py b/examples/event_handler_bedrock_agents/src/generating_openapi_schema.py new file mode 100644 index 00000000000..fba9d10d2ce --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/generating_openapi_schema.py @@ -0,0 +1,25 @@ +from time import time + +from aws_lambda_powertools import Logger, Tracer +from aws_lambda_powertools.event_handler import BedrockAgentResolver +from aws_lambda_powertools.utilities.typing import LambdaContext + +tracer = Tracer() +logger = Logger() +app = BedrockAgentResolver() + + +@app.get("/current_time", description="Gets the current time in seconds") +@tracer.capture_method +def current_time() -> int: + return int(time()) + + +@logger.inject_lambda_context +@tracer.capture_lambda_handler +def lambda_handler(event: dict, context: LambdaContext): + return app.resolve(event, context) + + +if __name__ == "__main__": # (1)! + print(app.get_openapi_json_schema()) # (2)! diff --git a/examples/event_handler_bedrock_agents/src/getting_started.json b/examples/event_handler_bedrock_agents/src/getting_started.json new file mode 100644 index 00000000000..bc0f462de6d --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/getting_started.json @@ -0,0 +1,16 @@ +{ + "sessionId": "123456789012345", + "sessionAttributes": {}, + "inputText": "What is the current time?", + "promptSessionAttributes": {}, + "apiPath": "/current_time", + "agent": { + "name": "TimeAgent", + "version": "DRAFT", + "id": "XLHH72XNF2", + "alias": "TSTALIASID" + }, + "httpMethod": "GET", + "messageVersion": "1.0", + "actionGroup": "CurrentTime" +} diff --git a/examples/event_handler_bedrock_agents/src/getting_started.py b/examples/event_handler_bedrock_agents/src/getting_started.py new file mode 100644 index 00000000000..b937ac2953a --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/getting_started.py @@ -0,0 +1,21 @@ +from time import time + +from aws_lambda_powertools import Logger, Tracer +from aws_lambda_powertools.event_handler import BedrockAgentResolver +from aws_lambda_powertools.utilities.typing import LambdaContext + +tracer = Tracer() +logger = Logger() +app = BedrockAgentResolver() + + +@app.get("/current_time", description="Gets the current time in seconds") # (1)! +@tracer.capture_method +def current_time() -> int: + return int(time()) + + +@logger.inject_lambda_context +@tracer.capture_lambda_handler +def lambda_handler(event: dict, context: LambdaContext): + return app.resolve(event, context) # (2)! diff --git a/examples/event_handler_bedrock_agents/src/getting_started_output.json b/examples/event_handler_bedrock_agents/src/getting_started_output.json new file mode 100644 index 00000000000..373d8b604bd --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/getting_started_output.json @@ -0,0 +1,14 @@ +{ + "messageVersion": "1.0", + "response": { + "actionGroup": "CurrentTime", + "apiPath": "/current_time", + "httpMethod": "GET", + "httpStatusCode": 200, + "responseBody": { + "application/json": { + "body": "1704708165" + } + } + } +} diff --git a/examples/event_handler_bedrock_agents/src/getting_started_schema.json b/examples/event_handler_bedrock_agents/src/getting_started_schema.json new file mode 100644 index 00000000000..7e492082d43 --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/getting_started_schema.json @@ -0,0 +1,94 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Powertools API", + "version": "1.0.0" + }, + "servers": [ + { + "url": "/" + } + ], + "paths": { + "/current_time": { + "get": { + "summary": "GET /current_time", + "description": "Gets the current time in seconds", + "operationId": "current_time_current_time_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "integer", + "title": "Return" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + } + } + } +} diff --git a/examples/event_handler_bedrock_agents/src/getting_started_with_validation.json b/examples/event_handler_bedrock_agents/src/getting_started_with_validation.json new file mode 100644 index 00000000000..ccabb84a737 --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/getting_started_with_validation.json @@ -0,0 +1,23 @@ +{ + "sessionId": "123456789012345", + "sessionAttributes": {}, + "inputText": "Schedule a meeting with the team. My email is foo@example.org", + "promptSessionAttributes": {}, + "apiPath": "/schedule_meeting", + "parameters": [ + { + "name": "email", + "type": "string", + "value": "foo@example.org" + } + ], + "agent": { + "name": "TimeAgent", + "version": "DRAFT", + "id": "XLHH72XNF2", + "alias": "TSTALIASID" + }, + "httpMethod": "GET", + "messageVersion": "1.0", + "actionGroup": "SupportAssistant" +} diff --git a/examples/event_handler_bedrock_agents/src/getting_started_with_validation.py b/examples/event_handler_bedrock_agents/src/getting_started_with_validation.py new file mode 100644 index 00000000000..1a2ce9742a0 --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/getting_started_with_validation.py @@ -0,0 +1,26 @@ +from pydantic import EmailStr +from typing_extensions import Annotated + +from aws_lambda_powertools import Logger, Tracer +from aws_lambda_powertools.event_handler import BedrockAgentResolver +from aws_lambda_powertools.event_handler.openapi.params import Body, Query +from aws_lambda_powertools.utilities.typing import LambdaContext + +tracer = Tracer() +logger = Logger() +app = BedrockAgentResolver() # (1)! + + +@app.get("/schedule_meeting", description="Schedules a meeting with the team") +@tracer.capture_method +def schedule_meeting( + email: Annotated[EmailStr, Query(description="The email address of the customer")], # (2)! +) -> Annotated[bool, Body(description="Whether the meeting was scheduled successfully")]: # (3)! + logger.info("Scheduling a meeting", email=email) + return True + + +@logger.inject_lambda_context +@tracer.capture_lambda_handler +def lambda_handler(event: dict, context: LambdaContext): + return app.resolve(event, context) diff --git a/examples/event_handler_bedrock_agents/src/getting_started_with_validation_output.json b/examples/event_handler_bedrock_agents/src/getting_started_with_validation_output.json new file mode 100644 index 00000000000..6f791a3155f --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/getting_started_with_validation_output.json @@ -0,0 +1,14 @@ +{ + "messageVersion": "1.0", + "response": { + "actionGroup": "SupportAssistant", + "apiPath": "/schedule_meeting", + "httpMethod": "GET", + "httpStatusCode": 200, + "responseBody": { + "application/json": { + "body": "true" + } + } + } +} diff --git a/examples/event_handler_bedrock_agents/src/getting_started_with_validation_schema.json b/examples/event_handler_bedrock_agents/src/getting_started_with_validation_schema.json new file mode 100644 index 00000000000..0d307287096 --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/getting_started_with_validation_schema.json @@ -0,0 +1,109 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Powertools API", + "version": "1.0.0" + }, + "servers": [ + { + "url": "/" + } + ], + "paths": { + "/schedule_meeting": { + "get": { + "summary": "GET /schedule_meeting", + "description": "Schedules a meeting with the team", + "operationId": "schedule_meeting_schedule_meeting_get", + "parameters": [ + { + "description": "The email address of the customer", + "required": true, + "schema": { + "type": "string", + "format": "email", + "title": "Email", + "description": "The email address of the customer" + }, + "name": "email", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Return", + "description": "Whether the meeting was scheduled successfully" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + } + } + } +} diff --git a/examples/event_handler_bedrock_agents/src/validation_failure_input.json b/examples/event_handler_bedrock_agents/src/validation_failure_input.json new file mode 100644 index 00000000000..ca8e73b0fcc --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/validation_failure_input.json @@ -0,0 +1,23 @@ +{ + "sessionId": "123456789012345", + "sessionAttributes": {}, + "inputText": "Schedule a meeting with the team. My email is foo@example@org", + "promptSessionAttributes": {}, + "apiPath": "/schedule_meeting", + "parameters": [ + { + "name": "email", + "type": "string", + "value": "foo@example@org" + } + ], + "agent": { + "name": "TimeAgent", + "version": "DRAFT", + "id": "XLHH72XNF2", + "alias": "TSTALIASID" + }, + "httpMethod": "GET", + "messageVersion": "1.0", + "actionGroup": "SupportAssistant" +} diff --git a/examples/event_handler_bedrock_agents/src/validation_failure_output.json b/examples/event_handler_bedrock_agents/src/validation_failure_output.json new file mode 100644 index 00000000000..e5faa7fe524 --- /dev/null +++ b/examples/event_handler_bedrock_agents/src/validation_failure_output.json @@ -0,0 +1,14 @@ +{ + "messageVersion": "1.0", + "response": { + "actionGroup": "SupportAssistant", + "apiPath": "/schedule_meeting", + "httpMethod": "GET", + "httpStatusCode": 200, + "responseBody": { + "application/json": { + "body": "{\"statusCode\":422,\"detail\":[{\"loc\":[\"query\",\"email\"],\"type\":\"value_error.email\"}]}" + } + } + } +} diff --git a/mkdocs.yml b/mkdocs.yml index fc3373b5c98..17a8f1a73a9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -22,6 +22,7 @@ nav: - Event Handler: - core/event_handler/api_gateway.md - core/event_handler/appsync.md + - core/event_handler/bedrock_agents.md - utilities/parameters.md - utilities/batch.md - utilities/typing.md diff --git a/poetry.lock b/poetry.lock index 4cc2a45d1af..954dd3e948e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.1 and should not be changed by hand. [[package]] name = "anyio" @@ -156,6 +156,24 @@ jsii = ">=1.92.0,<2.0.0" publication = ">=0.0.3" typeguard = ">=2.13.3,<2.14.0" +[[package]] +name = "aws-cdk-aws-lambda-python-alpha" +version = "2.131.0a0" +description = "The CDK Construct Library for AWS Lambda in Python" +optional = false +python-versions = "~=3.8" +files = [ + {file = "aws-cdk.aws-lambda-python-alpha-2.131.0a0.tar.gz", hash = "sha256:ab93373302059a147bc8dc393d293035aae654d1d4bdbfd3aa4a12481abd8cce"}, + {file = "aws_cdk.aws_lambda_python_alpha-2.131.0a0-py3-none-any.whl", hash = "sha256:3725708b5099053e3c4814a80cf9d073f4ccf43d683e0bc53e895f61df7cc481"}, +] + +[package.dependencies] +aws-cdk-lib = ">=2.131.0,<3.0.0" +constructs = ">=10.0.0,<11.0.0" +jsii = ">=1.94.0,<2.0.0" +publication = ">=0.0.3" +typeguard = ">=2.13.3,<2.14.0" + [[package]] name = "aws-cdk-lib" version = "2.131.0" @@ -330,17 +348,17 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "boto3" -version = "1.34.50" +version = "1.34.55" description = "The AWS SDK for Python" optional = false python-versions = ">= 3.8" files = [ - {file = "boto3-1.34.50-py3-none-any.whl", hash = "sha256:8d709365231234bc4f0ca98fdf33a25eeebf78072853c6aa3d259f0f5cf09877"}, - {file = "boto3-1.34.50.tar.gz", hash = "sha256:290952be7899560039cb0042e8a2354f61a7dead0d0ca8bea6ba901930df0468"}, + {file = "boto3-1.34.55-py3-none-any.whl", hash = "sha256:ee2c96e8a4a741ecb3380e0a406baa67bfea6186be99b75bdeca3e1b5044c088"}, + {file = "boto3-1.34.55.tar.gz", hash = "sha256:9a6d59e035fac4366dbdaf909c4f66fc817dfbec044fa71564dcf036ad46bb19"}, ] [package.dependencies] -botocore = ">=1.34.50,<1.35.0" +botocore = ">=1.34.55,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -349,13 +367,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.50" +version = "1.34.55" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">= 3.8" files = [ - {file = "botocore-1.34.50-py3-none-any.whl", hash = "sha256:fda510559dbe796eefdb59561cc81be1b99afba3dee53fd23db9a3d587adc0ab"}, - {file = "botocore-1.34.50.tar.gz", hash = "sha256:33ab82cb96c4bb684f0dbafb071808e4817d83debc88b223e7d988256370c6d7"}, + {file = "botocore-1.34.55-py3-none-any.whl", hash = "sha256:07044c3cbfb86d0ecb9c56d887b8ad63a72eff0e4f6ab329cf335f1fd867ea0b"}, + {file = "botocore-1.34.55.tar.gz", hash = "sha256:bb333e3845bfe65600f36bf92d09668306e224fa9f4e4f87b77f6957192ae59f"}, ] [package.dependencies] @@ -408,6 +426,43 @@ pyyaml = ["pyyaml (>=6.0)"] tomlkit = ["tomlkit (>=0.11.8)"] ujson = ["ujson (>=5.7.0)"] +[[package]] +name = "cdk-nag" +version = "2.28.54" +description = "Check CDK v2 applications for best practices using a combination on available rule packs." +optional = false +python-versions = "~=3.8" +files = [ + {file = "cdk-nag-2.28.54.tar.gz", hash = "sha256:55f17c15369e38ec1f3b8dc40ff1830bfe0452c95c35d1e37317dc36269be3ce"}, + {file = "cdk_nag-2.28.54-py3-none-any.whl", hash = "sha256:bf901e9f33d39b9d2bfa7117aef14917f708b518494f2247dbdbdba666154164"}, +] + +[package.dependencies] +aws-cdk-lib = ">=2.116.0,<3.0.0" +constructs = ">=10.0.5,<11.0.0" +jsii = ">=1.94.0,<2.0.0" +publication = ">=0.0.3" +typeguard = ">=2.13.3,<2.14.0" + +[[package]] +name = "cdklabs-generative-ai-cdk-constructs" +version = "0.1.81" +description = "AWS Generative AI CDK Constructs is a library for well-architected generative AI patterns." +optional = false +python-versions = "~=3.8" +files = [ + {file = "cdklabs.generative-ai-cdk-constructs-0.1.81.tar.gz", hash = "sha256:c9cd1cef2f97ee17846be91611142dfaf7147a698eeeba6346819160f5d5b611"}, + {file = "cdklabs.generative_ai_cdk_constructs-0.1.81-py3-none-any.whl", hash = "sha256:0d8d186ee6bc802707ecfcaf6372f43158fd0f0f3e303c8865569a4a03517dce"}, +] + +[package.dependencies] +aws-cdk-lib = ">=2.122.0,<3.0.0" +cdk-nag = ">=2.28.52,<3.0.0" +constructs = ">=10.3.0,<11.0.0" +jsii = ">=1.94.0,<2.0.0" +publication = ">=0.0.3" +typeguard = ">=2.13.3,<2.14.0" + [[package]] name = "certifi" version = "2024.2.2" @@ -832,71 +887,71 @@ six = "*" [[package]] name = "ddtrace" -version = "2.6.5" +version = "2.7.0" description = "Datadog APM client library" optional = false python-versions = ">=3.7" files = [ - {file = "ddtrace-2.6.5-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:04b0849a7017401ac05b1706e6ba52a04b142e454ed28d57f55620ec511077a8"}, - {file = "ddtrace-2.6.5-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:765386e0f13603e4747a7530b6f96dc0b524dcae80d22b2599bbe32db0f0db7b"}, - {file = "ddtrace-2.6.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6212e5fe2b8d16ee8d63b5992276cd48b3d8904364598d67735072a02c9c9d61"}, - {file = "ddtrace-2.6.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d732dbcc9f95f6133aff77d9a185a6d624ed3650764cb305244ad08da7a58f7"}, - {file = "ddtrace-2.6.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ebda479f5ee2ad139e2af23e7c1295fbbf0612a618ffed1fa0feee22a3c265c"}, - {file = "ddtrace-2.6.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:098db83b90725a612889d9228dc4047ed82ba2078c9bd2e026c334f7ef6d8d82"}, - {file = "ddtrace-2.6.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1e9dde12dc9f9864eda2936b73580d61d089559c8908d967aad0ac5a3e5d3f6d"}, - {file = "ddtrace-2.6.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3975099aa4ade5728b90d79b15bfe2aec84ea134f1ce746ac66fd05d38a232b6"}, - {file = "ddtrace-2.6.5-cp310-cp310-win32.whl", hash = "sha256:ca386c616073542f5fe5333864184e5714cb7685f83cfd659ce9bb241e634361"}, - {file = "ddtrace-2.6.5-cp310-cp310-win_amd64.whl", hash = "sha256:c4978637d6018e1fbd3abe2176d459d783052a5a8f946fccdd62e80ebbaf7d7a"}, - {file = "ddtrace-2.6.5-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:5b17c78ba62a2ed0fe095f96aeb5952baf36a5a449ec667a00aa84943e9a51c9"}, - {file = "ddtrace-2.6.5-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5bb9ba523affeea2ecdc9420bdd6b3393b4e15728eb6b5bc18619fc6ebe80db7"}, - {file = "ddtrace-2.6.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eafd0be4b50f4208f948451468e64973136a282fd08e1567d65ba77e7927ab7"}, - {file = "ddtrace-2.6.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d846c0209fd6354098e841857b7f96d38ec87ba4933460abce0a0222d678920"}, - {file = "ddtrace-2.6.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82037fcd43c23a3616c723041fae15b4ac1cd90489d92a05d265f83d11d6a478"}, - {file = "ddtrace-2.6.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:585b8c5ce46a1abfdeb4b07a15ef5d8a929bd7719bfdaa6e7f7cdda0daec61e2"}, - {file = "ddtrace-2.6.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:67083e775f8d77404184217735fcc8181104130c657e8c12ae1fe04749164ccc"}, - {file = "ddtrace-2.6.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dacd18d2ffbbfb2d63bd0dc840f40f5dbdeb6dfed9ace61a719dc42b3edf26c2"}, - {file = "ddtrace-2.6.5-cp311-cp311-win32.whl", hash = "sha256:ee385ec24d1a8c5cd3b832b754ff88837dcce1a9ed6b0119da789bd00eaf7483"}, - {file = "ddtrace-2.6.5-cp311-cp311-win_amd64.whl", hash = "sha256:341331b0698d149e98a0b7dd557af878c5a4b16f531cecec7f7a09f88abed87c"}, - {file = "ddtrace-2.6.5-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:cb28b497bd6aceae95e7142f13d8571be960c2e884a63780d2c84fcc7688edeb"}, - {file = "ddtrace-2.6.5-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:078d6760e41b12647215ce547ac632687e20c16f4472eae7eb43644a4cca9e68"}, - {file = "ddtrace-2.6.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:678094a0626c4b1fc3cad2c9e938158f39faabcb094867bb0603b9a4c5d360d0"}, - {file = "ddtrace-2.6.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4276b9ba782268473741f7b6f9a53407b4f3922c41f5e96bfe60d001d0a17ba9"}, - {file = "ddtrace-2.6.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4459e582951235e4053db725cf11643dfa117d5f22932b34c0f76a5d6d22f3f"}, - {file = "ddtrace-2.6.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:af060a53d7e8ee53a0755355d16d4e45b09db44cf3494716b6097b71a615be95"}, - {file = "ddtrace-2.6.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1a22f0cd53c1d467feb1a174a1ab9d5d02c0fecca4a2f11834683eb70f3cad80"}, - {file = "ddtrace-2.6.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40882fc48888f9322ccfc9e6162e8a3e4b2eb898f240ca3cd1826ca68508db8b"}, - {file = "ddtrace-2.6.5-cp312-cp312-win32.whl", hash = "sha256:4817772cd7f7b06f180241815587ba6236ac3afafdd1930038d773adf2be3d48"}, - {file = "ddtrace-2.6.5-cp312-cp312-win_amd64.whl", hash = "sha256:69a8f71818ef017fe049119ac57e890294cadaead8402508399f94a9962731a0"}, - {file = "ddtrace-2.6.5-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:4dd0a1d2bbee33e1f7128290ba01ecf659a8c40007d89824f35a9fdebdde6f76"}, - {file = "ddtrace-2.6.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fe5fd5479bd0126551e3370d7b13484490a6874069e518ab4b5be1e8e9c8ecb"}, - {file = "ddtrace-2.6.5-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3b82ddfe13c5ccc1ef3ec6e379d686f04bf387b8bd6ab6d674b6188698fd37e"}, - {file = "ddtrace-2.6.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b691a5d8cdc3af7c38d6cf4a34be781d7868854e6a5efd84ea856f3ba81e9aef"}, - {file = "ddtrace-2.6.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1ddec7e5f1f0a2066574affd9198f6c348bc559752a5bf063792aa9c2b5fced8"}, - {file = "ddtrace-2.6.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a086420b0d0d2a5fb4358dc2d03b85b6c022949cad89043f0a96c82996280594"}, - {file = "ddtrace-2.6.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:71485ff240b0ba110662ec8628c1c320266e44945568c72b9cded4365406a028"}, - {file = "ddtrace-2.6.5-cp37-cp37m-win32.whl", hash = "sha256:bee1ca5e3f61743b8197435dd1d1427887ad676084d67b3b1bb1a2055dd682d2"}, - {file = "ddtrace-2.6.5-cp37-cp37m-win_amd64.whl", hash = "sha256:6509de90ca1dc472e2a70dbe699c12659086e21bc733237a3e503f5b1948ab7a"}, - {file = "ddtrace-2.6.5-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:7f2cbbad82df0d6bc9390fc1fd4f8f589f1e1c37a96059493c99189855ed0882"}, - {file = "ddtrace-2.6.5-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:0f52f9d3a8ebdb21c7ac9b48cca9f13ecb55db3c58a459279804f8b7990cc432"}, - {file = "ddtrace-2.6.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:944d85bada913503cf85972ab818bccf19d974ff8a31f34a84232e5bf256254f"}, - {file = "ddtrace-2.6.5-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7374f3888be2be3ee1b90935da0a192e001c43accb7b35d7858fc4bd7345d6a"}, - {file = "ddtrace-2.6.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32129805c54348d4285e8e8618f8bf31e38bdad8b261ba6cfa9d63886c8b0819"}, - {file = "ddtrace-2.6.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5d90aef6c9429633b17bd9b16f7a3712657119b8caf870486df82a5adafd7a78"}, - {file = "ddtrace-2.6.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:02351e7eb23bf4ca962ad704d976ce52a33286a01bdb8e514a334c541bec4e1c"}, - {file = "ddtrace-2.6.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:33d0676debceeac53459f54ad79ae0a2efcfe7f2d512624ad7241a453368fc21"}, - {file = "ddtrace-2.6.5-cp38-cp38-win32.whl", hash = "sha256:1d581c4c9e2beba054bdaec04f2cd267713af309244bb8227c5e277e019dd429"}, - {file = "ddtrace-2.6.5-cp38-cp38-win_amd64.whl", hash = "sha256:672c42f99e7098cad3e46df80649d8b510e9baef62bb094b90dad22d8e0bde7d"}, - {file = "ddtrace-2.6.5-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:dbfdfc55d28b3fec547634bdfacce200934bc73a75d73ff2101c7c0b4cd51233"}, - {file = "ddtrace-2.6.5-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:ea8c9b99a979b392df41183b3879f9981440cafe47b926f73866892fc4bb8af1"}, - {file = "ddtrace-2.6.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0995e5d6ef3082adc2f8657f07518127eee84074ff7101582c5f2a3e6813ca49"}, - {file = "ddtrace-2.6.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aed320f14742188ce8e7697aee9445a6034ececb5fbdc468e8d5bf92a2ed5125"}, - {file = "ddtrace-2.6.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1c345752a9758d50f5daa43d700284f5a47d3e73faf69abdfadd20ef65511e0"}, - {file = "ddtrace-2.6.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5679167188adfb769414b91ddf810319bbe2fa2f64db7dc0b02d67775b74c802"}, - {file = "ddtrace-2.6.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8d6974459ab84b310ada4325b4178e435ae1476397f33999baf10d9a45d6d2b0"}, - {file = "ddtrace-2.6.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:32f283b9168fafe82a0d95dac516991951992e08c8aa67e67ebca20c5348555c"}, - {file = "ddtrace-2.6.5-cp39-cp39-win32.whl", hash = "sha256:34cc4f978494ffb7304613b9ae8f0e1d847376ea6ddc6b5db98a5af717cddd64"}, - {file = "ddtrace-2.6.5-cp39-cp39-win_amd64.whl", hash = "sha256:65d6ab3565a8fbff1bc8ebff717d0ac88a69055404597c294ac515610af93bab"}, - {file = "ddtrace-2.6.5.tar.gz", hash = "sha256:a04e6b8931300bc258fcfaa21a99707d5c307518162fe84538e1ee7ed9c970df"}, + {file = "ddtrace-2.7.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:7c8589a46ce4170dff7d00dbcfd553413353cb40e985079fd2a8bb0da66c82f1"}, + {file = "ddtrace-2.7.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:195d7989dd2cbfd63f14a27f6872fe784deb428694082895ec63463f475c2c64"}, + {file = "ddtrace-2.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e04538a9428d7d4cb0937e88095e883f390167fef168118ce41b96afa2d6986"}, + {file = "ddtrace-2.7.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94ad305f16b0c377e723e3ee65599304f2bbb8a40894c2b9ce3a7fb2db8c61a2"}, + {file = "ddtrace-2.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80d70b4c9e0a6a6831150ac73dadb76b0b46f15f770f89274e9b1bb371beb848"}, + {file = "ddtrace-2.7.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7d11da8ae2b26ad31fa6c0c0394d3d461fea302023cf28c5c026982ece832cfa"}, + {file = "ddtrace-2.7.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:60edceb914802fbf8c762eee96aa12eb35848fbc13eb237c69de34be1094db80"}, + {file = "ddtrace-2.7.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8661558dc12092890459336431b34427e29c63fbf83af06731b651209821359c"}, + {file = "ddtrace-2.7.0-cp310-cp310-win32.whl", hash = "sha256:3ef9cd5cf948805678fd7741a966be635fde5ac601a7a4d15a03f8ea41251bea"}, + {file = "ddtrace-2.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:913a8eeb25af5721997c411214cce8502bca5cd6525d79c50b0cb90e1fbb21cf"}, + {file = "ddtrace-2.7.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:6e18db8ea6a174902eedbcc0952ed57492f7fa5660d9c979bd50af0966bd8a71"}, + {file = "ddtrace-2.7.0-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:39669deca72eb2a07e55dd1e8d78e59b2b050a028d5f0879d60745d0d80a7c0b"}, + {file = "ddtrace-2.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9533332fe12f331333cc652dacf107329a9af0f328539d4cbea578b4a999331"}, + {file = "ddtrace-2.7.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdd6522429c5bf51e6db26824cffa27d7e45a97fc14c2c1390ca303307c6996c"}, + {file = "ddtrace-2.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:552b4933f1b1807fedc674b9505c2f2113899f0a92c47a9b811f652c87c4c294"}, + {file = "ddtrace-2.7.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:61be4adc2607e121fc3f576fbe6ebcee06cd618862bdbe49e699b5980d474400"}, + {file = "ddtrace-2.7.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ebb200c292c9126d8f74c77ae8f37d395c20ad7fd70bc7082e488a5bffaf4083"}, + {file = "ddtrace-2.7.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:23bd2ffa56efccc5349d04cc290447223538ab002b094e7f66d2c6ebcd2e4237"}, + {file = "ddtrace-2.7.0-cp311-cp311-win32.whl", hash = "sha256:6970c21c82db282a99958ea8ac86defda3d69acc4a1db6eb6061208e6fea5e77"}, + {file = "ddtrace-2.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:9da6df8b0d33ef6692ab5cec8c9db507f11168986aed3f0a34254a8b7edeb73f"}, + {file = "ddtrace-2.7.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:28cd2b4e4eb890232382d8cde1734d2ebea949817b762fec541fcf41f36ec311"}, + {file = "ddtrace-2.7.0-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:89d6f6a73005de56deb0ec4d0c83a32fd3cc698eabc3477a9b99608717b9868e"}, + {file = "ddtrace-2.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67e9a5b73c693e69455f598ab2c62d484952f38cf9cb714efd2c6463bf845908"}, + {file = "ddtrace-2.7.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a78b5c7c948809146c8cec17e5e54b0c688e60c7ff92e4fdb32ce6f42b169b8"}, + {file = "ddtrace-2.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db01ac0cfb2eafd2d4ea24c879c5d0b1b7598ba45e8da06d454e70fbd2688c20"}, + {file = "ddtrace-2.7.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6bd9f7fd4bd8848a2a8c618682ddeaeaaf6ea8b584eb83e74a3900d6afc6d36e"}, + {file = "ddtrace-2.7.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:00f6946520c91b8d5436a7b0bf2919542ab31493435558beeb6c17fddb089cb4"}, + {file = "ddtrace-2.7.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:98f2185ad640c7ad0a373ecfb20e15bb1348bb2f93faed1f20ee77acadbb73f6"}, + {file = "ddtrace-2.7.0-cp312-cp312-win32.whl", hash = "sha256:def38d16345cf236afc80bc0da0ad17e083ea921ac94ca37562bc996d53569df"}, + {file = "ddtrace-2.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:98de3812ef588bad53797e777aa99902252f77e21ae382d6e317258d14785e26"}, + {file = "ddtrace-2.7.0-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:0b852adc63c500c80b8ff36702c00a8ca76b910a75b052bd1281105a41655288"}, + {file = "ddtrace-2.7.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27d0d08eabf84f508b8b3a68fea587d6525cee7c8b602a1609007e2417d1fa5a"}, + {file = "ddtrace-2.7.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf78c3499911a18b7056aba5700e595c1bec4e18b98b21767c61dbacd084ce86"}, + {file = "ddtrace-2.7.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd196a8884247bca392af7a4e1202c698cc489ede48535f3149b12b802eb5842"}, + {file = "ddtrace-2.7.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:264dafd983598de4d05893b37ffcfa35e943f2c896a5b04b76533a811a5e66d6"}, + {file = "ddtrace-2.7.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ab073544aad96d5275ac18b962a71554c55ca8c04b110f5fe68646f103e81349"}, + {file = "ddtrace-2.7.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4ef1a6dd2c86aaea539fc2607c24751eafb0a5120c80fb051a31e81c58c04cba"}, + {file = "ddtrace-2.7.0-cp37-cp37m-win32.whl", hash = "sha256:ac59ec4ceb40287abdcec097d5f6c5ab1a7be65793cdff99c14b38c214be76b4"}, + {file = "ddtrace-2.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4f2da60724d362c7b4f8cb15623fc8d160d18a116532abbeddbcbf69e4bf00ea"}, + {file = "ddtrace-2.7.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:948539aa83c49218609be87517ef87961c0a67f9bf7c21fe2c3309d2df62fb67"}, + {file = "ddtrace-2.7.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:8a2c175404361f49f674bae47229a8a7659a948249aa752888adc427fa65ec03"}, + {file = "ddtrace-2.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25c4ff71805aa4b48bda7ed56d3b63f76e9c2207a3b3f871fc309233185d7ec3"}, + {file = "ddtrace-2.7.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd1efe43cc3a97a1f2f634c500f69f7c4c73c9bdde5148a70dabd04bc81f05d6"}, + {file = "ddtrace-2.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56efaf8620803c9e11692be72e6eed358065230d2a2e9188ad1d127f1ae9fe6a"}, + {file = "ddtrace-2.7.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:993493378144dd90af2b5bada3bf4128a94aeb02e8b618fa30e86881e821fd94"}, + {file = "ddtrace-2.7.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ef2a2d068e99f0e3f0a604553c7861d5c36bba7ae92ec5af4a74547fc2be1a9b"}, + {file = "ddtrace-2.7.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c61d70d9408df7ec6742d7b57bb80c7168aa38b6aebe9074d360b99be2b9f1f"}, + {file = "ddtrace-2.7.0-cp38-cp38-win32.whl", hash = "sha256:2ee477ee452801746e2614f27030c2b471cdd73129d66c754ac5e7b2a1bfbe58"}, + {file = "ddtrace-2.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:ec6c102bd44f76ca06195c7e3c028165fc579fddb28b27951c1efff42b5e3452"}, + {file = "ddtrace-2.7.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:876a0161344e45a21f1bf7ce7c9413477adee6d425e027ee60f421146ab00942"}, + {file = "ddtrace-2.7.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:2ee00771f4deea3c354cbd73e781450ac0db34ed1c38a68f0aa0a5cbbc501e81"}, + {file = "ddtrace-2.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52778b06ccd5f595e746547660f0e3f4ce0e0dbcbd2857cf42ec56868cbbc647"}, + {file = "ddtrace-2.7.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfa96b9ec4e2a6e8a6edbfa3dc210cff8fe70807d53ab36ec770f7adb5c522ca"}, + {file = "ddtrace-2.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11aa0e7573ef221b26059ca36a367d34cfd2b4a6bb38215a4c7e158367fc279d"}, + {file = "ddtrace-2.7.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9b0f7c93786028a1afc2f7a6aa59d58c549d9ef5e1cbc90116b8d77417aa1526"}, + {file = "ddtrace-2.7.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:089593e3d00180388709523384d3a4da528bb6b040f5ab43e03479d91ee95cd6"}, + {file = "ddtrace-2.7.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4fe5392100712056d9852070612c5ae937f86fba6c99ee1493abfd8009141652"}, + {file = "ddtrace-2.7.0-cp39-cp39-win32.whl", hash = "sha256:950f57688b248d7be68e9614eee362dcda6f3d8e41ddf07ba475c0765e267a62"}, + {file = "ddtrace-2.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:d6b213a0e129ed4ef93845d1d6af7bb79e7e6319d20f1c0b87099e23d7d38512"}, + {file = "ddtrace-2.7.0.tar.gz", hash = "sha256:15702a0c4345a7610a548a22889d452b271c49100896204ab5ab0d95ae01ea52"}, ] [package.dependencies] @@ -909,6 +964,7 @@ opentelemetry-api = ">=1" protobuf = ">=3" setuptools = {version = "*", markers = "python_version >= \"3.12\""} six = ">=1.12.0" +sqlparse = ">=0.2.2" typing-extensions = "*" xmltodict = ">=0.12" @@ -1403,13 +1459,13 @@ pbr = "*" [[package]] name = "jsii" -version = "1.94.0" +version = "1.95.0" description = "Python client for jsii runtime" optional = false python-versions = "~=3.8" files = [ - {file = "jsii-1.94.0-py3-none-any.whl", hash = "sha256:1105bae271ae47c27cf31c1565c5157306efed5ad9323c9a27336f962f465716"}, - {file = "jsii-1.94.0.tar.gz", hash = "sha256:175abc356603d98f18ab6f6aa74bfeae253e4e56340aef9dc40bbb1a6a59868b"}, + {file = "jsii-1.95.0-py3-none-any.whl", hash = "sha256:456fe823847e1ebbb49ab4737b181cdb8eeb623058de4669283fba7bb261e8f4"}, + {file = "jsii-1.95.0.tar.gz", hash = "sha256:f2461a0c1fdd6bb400caac5825596b0068a8c3adb9184549366adcf272815172"}, ] [package.dependencies] @@ -1921,13 +1977,13 @@ typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.12\""} [[package]] name = "mypy-boto3-cloudformation" -version = "1.34.32" -description = "Type annotations for boto3.CloudFormation 1.34.32 service generated with mypy-boto3-builder 7.23.1" +version = "1.34.55" +description = "Type annotations for boto3.CloudFormation 1.34.55 service generated with mypy-boto3-builder 7.23.2" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-boto3-cloudformation-1.34.32.tar.gz", hash = "sha256:49d04c090dae3fd8289738ae592cac9d6faa5169684de40c2730b425bba2a32d"}, - {file = "mypy_boto3_cloudformation-1.34.32-py3-none-any.whl", hash = "sha256:bfe5ec405eae6dae31dc9874729eef5e668e634eae8972032f00400d17bd2c7d"}, + {file = "mypy-boto3-cloudformation-1.34.55.tar.gz", hash = "sha256:de9f4d45d0a4e57bb5ef8b8d7a03476f8ef24ffa253d636cd824cde061227439"}, + {file = "mypy_boto3_cloudformation-1.34.55-py3-none-any.whl", hash = "sha256:78c4d25ef7102bd02c8e87d32211ff60a7b474ec546e9948b46f170d341ae0a2"}, ] [package.dependencies] @@ -2321,13 +2377,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" -version = "10.7" +version = "10.7.1" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" files = [ - {file = "pymdown_extensions-10.7-py3-none-any.whl", hash = "sha256:6ca215bc57bc12bf32b414887a68b810637d039124ed9b2e5bd3325cbb2c050c"}, - {file = "pymdown_extensions-10.7.tar.gz", hash = "sha256:c0d64d5cf62566f59e6b2b690a4095c931107c250a8c8e1351c1de5f6b036deb"}, + {file = "pymdown_extensions-10.7.1-py3-none-any.whl", hash = "sha256:f5cc7000d7ff0d1ce9395d216017fa4df3dde800afb1fb72d1c7d3fd35e710f4"}, + {file = "pymdown_extensions-10.7.1.tar.gz", hash = "sha256:c70e146bdd83c744ffc766b4671999796aba18842b268510a329f7f64700d584"}, ] [package.dependencies] @@ -2468,13 +2524,13 @@ testing = ["filelock"] [[package]] name = "python-dateutil" -version = "2.8.2" +version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] [package.dependencies] @@ -2777,13 +2833,13 @@ decorator = ">=3.4.2" [[package]] name = "rich" -version = "13.7.0" +version = "13.7.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, - {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, ] [package.dependencies] @@ -3054,6 +3110,22 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] +[[package]] +name = "sqlparse" +version = "0.4.4" +description = "A non-validating SQL parser." +optional = false +python-versions = ">=3.5" +files = [ + {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, + {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, +] + +[package.extras] +dev = ["build", "flake8"] +doc = ["sphinx"] +test = ["pytest", "pytest-cov"] + [[package]] name = "stevedore" version = "5.2.0" @@ -3144,13 +3216,13 @@ test = ["mypy", "pytest", "typing-extensions"] [[package]] name = "types-pyopenssl" -version = "24.0.0.20240130" +version = "24.0.0.20240228" description = "Typing stubs for pyOpenSSL" optional = false python-versions = ">=3.8" files = [ - {file = "types-pyOpenSSL-24.0.0.20240130.tar.gz", hash = "sha256:c812e5c1c35249f75ef5935708b2a997d62abf9745be222e5f94b9595472ab25"}, - {file = "types_pyOpenSSL-24.0.0.20240130-py3-none-any.whl", hash = "sha256:24a255458b5b8a7fca8139cf56f2a8ad5a4f1a5f711b73a5bb9cb50dc688fab5"}, + {file = "types-pyOpenSSL-24.0.0.20240228.tar.gz", hash = "sha256:cd990717d8aa3743ef0e73e0f462e64b54d90c304249232d48fece4f0f7c3c6a"}, + {file = "types_pyOpenSSL-24.0.0.20240228-py3-none-any.whl", hash = "sha256:a472cf877a873549175e81972f153f44e975302a3cf17381eb5f3d41ccfb75a4"}, ] [package.dependencies] @@ -3196,6 +3268,20 @@ files = [ [package.dependencies] types-urllib3 = "*" +[[package]] +name = "types-requests" +version = "2.31.0.20240218" +description = "Typing stubs for requests" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-requests-2.31.0.20240218.tar.gz", hash = "sha256:f1721dba8385958f504a5386240b92de4734e047a08a40751c1654d1ac3349c5"}, + {file = "types_requests-2.31.0.20240218-py3-none-any.whl", hash = "sha256:a82807ec6ddce8f00fe0e949da6d6bc1fbf1715420218a9640d695f70a9e5a9b"}, +] + +[package.dependencies] +urllib3 = ">=2" + [[package]] name = "types-urllib3" version = "1.26.25.14" @@ -3440,4 +3526,4 @@ validation = ["fastjsonschema"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<4.0.0" -content-hash = "c0a29241a0047b295afb73576d13071d66f74df5ccb6f5339831ea1b44065406" +content-hash = "43badf89ca0a215e43fe6ccd96a06ce01f07ec48fda7bb7a0c7946bea8db3b85" diff --git a/pyproject.toml b/pyproject.toml index 6a23f5ef4df..94827ca89cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,6 +69,8 @@ aws-cdk-lib = "^2.131.0" "aws-cdk.aws-apigatewayv2-alpha" = "^2.38.1-alpha.0" "aws-cdk.aws-apigatewayv2-integrations-alpha" = "^2.38.1-alpha.0" "aws-cdk.aws-apigatewayv2-authorizers-alpha" = "^2.38.1-alpha.0" +"aws-cdk.aws-lambda-python-alpha" = "^2.130.0a0" +"cdklabs.generative-ai-cdk-constructs" = "^0.1.74" pytest-benchmark = "^4.0.0" mypy-boto3-appconfig = "^1.34.0" mypy-boto3-cloudformation = "^1.34.32" diff --git a/tests/functional/event_handler/test_bedrock_agent.py b/tests/functional/event_handler/test_bedrock_agent.py index 266edd10de0..9f9a64427bf 100644 --- a/tests/functional/event_handler/test_bedrock_agent.py +++ b/tests/functional/event_handler/test_bedrock_agent.py @@ -13,7 +13,7 @@ def test_bedrock_agent_event(): # GIVEN a Bedrock Agent event app = BedrockAgentResolver() - @app.get("/claims") + @app.get("/claims", description="Gets claims") def claims() -> Dict[str, Any]: assert isinstance(app.current_event, BedrockAgentEvent) assert app.lambda_context == {} @@ -38,7 +38,7 @@ def test_bedrock_agent_with_path_params(): # GIVEN a Bedrock Agent event app = BedrockAgentResolver() - @app.get("/claims/") + @app.get("/claims/", description="Gets claims by ID") def claims(claim_id: str): assert isinstance(app.current_event, BedrockAgentEvent) assert app.lambda_context == {} @@ -61,7 +61,7 @@ def test_bedrock_agent_event_with_response(): app = BedrockAgentResolver() output = {"output": claims_response} - @app.get("/claims") + @app.get("/claims", description="Gets claims") def claims(): assert isinstance(app.current_event, BedrockAgentEvent) assert app.lambda_context == {} @@ -86,7 +86,7 @@ def test_bedrock_agent_event_with_no_matches(): # GIVEN a Bedrock Agent event app = BedrockAgentResolver() - @app.get("/no_match") + @app.get("/no_match", description="Matches nothing") def claims(): raise RuntimeError() @@ -106,7 +106,7 @@ def test_bedrock_agent_event_with_validation_error(): # GIVEN a Bedrock Agent event app = BedrockAgentResolver() - @app.get("/claims") + @app.get("/claims", description="Gets claims") def claims() -> Dict[str, Any]: return "oh no, this is not a dict" # type: ignore @@ -140,7 +140,7 @@ def handle_runtime_error(ex: RuntimeError): body="Something went wrong", ) - @app.get("/claims") + @app.get("/claims", description="Gets claims") def claims(): raise RuntimeError()