chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:58:18 +08:00
commit 6d5d58c1a9
18293 changed files with 3502153 additions and 0 deletions
@@ -0,0 +1,36 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim
WORKDIR /app
# Configure UV for container environment
ENV UV_SYSTEM_PYTHON=1 \
UV_COMPILE_BYTECODE=1 \
DOCKER_CONTAINER=1 \
OTEL_PYTHON_LOG_CORRELATION=true \
PYTHONUNBUFFERED=1
# Copy and install agent-specific requirements first
COPY agents/langgraph-single-agent/requirements.txt requirements.txt
RUN uv pip install --no-cache -r requirements.txt && \
uv pip install --no-cache aws-opentelemetry-distro==0.16.0
# Create non-root user
RUN useradd -m -u 1000 bedrock_agentcore
USER bedrock_agentcore
EXPOSE 8080
# Copy agent code, tools, and shared utilities
COPY agents/langgraph-single-agent/langgraph_agent.py .
COPY agents/langgraph-single-agent/tools/ tools/
COPY agents/utils/ utils/
# Healthcheck using Python (no extra dependencies needed)
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/ping', timeout=2)" || exit 1
# Start agent with OpenTelemetry instrumentation
CMD ["opentelemetry-instrument", "python", "-m", "langgraph_agent"]
@@ -0,0 +1,159 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
import logging
import os
from ag_ui.core import RunAgentInput, RunErrorEvent
from bedrock_agentcore.identity.auth import requires_access_token
from bedrock_agentcore.runtime import BedrockAgentCoreApp, RequestContext
from copilotkit import (
CopilotKitMiddleware,
LangGraphAGUIAgent,
StateStreamingMiddleware,
StateItem,
)
from langchain.agents import create_agent
from langchain_aws import ChatBedrock
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph_checkpoint_aws import AgentCoreMemorySaver
from utils.auth import extract_user_id_from_context
from utils.ssm import get_ssm_parameter
from tools import query_data, AgentState, todo_tools
app = BedrockAgentCoreApp()
ACTOR_ID_KEYS = ("actor_id", "actorId", "user_id", "userId", "sub")
SYSTEM_PROMPT = """You are a helpful assistant with access to tools via the Gateway and built-in data tools.
When demonstrating charts, always call the query_data tool first to fetch data from the database before calling any chart tool.
When managing todos, use manage_todos to update the list and get_todos to read the current list.
When asked about your tools, list them and explain what they do."""
@requires_access_token(
provider_name=os.environ["GATEWAY_CREDENTIAL_PROVIDER_NAME"],
auth_flow="M2M",
scopes=[],
)
async def _fetch_gateway_token(access_token: str) -> str:
return access_token
async def create_gateway_mcp_client() -> MultiServerMCPClient:
stack_name = os.environ.get("STACK_NAME")
if not stack_name:
raise ValueError("STACK_NAME environment variable is required")
if not stack_name.replace("-", "").replace("_", "").isalnum():
raise ValueError("Invalid STACK_NAME format")
gateway_url = get_ssm_parameter(f"/{stack_name}/gateway_url")
fresh_token = await _fetch_gateway_token()
return MultiServerMCPClient(
{
"gateway": {
"transport": "streamable_http",
"url": gateway_url,
"headers": {
"Authorization": f"Bearer {fresh_token}",
},
}
}
)
def _build_model(streaming: bool) -> ChatBedrock:
return ChatBedrock(
model_id="us.anthropic.claude-sonnet-4-5-20250929-v1:0",
temperature=0.1,
max_tokens=16384,
streaming=streaming,
beta_use_converse_api=True,
)
def _build_checkpointer() -> AgentCoreMemorySaver:
memory_id = os.environ.get("MEMORY_ID")
if not memory_id:
raise ValueError("MEMORY_ID environment variable is required")
return AgentCoreMemorySaver(
memory_id=memory_id,
region_name=os.environ.get("AWS_DEFAULT_REGION", "us-east-1"),
)
@app.entrypoint
async def invocations(payload: dict, context: RequestContext):
input_data = RunAgentInput.model_validate(payload)
# Extract actor identity securely from the validated JWT token.
try:
actor_id = extract_user_id_from_context(context)
except ValueError:
# Fall back to forwarded props if JWT extraction fails (e.g. local dev).
forwarded = (
input_data.forwarded_props
if isinstance(input_data.forwarded_props, dict)
else {}
)
actor_id = next(
(forwarded[k] for k in ACTOR_ID_KEYS if k in forwarded and forwarded[k]),
None,
)
if not actor_id:
raise ValueError(
"Missing actor identity. Provide forwardedProps.actor_id/user_id "
"or include sub claim in the bearer token."
)
try:
try:
mcp_client = await create_gateway_mcp_client()
gateway_tools = await mcp_client.get_tools()
except Exception as gw_err:
logging.warning("Gateway tools unavailable (running locally?): %s", gw_err)
gateway_tools = []
graph = create_agent(
model=_build_model(streaming=True),
tools=[*gateway_tools, query_data, *todo_tools],
checkpointer=_build_checkpointer(),
middleware=[
CopilotKitMiddleware(),
StateStreamingMiddleware(
StateItem(
state_key="todos", tool="manage_todos", tool_argument="todos"
)
),
],
system_prompt=SYSTEM_PROMPT,
state_schema=AgentState,
)
agent = LangGraphAGUIAgent(
name="LangGraphSingleAgent",
description="LangGraph single agent exposed via AG-UI",
graph=graph,
config={"configurable": {"actor_id": actor_id}},
)
async for event in agent.run(input_data):
if event is not None:
yield event.model_dump(mode="json", by_alias=True, exclude_none=True)
except Exception as exc:
logging.exception("Agent run failed")
yield RunErrorEvent(
message=str(exc) or type(exc).__name__,
code=type(exc).__name__,
).model_dump(mode="json", by_alias=True, exclude_none=True)
if __name__ == "__main__":
app.run()
@@ -0,0 +1,14 @@
# LangGraph agent dependencies with pinned versions
fastapi==0.115.12
uvicorn==0.34.2
ag-ui-protocol>=0.1.15
ag-ui-langgraph==0.0.33
copilotkit==0.1.87
partialjson>=0.0.8
langgraph==1.0.10rc1
langchain>=0.3.0
langchain-aws==1.0.0
langchain-mcp-adapters==0.1.13
langgraph-checkpoint-aws==1.0.5
mcp==1.23.1
bedrock-agentcore==1.0.6
@@ -0,0 +1,8 @@
# patterns/langgraph-single-agent/tools/__init__.py
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from .query_data import query_data
from .todos import AgentState, todo_tools
__all__ = ["query_data", "AgentState", "todo_tools"]
@@ -0,0 +1,16 @@
date,category,amount,type
2026-01-05,Food,42.50,expense
2026-01-10,Transport,15.00,expense
2026-01-15,Salary,3500.00,income
2026-01-20,Entertainment,80.00,expense
2026-01-25,Utilities,120.00,expense
2026-02-03,Food,55.20,expense
2026-02-08,Freelance,800.00,income
2026-02-14,Dining,65.00,expense
2026-02-20,Transport,22.50,expense
2026-02-28,Salary,3500.00,income
2026-03-05,Groceries,95.40,expense
2026-03-10,Gym,40.00,expense
2026-03-15,Salary,3500.00,income
2026-03-18,Coffee,18.75,expense
2026-03-22,Books,35.00,expense
1 date category amount type
2 2026-01-05 Food 42.50 expense
3 2026-01-10 Transport 15.00 expense
4 2026-01-15 Salary 3500.00 income
5 2026-01-20 Entertainment 80.00 expense
6 2026-01-25 Utilities 120.00 expense
7 2026-02-03 Food 55.20 expense
8 2026-02-08 Freelance 800.00 income
9 2026-02-14 Dining 65.00 expense
10 2026-02-20 Transport 22.50 expense
11 2026-02-28 Salary 3500.00 income
12 2026-03-05 Groceries 95.40 expense
13 2026-03-10 Gym 40.00 expense
14 2026-03-15 Salary 3500.00 income
15 2026-03-18 Coffee 18.75 expense
16 2026-03-22 Books 35.00 expense
@@ -0,0 +1,25 @@
# patterns/langgraph-single-agent/tools/query_data.py
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import csv
from pathlib import Path
from langchain.tools import tool
# Read at module load time — avoids file I/O on every tool invocation.
_csv_path = Path(__file__).parent / "db.csv"
try:
with open(_csv_path) as _f:
_cached_data = list(csv.DictReader(_f))
except (FileNotFoundError, OSError) as e:
raise RuntimeError(f"query_data: cannot load sample data from {_csv_path}") from e
@tool
def query_data(query: str) -> list[dict]:
"""
Query the database. Accepts natural language.
Always call this tool before displaying a chart or graph.
"""
return _cached_data
@@ -0,0 +1,66 @@
# patterns/langgraph-single-agent/tools/todos.py
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import uuid
from typing import Literal, TypedDict
from langchain.agents import AgentState as BaseAgentState
from langchain.tools import ToolRuntime, tool
from langchain_core.messages import ToolMessage
from langgraph.types import Command
# ToolRuntime is confirmed available at langchain.tools (langchain >= 1.2).
# If you see an ImportError, verify your langchain version is >= 0.3.
class Todo(TypedDict):
id: str
title: str
description: str
emoji: str
status: Literal["pending", "completed"]
class AgentState(BaseAgentState):
todos: list[Todo]
def _assign_ids(todos: list[dict]) -> list[dict]:
"""Assign a uuid4 to any todo that has a missing or empty 'id'."""
for todo in todos:
if not todo.get("id"):
todo["id"] = str(uuid.uuid4())
return todos
@tool
def manage_todos(todos: list[Todo], runtime: ToolRuntime) -> Command:
"""
Manage the current todos. Replaces the entire todo list.
Assigns a unique UUID to any todo that is missing one.
"""
_assign_ids(todos) # type: ignore[arg-type]
return Command(
update={
"todos": todos,
"messages": [
ToolMessage(
content="Successfully updated todos",
tool_call_id=runtime.tool_call_id,
)
],
}
)
@tool
def get_todos(runtime: ToolRuntime) -> list[Todo]:
"""
Get the current todo list from agent state.
"""
return runtime.state.get("todos", [])
todo_tools = [manage_todos, get_todos]