Skip to content

Commit 1589cb5

Browse files
authored
feat: add aws bedrock agentcore examples (#811)
1 parent 631c073 commit 1589cb5

160 files changed

Lines changed: 60563 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# LangGraph JS Minimal Agent - Dockerfile
2+
#
3+
# Multi-stage build for smaller production image.
4+
# Uses Node.js 20 LTS with ES Modules support.
5+
6+
# =============================================================================
7+
# Stage 1: Dependencies
8+
# =============================================================================
9+
FROM node:20-slim AS deps
10+
11+
WORKDIR /app
12+
13+
# Copy package files
14+
COPY package*.json ./
15+
16+
# Install production dependencies only
17+
RUN npm ci --omit=dev
18+
19+
# =============================================================================
20+
# Stage 2: Production
21+
# =============================================================================
22+
FROM node:20-slim AS production
23+
24+
WORKDIR /app
25+
26+
# Create non-root user for security
27+
RUN groupadd --gid 1001 nodejs && \
28+
useradd --uid 1001 --gid nodejs --shell /bin/bash --create-home agent
29+
30+
# Copy dependencies from deps stage
31+
COPY --from=deps /app/node_modules ./node_modules
32+
33+
# Copy application code
34+
COPY agent.js ./
35+
COPY package.json ./
36+
37+
# Set environment variables
38+
ENV NODE_ENV=production
39+
ENV PORT=8080
40+
41+
# Switch to non-root user
42+
USER agent
43+
44+
# Expose the AgentCore runtime port
45+
EXPOSE 8080
46+
47+
# Start the agent
48+
CMD ["node", "agent.js"]
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# LangGraph JS Minimal Example
2+
3+
A minimal LangGraph JavaScript agent deployed to AWS Bedrock AgentCore using Serverless Framework.
4+
5+
## Features
6+
7+
- **LangGraph JS**: ReAct agent pattern with tool calling
8+
- **Claude Sonnet 4.5**: Powered by Amazon Bedrock
9+
- **Simple Tools**: Calculator operations and time queries
10+
- **Docker Deployment**: Auto-detected Dockerfile for easy deployment
11+
12+
## Quick Start
13+
14+
### Prerequisites
15+
16+
- Node.js 20+
17+
- Docker (for local development)
18+
- AWS credentials configured
19+
- Serverless Framework CLI (`npm install -g serverless`)
20+
21+
### Install Dependencies
22+
23+
```bash
24+
npm install
25+
```
26+
27+
### Local Development
28+
29+
Start the agent locally with hot reload:
30+
31+
```bash
32+
sls dev
33+
```
34+
35+
This will:
36+
37+
1. Build the Docker image
38+
2. Start the container with AWS credentials injected
39+
3. Open an interactive chat interface
40+
4. Watch for file changes and auto-rebuild
41+
42+
Try these prompts:
43+
44+
- "What time is it?"
45+
- "What time is it in Tokyo?"
46+
- "Calculate 25 multiplied by 4"
47+
- "Add 100 and 250, then divide the result by 7"
48+
49+
### Deploy to AWS
50+
51+
```bash
52+
sls deploy
53+
```
54+
55+
### Invoke Deployed Agent
56+
57+
```bash
58+
# Via Serverless Framework CLI
59+
sls invoke --agent assistant --data '{"prompt":"Hello! What can you help me with?"}'
60+
61+
# Plain string is also supported:
62+
# sls invoke --agent assistant -d "Hello! What can you help me with?"
63+
64+
# Or via curl (replace URL with your runtime URL from `sls info`)
65+
curl -X POST https://your-runtime-url/invoke \
66+
-H "Content-Type: application/json" \
67+
-d '{"prompt":"Hello! What can you help me with?"}'
68+
```
69+
70+
### Remove
71+
72+
```bash
73+
sls remove
74+
```
75+
76+
## Project Structure
77+
78+
```text
79+
langgraph-basic-dockerfile/
80+
├── serverless.yml # Serverless Framework configuration
81+
├── agent.js # LangGraph JS agent with tools
82+
├── package.json # npm dependencies
83+
├── Dockerfile # Container definition
84+
└── README.md # This file
85+
```
86+
87+
## How It Works
88+
89+
### Agent Architecture
90+
91+
```text
92+
User Input
93+
94+
95+
┌─────────────────┐
96+
│ BedrockAgent │
97+
│ CoreApp │◄─── HTTP Server (port 8080)
98+
└────────┬────────┘
99+
100+
101+
┌─────────────────┐
102+
│ LangGraph │
103+
│ ReAct Agent │◄─── Alternates between LLM and tools
104+
└────────┬────────┘
105+
106+
┌────┴────┐
107+
▼ ▼
108+
┌───────┐ ┌───────┐
109+
│ Tools │ │ Claude│
110+
│ │ │ LLM │
111+
└───────┘ └───────┘
112+
```
113+
114+
### Tools Available
115+
116+
| Tool | Description |
117+
| ------------------ | -------------------------------------------- |
118+
| `get_current_time` | Get current date/time with optional timezone |
119+
| `add` | Add two numbers |
120+
| `multiply` | Multiply two numbers |
121+
| `divide` | Divide two numbers |
122+
123+
### Configuration
124+
125+
The `serverless.yml` is intentionally minimal:
126+
127+
```yaml
128+
service: langgraph-basic-dockerfile
129+
130+
provider:
131+
name: aws
132+
133+
ai:
134+
agents:
135+
assistant: {}
136+
```
137+
138+
The Dockerfile is auto-detected, and default settings are applied:
139+
140+
- Protocol: HTTP
141+
- Network: PUBLIC
142+
- Port: 8080
143+
144+
## Customization
145+
146+
### Adding New Tools
147+
148+
Edit `agent.js` to add new tools:
149+
150+
```javascript
151+
const myNewTool = tool(
152+
async ({ input }) => {
153+
// Tool implementation
154+
return `Result: ${input}`
155+
},
156+
{
157+
name: 'my_new_tool',
158+
description: 'Description for the LLM',
159+
schema: z.object({
160+
input: z.string().describe('Input parameter'),
161+
}),
162+
},
163+
)
164+
165+
// Add to tools array
166+
const tools = [getCurrentTime, add, multiply, divide, myNewTool]
167+
```
168+
169+
### Changing the Model
170+
171+
Edit the model configuration in `agent.js`:
172+
173+
```javascript
174+
const model = new ChatBedrockConverse({
175+
// Claude Sonnet 4.5
176+
model: 'us.anthropic.claude-sonnet-4-5-20250929-v1:0',
177+
// Or use other models:
178+
// model: 'us.amazon.nova-2-lite-v1:0',
179+
// model: 'us.meta.llama3-70b-instruct-v1:0',
180+
region: process.env.AWS_REGION || 'us-east-1',
181+
})
182+
```
183+
184+
## Related Examples
185+
186+
- [langgraph-basic-docker](../../python/langgraph-basic-docker/) - Python version
187+
- [langgraph-gateway](../../python/langgraph-gateway/) - Lambda functions as tools
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/**
2+
* Minimal LangGraph JS agent with simple built-in tools.
3+
*
4+
* This agent demonstrates:
5+
* - BedrockAgentCoreApp entrypoint pattern for JavaScript
6+
* - LangChain createAgent (backed by LangGraph) with Claude Sonnet 4.5 via Bedrock
7+
* - Simple tool integration (calculator, time)
8+
* - Docker-based deployment
9+
*/
10+
11+
import { BedrockAgentCoreApp } from 'bedrock-agentcore/runtime'
12+
import { createAgent } from 'langchain'
13+
import { ChatBedrockConverse } from '@langchain/aws'
14+
import { tool } from '@langchain/core/tools'
15+
import { z } from 'zod'
16+
17+
// Initialize Claude Sonnet 4.5 via US inference profile
18+
const model = new ChatBedrockConverse({
19+
model: 'us.anthropic.claude-sonnet-4-5-20250929-v1:0',
20+
region: process.env.AWS_REGION || 'us-east-1',
21+
})
22+
23+
// Define simple tools using the tool() helper
24+
25+
/**
26+
* Get the current date and time
27+
*/
28+
const getCurrentTime = tool(
29+
async ({ timezone }) => {
30+
const now = new Date()
31+
const options = {
32+
timeZone: timezone || 'UTC',
33+
dateStyle: 'full',
34+
timeStyle: 'long',
35+
}
36+
return `Current time: ${now.toLocaleString('en-US', options)}`
37+
},
38+
{
39+
name: 'get_current_time',
40+
description:
41+
'Get the current date and time. Optionally specify a timezone.',
42+
schema: z.object({
43+
timezone: z
44+
.string()
45+
.optional()
46+
.describe(
47+
'Timezone (e.g., "America/New_York", "Europe/London", "UTC")',
48+
),
49+
}),
50+
},
51+
)
52+
53+
/**
54+
* Add two numbers together
55+
*/
56+
const add = tool(
57+
async ({ a, b }) => {
58+
const result = a + b
59+
return `${a} + ${b} = ${result}`
60+
},
61+
{
62+
name: 'add',
63+
description: 'Add two numbers together.',
64+
schema: z.object({
65+
a: z.number().describe('First number'),
66+
b: z.number().describe('Second number'),
67+
}),
68+
},
69+
)
70+
71+
/**
72+
* Multiply two numbers together
73+
*/
74+
const multiply = tool(
75+
async ({ a, b }) => {
76+
const result = a * b
77+
return `${a} × ${b} = ${result}`
78+
},
79+
{
80+
name: 'multiply',
81+
description: 'Multiply two numbers together.',
82+
schema: z.object({
83+
a: z.number().describe('First number'),
84+
b: z.number().describe('Second number'),
85+
}),
86+
},
87+
)
88+
89+
/**
90+
* Divide two numbers
91+
*/
92+
const divide = tool(
93+
async ({ a, b }) => {
94+
if (b === 0) {
95+
return 'Error: Cannot divide by zero'
96+
}
97+
const result = a / b
98+
return `${a} ÷ ${b} = ${result}`
99+
},
100+
{
101+
name: 'divide',
102+
description: 'Divide two numbers.',
103+
schema: z.object({
104+
a: z.number().describe('Dividend (number to divide)'),
105+
b: z.number().describe('Divisor (number to divide by)'),
106+
}),
107+
},
108+
)
109+
110+
// Collect all tools
111+
const tools = [getCurrentTime, add, multiply, divide]
112+
113+
// Create LangGraph agent
114+
// This creates a graph that alternates between calling the LLM and executing tools
115+
const agent = createAgent({
116+
model,
117+
tools,
118+
})
119+
120+
// Initialize the AgentCore application with invocation handler
121+
const app = new BedrockAgentCoreApp({
122+
invocationHandler: {
123+
// Define the expected request schema
124+
requestSchema: z.object({
125+
prompt: z.string().describe('The user message to process'),
126+
}),
127+
128+
// Process incoming requests (non-streaming)
129+
async process(request, context) {
130+
console.log(`Received message: ${request.prompt}`)
131+
console.log(`Request ID: ${context?.requestId || 'unknown'}`)
132+
133+
// Invoke the LangGraph agent with the user message
134+
const result = await agent.invoke({
135+
messages: [{ role: 'user', content: request.prompt }],
136+
})
137+
138+
// Extract the final message from the graph result
139+
const finalMessage = result.messages[result.messages.length - 1]
140+
const response = finalMessage.content
141+
142+
console.log(`Responding with: ${response}`)
143+
144+
// Return the response (non-streaming)
145+
return response
146+
},
147+
},
148+
})
149+
150+
// Start the AgentCore application
151+
// This starts an HTTP server on port 8080 (default)
152+
app.run()

0 commit comments

Comments
 (0)