
Building GPT 4 Serverless AWS Lambda Application
• October 23, 2023
Learn how to build a GPT 4 enabled microservice using AWS lambda, OpenAI GPT4, and serverless express
Table of Contents
- Introduction
- Prerequisites
- Understanding AWS CDK and its Benefits
- Setting Up Your Development Environment
- Building the AWS Lambda Function
- Setting up Serverless Express
- Integrating with OpenAI's GPT-4 Endpoint
- Deploying with AWS CDK
- Conclusion
1. Introduction to GPT and AWS CDK
Deploying applications and services to the cloud can often be a tedious process. Fortunately, the AWS Cloud Development Kit (AWS CDK) offers a high-level, declarative approach for defining cloud resources using familiar programming languages. In this guide, we will walk through building and deploying a GPT-4 endpoint using AWS CDK, AWS Lambda, Serverless Express, and OpenAI's GPT-4.
2. Prerequisites
- An AWS account.
- Familiarity with AWS services, particularly AWS Lambda and AWS CDK.
- Node.js installed.
- OpenAI API key for GPT-4 access.
3. Understanding AWS CDK and its Benefits
AWS CDK allows developers to define cloud resources in code and provision them using AWS CloudFormation. Here's why it stands out:
- Flexibility: Craft resources using familiar programming languages.
- Reusable Components: Develop and reuse high-level components.
- Integrated with AWS: Seamless integration with AWS services and best practices.
From my own experience, transitioning to AWS CDK made cloud deployments smoother and more intuitive.
4. GPT 4 Chat Route Handler
Serverless Express allows you to run Express.js applications on AWS Lambda. Below, an example route handler for a basic GPT request/response using the OpenAI SDK
import OpenAI from "openai";
import {Request, Response} from "express";
const generatePrompt = () => {
return (
`You are developer GPT, expert in software engineering and programming.`
)
}
export async function handler(req: Request, res: Response) {
try {
const {messages} = req.body;
const systemPrompt = generatePrompt();
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
const completion = await client.chat.completions.create({
model: "gpt-4",
messages: [
{
role: "system",
content: systemPrompt,
},
...messages,
],
stream: false,
});
const result = completion.choices[0].message;
if (!result || !result?.content) {
throw Error("No result");
} else {
console.log(result);
res.status(200).send(result);
}
} catch (err) {
console.error(err);
res.sendStatus(500);
}
}
5. Fetching OpenAI GPT API Key with Secret Manager
AWS Secrets Manager is used to securely retrieve and utilize secrets, specifically the OpenAI API key. This integration provides a higher level of security by ensuring the API key is never hard-coded or directly stored in the function. Let's break down the code and see how it all comes together.
import serverlessExpress from '@vendia/serverless-express';
import {app} from './app';
import AWS from 'aws-sdk';
let serverlessExpressInstance: any;
async function retrieveSecret(secretName: string): Promise<string | undefined> {
const secretsManager = new AWS.SecretsManager({
region: process.env.AWS_REGION ?? 'us-west-2'
});
try {
const data = await secretsManager.getSecretValue({ SecretId: secretName }).promise();
if (data && data.SecretString) {
return data.SecretString;
}
// If the secret is binary, you can access it using `data.SecretBinary`.
return undefined;
} catch (err) {
console.error('Error retrieving the secret:', err);
throw err;
}
}
async function asyncTask () {
const openAiSecret = await retrieveSecret('prod/Openai');
return {openAiSecret}
}
async function setup (event: any, context: any) {
const {openAiSecret} = await asyncTask();
process.env.OPENAI_API_KEY = openAiSecret;
serverlessExpressInstance = serverlessExpress({ app })
return serverlessExpressInstance(event, context)
}
export function handler (event: any, context: any) {
if (serverlessExpressInstance) return serverlessExpressInstance(event, context)
return setup(event, context)
}
8. Deploying GPT with AWS CDK
After building out the application, we need to deploy our application to AWS. Below, we'll create a stack that will create a CI/CD pipeline and deploy the AWS Lambda application.
import {BackendServiceStage} from "./stage";
import {ComputeType} from "aws-cdk-lib/aws-codebuild";
import {CodeBuildStep, CodePipeline, CodePipelineSource, ManualApprovalStep} from "aws-cdk-lib/pipelines";
import {PolicyStatement} from "aws-cdk-lib/aws-iam";
import {Stack, StackProps} from "aws-cdk-lib";
import {Construct} from "constructs";
interface PipelineStackProps extends StackProps {
repoName: string,
repoOwner: string,
branch: string,
connectionArn: string,
account: string,
region: string;
openAiSecretName: string,
}
export class BackendPipelineStack extends Stack {
constructor(scope: Construct, id: string, props: PipelineStackProps) {
super(scope, id, props);
let source = CodePipelineSource.connection(`${props.repoOwner}/${props.repoName}`, props.branch, {
connectionArn: props.connectionArn
});
const synth = new CodeBuildStep('Synth', {
input: source,
commands: [
'cd deployment/',
'export npm_config_cache=/tmp/.npm', // simplifies local development to avoid root owned .npm cache
'npm ci',
'npm run build',
'npx cdk synth'
],
primaryOutputDirectory: 'deployment/cdk.out',
});
const pipeline = new CodePipeline(this, 'pipeline', {
crossAccountKeys: false,
synth: synth,
selfMutation: true,
codeBuildDefaults: {
buildEnvironment: {
privileged: true,
computeType: ComputeType.MEDIUM
},
rolePolicy: [new PolicyStatement({
resources: ["*"],
actions: ["secretsmanager:GetSecretValue"]
})]
}
});
const prod = new BackendServiceStage(this, 'ProdBackend', {
openAiSecretName: props.openAiSecretName,
});
pipeline.addStage(prod, {
pre: [
new ManualApprovalStep("Promote to Prod")
]
});
}
}
import {Stack, StackProps} from "aws-cdk-lib";
import {Construct} from "constructs";
import {ApprovedNodeLambda} from "./constructs/approvedNodeLambdaConstruct";
import {Cors, LambdaIntegration, RestApi} from "aws-cdk-lib/aws-apigateway";
import {Secret} from "aws-cdk-lib/aws-secretsmanager";
export interface DeploymentStackProps extends StackProps {
openAiSecretName: string;
envVars?: Record<string, string>,
}
export class DeploymentStack extends Stack {
constructor(scope: Construct, id: string, props: DeploymentStackProps) {
super(scope, id, props);
const openAiSecret = Secret.fromSecretNameV2(this, 'openAiSecret', props.openAiSecretName);
const serverFunction = new ApprovedNodeLambda(this, 'backend-server', {
codeDir: '../source/',
description: 'backend server lambda function',
handler: 'src/lambdaServer.handler',
runtimeEnvironment: props.envVars ?? {}
});
openAiSecret.grantRead(serverFunction.lambda);
const api = new RestApi(this, 'api', {
restApiName: 'BackendApi',
description: 'Api gateway for backend api',
binaryMediaTypes: ['*/*']
});
api.root.addProxy({
defaultCorsPreflightOptions: {
allowOrigins: Cors.ALL_ORIGINS,
allowMethods: Cors.ALL_METHODS,
},
defaultIntegration: new LambdaIntegration(serverFunction.lambda, {
proxy: true
}),
});
}
}
9. Conclusion
Creating a robust and scalable GPT-4 endpoint on AWS has never been easier, thanks to tools like AWS CDK and Serverless Express. Not only have we automated the deployment process, but we've also built an endpoint that leverages the power of GPT-4 using the OpenAI API. Now, developers can seamlessly interact with GPT-4, expanding the horizons of what's possible in the realm of AI-driven applications.
If you're looking to deploy a GPT powered app without having to do this process manually, check out https://dev-kit.io/deploy