73
73
_DEFAULT_OPENAPI_RESPONSE_DESCRIPTION = "Successful Response"
74
74
_ROUTE_REGEX = "^{}$"
75
75
_JSON_DUMP_CALL = partial (json .dumps , separators = ("," , ":" ), cls = Encoder )
76
+ _DEFAULT_CONTENT_TYPE = "application/json"
76
77
77
78
ResponseEventT = TypeVar ("ResponseEventT" , bound = BaseProxyEvent )
78
79
ResponseT = TypeVar ("ResponseT" )
@@ -255,6 +256,35 @@ def build_allow_methods(methods: set[str]) -> str:
255
256
return "," .join (sorted (methods ))
256
257
257
258
259
+ class BedrockResponse (Generic [ResponseT ]):
260
+ """
261
+ Contains the response body, status code, content type, and optional attributes
262
+ for session management and knowledge base configuration.
263
+ """
264
+
265
+ def __init__ (
266
+ self ,
267
+ body : Any = None ,
268
+ status_code : int = 200 ,
269
+ content_type : str = _DEFAULT_CONTENT_TYPE ,
270
+ session_attributes : dict [str , Any ] | None = None ,
271
+ prompt_session_attributes : dict [str , Any ] | None = None ,
272
+ knowledge_bases_configuration : list [dict [str , Any ]] | None = None ,
273
+ ) -> None :
274
+ self .body = body
275
+ self .status_code = status_code
276
+ self .content_type = content_type
277
+ self .session_attributes = session_attributes
278
+ self .prompt_session_attributes = prompt_session_attributes
279
+ self .knowledge_bases_configuration = knowledge_bases_configuration
280
+
281
+ def is_json (self ) -> bool :
282
+ """
283
+ Returns True if the response is JSON, based on the Content-Type.
284
+ """
285
+ return True
286
+
287
+
258
288
class Response (Generic [ResponseT ]):
259
289
"""Response data class that provides greater control over what is returned from the proxy event"""
260
290
@@ -300,7 +330,7 @@ def is_json(self) -> bool:
300
330
content_type = self .headers .get ("Content-Type" , "" )
301
331
if isinstance (content_type , list ):
302
332
content_type = content_type [0 ]
303
- return content_type .startswith ("application/json" )
333
+ return content_type .startswith (_DEFAULT_CONTENT_TYPE )
304
334
305
335
306
336
class Route :
@@ -572,7 +602,7 @@ def _get_openapi_path(
572
602
operation_responses : dict [int , OpenAPIResponse ] = {
573
603
422 : {
574
604
"description" : "Validation Error" ,
575
- "content" : {"application/json" : {"schema" : {"$ref" : f"{ COMPONENT_REF_PREFIX } HTTPValidationError" }}},
605
+ "content" : {_DEFAULT_CONTENT_TYPE : {"schema" : {"$ref" : f"{ COMPONENT_REF_PREFIX } HTTPValidationError" }}},
576
606
},
577
607
}
578
608
@@ -581,7 +611,9 @@ def _get_openapi_path(
581
611
http_code = self .custom_response_validation_http_code .value
582
612
operation_responses [http_code ] = {
583
613
"description" : "Response Validation Error" ,
584
- "content" : {"application/json" : {"schema" : {"$ref" : f"{ COMPONENT_REF_PREFIX } ResponseValidationError" }}},
614
+ "content" : {
615
+ _DEFAULT_CONTENT_TYPE : {"schema" : {"$ref" : f"{ COMPONENT_REF_PREFIX } ResponseValidationError" }},
616
+ },
585
617
}
586
618
# Add model definition
587
619
definitions ["ResponseValidationError" ] = response_validation_error_response_definition
@@ -594,7 +626,7 @@ def _get_openapi_path(
594
626
# Case 1: there is not 'content' key
595
627
if "content" not in response :
596
628
response ["content" ] = {
597
- "application/json" : self ._openapi_operation_return (
629
+ _DEFAULT_CONTENT_TYPE : self ._openapi_operation_return (
598
630
param = dependant .return_param ,
599
631
model_name_map = model_name_map ,
600
632
field_mapping = field_mapping ,
@@ -645,7 +677,7 @@ def _get_openapi_path(
645
677
# Add the response schema to the OpenAPI 200 response
646
678
operation_responses [200 ] = {
647
679
"description" : self .response_description or _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION ,
648
- "content" : {"application/json" : response_schema },
680
+ "content" : {_DEFAULT_CONTENT_TYPE : response_schema },
649
681
}
650
682
651
683
operation ["responses" ] = operation_responses
@@ -1474,7 +1506,10 @@ def __call__(self, app: ApiGatewayResolver) -> dict | tuple | Response:
1474
1506
return self .current_middleware (app , self .next_middleware )
1475
1507
1476
1508
1477
- def _registered_api_adapter (app : ApiGatewayResolver , next_middleware : Callable [..., Any ]) -> dict | tuple | Response :
1509
+ def _registered_api_adapter (
1510
+ app : ApiGatewayResolver ,
1511
+ next_middleware : Callable [..., Any ],
1512
+ ) -> dict | tuple | Response | BedrockResponse :
1478
1513
"""
1479
1514
Calls the registered API using the "_route_args" from the Resolver context to ensure the last call
1480
1515
in the chain will match the API route function signature and ensure that Powertools passes the API
@@ -1632,7 +1667,7 @@ def _add_resolver_response_validation_error_response_to_route(
1632
1667
response_validation_error_response = {
1633
1668
"description" : "Response Validation Error" ,
1634
1669
"content" : {
1635
- "application/json" : {
1670
+ _DEFAULT_CONTENT_TYPE : {
1636
1671
"schema" : {"$ref" : f"{ COMPONENT_REF_PREFIX } ResponseValidationError" },
1637
1672
},
1638
1673
},
@@ -2151,7 +2186,7 @@ def swagger_handler():
2151
2186
if query_params .get ("format" ) == "json" :
2152
2187
return Response (
2153
2188
status_code = 200 ,
2154
- content_type = "application/json" ,
2189
+ content_type = _DEFAULT_CONTENT_TYPE ,
2155
2190
body = escaped_spec ,
2156
2191
)
2157
2192
@@ -2538,7 +2573,7 @@ def _call_route(self, route: Route, route_arguments: dict[str, str]) -> Response
2538
2573
self ._reset_processed_stack ()
2539
2574
2540
2575
return self ._response_builder_class (
2541
- response = self ._to_response (
2576
+ response = self ._to_response ( # type: ignore[arg-type]
2542
2577
route (router_middlewares = self ._router_middlewares , app = self , route_arguments = route_arguments ),
2543
2578
),
2544
2579
serializer = self ._serializer ,
@@ -2627,7 +2662,7 @@ def _call_exception_handler(self, exp: Exception, route: Route) -> ResponseBuild
2627
2662
2628
2663
return None
2629
2664
2630
- def _to_response (self , result : dict | tuple | Response ) -> Response :
2665
+ def _to_response (self , result : dict | tuple | Response | BedrockResponse ) -> Response | BedrockResponse :
2631
2666
"""Convert the route's result to a Response
2632
2667
2633
2668
3 main result types are supported:
@@ -2638,7 +2673,7 @@ def _to_response(self, result: dict | tuple | Response) -> Response:
2638
2673
- Response: returned as is, and allows for more flexibility
2639
2674
"""
2640
2675
status_code = HTTPStatus .OK
2641
- if isinstance (result , Response ):
2676
+ if isinstance (result , ( Response , BedrockResponse ) ):
2642
2677
return result
2643
2678
elif isinstance (result , tuple ) and len (result ) == 2 :
2644
2679
# Unpack result dict and status code from tuple
@@ -2971,8 +3006,9 @@ def _get_base_path(self) -> str:
2971
3006
# ALB doesn't have a stage variable, so we just return an empty string
2972
3007
return ""
2973
3008
3009
+ # BedrockResponse is not used here but adding the same signature to keep strong typing
2974
3010
@override
2975
- def _to_response (self , result : dict | tuple | Response ) -> Response :
3011
+ def _to_response (self , result : dict | tuple | Response | BedrockResponse ) -> Response | BedrockResponse :
2976
3012
"""Convert the route's result to a Response
2977
3013
2978
3014
ALB requires a non-null body otherwise it converts as HTTP 5xx
0 commit comments