Skip to content

Commit 107fd53

Browse files
jona62Jonathan James
andauthored
Feature/boto client config (#98)
* feat: add support for boto_client_config * feat: override the timestamp serializer of the _data_plane_client to support millisecond granularity --------- Co-authored-by: Jonathan James <[email protected]>
1 parent 72e37df commit 107fd53

File tree

2 files changed

+827
-16
lines changed

2 files changed

+827
-16
lines changed

src/bedrock_agentcore/memory/session.py

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
77

88
import boto3
9+
from botocore.config import Config as BotocoreConfig
910
from botocore.exceptions import ClientError
1011

1112
from .constants import BlobMessage, ConversationalMessage, MessageRole, RetrievalConfig
@@ -89,7 +90,11 @@ def my_llm(user_input: str, memories: List[Dict]) -> str:
8990
"""
9091

9192
def __init__(
92-
self, memory_id: str, region_name: Optional[str] = None, boto3_session: Optional[boto3.Session] = None
93+
self,
94+
memory_id: str,
95+
region_name: Optional[str] = None,
96+
boto3_session: Optional[boto3.Session] = None,
97+
boto_client_config: Optional[BotocoreConfig] = None,
9398
):
9499
"""Initialize a MemorySessionManager instance.
95100
@@ -99,28 +104,29 @@ def __init__(
99104
will use the region from boto3_session or default session.
100105
boto3_session: Optional boto3 Session to use. If provided and region_name
101106
parameter is also specified, validation will ensure they match.
107+
boto_client_config: Optional boto3 client configuration. If provided, will be
108+
merged with default configuration including user agent.
102109
103110
Raises:
104111
ValueError: If region_name parameter conflicts with boto3_session region.
105112
"""
113+
# Initialize core attributes
114+
self._memory_id = memory_id
115+
116+
# Setup session and validate region consistency
106117
session = boto3_session if boto3_session else boto3.Session()
107-
session_region = session.region_name
118+
self.region_name = self._validate_and_resolve_region(region_name, session)
108119

109-
# Validate region consistency if both are provided
110-
if region_name and boto3_session and session_region and region_name != session_region:
111-
raise ValueError(
112-
f"Region mismatch: provided region_name '{region_name}' does not match "
113-
f"boto3_session region '{session_region}'. Please ensure both "
114-
f"parameters specify the same region or omit the region_name parameter "
115-
f"to use the session's region."
116-
)
120+
# Configure and create boto3 client
121+
client_config = self._build_client_config(boto_client_config)
122+
self._data_plane_client = session.client(
123+
"bedrock-agentcore", region_name=self.region_name, config=client_config
124+
)
117125

118-
# Use provided region_name or fall back to session region
119-
self.region_name = region_name or session_region
120-
self._memory_id = memory_id
121-
self._data_plane_client = session.client("bedrock-agentcore", region_name=self.region_name)
126+
# Configure timestamp serialization to use float representation
127+
self._configure_timestamp_serialization()
122128

123-
# AgentCore Memory data plane methods
129+
# Define allowed data plane methods
124130
self._ALLOWED_DATA_PLANE_METHODS = {
125131
"retrieve_memory_records",
126132
"get_memory_record",
@@ -132,6 +138,70 @@ def __init__(
132138
"list_events",
133139
}
134140

141+
def _validate_and_resolve_region(self, region_name: Optional[str], session: boto3.Session) -> str:
142+
"""Validate region consistency and resolve the final region to use.
143+
144+
Args:
145+
region_name: Explicitly provided region name
146+
session: Boto3 session instance
147+
148+
Returns:
149+
The resolved region name to use
150+
151+
Raises:
152+
ValueError: If region_name conflicts with session region
153+
"""
154+
session_region = session.region_name
155+
156+
# Validate region consistency if both are provided
157+
if region_name and session_region and isinstance(session_region, str) and region_name != session_region:
158+
raise ValueError(
159+
f"Region mismatch: provided region_name '{region_name}' does not match "
160+
f"boto3_session region '{session_region}'. Please ensure both "
161+
f"parameters specify the same region or omit the region_name parameter "
162+
f"to use the session's region."
163+
)
164+
165+
return region_name or session_region
166+
167+
def _build_client_config(self, boto_client_config: Optional[BotocoreConfig]) -> BotocoreConfig:
168+
"""Build the final boto3 client configuration with SDK user agent.
169+
170+
Args:
171+
boto_client_config: Optional user-provided client configuration
172+
173+
Returns:
174+
Final client configuration with SDK user agent
175+
"""
176+
sdk_user_agent = "bedrock-agentcore-sdk"
177+
178+
if boto_client_config:
179+
existing_user_agent = getattr(boto_client_config, "user_agent_extra", None)
180+
if existing_user_agent:
181+
new_user_agent = f"{existing_user_agent} {sdk_user_agent}"
182+
else:
183+
new_user_agent = sdk_user_agent
184+
return boto_client_config.merge(BotocoreConfig(user_agent_extra=new_user_agent))
185+
else:
186+
return BotocoreConfig(user_agent_extra=sdk_user_agent)
187+
188+
def _configure_timestamp_serialization(self) -> None:
189+
"""Configure the boto3 client to serialize timestamps as float values.
190+
191+
This method overrides the default timestamp serialization to convert datetime objects
192+
to float timestamps (seconds since Unix epoch) which preserves millisecond precision
193+
when sending datetime objects to the AgentCore Memory service.
194+
"""
195+
original_serialize_timestamp = self._data_plane_client._serializer._serializer._serialize_type_timestamp
196+
197+
def serialize_timestamp_as_float(serialized, value, shape, name):
198+
if isinstance(value, datetime):
199+
serialized[name] = value.timestamp() # Convert to float (seconds since epoch with fractional seconds)
200+
else:
201+
original_serialize_timestamp(serialized, value, shape, name)
202+
203+
self._data_plane_client._serializer._serializer._serialize_type_timestamp = serialize_timestamp_as_float
204+
135205
def __getattr__(self, name: str):
136206
"""Dynamically forward method calls to the appropriate boto3 client.
137207

0 commit comments

Comments
 (0)