Skip to content

Commit 07a31aa

Browse files
authored
feat(parser): add BedrockEventModel parser and envelope (#3286)
1 parent 0964e75 commit 07a31aa

File tree

7 files changed

+169
-0
lines changed

7 files changed

+169
-0
lines changed

Diff for: aws_lambda_powertools/utilities/parser/envelopes/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from .apigw import ApiGatewayEnvelope
22
from .apigwv2 import ApiGatewayV2Envelope
33
from .base import BaseEnvelope
4+
from .bedrock_agent import BedrockAgentEnvelope
45
from .cloudwatch import CloudWatchLogsEnvelope
56
from .dynamodb import DynamoDBStreamEnvelope
67
from .event_bridge import EventBridgeEnvelope
@@ -16,6 +17,7 @@
1617
__all__ = [
1718
"ApiGatewayEnvelope",
1819
"ApiGatewayV2Envelope",
20+
"BedrockAgentEnvelope",
1921
"CloudWatchLogsEnvelope",
2022
"DynamoDBStreamEnvelope",
2123
"EventBridgeEnvelope",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import logging
2+
from typing import Any, Dict, Optional, Type, Union
3+
4+
from ..models import BedrockAgentEventModel
5+
from ..types import Model
6+
from .base import BaseEnvelope
7+
8+
logger = logging.getLogger(__name__)
9+
10+
11+
class BedrockAgentEnvelope(BaseEnvelope):
12+
"""Bedrock Agent envelope to extract data within input_text key"""
13+
14+
def parse(self, data: Optional[Union[Dict[str, Any], Any]], model: Type[Model]) -> Optional[Model]:
15+
"""Parses data found with model provided
16+
17+
Parameters
18+
----------
19+
data : Dict
20+
Lambda event to be parsed
21+
model : Type[Model]
22+
Data model provided to parse after extracting data using envelope
23+
24+
Returns
25+
-------
26+
Optional[Model]
27+
Parsed detail payload with model provided
28+
"""
29+
logger.debug(f"Parsing incoming data with Bedrock Agent model {BedrockAgentEventModel}")
30+
parsed_envelope: BedrockAgentEventModel = BedrockAgentEventModel.parse_obj(data)
31+
logger.debug(f"Parsing event payload in `input_text` with {model}")
32+
return self._parse(data=parsed_envelope.input_text, model=model)

Diff for: aws_lambda_powertools/utilities/parser/models/__init__.py

+12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
RequestContextV2AuthorizerJwt,
1919
RequestContextV2Http,
2020
)
21+
from .bedrock_agent import (
22+
BedrockAgentEventModel,
23+
BedrockAgentModel,
24+
BedrockAgentPropertyModel,
25+
BedrockAgentRequestBodyModel,
26+
BedrockAgentRequestMediaModel,
27+
)
2128
from .cloudformation_custom_resource import (
2229
CloudFormationCustomResourceBaseModel,
2330
CloudFormationCustomResourceCreateModel,
@@ -165,4 +172,9 @@
165172
"CloudFormationCustomResourceBaseModel",
166173
"VpcLatticeModel",
167174
"VpcLatticeV2Model",
175+
"BedrockAgentModel",
176+
"BedrockAgentPropertyModel",
177+
"BedrockAgentEventModel",
178+
"BedrockAgentRequestBodyModel",
179+
"BedrockAgentRequestMediaModel",
168180
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from typing import Dict, List, Optional
2+
3+
from pydantic import BaseModel, Field
4+
5+
6+
class BedrockAgentModel(BaseModel):
7+
name: str
8+
id_: str = Field(..., alias="id")
9+
alias: str
10+
version: str
11+
12+
13+
class BedrockAgentPropertyModel(BaseModel):
14+
name: str
15+
type_: str = Field(..., alias="type")
16+
value: str
17+
18+
19+
class BedrockAgentRequestMediaModel(BaseModel):
20+
properties: List[BedrockAgentPropertyModel]
21+
22+
23+
class BedrockAgentRequestBodyModel(BaseModel):
24+
content: Dict[str, BedrockAgentRequestMediaModel]
25+
26+
27+
class BedrockAgentEventModel(BaseModel):
28+
message_version: str = Field(..., alias="messageVersion")
29+
input_text: str = Field(..., alias="inputText")
30+
session_id: str = Field(..., alias="sessionId")
31+
action_group: str = Field(..., alias="actionGroup")
32+
api_path: str = Field(..., alias="apiPath")
33+
http_method: str = Field(..., alias="httpMethod")
34+
session_attributes: Dict[str, str] = Field({}, alias="sessionAttributes")
35+
prompt_session_attributes: Dict[str, str] = Field({}, alias="promptSessionAttributes")
36+
agent: BedrockAgentModel
37+
parameters: Optional[List[BedrockAgentPropertyModel]] = None
38+
request_body: Optional[BedrockAgentRequestBodyModel] = Field(None, alias="requestBody")

Diff for: docs/utilities/parser.md

+2
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ Parser comes with the following built-in models:
178178
| **AlbModel** | Lambda Event Source payload for Amazon Application Load Balancer |
179179
| **APIGatewayProxyEventModel** | Lambda Event Source payload for Amazon API Gateway |
180180
| **APIGatewayProxyEventV2Model** | Lambda Event Source payload for Amazon API Gateway v2 payload |
181+
| **BedrockAgentEventModel** | Lambda Event Source payload for Bedrock Agents |
181182
| **CloudFormationCustomResourceCreateModel** | Lambda Event Source payload for AWS CloudFormation `CREATE` operation |
182183
| **CloudFormationCustomResourceUpdateModel** | Lambda Event Source payload for AWS CloudFormation `UPDATE` operation |
183184
| **CloudFormationCustomResourceDeleteModel** | Lambda Event Source payload for AWS CloudFormation `DELETE` operation |
@@ -356,6 +357,7 @@ Parser comes with the following built-in envelopes, where `Model` in the return
356357
| **LambdaFunctionUrlEnvelope** | 1. Parses data using `LambdaFunctionUrlModel`. <br/> 2. Parses `body` key using your model and returns it. | `Model` |
357358
| **KafkaEnvelope** | 1. Parses data using `KafkaRecordModel`. <br/> 2. Parses `value` key using your model and returns it. | `Model` |
358359
| **VpcLatticeEnvelope** | 1. Parses data using `VpcLatticeModel`. <br/> 2. Parses `value` key using your model and returns it. | `Model` |
360+
| **BedrockAgentEnvelope** | 1. Parses data using `BedrockAgentEventModel`. <br/> 2. Parses `inputText` key using your model and returns it. | `Model` |
359361

360362
#### Bringing your own envelope
361363

Diff for: tests/unit/parser/schemas.py

+5
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,8 @@ class MyKinesisFirehoseBusiness(BaseModel):
104104
class MyVpcLatticeBusiness(BaseModel):
105105
username: str
106106
name: str
107+
108+
109+
class MyBedrockAgentBusiness(BaseModel):
110+
username: str
111+
name: str

Diff for: tests/unit/parser/test_bedrock_agent.py

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
from aws_lambda_powertools.utilities.parser import envelopes, parse
2+
from aws_lambda_powertools.utilities.parser.models import BedrockAgentEventModel
3+
from tests.functional.utils import load_event
4+
from tests.unit.parser.schemas import MyBedrockAgentBusiness
5+
6+
7+
def test_bedrock_agent_event_with_envelope():
8+
raw_event = load_event("bedrockAgentEvent.json")
9+
raw_event["inputText"] = '{"username": "Ruben", "name": "Fonseca"}'
10+
parsed_event: MyBedrockAgentBusiness = parse(
11+
event=raw_event,
12+
model=MyBedrockAgentBusiness,
13+
envelope=envelopes.BedrockAgentEnvelope,
14+
)
15+
16+
assert parsed_event.username == "Ruben"
17+
assert parsed_event.name == "Fonseca"
18+
19+
20+
def test_bedrock_agent_event():
21+
raw_event = load_event("bedrockAgentEvent.json")
22+
model = BedrockAgentEventModel(**raw_event)
23+
24+
assert model.message_version == raw_event["messageVersion"]
25+
assert model.session_id == raw_event["sessionId"]
26+
assert model.input_text == raw_event["inputText"]
27+
assert model.message_version == raw_event["messageVersion"]
28+
assert model.http_method == raw_event["httpMethod"]
29+
assert model.api_path == raw_event["apiPath"]
30+
assert model.session_attributes == {}
31+
assert model.prompt_session_attributes == {}
32+
assert model.action_group == raw_event["actionGroup"]
33+
34+
assert model.request_body is None
35+
36+
agent = model.agent
37+
raw_agent = raw_event["agent"]
38+
assert agent.alias == raw_agent["alias"]
39+
assert agent.name == raw_agent["name"]
40+
assert agent.version == raw_agent["version"]
41+
assert agent.id_ == raw_agent["id"]
42+
43+
44+
def test_bedrock_agent_event_with_post():
45+
raw_event = load_event("bedrockAgentPostEvent.json")
46+
model = BedrockAgentEventModel(**raw_event)
47+
48+
assert model.session_id == raw_event["sessionId"]
49+
assert model.input_text == raw_event["inputText"]
50+
assert model.message_version == raw_event["messageVersion"]
51+
assert model.http_method == raw_event["httpMethod"]
52+
assert model.api_path == raw_event["apiPath"]
53+
assert model.session_attributes == {}
54+
assert model.prompt_session_attributes == {}
55+
assert model.action_group == raw_event["actionGroup"]
56+
57+
agent = model.agent
58+
raw_agent = raw_event["agent"]
59+
assert agent.alias == raw_agent["alias"]
60+
assert agent.name == raw_agent["name"]
61+
assert agent.version == raw_agent["version"]
62+
assert agent.id_ == raw_agent["id"]
63+
64+
request_body = model.request_body.content
65+
assert "application/json" in request_body
66+
67+
json_request = request_body["application/json"]
68+
properties = json_request.properties
69+
assert len(properties) == 2
70+
71+
raw_properties = raw_event["requestBody"]["content"]["application/json"]["properties"]
72+
assert properties[0].name == raw_properties[0]["name"]
73+
assert properties[0].type_ == raw_properties[0]["type"]
74+
assert properties[0].value == raw_properties[0]["value"]
75+
76+
assert properties[1].name == raw_properties[1]["name"]
77+
assert properties[1].type_ == raw_properties[1]["type"]
78+
assert properties[1].value == raw_properties[1]["value"]

0 commit comments

Comments
 (0)