11"""Strands agent with Gateway MCP tools, Memory, and Code Interpreter."""
22
3- import json
43import logging
54import os
65
6+ from ag_ui .core import RunAgentInput , RunErrorEvent
7+ from ag_ui_strands import StrandsAgent
78from bedrock_agentcore .memory .integrations .strands .config import AgentCoreMemoryConfig
89from bedrock_agentcore .memory .integrations .strands .session_manager import (
910 AgentCoreMemorySessionManager ,
2627)
2728
2829
30+ def _build_model () -> BedrockModel :
31+ return BedrockModel (
32+ model_id = "us.anthropic.claude-sonnet-4-5-20250929-v1:0" , temperature = 0.1
33+ )
34+
35+
2936def _create_session_manager (
3037 user_id : str , session_id : str
3138) -> AgentCoreMemorySessionManager :
@@ -41,58 +48,68 @@ def _create_session_manager(
4148 )
4249
4350
44- def create_strands_agent (user_id : str , session_id : str ) -> Agent :
45- """Create a Strands agent with Gateway tools, memory, and Code Interpreter."""
46-
47- bedrock_model = BedrockModel (
48- model_id = "us.anthropic.claude-sonnet-4-5-20250929-v1:0" , temperature = 0.1
49- )
50-
51- session_manager = _create_session_manager (user_id , session_id )
51+ def _create_agent (user_id : str , session_id : str ) -> Agent :
52+ """Create a Strands Agent with Gateway MCP tools, Memory, and Code Interpreter."""
53+ gateway_client = create_gateway_mcp_client ()
5254
5355 region = os .environ .get ("AWS_DEFAULT_REGION" , "us-east-1" )
5456 code_tools = StrandsCodeInterpreterTools (region )
5557
56- gateway_client = create_gateway_mcp_client ()
57-
5858 return Agent (
5959 name = "strands_agent" ,
6060 system_prompt = SYSTEM_PROMPT ,
6161 tools = [gateway_client , code_tools .execute_python_securely ],
62- model = bedrock_model ,
63- session_manager = session_manager ,
64- trace_attributes = {"user.id" : user_id , "session.id" : session_id },
62+ model = _build_model (),
63+ session_manager = _create_session_manager (user_id , session_id ),
6564 )
6665
6766
68- @app .entrypoint
69- async def invocations (payload , context : RequestContext ):
70- """Main entrypoint — called by AgentCore Runtime on each request.
71-
72- Extracts user ID from the validated JWT token (not the payload body)
73- to prevent impersonation via prompt injection.
74- """
75- user_query = payload .get ("prompt" )
76- session_id = payload .get ("runtimeSessionId" )
77-
78- if not all ([user_query , session_id ]):
79- yield {
80- "status" : "error" ,
81- "error" : "Missing required fields: prompt or runtimeSessionId" ,
82- }
83- return
67+ class ActorAwareStrandsAgent (StrandsAgent ):
68+ """StrandsAgent that creates the underlying agent per-request with the
69+ correct user/session scope for AgentCore memory."""
8470
85- try :
86- user_id = extract_user_id_from_context (context )
87- agent = create_strands_agent (user_id , session_id )
71+ def __init__ (self , * , user_id : str , session_id : str , name : str , description : str ):
72+ self ._user_id = user_id
73+ self ._session_id = session_id
74+ super ().__init__ (
75+ agent = Agent (model = _build_model (), system_prompt = SYSTEM_PROMPT ),
76+ name = name ,
77+ description = description ,
78+ )
8879
89- async for event in agent .stream_async (user_query ):
90- yield json .loads (json .dumps (dict (event ), default = str ))
80+ async def run (self , input_data : RunAgentInput ):
81+ thread_id = input_data .thread_id or self ._session_id
82+ self ._agents_by_thread [thread_id ] = _create_agent (
83+ self ._user_id , self ._session_id
84+ )
85+ async for event in super ().run (input_data ):
86+ yield event
9187
92- except Exception as e :
88+
89+ @app .entrypoint
90+ async def invocations (payload : dict , context : RequestContext ):
91+ """Main entrypoint — called by AgentCore Runtime on each AG-UI request."""
92+ input_data = RunAgentInput .model_validate (payload )
93+ user_id = extract_user_id_from_context (context )
94+
95+ agent = ActorAwareStrandsAgent (
96+ user_id = user_id ,
97+ session_id = input_data .thread_id ,
98+ name = "strands_agent" ,
99+ description = "Strands agent with Gateway MCP tools and Memory" ,
100+ )
101+
102+ try :
103+ async for event in agent .run (input_data ):
104+ if event is not None :
105+ yield event .model_dump (mode = "json" , by_alias = True , exclude_none = True )
106+ except Exception as exc :
93107 logger .exception ("Agent run failed" )
94- yield {"status" : "error" , "error" : str (e )}
108+ yield RunErrorEvent (
109+ message = str (exc ) or type (exc ).__name__ ,
110+ code = type (exc ).__name__ ,
111+ ).model_dump (mode = "json" , by_alias = True , exclude_none = True )
95112
96113
97114if __name__ == "__main__" :
98- app .run ()
115+ app .run ()
0 commit comments