Skip to content

Commit

Permalink
Merge pull request #2553 from kaustavbecs/kaustavbecs-feature-appsync…
Browse files Browse the repository at this point in the history
…-lambda-bedrock-async-stream-subscription-cdk

New serverless pattern - AppSync Async Bedrock Streaming with Lambda Event Mode
  • Loading branch information
julianwood authored Jan 6, 2025
2 parents 432cffd + 9c7faa0 commit 8f4d800
Show file tree
Hide file tree
Showing 15 changed files with 989 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*.js
!jest.config.js
*.d.ts
node_modules

# CDK asset staging directory
.cdk.staging
cdk.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.ts
!*.d.ts

# CDK asset staging directory
.cdk.staging
cdk.out
131 changes: 131 additions & 0 deletions appsync-lambda-bedrock-async-stream-subscription-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Long running invocations of Amazon Bedrock using Amazon AppSync and AWS Lambda streaming

This pattern demonstrates how to implement [long-running invocations](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-bedrock-js.html#long-running-invocations) with Amazon Bedrock using AWS AppSync subscriptions and AWS Lambda in Event Mode, following the official AWS AppSync documentation pattern.

Learn more about this pattern at [Serverless Land Patterns](https://serverlessland.com/patterns/appsync-lambda-bedrock-async-stream-subscription-cdk).

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [Node and NPM](https://nodejs.org/en/download/) installed
* [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/v2/guide/cli.html) (AWS CDK) installed
* Enable the **Anthropic - Claude Sonnet 3.5 V2** model in **us-east-1** region through the [Bedrock console](https://console.aws.amazon.com/bedrock/home#/modelaccess). This implementation uses the [cross-region inference profile](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html#inference-profiles-support-system) from us-east-1.

## How it works

The pattern implements an asynchronous [streaming architecture](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-bedrock-js.html#long-running-invocations) where:

1. Client initiates a WebSocket subscription and makes a request to AppSync
2. AppSync invokes Lambda function in Event mode, enabling asynchronous processing
3. Lambda function streams responses from Bedrock using ConverseStream
4. Lambda sends updates via mutations to AppSync
5. Updates are delivered to client through WebSocket subscription

![alt text](image.png)

**Key Benefits**
- **Asynchronous Processing**: AppSync immediately returns a response while Lambda processes the request asynchronously, preventing timeouts for long-running operations
- **Real-time Updates**: Clients receive progressive updates through WebSocket subscriptions as the model generates responses
- **Scalable Architecture**: Event-driven design allows handling multiple concurrent requests without blocking
- **Enhanced User Experience**: Progressive updates enable responsive interfaces even during lengthy AI model invocations

## Deployment Instructions

1. Clone the repository:
```sh
git clone https://github.com/aws-samples/serverless-patterns
```
2. Navigate to pattern directory:
```sh
cd appsync-lambda-bedrock-async-stream-subscription-cdk
```

3. Install dependencies:
```sh
npm install
```

4. Bootstrap CDK (if needed):
```sh
cdk bootstrap
```

5. Deploy stack:
```sh
npm run deploy
```

### Important:
Note the GraphQL API URL and API Key from the stack outputs - you'll need these for testing.

## Testing

After deployment, you can test the Bedrock streaming integration using the provided test script. The script demonstrates:
- WebSocket subscription initialization
- Conversation start with Bedrock
- Real-time streaming chunks display
- Graceful cleanup on exit

1. Configure test credentials:
```sh
Open test/test.ts
Replace APPSYNC_API_URL with the API URL from stack outputs
Replace APPSYNC_API_KEY with the API Key from stack outputs
```

2. Run the test:
```sh
npx tsx test/test.ts
```

3. Expected Output:
```sh
Starting subscription...
Starting conversation...
StartConversation response: {
data: {
startConversation: {
conversationId: '123e4567-e89b-12d3-a456-426614174000',
status: 'STARTED'
}
}
}
Received chunk: {
conversationId: '123e4567-e89b-12d3-a456-426614174000',
chunk: "Here's a joke for you: Why don't scientists trust atoms? Because they make"
}
Received chunk: {
conversationId: '123e4567-e89b-12d3-a456-426614174000',
chunk: 'up everything!'
}
```

If you do not receive any response, please check your Bedrock Model access for Claude Sonnet 3.5 V2 in us-east-1 region.

4. Stop the test:
```sh
Press Ctrl+C to terminate the process
```


## Cleanup

1. Delete the stack
```sh
cdk destroy --all
```

## Author bio
Kaustav Dey,
https://www.linkedin.com/in/kaustavbecs/
Solution Architect

----
Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"title": "Amazon Bedrock calls via AppSync & Lambda streaming for long tasks",
"language": "TypeScript",
"level": "300",
"framework": "CDK",
"introBox": {
"headline": "How it works",
"text": [
"The pattern implements an asynchronous streaming architecture.",
"Client initiates a WebSocket subscription and makes a request to AWS AppSync. AppSync invokes Lambda function in event mode, enabling asynchronous processing.",
"Lambda function streams responses from Amazon Bedrock using ConverseStream. Lambda function sends updates via mutations to AppSync. Updates are delivered to client through WebSocket subscription."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/appsync-lambda-bedrock-async-stream-subscription-cdk",
"templateURL": "serverless-patterns/appsync-lambda-bedrock-async-stream-subscription-cdk",
"projectFolder": "appsync-lambda-bedrock-async-stream-subscription-cdk",
"templateFile": "/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts"
}
},
"resources": {
"bullets": [
{
"text": "AWS AppSync JavaScript resolver and function reference for Amazon Bedrock runtime",
"link": "https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-bedrock-js.html#long-running-invocations"
},
{
"text": "Bedrock ConverseStream API",
"link": "https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ConverseStream.html"
}
]
},
"deploy": {
"text": ["cdk deploy"]
},
"testing": {
"text": ["See the GitHub repo for detailed testing instructions."]
},
"cleanup": {
"text": ["Delete the stack: <code>cdk destroy --all</code>."]
},
"authors": [
{
"name": "Kaustav Dey",
"image": "https://avatars.githubusercontent.com/u/13236519",
"bio": "Solution Architect at AWS",
"linkedin": "kaustavbecs"
}
],
"patternArch": {
"icon1": {
"x": 20,
"y": 50,
"service": "appsync",
"label": "AWS AppSync"
},
"icon2": {
"x": 50,
"y": 50,
"service": "lambda",
"label": "AWS Lambda"
},
"icon3": {
"x": 80,
"y": 50,
"service": "bedrock",
"label": "Amazon Bedrock"
},
"line1": {
"from": "icon1",
"to": "icon2",
"label": ""
},
"line2": {
"from": "icon2",
"to": "icon3",
"label": ""
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack } from '../lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack';

const app = new cdk.App();
new AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack(app, 'AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack', {
/* If you don't specify 'env', this stack will be environment-agnostic.
* Account/Region-dependent features and context lookups will not work,
* but a single synthesized template can be deployed anywhere. */

/* Uncomment the next line to specialize this stack for the AWS Account
* and Region that are implied by the current CLI configuration. */
// env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },

/* Uncomment the next line if you know exactly what Account and Region you
* want to deploy the stack to. */
// env: { account: '123456789012', region: 'us-east-1' },

/* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
});
81 changes: 81 additions & 0 deletions appsync-lambda-bedrock-async-stream-subscription-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"app": "npx ts-node --prefer-ts-exts bin/appsync-lambda-bedrock-async-stream-subscription-cdk.ts",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"**/*.d.ts",
"**/*.js",
"tsconfig.json",
"package*.json",
"yarn.lock",
"node_modules",
"test"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false,
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true,
"@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true,
"@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true,
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true,
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true,
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true,
"@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true,
"@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"title": "Long running invocations of Amazon Bedrock using Amazon AppSync and AWS Lambda streaming",
"language": "Typescript",
"level": "300",
"framework": "CDK",
"introBox": {
"headline": "How it works",
"text": [
"The pattern implements an asynchronous streaming architecture.",
"Client initiates a WebSocket subscription and makes a request to AppSync. AppSync invokes Lambda function in Event mode, enabling asynchronous processing.",
"Lambda function streams responses from Bedrock using ConverseStream. Lambda sends updates via mutations to AppSync. Updates are delivered to client through WebSocket subscription"
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/appsync-lambda-bedrock-async-stream-subscription-cdk",
"templateURL": "serverless-patterns/appsync-lambda-bedrock-async-stream-subscription-cdk",
"projectFolder": "appsync-lambda-bedrock-async-stream-subscription-cdk",
"templateFile": "lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts"
}
},
"resources": {
"bullets": [
{
"text": "AWS AppSync JavaScript resolver and function reference for Amazon Bedrock runtime",
"link": "https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-bedrock-js.html#long-running-invocations"
},
{
"text": "Bedrock ConverseStream API",
"link": "https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ConverseStream.html"
}
]
},
"deploy": {
"text": [
"cdk deploy"
]
},
"testing": {
"text": [
"See the GitHub repo for detailed testing instructions."
]
},
"cleanup": {
"text": [
"Delete the stack: <code>cdk destroy --all</code>."
]
},
"authors": [
{
"name": "Kaustav Dey",
"image": "https://avatars.githubusercontent.com/u/13236519",
"bio": "Solution Architect at AWS",
"linkedin": "https://www.linkedin.com/in/kaustavbecs/"
}
]
}

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 8f4d800

Please sign in to comment.