Skip to content

Commit 8ce5958

Browse files
committed
fix(patterns): align langgraph and strands agents with AG-UI docs
Both patterns were using the legacy HTTP streaming approach — parsing prompt/runtimeSessionId from the payload and yielding raw dicts — instead of the AG-UI protocol described in the AgentCore docs. Switch to RunAgentInput as the input format and wrap agents with LangGraphAgent (ag_ui_langgraph) and StrandsAgent (ag_ui_strands) so both patterns produce proper AG-UI event streams when deployed with --protocol AGUI.
1 parent 0f0e83a commit 8ce5958

4 files changed

Lines changed: 83 additions & 69 deletions

File tree

patterns/langgraph-single-agent/langgraph_agent.py

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import logging
44
import os
55

6+
from ag_ui.core import RunAgentInput, RunErrorEvent
7+
from ag_ui_langgraph import LangGraphAgent
68
from bedrock_agentcore.runtime import BedrockAgentCoreApp, RequestContext
79
from langchain.agents import create_agent
810
from langchain_aws import ChatBedrock
@@ -59,38 +61,31 @@ async def create_langgraph_agent():
5961

6062

6163
@app.entrypoint
62-
async def invocations(payload, context: RequestContext):
63-
"""Main entrypoint — called by AgentCore Runtime on each request."""
64-
user_query = payload.get("prompt")
65-
session_id = payload.get("runtimeSessionId")
66-
67-
if not all([user_query, session_id]):
68-
yield {
69-
"status": "error",
70-
"error": "Missing required fields: prompt or runtimeSessionId",
71-
}
72-
return
64+
async def invocations(payload: dict, context: RequestContext):
65+
"""Main entrypoint — called by AgentCore Runtime on each AG-UI request."""
66+
input_data = RunAgentInput.model_validate(payload)
67+
68+
user_id = extract_user_id_from_context(context)
69+
graph = await create_langgraph_agent()
70+
71+
agent = LangGraphAgent(
72+
name="langgraph_agent",
73+
description="LangGraph agent with Gateway MCP tools and Memory",
74+
graph=graph,
75+
config={"configurable": {"actor_id": user_id}},
76+
)
7377

7478
try:
75-
user_id = extract_user_id_from_context(context)
76-
77-
graph = await create_langgraph_agent()
78-
79-
config = {"configurable": {"thread_id": session_id, "actor_id": user_id}}
80-
81-
async for event in graph.astream(
82-
{"messages": [("user", user_query)]},
83-
config=config,
84-
stream_mode="messages",
85-
):
86-
message_chunk, metadata = event
87-
yield message_chunk.model_dump()
88-
89-
except Exception as e:
90-
error_msg = str(e) if str(e) else f"{type(e).__name__}: {repr(e)}"
79+
async for event in agent.run(input_data):
80+
if event is not None:
81+
yield event.model_dump(mode="json", by_alias=True, exclude_none=True)
82+
except Exception as exc:
9183
logger.exception("Agent run failed")
92-
yield {"status": "error", "error": error_msg}
84+
yield RunErrorEvent(
85+
message=str(exc) or type(exc).__name__,
86+
code=type(exc).__name__,
87+
).model_dump(mode="json", by_alias=True, exclude_none=True)
9388

9489

9590
if __name__ == "__main__":
96-
app.run()
91+
app.run()

patterns/langgraph-single-agent/requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# LangGraph agent dependencies
1+
# LangGraph AG-UI agent dependencies
2+
ag-ui-langgraph
23
langchain==1.2.13
34
langgraph==1.1.3
45
langchain-aws==1.4.1
Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"""Strands agent with Gateway MCP tools, Memory, and Code Interpreter."""
22

3-
import json
43
import logging
54
import os
65

6+
from ag_ui.core import RunAgentInput, RunErrorEvent
7+
from ag_ui_strands import StrandsAgent
78
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig
89
from bedrock_agentcore.memory.integrations.strands.session_manager import (
910
AgentCoreMemorySessionManager,
@@ -26,6 +27,12 @@
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+
2936
def _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

97114
if __name__ == "__main__":
98-
app.run()
115+
app.run()

patterns/strands-single-agent/requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# Strands agent dependencies
1+
# Strands AG-UI agent dependencies
2+
ag-ui-strands
23
strands-agents==1.32.0
34
bedrock-agentcore==1.4.7
45
mcp==1.26.0

0 commit comments

Comments
 (0)