Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apigw-lambda-bedrock-code-interpreter-cdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules/
cdk.out/
build/
*.js
*.d.ts
cdk.context.json
5 changes: 5 additions & 0 deletions apigw-lambda-bedrock-code-interpreter-cdk/bin/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { ApigwLambdaBedrockCodeInterpreterStack } from '../lib/apigw-lambda-bedrock-code-interpreter-stack';
const app = new cdk.App();
new ApigwLambdaBedrockCodeInterpreterStack(app, 'ApigwLambdaBedrockCodeInterpreterStack', { env: { region: 'us-east-1' } });
1 change: 1 addition & 0 deletions apigw-lambda-bedrock-code-interpreter-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"app":"node build/bin/app.js"}
96 changes: 96 additions & 0 deletions apigw-lambda-bedrock-code-interpreter-cdk/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{
"title": "AI data analyst with Amazon Bedrock and Amazon Bedrock AgentCore Code Interpreter",
"description": "Deploy an Amazon API Gateway REST API backed by an AWS Lambda function that uses Amazon Bedrock to generate Python analysis code from natural language questions, then executes it safely in Amazon Bedrock AgentCore Code Interpreter.",
"language": "TypeScript",
"level": "300",
"framework": "AWS CDK",
"introBox": {
"headline": "How it works",
"text": [
"A user submits a data analysis question in natural language via the Amazon API Gateway REST API.",
"The AWS Lambda function sends the question to Amazon Bedrock, which generates Python code to answer it.",
"The generated code is executed in Amazon Bedrock AgentCore Code Interpreter \u2014 a sandboxed environment with no access to the host system.",
"Results are returned to the user along with the generated code for transparency."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-lambda-bedrock-code-interpreter-cdk",
"templateURL": "serverless-patterns/apigw-lambda-bedrock-code-interpreter-cdk",
"projectFolder": "apigw-lambda-bedrock-code-interpreter-cdk",
"templateFile": "lib/apigw-lambda-bedrock-code-interpreter-stack.ts"
}
},
"resources": {
"bullets": [
{
"text": "Amazon Bedrock AgentCore Code Interpreter documentation",
"link": "https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/code-interpreter.html"
},
{
"text": "Amazon Bedrock InvokeModel API",
"link": "https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_InvokeModel.html"
}
]
},
"deploy": {
"text": [
"<code>npx cdk deploy</code>"
]
},
"testing": {
"text": [
"See the GitHub repo for detailed testing instructions."
]
},
"cleanup": {
"text": [
"<code>npx cdk destroy</code>"
]
},
"authors": [
{
"name": "Nithin Chandran R",
"bio": "Technical Account Manager at AWS",
"linkedin": "nithin-chandran-r"
}
],
"patternArch": {
"icon1": {
"x": 10,
"y": 50,
"service": "apigateway",
"label": "Amazon API Gateway"
},
"icon2": {
"x": 35,
"y": 50,
"service": "lambda",
"label": "AWS Lambda"
},
"icon3": {
"x": 60,
"y": 30,
"service": "bedrock",
"label": "Amazon Bedrock"
},
"icon4": {
"x": 60,
"y": 70,
"service": "bedrock",
"label": "Code Interpreter"
},
"line1": {
"from": "icon1",
"to": "icon2"
},
"line2": {
"from": "icon2",
"to": "icon3"
},
"line3": {
"from": "icon2",
"to": "icon4"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as agentcore from 'aws-cdk-lib/aws-bedrockagentcore';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';

export class ApigwLambdaBedrockCodeInterpreterStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

// Amazon Bedrock AgentCore Code Interpreter for safe code execution
const codeInterpreter = new agentcore.CodeInterpreterCustom(this, 'CodeInterpreter', {
codeInterpreterCustomName: 'data_analyst',
description: 'Sandboxed Python execution for AI-generated data analysis code',
networkConfiguration: agentcore.CodeInterpreterNetworkConfiguration.usingPublicNetwork(),
});

// AWS Lambda function: Bedrock generates code, Code Interpreter executes it
const fn = new lambda.Function(this, 'AnalystFunction', {
runtime: lambda.Runtime.PYTHON_3_12,
handler: 'index.handler',
code: lambda.Code.fromAsset('src/handler'),
timeout: cdk.Duration.seconds(90),
environment: {
CODE_INTERPRETER_ID: (codeInterpreter.node.defaultChild as cdk.CfnResource).ref,
MODEL_ID: 'us.anthropic.claude-sonnet-4-20250514-v1:0',
},
});

// Grant permissions for Amazon Bedrock AgentCore Code Interpreter
codeInterpreter.grantUse(fn);

// Grant permission to invoke Amazon Bedrock models
fn.addToRolePolicy(new iam.PolicyStatement({
actions: ['bedrock:InvokeModel'],
resources: [
`arn:aws:bedrock:*::foundation-model/*`,
`arn:aws:bedrock:*:${this.account}:inference-profile/*`,
],
}));

// Amazon API Gateway REST API
const api = new apigateway.RestApi(this, 'AnalystApi', {
restApiName: 'AI Data Analyst',
description: 'Ask data questions in natural language — Amazon Bedrock writes Python, Amazon Bedrock AgentCore Code Interpreter executes it safely',
});

api.root.addResource('analyze').addMethod('POST', new apigateway.LambdaIntegration(fn));

// Outputs
new cdk.CfnOutput(this, 'ApiEndpoint', { value: api.url });
new cdk.CfnOutput(this, 'FunctionName', { value: fn.functionName });
}
}
1 change: 1 addition & 0 deletions apigw-lambda-bedrock-code-interpreter-cdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"apigw-lambda-bedrock-code-interpreter-cdk","version":"1.0.0","bin":{"app":"build/bin/app.js"},"scripts":{"build":"tsc","synth":"cdk synth"},"dependencies":{"aws-cdk-lib":"^2.260.0","constructs":"^10.3.0"},"devDependencies":{"aws-cdk":"^2.1128.0","typescript":"~5.4.0"}}
120 changes: 120 additions & 0 deletions apigw-lambda-bedrock-code-interpreter-cdk/src/handler/index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"""
AWS Lambda function: AI Data Analyst.
Amazon Bedrock generates Python analysis code from natural language questions,
Amazon Bedrock AgentCore Code Interpreter executes it in a secure sandbox.
"""

import json
import os
import boto3


def handler(event, context):
"""Generate and execute data analysis code from natural language."""
try:
body = json.loads(event.get('body', '{}')) if isinstance(event.get('body'), str) else event
question = body.get('question', '')
data = body.get('data', '')

if not question:
return {
'statusCode': 400,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps({'error': 'question field is required'}),
}

code_interpreter_id = os.environ['CODE_INTERPRETER_ID']
model_id = os.environ.get('MODEL_ID', 'us.anthropic.claude-sonnet-4-20250514-v1:0')
region = os.environ.get('AWS_REGION', 'us-east-1')

# Step 1: Use Amazon Bedrock to generate Python code for the question
bedrock_client = boto3.client('bedrock-runtime', region_name=region)

code_gen_prompt = f"""Write a Python script that answers this data analysis question.
The script should print the answer clearly to stdout.
Use only standard library modules (json, math, statistics, datetime, collections, itertools, csv, re).
Do not use external packages like pandas or numpy.

Question: {question}
{f"Data: {data}" if data else ""}

Return ONLY the Python code, no explanation. No markdown code fences."""

response = bedrock_client.invoke_model(
modelId=model_id, contentType='application/json', accept='application/json',
body=json.dumps({
'anthropic_version': 'bedrock-2023-05-31',
'max_tokens': 1024,
'messages': [{'role': 'user', 'content': code_gen_prompt}],
}),
)

bedrock_body = json.loads(response['body'].read())
generated_code = bedrock_body['content'][0]['text'].strip()
# Strip markdown fences if present
if generated_code.startswith('```'):
generated_code = '\n'.join(generated_code.split('\n')[1:-1])

# Step 2: Execute the generated code in Amazon Bedrock AgentCore Code Interpreter
agentcore_client = boto3.client('bedrock-agentcore', region_name=region)

session = agentcore_client.start_code_interpreter_session(
codeInterpreterIdentifier=code_interpreter_id,
)
session_id = session['sessionId']

exec_response = agentcore_client.invoke_code_interpreter(
codeInterpreterIdentifier=code_interpreter_id,
sessionId=session_id,
name='executeCode',
arguments={
'code': generated_code,
'language': 'python',
},
)

# Parse EventStream response
result_text = ''
error_text = ''
event_stream = exec_response.get('body', {})
for event in event_stream:
if 'chunk' in event:
chunk = event['chunk']
sc = chunk.get('structuredContent', {})
if sc.get('stdout'):
result_text += sc['stdout']
if sc.get('stderr'):
error_text += sc['stderr']
elif 'result' in event:
r = event['result']
sc = r.get('structuredContent', {})
if sc.get('stdout'):
result_text += sc['stdout']
if sc.get('stderr'):
error_text += sc['stderr']

stdout = result_text or 'Code executed successfully (no stdout output)'
stderr = error_text

agentcore_client.stop_code_interpreter_session(
codeInterpreterIdentifier=code_interpreter_id,
sessionId=session_id,
)

return {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps({
'question': question,
'generatedCode': generated_code,
'result': stdout,
'errors': stderr if stderr else None,
}),
}

except Exception as e:
return {
'statusCode': 500,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps({'error': str(e)}),
}
1 change: 1 addition & 0 deletions apigw-lambda-bedrock-code-interpreter-cdk/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"compilerOptions":{"target":"ES2022","module":"commonjs","lib":["es2022"],"declaration":true,"strict":true,"noImplicitAny":true,"noImplicitReturns":true,"inlineSourceMap":true,"inlineSources":true,"experimentalDecorators":true,"strictPropertyInitialization":false,"outDir":"./build","rootDir":".","skipLibCheck":true},"exclude":["node_modules","cdk.out","build"]}