feat: add AG-UI protocol support via serve_ag_ui and AGUIApp#350
feat: add AG-UI protocol support via serve_ag_ui and AGUIApp#350sundargthb merged 2 commits intomainfrom
Conversation
Add AG-UI protocol support with SSE and WebSocket dual transport per the Bedrock AgentCore AG-UI contract. A single @app.entrypoint handler is automatically served on both POST /invocations (SSE) and /ws (WebSocket). - AGUIApp: focused Starlette app with /invocations, /ws, and /ping - serve_ag_ui(): one-liner to start a Bedrock-compatible AG-UI server - build_ag_ui_app(): returns app without starting (for TestClient/mounting) - Bedrock header extraction, Docker host detection, error → RunErrorEvent - ag-ui-protocol added as optional dependency: pip install "bedrock-agentcore[ag-ui]"
59ea7d7 to
322c412
Compare
|
Two things to verify against the ag-ui-protocol SDK before merging: The curl example uses thread_id/run_id (snake_case), but the AG-UI spec defines these as threadId/runId (camelCase). Confirm that RunAgentInput(**payload) handle both formats. The error path uses RunErrorEvent(message=str(e)) — does RunErrorEvent from ag_ui.core accept message as a constructor parameter? The AG-UI spec defines RUN_ERROR with both code and message fields. Passing only message might be fine as a default, but confirm that the constructor signature and whether we should include a code (e.g. "INTERNAL_ERROR") for spec compliance. Otherwise, LGTM |
|
Thanks for the review! Verified both points against 1. snake_case vs camelCase in
# camelCase (AG-UI spec / what CopilotKit sends)
RunAgentInput(**{"threadId": "t1", "runId": "r1", ...}) # ✅
# snake_case (curl convenience / Python convention)
RunAgentInput(**{"thread_id": "t1", "run_id": "r1", ...}) # ✅Both work. The curl example uses 2. RunErrorEvent(*, message: str, code: Optional[str] = None, ...)
That said, adding |
Summary
AGUIApp,serve_ag_ui(), andbuild_ag_ui_app()for AG-UI protocol support@app.entrypointhandler is served on both transports per the AG-UI contract:POST /invocations— SSE (unidirectional streaming)/ws— WebSocket (bidirectional, same AG-UI events)/pinghealth checkRunErrorEventag-ui-protocol>=0.1.10added as optional dependency (pip install "bedrock-agentcore[ag-ui]")a2a.pypatterns (lazy imports,_check_sdk(),_takes_context,_build_request_context)Sample: Framework agent (e.g. Strands + ag-ui-strands)
For agents that already expose a
.run()method (Strands, LangGraph adapters, etc.):Sample: Custom agent (decorator form)
For writing agent logic directly without a framework adapter:
Sample: Testing with TestClient
Files
src/bedrock_agentcore/runtime/ag_ui.py—AGUIApp,serve_ag_ui,build_ag_ui_appsrc/bedrock_agentcore/runtime/__init__.py— export new symbolspyproject.toml— addag-uioptional dep group + dev depuv.lock— updated lockfileREADME.md— AG-UI section with examplesdocs/proposals/ag-ui-protocol-support.md— updated design doctests/bedrock_agentcore/runtime/test_ag_ui.py— 20 unit teststests/integration/runtime/test_ag_ui_integration.py— 11 integration testsTest plan
pytest tests/bedrock_agentcore/runtime/test_ag_ui.py— 20 unit tests passpytest tests/integration/runtime/test_ag_ui_integration.py— 11 integration tests passpytest tests/bedrock_agentcore/runtime/— all 252 runtime tests pass (no regressions)ruff check+ruff format --check— cleanManual testing
Verified end-to-end with four framework integrations and both AG-UI transports (SSE and WebSocket).
1. Strands on Bedrock (SSE)
Scaffolded a sample project using
ag-ui-strands+StrandsAgentwrapping a StrandsAgentwithBedrockModel(Claude Sonnet). Served viaserve_ag_ui(agui_agent). Connected a CopilotKit React frontend (HttpAgent→POST /invocations). Confirmed streaming text responses over SSE.2. Google ADK (WebSocket)
Replaced Strands with
google-adkusingAgent+Runner+InMemorySessionService. Used the@app.entrypointdecorator form to map ADK runner events to AG-UI events. Connected a custom React chat UI via WebSocket to/ws. Confirmed bidirectional WebSocket transport — sentRunAgentInputas first JSON frame, received AG-UI events as text frames, connection closed on completion.3. LangChain on Bedrock (SSE)
Replaced ADK with
langchain-aws(ChatBedrockConverse). Used@app.entrypointdecorator withmodel.astream()to stream LangChain chunks asTextMessageContentEvents. Connected CopilotKit React frontend (HttpAgent→POST /invocations). Confirmed streaming text responses over SSE.4. LangGraph on Bedrock (SSE) — native AG-UI integration
Used
LangGraphAGUIAgentfromcopilotkitwrapping a compiled LangGraphStateGraph(withMemorySavercheckpointer) andChatBedrockConverse(Claude Sonnet). The nativeag-ui-langgraphadapter handles all AG-UI event translation — no manual event yielding needed. Passed the agent directly toserve_ag_ui(agui_agent), which detected the.run()method and wired it to both transports. Connected a CopilotKit React frontend (HttpAgent→POST /invocations) withExperimentalEmptyAdapter. Confirmed streaming text responses over SSE end-to-end.Transports tested
POST /invocations/wsGET /pingcurl verification
Both returned expected AG-UI event stream and
{"status":"Healthy",...}respectively.