Skip to content

Commit 62d297d

Browse files
Abishek10Abishek Kumar
andauthored
feat(runtime): Add session_id support to WebSocket connection methods (#186)
* feat(runtime): Add session_id support to WebSocket connection methods Add session_id parameter to both generate_ws_connection and generate_presigned_url methods in AgentCoreRuntimeClient to enable proper session tracking in WebSocket connections. Changes: - generate_ws_connection: Add X-Amzn-Bedrock-AgentCore-Runtime-Session-Id header - generate_ws_connection: Update User-Agent to AgentCoreRuntimeClient/1.0 - generate_presigned_url: Add session_id as query parameter via custom_headers - Update unit tests to verify session_id in headers and URL params - Update integration tests to verify session_id end-to-end All tests passing (32 unit tests, 4 integration tests) * docs(runtime): Update class name in docstring examples from AgentRuntimeClient to AgentCoreRuntimeClient --------- Co-authored-by: Abishek Kumar <[email protected]>
1 parent 049ccdc commit 62d297d

File tree

3 files changed

+77
-4
lines changed

3 files changed

+77
-4
lines changed

src/bedrock_agentcore/runtime/agent_core_runtime_client.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def generate_ws_connection(
159159
ValueError: If runtime_arn format is invalid.
160160
161161
Example:
162-
>>> client = AgentRuntimeClient('us-west-2')
162+
>>> client = AgentCoreRuntimeClient('us-west-2')
163163
>>> ws_url, headers = client.generate_ws_connection(
164164
... runtime_arn='arn:aws:bedrock-agentcore:us-west-2:123:runtime/my-runtime',
165165
... endpoint_name='DEFAULT'
@@ -209,11 +209,12 @@ def generate_ws_connection(
209209
"Host": host,
210210
"X-Amz-Date": request.headers["x-amz-date"],
211211
"Authorization": request.headers["Authorization"],
212+
"X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": session_id,
212213
"Upgrade": "websocket",
213214
"Connection": "Upgrade",
214215
"Sec-WebSocket-Version": "13",
215216
"Sec-WebSocket-Key": base64.b64encode(secrets.token_bytes(16)).decode(),
216-
"User-Agent": f"AgentRuntime-Client/1.0 (Session: {session_id})",
217+
"User-Agent": "AgentCoreRuntimeClient/1.0",
217218
}
218219

219220
# Add session token if present
@@ -256,7 +257,7 @@ def generate_presigned_url(
256257
RuntimeError: If URL generation fails or no credentials found.
257258
258259
Example:
259-
>>> client = AgentRuntimeClient('us-west-2')
260+
>>> client = AgentCoreRuntimeClient('us-west-2')
260261
>>> presigned_url = client.generate_presigned_url(
261262
... runtime_arn='arn:aws:bedrock-agentcore:us-west-2:123:runtime/my-runtime',
262263
... endpoint_name='DEFAULT',
@@ -278,6 +279,11 @@ def generate_presigned_url(
278279
session_id = str(uuid.uuid4())
279280
self.logger.debug("Auto-generated session ID: %s", session_id)
280281

282+
# Add session_id to custom_headers (which become query params)
283+
if custom_headers is None:
284+
custom_headers = {}
285+
custom_headers["X-Amzn-Bedrock-AgentCore-Runtime-Session-Id"] = session_id
286+
281287
# Build WebSocket URL with query parameters
282288
ws_url = self._build_websocket_url(runtime_arn, endpoint_name, custom_headers)
283289

@@ -344,7 +350,7 @@ def generate_ws_connection_oauth(
344350
ValueError: If runtime_arn format is invalid or bearer_token is empty.
345351
346352
Example:
347-
>>> client = AgentRuntimeClient('us-west-2')
353+
>>> client = AgentCoreRuntimeClient('us-west-2')
348354
>>> ws_url, headers = client.generate_ws_connection_oauth(
349355
... runtime_arn='arn:aws:bedrock-agentcore:us-west-2:123:runtime/my-runtime',
350356
... bearer_token='eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...',

tests/integration/runtime/test_agent_core_runtime_client_integration.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,21 @@ def test_generate_ws_connection_returns_valid_format(self, mock_boto_session):
5656
assert "Authorization" in headers
5757
assert "X-Amz-Date" in headers
5858
assert "Host" in headers
59+
assert "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id" in headers
60+
assert "User-Agent" in headers
61+
assert headers["User-Agent"] == "AgentCoreRuntimeClient/1.0"
62+
63+
def test_generate_ws_connection_with_session_id(self, mock_boto_session):
64+
"""Test that generate_ws_connection includes provided session ID in headers."""
65+
client = AgentCoreRuntimeClient(region="us-west-2")
66+
runtime_arn = "arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/test-runtime"
67+
test_session_id = "integration-test-session-789"
68+
69+
ws_url, headers = client.generate_ws_connection(runtime_arn, session_id=test_session_id)
70+
71+
# Verify session ID is in headers
72+
assert "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id" in headers
73+
assert headers["X-Amzn-Bedrock-AgentCore-Runtime-Session-Id"] == test_session_id
5974

6075
def test_generate_presigned_url_returns_valid_format(self, mock_boto_session):
6176
"""Test that generate_presigned_url returns properly formatted URL."""
@@ -69,6 +84,19 @@ def test_generate_presigned_url_returns_valid_format(self, mock_boto_session):
6984
assert "runtimes" in presigned_url
7085
assert "X-Amz-Algorithm" in presigned_url
7186
assert "X-Amz-Signature" in presigned_url
87+
# Verify session ID is in query params
88+
assert "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id=" in presigned_url
89+
90+
def test_generate_presigned_url_with_session_id(self, mock_boto_session):
91+
"""Test that generate_presigned_url includes session ID in query params."""
92+
client = AgentCoreRuntimeClient(region="us-west-2")
93+
runtime_arn = "arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/test-runtime"
94+
test_session_id = "integration-test-presigned-session"
95+
96+
presigned_url = client.generate_presigned_url(runtime_arn, session_id=test_session_id)
97+
98+
# Verify session ID is in query params
99+
assert f"X-Amzn-Bedrock-AgentCore-Runtime-Session-Id={test_session_id}" in presigned_url
72100

73101
@pytest.mark.skip(reason="Requires actual runtime endpoint")
74102
async def test_connect_with_generated_headers(self):

tests/unit/runtime/test_agent_core_runtime_client.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,26 @@ def test_generate_connection_with_session_id(self, mock_endpoint, mock_session):
181181

182182
assert ws_url is not None
183183
assert headers is not None
184+
# Verify session ID is in headers
185+
assert "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id" in headers
186+
assert headers["X-Amzn-Bedrock-AgentCore-Runtime-Session-Id"] == "test-session-123"
187+
188+
@patch("bedrock_agentcore.runtime.agent_core_runtime_client.boto3.Session")
189+
@patch("bedrock_agentcore.runtime.agent_core_runtime_client.get_data_plane_endpoint")
190+
def test_generate_connection_user_agent(self, mock_endpoint, mock_session):
191+
"""Test that User-Agent header is set correctly."""
192+
mock_endpoint.return_value = "https://example.aws.dev"
193+
mock_creds = Mock()
194+
mock_creds.get_frozen_credentials.return_value = Mock(access_key="AKIATEST", secret_key="secret", token=None)
195+
mock_session.return_value.get_credentials.return_value = mock_creds
196+
197+
client = AgentCoreRuntimeClient(region="us-west-2")
198+
runtime_arn = "arn:aws:bedrock-agentcore:us-west-2:123:runtime/my-runtime"
199+
200+
ws_url, headers = client.generate_ws_connection(runtime_arn)
201+
202+
assert "User-Agent" in headers
203+
assert headers["User-Agent"] == "AgentCoreRuntimeClient/1.0"
184204

185205
@patch("bedrock_agentcore.runtime.agent_core_runtime_client.boto3.Session")
186206
@patch("bedrock_agentcore.runtime.agent_core_runtime_client.get_data_plane_endpoint")
@@ -271,6 +291,23 @@ def test_generate_presigned_url_with_custom_headers(self, mock_endpoint, mock_se
271291

272292
assert "abc=pqr" in presigned_url
273293

294+
@patch("bedrock_agentcore.runtime.agent_core_runtime_client.boto3.Session")
295+
@patch("bedrock_agentcore.runtime.agent_core_runtime_client.get_data_plane_endpoint")
296+
def test_generate_presigned_url_with_session_id(self, mock_endpoint, mock_session):
297+
"""Test generating presigned URL with explicit session ID."""
298+
mock_endpoint.return_value = "https://example.aws.dev"
299+
mock_creds = Mock()
300+
mock_creds.get_frozen_credentials.return_value = Mock(access_key="AKIATEST", secret_key="secret", token=None)
301+
mock_session.return_value.get_credentials.return_value = mock_creds
302+
303+
client = AgentCoreRuntimeClient(region="us-west-2")
304+
runtime_arn = "arn:aws:bedrock-agentcore:us-west-2:123:runtime/my-runtime"
305+
306+
presigned_url = client.generate_presigned_url(runtime_arn, session_id="test-session-456")
307+
308+
# Verify session ID is in query params
309+
assert "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id=test-session-456" in presigned_url
310+
274311
@patch("bedrock_agentcore.runtime.agent_core_runtime_client.boto3.Session")
275312
@patch("bedrock_agentcore.runtime.agent_core_runtime_client.get_data_plane_endpoint")
276313
def test_generate_presigned_url_with_custom_expires(self, mock_endpoint, mock_session):
@@ -286,6 +323,8 @@ def test_generate_presigned_url_with_custom_expires(self, mock_endpoint, mock_se
286323
presigned_url = client.generate_presigned_url(runtime_arn, expires=60)
287324

288325
assert "X-Amz-Expires=60" in presigned_url
326+
# Verify auto-generated session ID is present
327+
assert "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id=" in presigned_url
289328

290329
def test_generate_presigned_url_exceeds_max_expires_raises_error(self):
291330
"""Test that exceeding max expiration raises ValueError."""

0 commit comments

Comments
 (0)