Skip to content

Commit 7ca2c03

Browse files
authored
Merge pull request #113 from awslabs/feat/agui-agents-simplify-entrypoint
refactor(agui): build agent directly in entrypoint, make Memory optional
2 parents ab05210 + 7a764db commit 7ca2c03

2 files changed

Lines changed: 58 additions & 121 deletions

File tree

patterns/agui-langgraph-agent/agent.py

Lines changed: 28 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
"""AG-UI LangGraph agent with Gateway MCP tools, Memory, and Code Interpreter.
1+
"""AG-UI LangGraph agent with Gateway MCP tools, Memory, and Code Interpreter."""
22

3-
Uses copilotkit's LangGraphAGUIAgent to produce native AG-UI SSE events.
4-
AgentCore proxies these unchanged when deployed with --protocol AGUI.
5-
"""
3+
from __future__ import annotations
64

75
import logging
86
import os
@@ -12,7 +10,6 @@
1210
from copilotkit import CopilotKitMiddleware, LangGraphAGUIAgent
1311
from langchain.agents import create_agent
1412
from langchain_aws import ChatBedrock
15-
from langgraph.graph import END, START, StateGraph
1613
from langgraph_checkpoint_aws import AgentCoreMemorySaver
1714
from tools.gateway import create_gateway_mcp_client
1815
from utils.auth import extract_user_id_from_context
@@ -28,82 +25,54 @@
2825
"When asked about your tools, list them and explain what they do."
2926
)
3027

28+
REGION = os.environ.get("AWS_REGION", "us-east-1")
29+
MEMORY_ID = os.environ.get("MEMORY_ID")
30+
MODEL = ChatBedrock(
31+
model_id="us.anthropic.claude-sonnet-4-5-20250929-v1:0",
32+
temperature=0.1,
33+
streaming=True,
34+
beta_use_converse_api=True,
35+
)
36+
CODE_INTERPRETER = LangGraphCodeInterpreterTools(REGION).execute_python_securely
3137

32-
def _build_model() -> ChatBedrock:
33-
return ChatBedrock(
34-
model_id="us.anthropic.claude-sonnet-4-5-20250929-v1:0",
35-
temperature=0.1,
36-
streaming=True,
37-
beta_use_converse_api=True,
38-
)
3938

39+
def get_memory_saver() -> AgentCoreMemorySaver | None:
40+
"""Return an AgentCore Memory checkpointer, or None when MEMORY_ID is unset."""
41+
if not MEMORY_ID:
42+
return None
43+
return AgentCoreMemorySaver(memory_id=MEMORY_ID, region_name=REGION)
4044

41-
def _create_checkpointer() -> AgentCoreMemorySaver:
42-
memory_id = os.environ.get("MEMORY_ID")
43-
if not memory_id:
44-
raise ValueError("MEMORY_ID environment variable is required")
45-
return AgentCoreMemorySaver(
46-
memory_id=memory_id,
47-
region_name=os.environ.get("AWS_DEFAULT_REGION", "us-east-1"),
48-
)
4945

50-
51-
async def create_langgraph_agent(user_id: str):
52-
"""Create a LangGraph agent with Gateway tools, Memory, and Code Interpreter."""
53-
mcp_client = await create_gateway_mcp_client(user_id)
46+
async def build_graph(actor_id: str):
47+
"""Build a LangGraph compiled graph with Gateway tools and Memory."""
48+
mcp_client = await create_gateway_mcp_client(actor_id)
5449
tools = await mcp_client.get_tools()
55-
56-
region = os.environ.get("AWS_DEFAULT_REGION", "us-east-1")
57-
code_tools = LangGraphCodeInterpreterTools(region)
58-
tools.append(code_tools.execute_python_securely)
50+
tools.append(CODE_INTERPRETER)
5951

6052
return create_agent(
61-
model=_build_model(),
53+
model=MODEL,
6254
tools=tools,
63-
checkpointer=_create_checkpointer(),
55+
checkpointer=get_memory_saver(),
6456
middleware=[CopilotKitMiddleware()],
6557
system_prompt=SYSTEM_PROMPT,
6658
)
6759

6860

69-
class ActorAwareLangGraphAgent(LangGraphAGUIAgent):
70-
"""LangGraphAGUIAgent that creates the graph per-request with fresh tokens."""
71-
72-
def __init__(self, *, user_id: str, **kwargs):
73-
self._user_id = user_id
74-
# Create a minimal placeholder graph to satisfy validation in newer
75-
# copilotkit/ag_ui_langgraph versions that inspect self.graph.nodes
76-
# during __init__. The placeholder is overwritten in run().
77-
if kwargs.get("graph") is None:
78-
builder = StateGraph(dict)
79-
builder.add_node("placeholder", lambda x: x)
80-
builder.add_edge(START, "placeholder")
81-
builder.add_edge("placeholder", END)
82-
kwargs["graph"] = builder.compile()
83-
super().__init__(**kwargs)
84-
85-
async def run(self, input: RunAgentInput):
86-
self.graph = await create_langgraph_agent(self._user_id)
87-
async for event in super().run(input):
88-
yield event
89-
90-
9161
@app.entrypoint
9262
async def invocations(payload: dict, context: RequestContext):
9363
input_data = RunAgentInput.model_validate(payload)
64+
actor_id = extract_user_id_from_context(context)
9465

95-
user_id = extract_user_id_from_context(context)
96-
97-
agent = ActorAwareLangGraphAgent(
66+
graph = await build_graph(actor_id)
67+
agui_agent = LangGraphAGUIAgent(
9868
name="agui_langgraph_agent",
9969
description="AG-UI LangGraph agent with Gateway MCP tools and Memory",
100-
graph=None,
101-
config={"configurable": {"actor_id": user_id}},
102-
user_id=user_id,
70+
graph=graph,
71+
config={"configurable": {"actor_id": actor_id}},
10372
)
10473

10574
try:
106-
async for event in agent.run(input_data):
75+
async for event in agui_agent.run(input_data):
10776
if event is not None:
10877
yield event.model_dump(mode="json", by_alias=True, exclude_none=True)
10978
except Exception as exc:

patterns/agui-strands-agent/agent.py

Lines changed: 30 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
"""AG-UI Strands agent with Gateway MCP tools, Memory, and Code Interpreter.
1+
"""AG-UI Strands agent with Gateway MCP tools, Memory, and Code Interpreter."""
22

3-
Uses ag-ui-strands to produce native AG-UI SSE events.
4-
AgentCore proxies these unchanged when deployed with --protocol AGUI.
5-
"""
3+
from __future__ import annotations
64

75
import logging
86
import os
@@ -30,79 +28,49 @@
3028
"When asked about your tools, list them and explain what they do."
3129
)
3230

33-
34-
def _build_model() -> BedrockModel:
35-
return BedrockModel(
36-
model_id="us.anthropic.claude-sonnet-4-5-20250929-v1:0", temperature=0.1
37-
)
31+
REGION = os.environ.get("AWS_REGION", "us-east-1")
32+
MEMORY_ID = os.environ.get("MEMORY_ID")
33+
MODEL = BedrockModel(
34+
model_id="us.anthropic.claude-sonnet-4-5-20250929-v1:0",
35+
temperature=0.1,
36+
)
37+
CODE_INTERPRETER = StrandsCodeInterpreterTools(REGION).execute_python_securely
3838

3939

40-
def _create_session_manager(
41-
user_id: str, session_id: str
42-
) -> AgentCoreMemorySessionManager:
43-
memory_id = os.environ.get("MEMORY_ID")
44-
if not memory_id:
45-
raise ValueError("MEMORY_ID environment variable is required")
46-
config = AgentCoreMemoryConfig(
47-
memory_id=memory_id, session_id=session_id, actor_id=user_id
48-
)
40+
def get_memory_session_manager(
41+
actor_id: str, session_id: str
42+
) -> AgentCoreMemorySessionManager | None:
43+
"""Return an AgentCore Memory session manager, or None when MEMORY_ID is unset."""
44+
if not MEMORY_ID:
45+
return None
4946
return AgentCoreMemorySessionManager(
50-
agentcore_memory_config=config,
51-
region_name=os.environ.get("AWS_DEFAULT_REGION", "us-east-1"),
52-
)
53-
54-
55-
def _create_agent(user_id: str, session_id: str) -> Agent:
56-
"""Create a Strands Agent with Gateway MCP tools, Memory, and Code Interpreter."""
57-
gateway_client = create_gateway_mcp_client(user_id)
58-
59-
region = os.environ.get("AWS_DEFAULT_REGION", "us-east-1")
60-
code_tools = StrandsCodeInterpreterTools(region)
61-
62-
return Agent(
63-
name="strands_agent",
64-
system_prompt=SYSTEM_PROMPT,
65-
tools=[gateway_client, code_tools.execute_python_securely],
66-
model=_build_model(),
67-
session_manager=_create_session_manager(user_id, session_id),
47+
AgentCoreMemoryConfig(
48+
memory_id=MEMORY_ID, session_id=session_id, actor_id=actor_id
49+
),
50+
region_name=REGION,
6851
)
6952

7053

71-
class ActorAwareStrandsAgent(StrandsAgent):
72-
"""StrandsAgent that creates the agent per-request with fresh MCP context."""
73-
74-
def __init__(self, *, user_id: str, session_id: str, name: str, description: str):
75-
self._user_id = user_id
76-
self._session_id = session_id
77-
super().__init__(
78-
agent=Agent(model=_build_model(), system_prompt=SYSTEM_PROMPT),
79-
name=name,
80-
description=description,
81-
)
82-
83-
async def run(self, input_data: RunAgentInput):
84-
thread_id = input_data.thread_id or "default"
85-
self._agents_by_thread[thread_id] = _create_agent(
86-
self._user_id, self._session_id
87-
)
88-
async for event in super().run(input_data):
89-
yield event
90-
91-
9254
@app.entrypoint
9355
async def invocations(payload: dict, context: RequestContext):
9456
input_data = RunAgentInput.model_validate(payload)
95-
user_id = extract_user_id_from_context(context)
57+
actor_id = extract_user_id_from_context(context)
58+
session_id = input_data.thread_id or actor_id
9659

97-
agent = ActorAwareStrandsAgent(
98-
user_id=user_id,
99-
session_id=input_data.thread_id,
60+
agent = Agent(
61+
model=MODEL,
62+
system_prompt=SYSTEM_PROMPT,
63+
tools=[create_gateway_mcp_client(actor_id), CODE_INTERPRETER],
64+
session_manager=get_memory_session_manager(actor_id, session_id),
65+
)
66+
agui_agent = StrandsAgent(
67+
agent=agent,
10068
name="agui_strands_agent",
10169
description="AG-UI Strands agent with Gateway MCP tools and Code Interpreter",
10270
)
10371

10472
try:
105-
async for event in agent.run(input_data):
73+
async for event in agui_agent.run(input_data):
10674
if event is not None:
10775
yield event.model_dump(mode="json", by_alias=True, exclude_none=True)
10876
except Exception as exc:

0 commit comments

Comments
 (0)