From 6533d5a64db3a66496d4cf1fdf394ed4ad474e0f Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Mon, 16 Dec 2024 22:00:09 -0500 Subject: [PATCH 01/15] initial --- .../.gitignore | 8 ++ .../.npmignore | 6 + .../README.md | 102 ++++++++++++++ ...a-bedrock-async-stream-subscription-cdk.ts | 20 +++ .../cdk.json | 81 ++++++++++++ .../jest.config.js | 8 ++ ...ock-async-stream-subscription-cdk-stack.ts | 124 ++++++++++++++++++ .../lib/lambda/invocation/index.ts | 113 ++++++++++++++++ .../package.json | 33 +++++ .../schema.graphql | 35 +++++ .../tsconfig.json | 32 +++++ 11 files changed, 562 insertions(+) create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/.gitignore create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/.npmignore create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/README.md create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/bin/appsync-lambda-bedrock-async-stream-subscription-cdk.ts create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/cdk.json create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/jest.config.js create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/package.json create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/tsconfig.json diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/.gitignore b/appsync-lambda-bedrock-async-stream-subscription-cdk/.gitignore new file mode 100644 index 000000000..f60797b6a --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/.gitignore @@ -0,0 +1,8 @@ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/.npmignore b/appsync-lambda-bedrock-async-stream-subscription-cdk/.npmignore new file mode 100644 index 000000000..c1d6d45dc --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md new file mode 100644 index 000000000..415f47a77 --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md @@ -0,0 +1,102 @@ +# AppSync Lambda Bedrock Streaming Pattern + +This pattern demonstrates how to implement long-running invocations with Amazon Bedrock using AWS AppSync subscriptions and AWS Lambda, 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 +* Make sure to enable the **Anthropic - Claude V2** model on the [Bedrock console](https://console.aws.amazon.com/bedrock/home#/modelaccess). + +## How it works + +The pattern implements an asynchronous streaming architecture where: + +1. Client initiates a WebSocket subscription and makes a request to AppSync +2. AppSync invokes Lambda function in Event mode +3. Lambda function streams responses from Bedrock using InvokeModelWithResponseStream +4. Lambda sends updates via mutations to AppSync +5. Updates are delivered to client through WebSocket subscription + +This pattern is ideal for: +- Long-running AI model invocations +- Real-time streaming responses +- Asynchronous processing patterns +- Progressive UI updates + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: +```sh +git clone https://github.com/aws-samples/serverless-patterns +``` +2. Change directory to the pattern directory: +```sh +cd appsync-lambda-bedrock-async-stream-subscription-cdk +``` + +3. Install the required dependencies: +```sh +npm install +``` + +4. Deploy the stack to your default AWS account and region: +```sh +npm run deploy +``` + + +## Testing + +Run tests +```sh +npm run test +``` + +## Cleanup + +1. Delete the stack +```sh +cdk destroy --all +``` +---- +Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 + +Outputs: +AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack.GraphQLApiKey = da2-367hvetdp5f6flyii7xnlafgki +AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack.GraphQLApiUrl = https://3xbko6bpk5awvahhtvo3htsfpm.appsync-api.us-east-1.amazonaws.com/graphql + +mutation StartGeneration { + startGeneration(prompt: "Tell me a short story about a robot learning to paint") { + id + status + } +} + +# For specific stream +subscription OnSpecificStream { + onUpdateStream(id: "123") { + id + chunk + } +} + +# For all streams +subscription OnAllStreams { + onAnyStreamUpdate { + id + chunk + } +} + + +1734111451736 \ No newline at end of file diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/bin/appsync-lambda-bedrock-async-stream-subscription-cdk.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/bin/appsync-lambda-bedrock-async-stream-subscription-cdk.ts new file mode 100644 index 000000000..4b42d5bb4 --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/bin/appsync-lambda-bedrock-async-stream-subscription-cdk.ts @@ -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 */ +}); \ No newline at end of file diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/cdk.json b/appsync-lambda-bedrock-async-stream-subscription-cdk/cdk.json new file mode 100644 index 000000000..4c597d9f8 --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/cdk.json @@ -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 + } +} diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/jest.config.js b/appsync-lambda-bedrock-async-stream-subscription-cdk/jest.config.js new file mode 100644 index 000000000..08263b895 --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts new file mode 100644 index 000000000..438d90dfc --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts @@ -0,0 +1,124 @@ +import * as cdk from 'aws-cdk-lib'; +import * as appsync from 'aws-cdk-lib/aws-appsync'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as logs from 'aws-cdk-lib/aws-logs'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { Construct } from 'constructs'; +import * as path from 'path'; + +export class AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const api = new appsync.GraphqlApi(this, 'Api', { + name: 'bedrock-streaming-api', + schema: appsync.SchemaFile.fromAsset('schema.graphql'), + authorizationConfig: { + defaultAuthorization: { + authorizationType: appsync.AuthorizationType.API_KEY, + }, + }, + logConfig: { + fieldLogLevel: appsync.FieldLogLevel.ALL, + excludeVerboseContent: false, + retention: logs.RetentionDays.ONE_WEEK + }, + xrayEnabled: true + }); + + const invocationHandler = new NodejsFunction(this, 'InvocationHandler', { + runtime: lambda.Runtime.NODEJS_18_X, + handler: 'handler', + entry: path.join(__dirname, 'lambda/invocation/index.ts'), + timeout: cdk.Duration.seconds(300), + environment: { + APPSYNC_ENDPOINT: api.graphqlUrl, + APPSYNC_API_KEY: api.apiKey || '', + }, + logRetention: logs.RetentionDays.ONE_WEEK, + tracing: lambda.Tracing.ACTIVE + }); + + // Add Bedrock permissions to Lambda + invocationHandler.addToRolePolicy(new iam.PolicyStatement({ + actions: ['bedrock:InvokeModelWithResponseStream'], + resources: ['arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2'], + })); + + // Add AppSync permissions to Lambda + invocationHandler.addToRolePolicy(new iam.PolicyStatement({ + actions: ['appsync:GraphQL'], + resources: [api.arn + '/types/Mutation/fields/sendChunk'], + })); + + // Add CloudWatch Logs permissions to Lambda + invocationHandler.addToRolePolicy(new iam.PolicyStatement({ + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ], + resources: ['*'] + })); + + const invocationDS = api.addLambdaDataSource('InvocationDataSource', invocationHandler); + const noneDS = api.addNoneDataSource('NoneDataSource'); + + invocationDS.createResolver('StartConversationResolver', { + typeName: 'Mutation', + fieldName: 'startConversation', + }); + + noneDS.createResolver('SendChunkResolver', { + typeName: 'Mutation', + fieldName: 'sendChunk', + requestMappingTemplate: appsync.MappingTemplate.fromString(` + { + "version": "2018-05-29", + "payload": { + "conversationId": "$context.arguments.conversationId", + "chunk": "$context.arguments.chunk" + } + } + `), + responseMappingTemplate: appsync.MappingTemplate.fromString(` + #if($context.error) + $util.error($context.error.message, $context.error.type) + #end + $util.toJson({ + "conversationId": "$context.arguments.conversationId", + "chunk": "$context.arguments.chunk" + }) + `) + }); + + + noneDS.createResolver('SubscriptionResolver', { + typeName: 'Subscription', + fieldName: 'onReceiveChunk', + requestMappingTemplate: appsync.MappingTemplate.fromString(` + { + "version": "2018-05-29", + "payload": $util.toJson($context.arguments) + } + `), + responseMappingTemplate: appsync.MappingTemplate.fromString( + '$util.toJson($context.result)' + ), + }); + + // Add CloudWatch dashboard for monitoring + new cdk.CfnOutput(this, 'GraphQLAPIURL', { + value: api.graphqlUrl + }); + + new cdk.CfnOutput(this, 'GraphQLAPIKey', { + value: api.apiKey || '' + }); + + new cdk.CfnOutput(this, 'CloudWatchLogsURL', { + value: `https://console.aws.amazon.com/cloudwatch/home?region=${this.region}#logsV2:log-groups` + }); + } +} diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts new file mode 100644 index 000000000..599017135 --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts @@ -0,0 +1,113 @@ +import { BedrockRuntimeClient, InvokeModelWithResponseStreamCommand } from "@aws-sdk/client-bedrock-runtime"; +import { GraphQLClient } from 'graphql-request'; +import { v4 as uuidv4 } from 'uuid'; + +interface AppSyncEvent { + arguments: { + input: string; + }; +} + +export const handler = async (event: AppSyncEvent) => { + console.log('Step 2: Lambda received event:', JSON.stringify(event, null, 2)); + + const bedrockClient = new BedrockRuntimeClient(); + const graphQLClient = new GraphQLClient(process.env.APPSYNC_ENDPOINT!, { + headers: { + 'x-api-key': process.env.APPSYNC_API_KEY! + }, + }); + + try { + const input = event.arguments.input; + if (!input) { + throw new Error('Input is required'); + } + + const conversationId = uuidv4(); + console.log('Generated conversationId:', conversationId); + + // Initial response to client + const response = { + conversationId, + status: "PROCESSING" + }; + + // Invoke Bedrock with streaming + const params = { + modelId: "anthropic.claude-v2", + contentType: "application/json", + accept: "application/json", + body: JSON.stringify({ + anthropic_version: "bedrock-2023-05-31", + max_tokens: 4096, + messages: [{ + role: "user", + content: input + }] + }) + }; + + console.log('Step 3: Invoking Bedrock with params:', JSON.stringify(params, null, 2)); + const command = new InvokeModelWithResponseStreamCommand(params); + const stream = await bedrockClient.send(command); + + // Process stream and send updates via mutations + if (stream.body) { + let accumulatedText = ''; + + for await (const chunk of stream.body) { + if (chunk.chunk?.bytes) { + const parsed = JSON.parse( + Buffer.from(chunk.chunk.bytes).toString("utf-8") + ); + console.log('Received chunk from Bedrock:', parsed); + + if (parsed.delta?.text) { + accumulatedText += parsed.delta.text; + + // Send mutation to AppSync + const mutation = ` + mutation SendChunk($conversationId: ID!, $chunk: String!) { + sendChunk(conversationId: $conversationId, chunk: $chunk) { + conversationId + chunk + } + } + `; + + try { + const mutationResponse = await graphQLClient.request(mutation, { + conversationId, + chunk: parsed.delta.text + }); + console.log('Step 4: Mutation sent to AppSync:', mutationResponse); + } catch (error) { + console.error('Error sending mutation to AppSync:', error); + if (error instanceof Error) { + console.error(error.message); + } + } + } + } + } + + console.log('Step 4: Completed streaming. Total response:', accumulatedText); + } + + return response; + + } catch (error) { + console.error('Lambda execution error:', error); + if (error instanceof Error) { + return { + conversationId: 'error', + status: error.message + }; + } + return { + conversationId: 'error', + status: 'Unknown error occurred' + }; + } +}; diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json b/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json new file mode 100644 index 000000000..5652c4d70 --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json @@ -0,0 +1,33 @@ +{ + "name": "appsync-lambda-bedrock-async-stream-subscription-cdk", + "version": "0.1.0", + "bin": { + "cdk": "bin/cdk.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk", + "deploy": "cdk deploy" + }, + "devDependencies": { + "@types/jest": "^29.5.5", + "@types/node": "^20.7.1", + "aws-cdk": "^2.99.1", + "esbuild": "^0.19.4", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.1", + "typescript": "~5.2.2" + }, + "dependencies": { + "@aws-sdk/client-bedrock-runtime": "^3.428.0", + "@aws-sdk/client-appsync": "^3.428.0", + "aws-cdk-lib": "^2.99.1", + "constructs": "^10.0.0", + "graphql": "^16.8.1", + "graphql-request": "^6.1.0", + "source-map-support": "^0.5.21" + } +} diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql b/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql new file mode 100644 index 000000000..b06f9f5dd --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql @@ -0,0 +1,35 @@ +type Subscription { + # Subscribe to specific conversation + onReceiveChunk(conversationId: ID): ChunkResponse + @aws_subscribe(mutations: ["sendChunk"]) + + # Subscribe to all conversations + onReceiveChunkBroadcast: ChunkResponse + @aws_subscribe(mutations: ["sendChunk"]) +} + +type Mutation { + startConversation(input: String!): ConversationResponse! + sendChunk(conversationId: ID!, chunk: String!): ChunkResponse! + } + +type ConversationResponse { + conversationId: ID! + status: String! +} + +type ChunkResponse { + conversationId: ID! + chunk: String! +} + +type Query { + # Required but not used for this flow + dummy: String +} + +schema { + query: Query + mutation: Mutation + subscription: Subscription +} diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/tsconfig.json b/appsync-lambda-bedrock-async-stream-subscription-cdk/tsconfig.json new file mode 100644 index 000000000..bc46f183a --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020", + "dom" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "esModuleInterop": true, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} From 596f477d6f99d02bc3b3e1c4f651a56d0b64d84f Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Mon, 16 Dec 2024 22:22:11 -0500 Subject: [PATCH 02/15] sub working --- .../README.md | 31 ++++++++----------- ...ock-async-stream-subscription-cdk-stack.ts | 9 +++--- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md index 415f47a77..4086247de 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md @@ -75,28 +75,23 @@ Outputs: AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack.GraphQLApiKey = da2-367hvetdp5f6flyii7xnlafgki AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack.GraphQLApiUrl = https://3xbko6bpk5awvahhtvo3htsfpm.appsync-api.us-east-1.amazonaws.com/graphql -mutation StartGeneration { - startGeneration(prompt: "Tell me a short story about a robot learning to paint") { - id - status - } -} -# For specific stream -subscription OnSpecificStream { - onUpdateStream(id: "123") { - id - chunk - } -} +# Sample Queries to test from App Sync Console -# For all streams -subscription OnAllStreams { - onAnyStreamUpdate { - id +1. Trigger subscription: +subscription OnReceiveChunkBroadcast { + onReceiveChunkBroadcast { + conversationId chunk } } +2. -1734111451736 \ No newline at end of file +Mutation trigger +mutation StartConversation { + startConversation(input: "tell me a long joke") { + conversationId + status + } +} diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts index 438d90dfc..7caee03d7 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts @@ -24,20 +24,20 @@ export class AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack extends cdk.Sta excludeVerboseContent: false, retention: logs.RetentionDays.ONE_WEEK }, - xrayEnabled: true - }); + xrayEnabled: false // Disable X-Ray tracing + }); const invocationHandler = new NodejsFunction(this, 'InvocationHandler', { runtime: lambda.Runtime.NODEJS_18_X, handler: 'handler', entry: path.join(__dirname, 'lambda/invocation/index.ts'), - timeout: cdk.Duration.seconds(300), + timeout: cdk.Duration.minutes(15), // Set Lambda timeout to the maximum (15 minutes) environment: { APPSYNC_ENDPOINT: api.graphqlUrl, APPSYNC_API_KEY: api.apiKey || '', }, logRetention: logs.RetentionDays.ONE_WEEK, - tracing: lambda.Tracing.ACTIVE + tracing: lambda.Tracing.DISABLED // Disable X-Ray tracing for Lambda }); // Add Bedrock permissions to Lambda @@ -92,7 +92,6 @@ export class AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack extends cdk.Sta }) `) }); - noneDS.createResolver('SubscriptionResolver', { typeName: 'Subscription', From a04a952290c08fa6fec70849f99b8c6535084775 Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Mon, 16 Dec 2024 23:11:33 -0500 Subject: [PATCH 03/15] research1 --- .../invocation/index async func.ts.back | 125 ++++++++++++++++++ .../invocation/index sync sub func.ts.back | 113 ++++++++++++++++ .../lib/lambda/invocation/index.ts | 92 +++++++------ 3 files changed, 290 insertions(+), 40 deletions(-) create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index async func.ts.back create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index sync sub func.ts.back diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index async func.ts.back b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index async func.ts.back new file mode 100644 index 000000000..4f759751b --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index async func.ts.back @@ -0,0 +1,125 @@ +import { BedrockRuntimeClient, InvokeModelWithResponseStreamCommand } from "@aws-sdk/client-bedrock-runtime"; +import { GraphQLClient } from 'graphql-request'; +import { v4 as uuidv4 } from 'uuid'; + +interface AppSyncEvent { + arguments: { + input: string; + }; +} + +export const handler = async (event: AppSyncEvent, context: any) => { + console.log('Step 2: Lambda received event:', JSON.stringify(event, null, 2)); + + // Prevent Lambda from terminating early + context.callbackWaitsForEmptyEventLoop = false; + + const bedrockClient = new BedrockRuntimeClient(); + const graphQLClient = new GraphQLClient(process.env.APPSYNC_ENDPOINT!, { + headers: { + 'x-api-key': process.env.APPSYNC_API_KEY! + }, + }); + + try { + const input = event.arguments.input; + if (!input) { + throw new Error('Input is required'); + } + + // Generate a unique conversationId + const conversationId = uuidv4(); + console.log('Generated conversationId:', conversationId); + + // Start processing Bedrock stream in the background + processBedrockStream(bedrockClient, graphQLClient, input, conversationId); + + // Return immediate response to AppSync mutation caller + return { + conversationId, + status: "PROCESSING" + }; + + } catch (error) { + console.error('Lambda execution error:', error); + if (error instanceof Error) { + return { + conversationId: 'error', + status: error.message + }; + } + return { + conversationId: 'error', + status: 'Unknown error occurred' + }; + } +}; + +// Function to handle Bedrock streaming in the background +async function processBedrockStream( + bedrockClient: BedrockRuntimeClient, + graphQLClient: GraphQLClient, + input: string, + conversationId: string +) { + try { + const params = { + modelId: "anthropic.claude-v2", + contentType: "application/json", + accept: "application/json", + body: JSON.stringify({ + anthropic_version: "bedrock-2023-05-31", + max_tokens: 4096, + messages: [{ + role: "user", + content: input + }] + }) + }; + + console.log('Invoking Bedrock with params:', JSON.stringify(params, null, 2)); + const command = new InvokeModelWithResponseStreamCommand(params); + const stream = await bedrockClient.send(command); + + if (stream.body) { + let accumulatedText = ''; + + for await (const chunk of stream.body) { + if (chunk.chunk?.bytes) { + const parsed = JSON.parse( + Buffer.from(chunk.chunk.bytes).toString("utf-8") + ); + console.log('Received chunk from Bedrock:', parsed); + + if (parsed.delta?.text) { + accumulatedText += parsed.delta.text; + + // Send chunk update via AppSync mutation + const mutation = ` + mutation SendChunk($conversationId: ID!, $chunk: String!) { + sendChunk(conversationId: $conversationId, chunk: $chunk) { + conversationId + chunk + } + } + `; + + try { + await graphQLClient.request(mutation, { + conversationId, + chunk: parsed.delta.text + }); + console.log('Chunk sent to subscription:', parsed.delta.text); + } catch (error) { + console.error('Error sending chunk to subscription:', error); + } + } + } + } + + console.log('Completed streaming. Total accumulated text:', accumulatedText); + } + } catch (error) { + console.error('Error during Bedrock streaming:', error); + } +} diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index sync sub func.ts.back b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index sync sub func.ts.back new file mode 100644 index 000000000..599017135 --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index sync sub func.ts.back @@ -0,0 +1,113 @@ +import { BedrockRuntimeClient, InvokeModelWithResponseStreamCommand } from "@aws-sdk/client-bedrock-runtime"; +import { GraphQLClient } from 'graphql-request'; +import { v4 as uuidv4 } from 'uuid'; + +interface AppSyncEvent { + arguments: { + input: string; + }; +} + +export const handler = async (event: AppSyncEvent) => { + console.log('Step 2: Lambda received event:', JSON.stringify(event, null, 2)); + + const bedrockClient = new BedrockRuntimeClient(); + const graphQLClient = new GraphQLClient(process.env.APPSYNC_ENDPOINT!, { + headers: { + 'x-api-key': process.env.APPSYNC_API_KEY! + }, + }); + + try { + const input = event.arguments.input; + if (!input) { + throw new Error('Input is required'); + } + + const conversationId = uuidv4(); + console.log('Generated conversationId:', conversationId); + + // Initial response to client + const response = { + conversationId, + status: "PROCESSING" + }; + + // Invoke Bedrock with streaming + const params = { + modelId: "anthropic.claude-v2", + contentType: "application/json", + accept: "application/json", + body: JSON.stringify({ + anthropic_version: "bedrock-2023-05-31", + max_tokens: 4096, + messages: [{ + role: "user", + content: input + }] + }) + }; + + console.log('Step 3: Invoking Bedrock with params:', JSON.stringify(params, null, 2)); + const command = new InvokeModelWithResponseStreamCommand(params); + const stream = await bedrockClient.send(command); + + // Process stream and send updates via mutations + if (stream.body) { + let accumulatedText = ''; + + for await (const chunk of stream.body) { + if (chunk.chunk?.bytes) { + const parsed = JSON.parse( + Buffer.from(chunk.chunk.bytes).toString("utf-8") + ); + console.log('Received chunk from Bedrock:', parsed); + + if (parsed.delta?.text) { + accumulatedText += parsed.delta.text; + + // Send mutation to AppSync + const mutation = ` + mutation SendChunk($conversationId: ID!, $chunk: String!) { + sendChunk(conversationId: $conversationId, chunk: $chunk) { + conversationId + chunk + } + } + `; + + try { + const mutationResponse = await graphQLClient.request(mutation, { + conversationId, + chunk: parsed.delta.text + }); + console.log('Step 4: Mutation sent to AppSync:', mutationResponse); + } catch (error) { + console.error('Error sending mutation to AppSync:', error); + if (error instanceof Error) { + console.error(error.message); + } + } + } + } + } + + console.log('Step 4: Completed streaming. Total response:', accumulatedText); + } + + return response; + + } catch (error) { + console.error('Lambda execution error:', error); + if (error instanceof Error) { + return { + conversationId: 'error', + status: error.message + }; + } + return { + conversationId: 'error', + status: 'Unknown error occurred' + }; + } +}; diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts index 599017135..4f759751b 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts @@ -8,32 +8,61 @@ interface AppSyncEvent { }; } -export const handler = async (event: AppSyncEvent) => { +export const handler = async (event: AppSyncEvent, context: any) => { console.log('Step 2: Lambda received event:', JSON.stringify(event, null, 2)); - + + // Prevent Lambda from terminating early + context.callbackWaitsForEmptyEventLoop = false; + const bedrockClient = new BedrockRuntimeClient(); const graphQLClient = new GraphQLClient(process.env.APPSYNC_ENDPOINT!, { headers: { 'x-api-key': process.env.APPSYNC_API_KEY! }, }); - + try { const input = event.arguments.input; if (!input) { throw new Error('Input is required'); } - + + // Generate a unique conversationId const conversationId = uuidv4(); console.log('Generated conversationId:', conversationId); - - // Initial response to client - const response = { + + // Start processing Bedrock stream in the background + processBedrockStream(bedrockClient, graphQLClient, input, conversationId); + + // Return immediate response to AppSync mutation caller + return { conversationId, status: "PROCESSING" }; - - // Invoke Bedrock with streaming + + } catch (error) { + console.error('Lambda execution error:', error); + if (error instanceof Error) { + return { + conversationId: 'error', + status: error.message + }; + } + return { + conversationId: 'error', + status: 'Unknown error occurred' + }; + } +}; + +// Function to handle Bedrock streaming in the background +async function processBedrockStream( + bedrockClient: BedrockRuntimeClient, + graphQLClient: GraphQLClient, + input: string, + conversationId: string +) { + try { const params = { modelId: "anthropic.claude-v2", contentType: "application/json", @@ -47,26 +76,25 @@ export const handler = async (event: AppSyncEvent) => { }] }) }; - - console.log('Step 3: Invoking Bedrock with params:', JSON.stringify(params, null, 2)); + + console.log('Invoking Bedrock with params:', JSON.stringify(params, null, 2)); const command = new InvokeModelWithResponseStreamCommand(params); const stream = await bedrockClient.send(command); - // Process stream and send updates via mutations if (stream.body) { let accumulatedText = ''; - + for await (const chunk of stream.body) { if (chunk.chunk?.bytes) { const parsed = JSON.parse( Buffer.from(chunk.chunk.bytes).toString("utf-8") ); console.log('Received chunk from Bedrock:', parsed); - + if (parsed.delta?.text) { accumulatedText += parsed.delta.text; - - // Send mutation to AppSync + + // Send chunk update via AppSync mutation const mutation = ` mutation SendChunk($conversationId: ID!, $chunk: String!) { sendChunk(conversationId: $conversationId, chunk: $chunk) { @@ -75,39 +103,23 @@ export const handler = async (event: AppSyncEvent) => { } } `; - + try { - const mutationResponse = await graphQLClient.request(mutation, { + await graphQLClient.request(mutation, { conversationId, chunk: parsed.delta.text }); - console.log('Step 4: Mutation sent to AppSync:', mutationResponse); + console.log('Chunk sent to subscription:', parsed.delta.text); } catch (error) { - console.error('Error sending mutation to AppSync:', error); - if (error instanceof Error) { - console.error(error.message); - } + console.error('Error sending chunk to subscription:', error); } } } } - - console.log('Step 4: Completed streaming. Total response:', accumulatedText); - } - return response; - - } catch (error) { - console.error('Lambda execution error:', error); - if (error instanceof Error) { - return { - conversationId: 'error', - status: error.message - }; + console.log('Completed streaming. Total accumulated text:', accumulatedText); } - return { - conversationId: 'error', - status: 'Unknown error occurred' - }; + } catch (error) { + console.error('Error during Bedrock streaming:', error); } -}; +} From 2ef921d7f2af2e17456021f4396c9526dc5e2faa Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Thu, 19 Dec 2024 14:47:08 -0500 Subject: [PATCH 04/15] stable no sub --- .../README.md | 26 ++ ...ock-async-stream-subscription-cdk-stack.ts | 101 +++++++- .../lib/lambda/invocation/index.ts | 237 +++++++++++------- .../package.json | 3 + .../schema.graphql | 43 ++-- .../test/test.ts | 128 ++++++++++ 6 files changed, 417 insertions(+), 121 deletions(-) create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md index 4086247de..70fd97b81 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md @@ -95,3 +95,29 @@ mutation StartConversation { status } } +====== + +Based on the search results and architecture diagram, I need to correct some misconceptions about AWS AppSync's Event mode Lambda invocations: +The Issue +The article's proposed solution won't work as intended because: + + AppSync's Event mode invocation doesn't maintain the Lambda execution context after returning the initial response2 + + The Lambda function will still terminate after returning the response, regardless of any background processing attempts6 + +Why It Doesn't Work +The fundamental problems are: + + AppSync's Event mode is designed for fire-and-forget scenarios, not for maintaining long-running connections3 + The Lambda execution context cannot be kept alive after the initial response is sent back to AppSync10 + The WebSocket connection through AppSync subscriptions needs a continuous execution context to send updates11 + +Correct Implementation +For long-running AI model invocations with streaming responses, you should instead use one of these patterns: + + Split the process into two Lambda functions - one for immediate response and another for processing4 + Use a queue-based architecture with SQS to handle the long-running process2 + + For streaming responses under 10 seconds, use AppSync's direct Bedrock integration9 + +The article's proposed architecture, while theoretically sound, doesn't account for Lambda's execution context limitations in practice. \ No newline at end of file diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts index 7caee03d7..d6e54f281 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts @@ -20,25 +20,32 @@ export class AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack extends cdk.Sta }, }, logConfig: { - fieldLogLevel: appsync.FieldLogLevel.ALL, - excludeVerboseContent: false, + fieldLogLevel: appsync.FieldLogLevel.ALL, // Change to ALL to see resolver details + excludeVerboseContent: false, // Include verbose content retention: logs.RetentionDays.ONE_WEEK }, - xrayEnabled: false // Disable X-Ray tracing + xrayEnabled: false }); + + const invocationHandler = new NodejsFunction(this, 'InvocationHandler', { runtime: lambda.Runtime.NODEJS_18_X, handler: 'handler', entry: path.join(__dirname, 'lambda/invocation/index.ts'), - timeout: cdk.Duration.minutes(15), // Set Lambda timeout to the maximum (15 minutes) + timeout: cdk.Duration.minutes(15), environment: { APPSYNC_ENDPOINT: api.graphqlUrl, APPSYNC_API_KEY: api.apiKey || '', }, logRetention: logs.RetentionDays.ONE_WEEK, - tracing: lambda.Tracing.DISABLED // Disable X-Ray tracing for Lambda + tracing: lambda.Tracing.DISABLED, + bundling: { + minify: true, + sourceMap: false // Disable source maps to reduce log size + } }); + // Add Bedrock permissions to Lambda invocationHandler.addToRolePolicy(new iam.PolicyStatement({ @@ -63,13 +70,48 @@ export class AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack extends cdk.Sta })); const invocationDS = api.addLambdaDataSource('InvocationDataSource', invocationHandler); - const noneDS = api.addNoneDataSource('NoneDataSource'); + invocationDS.createResolver('StartConversationResolver', { typeName: 'Mutation', fieldName: 'startConversation', + requestMappingTemplate: appsync.MappingTemplate.fromString(` + { + "version": "2018-05-29", + "operation": "Invoke", + "invocationType": "Event", + "payload": $util.toJson($context.arguments) + } + `), + responseMappingTemplate: appsync.MappingTemplate.fromString(` + #if($context.error) + $util.error($context.error.message, $context.error.type) + #end + { + "conversationId": "$context.arguments.input.conversationId", + "status": "STARTED" + } + `) }); - + + const noneDS = api.addNoneDataSource('NoneDataSource'); + noneDS.createResolver('CreateConversationResolver', { + typeName: 'Mutation', + fieldName: 'createConversation', + requestMappingTemplate: appsync.MappingTemplate.fromString(` + { + "version": "2018-05-29", + "payload": { + "conversationId": "$context.arguments.conversationId", + "status": "CREATED" + } + } + `), + responseMappingTemplate: appsync.MappingTemplate.fromString(` + $util.toJson($context.result) + `), + }); + noneDS.createResolver('SendChunkResolver', { typeName: 'Mutation', fieldName: 'sendChunk', @@ -107,6 +149,51 @@ export class AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack extends cdk.Sta ), }); + noneDS.createResolver('SendErrorResolver', { + typeName: 'Mutation', + fieldName: 'sendError', + requestMappingTemplate: appsync.MappingTemplate.fromString(` + #set($logMessage = "sendError invoked with arguments - Conversation ID: $context.arguments.conversationId, Error Message: $context.arguments.error") + $util.log($logMessage) + { + "version": "2018-05-29", + "payload": { + "conversationId": "$context.arguments.conversationId", + "error": "$context.arguments.error" + } + } + `), + responseMappingTemplate: appsync.MappingTemplate.fromString(` + #if($context.error) + $util.error($context.error.message, $context.error.type) + #end + $util.toJson($context.result) + `), + }); + + noneDS.createResolver('CompleteStreamResolver', { + typeName: 'Mutation', + fieldName: 'completeStream', + requestMappingTemplate: appsync.MappingTemplate.fromString(` + { + "version": "2017-02-28", + "payload": { + "conversationId": "$context.arguments.conversationId", + "status": "COMPLETED" + } + } + `), + responseMappingTemplate: appsync.MappingTemplate.fromString(` + #if($context.error) + $util.error($context.error.message, $context.error.type) + #end + $util.toJson($context.result) + `) + }); + + + + // Add CloudWatch dashboard for monitoring new cdk.CfnOutput(this, 'GraphQLAPIURL', { value: api.graphqlUrl diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts index 4f759751b..3f880b826 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts @@ -1,125 +1,168 @@ -import { BedrockRuntimeClient, InvokeModelWithResponseStreamCommand } from "@aws-sdk/client-bedrock-runtime"; +import { BedrockRuntimeClient, InvokeModelWithResponseStreamCommand } from '@aws-sdk/client-bedrock-runtime'; import { GraphQLClient } from 'graphql-request'; -import { v4 as uuidv4 } from 'uuid'; -interface AppSyncEvent { - arguments: { - input: string; +interface Event { + input: { + conversationId: string; + prompt: string; }; } -export const handler = async (event: AppSyncEvent, context: any) => { - console.log('Step 2: Lambda received event:', JSON.stringify(event, null, 2)); - - // Prevent Lambda from terminating early - context.callbackWaitsForEmptyEventLoop = false; +function sanitizeGraphQLString(text: string): string { + return text + .replace(/[\n\r]/g, ' ') // Replace newlines with spaces + .replace(/\\/g, '\\\\') // Escape backslashes + .replace(/"/g, '\\"') // Escape double quotes + .replace(/\t/g, ' ') // Replace tabs with spaces + .trim(); // Remove leading/trailing whitespace +} - const bedrockClient = new BedrockRuntimeClient(); - const graphQLClient = new GraphQLClient(process.env.APPSYNC_ENDPOINT!, { - headers: { - 'x-api-key': process.env.APPSYNC_API_KEY! - }, - }); +export const handler = async (event: Event) => { + console.log('Received event:', JSON.stringify(event)); - try { - const input = event.arguments.input; - if (!input) { - throw new Error('Input is required'); - } + const { conversationId, prompt } = event.input; - // Generate a unique conversationId - const conversationId = uuidv4(); - console.log('Generated conversationId:', conversationId); + if (!conversationId || !prompt) { + console.error('Invalid input: Missing conversationId or prompt'); + return; + } - // Start processing Bedrock stream in the background - processBedrockStream(bedrockClient, graphQLClient, input, conversationId); + console.log(`Starting Bedrock stream for conversationId: ${conversationId}`); - // Return immediate response to AppSync mutation caller - return { - conversationId, - status: "PROCESSING" - }; + const bedrockClient = new BedrockRuntimeClient({}); + const graphQLClient = new GraphQLClient(process.env.APPSYNC_ENDPOINT!, { + headers: { 'x-api-key': process.env.APPSYNC_API_KEY! }, + }); + try { + await processBedrockStream(bedrockClient, graphQLClient, prompt, conversationId); + console.log(`Successfully completed processing for conversationId: ${conversationId}`); } catch (error) { - console.error('Lambda execution error:', error); - if (error instanceof Error) { - return { - conversationId: 'error', - status: error.message - }; - } - return { - conversationId: 'error', - status: 'Unknown error occurred' - }; + console.error(`Error during processing for conversationId ${conversationId}:`, error); + await notifyError(graphQLClient, conversationId, error); } }; -// Function to handle Bedrock streaming in the background async function processBedrockStream( bedrockClient: BedrockRuntimeClient, graphQLClient: GraphQLClient, input: string, conversationId: string -) { +): Promise { + const params = { + modelId: 'anthropic.claude-v2', + contentType: 'application/json', + accept: 'application/json', + body: JSON.stringify({ + anthropic_version: 'bedrock-2023-05-31', + max_tokens: 4096, + messages: [{ role: 'user', content: input }], + }), + }; + + const command = new InvokeModelWithResponseStreamCommand(params); + const stream = await bedrockClient.send(command); + + if (!stream.body) { + throw new Error('No response stream received from Bedrock'); + } + + let buffer = ''; + const chunkSize = 100; // Adjust based on your needs + try { - const params = { - modelId: "anthropic.claude-v2", - contentType: "application/json", - accept: "application/json", - body: JSON.stringify({ - anthropic_version: "bedrock-2023-05-31", - max_tokens: 4096, - messages: [{ - role: "user", - content: input - }] - }) - }; - - console.log('Invoking Bedrock with params:', JSON.stringify(params, null, 2)); - const command = new InvokeModelWithResponseStreamCommand(params); - const stream = await bedrockClient.send(command); - - if (stream.body) { - let accumulatedText = ''; - - for await (const chunk of stream.body) { - if (chunk.chunk?.bytes) { - const parsed = JSON.parse( - Buffer.from(chunk.chunk.bytes).toString("utf-8") - ); - console.log('Received chunk from Bedrock:', parsed); - - if (parsed.delta?.text) { - accumulatedText += parsed.delta.text; - - // Send chunk update via AppSync mutation - const mutation = ` - mutation SendChunk($conversationId: ID!, $chunk: String!) { - sendChunk(conversationId: $conversationId, chunk: $chunk) { - conversationId - chunk - } - } - `; - - try { - await graphQLClient.request(mutation, { - conversationId, - chunk: parsed.delta.text - }); - console.log('Chunk sent to subscription:', parsed.delta.text); - } catch (error) { - console.error('Error sending chunk to subscription:', error); - } - } + for await (const chunk of stream.body) { + if (!chunk.chunk?.bytes) continue; + + const parsed = JSON.parse(Buffer.from(chunk.chunk.bytes).toString('utf-8')); + if (!parsed.delta?.text) continue; + + buffer += parsed.delta.text; + + // Send chunks when buffer reaches certain size or contains complete sentences + if (buffer.length >= chunkSize || buffer.match(/[.!?]\s/)) { + const sanitizedChunk = sanitizeGraphQLString(buffer); + if (sanitizedChunk) { + await sendChunkToAppSync(graphQLClient, conversationId, sanitizedChunk); } + buffer = ''; + } + } + + // Send any remaining text in buffer + if (buffer) { + const sanitizedChunk = sanitizeGraphQLString(buffer); + if (sanitizedChunk) { + await sendChunkToAppSync(graphQLClient, conversationId, sanitizedChunk); } + } + + await completeStream(graphQLClient, conversationId); + } catch (error) { + console.error(`Error while processing stream for conversationId ${conversationId}:`, error); + throw error; + } +} - console.log('Completed streaming. Total accumulated text:', accumulatedText); +async function sendChunkToAppSync( + graphQLClient: GraphQLClient, + conversationId: string, + chunk: string +): Promise { + const mutation = ` + mutation SendChunk($conversationId: ID!, $chunk: String!) { + sendChunk(conversationId: $conversationId, chunk: $chunk) { + conversationId + chunk + } } + `; + + try { + await graphQLClient.request(mutation, { conversationId, chunk }); + console.log(`Sent chunk to AppSync for conversationId ${conversationId}`); } catch (error) { - console.error('Error during Bedrock streaming:', error); + console.error(`Failed to send chunk to AppSync for conversationId ${conversationId}:`, error); + throw error; } } + +async function completeStream( + graphQLClient: GraphQLClient, + conversationId: string +): Promise { + const mutation = ` + mutation Complete($conversationId: ID!) { + completeStream(conversationId: $conversationId) { + conversationId + status + } + } + `; + + await graphQLClient.request(mutation, { conversationId }); + console.log(`Stream completed for conversationId ${conversationId}`); +} + +async function notifyError( + graphQLClient: GraphQLClient, + conversationId: string, + error: unknown +): Promise { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + const sanitizedError = sanitizeGraphQLString(errorMessage); + + const mutation = ` + mutation SendError($conversationId: ID!, $error: String!) { + sendError(conversationId: $conversationId, error: $error) { + conversationId + error + } + } + `; + + await graphQLClient.request(mutation, { + conversationId, + error: sanitizedError, + }); +} diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json b/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json index 5652c4d70..db352981b 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json @@ -14,6 +14,7 @@ "devDependencies": { "@types/jest": "^29.5.5", "@types/node": "^20.7.1", + "@types/ws": "^8.5.5", "aws-cdk": "^2.99.1", "esbuild": "^0.19.4", "jest": "^29.7.0", @@ -28,6 +29,8 @@ "constructs": "^10.0.0", "graphql": "^16.8.1", "graphql-request": "^6.1.0", + "subscriptions-transport-ws": "^0.11.0", + "ws": "^8.14.2", "source-map-support": "^0.5.21" } } diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql b/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql index b06f9f5dd..cdf960d3a 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql @@ -1,17 +1,26 @@ -type Subscription { - # Subscribe to specific conversation - onReceiveChunk(conversationId: ID): ChunkResponse - @aws_subscribe(mutations: ["sendChunk"]) +type Mutation { + createConversation(conversationId: ID!): ConversationResponse! + startConversation(input: StartConversationInput!): ConversationResponse! + sendChunk(conversationId: ID!, chunk: String!): ChunkResponse! + completeStream(conversationId: ID!): CompletionResponse! - # Subscribe to all conversations - onReceiveChunkBroadcast: ChunkResponse + # Add this missing mutation + sendError(conversationId: ID!, error: String!): ErrorResponse! +} + +type Subscription { + onReceiveChunk(conversationId: ID!): ChunkResponse @aws_subscribe(mutations: ["sendChunk"]) } -type Mutation { - startConversation(input: String!): ConversationResponse! - sendChunk(conversationId: ID!, chunk: String!): ChunkResponse! - } +type Query { + dummyField: String +} + +input StartConversationInput { + prompt: String! + conversationId: ID! +} type ConversationResponse { conversationId: ID! @@ -23,13 +32,13 @@ type ChunkResponse { chunk: String! } -type Query { - # Required but not used for this flow - dummy: String +type CompletionResponse { + conversationId: ID! + status: String! } -schema { - query: Query - mutation: Mutation - subscription: Subscription +# Add this type for errors +type ErrorResponse { + conversationId: ID! + error: String! } diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts new file mode 100644 index 000000000..6c5cb10cc --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts @@ -0,0 +1,128 @@ +import { GraphQLClient } from 'graphql-request'; +import { SubscriptionClient } from 'subscriptions-transport-ws'; +import WebSocket from 'ws'; + +// Replace these with your AppSync API details +const APPSYNC_API_URL = 'https://utgypaxdjjcq3lo4y4jvk5v5pi.appsync-api.us-east-1.amazonaws.com/graphql'; // e.g., https://.appsync-api..amazonaws.com/graphql +const APPSYNC_API_KEY = 'da2-qkexqqvs4bhhpigpaodgqdhgwu'; +const WEBSOCKET_URL = APPSYNC_API_URL.replace('https', 'wss'); + +// Replace this with your test data +const TEST_CONVERSATION_ID = '123e4567-e89b-12d3-a456-426614174000'; +const TEST_PROMPT = 'Tell me a long story'; + +// Initialize GraphQL client +const graphQLClient = new GraphQLClient(APPSYNC_API_URL, { + headers: { + 'x-api-key': APPSYNC_API_KEY, + }, +}); + +// Function to create a conversation +async function createConversation(conversationId: string) { + const mutation = ` + mutation CreateConversation($conversationId: ID!) { + createConversation(conversationId: $conversationId) { + conversationId + status + } + } + `; + + const variables = { conversationId }; + + console.log('Creating conversation...'); + const response = await graphQLClient.request(mutation, variables); + console.log('CreateConversation response:', response); +} + +// Function to start a conversation +async function startConversation(prompt: string, conversationId: string) { + const mutation = ` + mutation StartConversation($input: StartConversationInput!) { + startConversation(input: $input) { + conversationId + status + } + } + `; + + const variables = { + input: { prompt, conversationId }, + }; + + console.log('Starting conversation...'); + const response = await graphQLClient.request(mutation, variables); + console.log('StartConversation response:', response); +} + +// Function to subscribe to updates +function subscribeToChunks(conversationId: string) { + return new Promise((resolve, reject) => { + const subscriptionQuery = ` + subscription OnReceiveChunk($conversationId: ID!) { + onReceiveChunk(conversationId: $conversationId) { + conversationId + chunk + } + } + `; + + const client = new SubscriptionClient(WEBSOCKET_URL, { + reconnect: true, + connectionParams: { + headers: { 'x-api-key': APPSYNC_API_KEY }, + }, + }, WebSocket); + + console.log('Starting subscription...'); + + const subscription = client.request({ + query: subscriptionQuery, + variables: { conversationId }, + }).subscribe({ + next(data) { + console.log('Subscription update received:', data); + if (data.errors) { + reject(data.errors); + } + }, + error(err) { + console.error('Subscription error:', err); + reject(err); + }, + complete() { + console.log('Subscription completed.'); + resolve(null); + }, + }); + + // Close the subscription after some time (optional) + setTimeout(() => { + console.log('Closing subscription...'); + subscription.unsubscribe(); + client.close(); + }, 60000); // Close after 60 seconds + }); +} + +// Main function to orchestrate the test +async function main() { + try { + // Step 1: Create a conversation + await createConversation(TEST_CONVERSATION_ID); + + // Step 2: Start a subscription for updates + subscribeToChunks(TEST_CONVERSATION_ID); + + // Step 3: Trigger the long-running invocation (start conversation) + setTimeout(async () => { + await startConversation(TEST_PROMPT, TEST_CONVERSATION_ID); + }, 5000); // Delay to ensure subscription is established before triggering mutation + + } catch (error) { + console.error('Error during test:', error); + } +} + +main(); From 300efb587d12c9f63e0f6a3a1c951815c1c08616 Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Thu, 19 Dec 2024 16:14:13 -0500 Subject: [PATCH 05/15] stable 1 --- .../package.json | 8 +- .../schema.graphql | 4 +- .../test/test.ts | 141 +++++++++--------- 3 files changed, 73 insertions(+), 80 deletions(-) diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json b/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json index db352981b..266c3515b 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json @@ -12,10 +12,11 @@ "deploy": "cdk deploy" }, "devDependencies": { + "@types/cookie": "^0.6.0", "@types/jest": "^29.5.5", "@types/node": "^20.7.1", - "@types/ws": "^8.5.5", "aws-cdk": "^2.99.1", + "cookie": "^0.5.0", "esbuild": "^0.19.4", "jest": "^29.7.0", "ts-jest": "^29.1.1", @@ -24,13 +25,12 @@ }, "dependencies": { "@aws-sdk/client-bedrock-runtime": "^3.428.0", - "@aws-sdk/client-appsync": "^3.428.0", + "aws-amplify": "^6.0.12", "aws-cdk-lib": "^2.99.1", "constructs": "^10.0.0", + "cookie": "^0.5.0", "graphql": "^16.8.1", "graphql-request": "^6.1.0", - "subscriptions-transport-ws": "^0.11.0", - "ws": "^8.14.2", "source-map-support": "^0.5.21" } } diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql b/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql index cdf960d3a..6287ca0c5 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql @@ -28,8 +28,8 @@ type ConversationResponse { } type ChunkResponse { - conversationId: ID! - chunk: String! + conversationId: ID + chunk: String } type CompletionResponse { diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts index 6c5cb10cc..33320e82a 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts @@ -1,24 +1,25 @@ -import { GraphQLClient } from 'graphql-request'; -import { SubscriptionClient } from 'subscriptions-transport-ws'; -import WebSocket from 'ws'; +import { Amplify } from 'aws-amplify'; +import { generateClient } from 'aws-amplify/api'; -// Replace these with your AppSync API details -const APPSYNC_API_URL = 'https://utgypaxdjjcq3lo4y4jvk5v5pi.appsync-api.us-east-1.amazonaws.com/graphql'; // e.g., https://.appsync-api..amazonaws.com/graphql +// AppSync API Configuration +const APPSYNC_API_URL = 'https://utgypaxdjjcq3lo4y4jvk5v5pi.appsync-api.us-east-1.amazonaws.com/graphql'; const APPSYNC_API_KEY = 'da2-qkexqqvs4bhhpigpaodgqdhgwu'; -const WEBSOCKET_URL = APPSYNC_API_URL.replace('https', 'wss'); -// Replace this with your test data -const TEST_CONVERSATION_ID = '123e4567-e89b-12d3-a456-426614174000'; -const TEST_PROMPT = 'Tell me a long story'; - -// Initialize GraphQL client -const graphQLClient = new GraphQLClient(APPSYNC_API_URL, { - headers: { - 'x-api-key': APPSYNC_API_KEY, - }, +// Configure Amplify +Amplify.configure({ + API: { + GraphQL: { + endpoint: APPSYNC_API_URL, + region: 'us-east-1', + defaultAuthMode: 'apiKey', + apiKey: APPSYNC_API_KEY + } + } }); -// Function to create a conversation +// Generate Amplify client +const client = generateClient(); + async function createConversation(conversationId: string) { const mutation = ` mutation CreateConversation($conversationId: ID!) { @@ -29,14 +30,14 @@ async function createConversation(conversationId: string) { } `; - const variables = { conversationId }; - console.log('Creating conversation...'); - const response = await graphQLClient.request(mutation, variables); + const response = await client.graphql({ + query: mutation, + variables: { conversationId } + }); console.log('CreateConversation response:', response); } -// Function to start a conversation async function startConversation(prompt: string, conversationId: string) { const mutation = ` mutation StartConversation($input: StartConversationInput!) { @@ -47,81 +48,73 @@ async function startConversation(prompt: string, conversationId: string) { } `; - const variables = { - input: { prompt, conversationId }, - }; - console.log('Starting conversation...'); - const response = await graphQLClient.request(mutation, variables); + const response = await client.graphql({ + query: mutation, + variables: { input: { prompt, conversationId } } + }); console.log('StartConversation response:', response); } -// Function to subscribe to updates function subscribeToChunks(conversationId: string) { - return new Promise((resolve, reject) => { - const subscriptionQuery = ` - subscription OnReceiveChunk($conversationId: ID!) { - onReceiveChunk(conversationId: $conversationId) { - conversationId - chunk - } + const subscription = ` + subscription OnReceiveChunk($conversationId: ID!) { + onReceiveChunk(conversationId: $conversationId) { + conversationId + chunk } - `; - - const client = new SubscriptionClient(WEBSOCKET_URL, { - reconnect: true, - connectionParams: { - headers: { 'x-api-key': APPSYNC_API_KEY }, - }, - }, WebSocket); - - console.log('Starting subscription...'); - - const subscription = client.request({ - query: subscriptionQuery, - variables: { conversationId }, - }).subscribe({ - next(data) { - console.log('Subscription update received:', data); - if (data.errors) { - reject(data.errors); - } - }, - error(err) { - console.error('Subscription error:', err); - reject(err); - }, - complete() { - console.log('Subscription completed.'); - resolve(null); - }, - }); + } + `; - // Close the subscription after some time (optional) - setTimeout(() => { - console.log('Closing subscription...'); - subscription.unsubscribe(); - client.close(); - }, 60000); // Close after 60 seconds + console.log('Starting subscription...'); + + // Explicitly cast to `Observable` + const observable = client.graphql({ + query: subscription, + variables: { conversationId } + }) as unknown as { subscribe: Function }; + + const subscriptionInstance = observable.subscribe({ + next: (data: { data?: { onReceiveChunk?: { conversationId: string; chunk: string } } }) => { + console.log('Received chunk:', data?.data?.onReceiveChunk); + }, + error: (error: Error) => { + console.error('Subscription error:', error); + }, + complete: () => { + console.log('Subscription completed'); + } }); + + return subscriptionInstance; } -// Main function to orchestrate the test async function main() { try { + const TEST_CONVERSATION_ID = '123e4567-e89b-12d3-a456-426614174000'; + const TEST_PROMPT = 'Tell me a joke'; + // Step 1: Create a conversation await createConversation(TEST_CONVERSATION_ID); - // Step 2: Start a subscription for updates - subscribeToChunks(TEST_CONVERSATION_ID); + // Step 2: Subscribe to chunks + const subscription = subscribeToChunks(TEST_CONVERSATION_ID); - // Step 3: Trigger the long-running invocation (start conversation) + // Step 3: Start the long-running invocation (start conversation) setTimeout(async () => { await startConversation(TEST_PROMPT, TEST_CONVERSATION_ID); - }, 5000); // Delay to ensure subscription is established before triggering mutation + }, 2000); + + // Cleanup on process exit + process.on('SIGINT', () => { + console.log('Cleaning up...'); + subscription.unsubscribe(); + process.exit(); + }); } catch (error) { console.error('Error during test:', error); + process.exit(1); } } From 9cb3c833515285690f4e9b2fd2aee529f518f09c Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Thu, 19 Dec 2024 17:24:27 -0500 Subject: [PATCH 06/15] st2 --- .../README.md | 51 ------------------- .../package.json | 2 +- .../schema.graphql | 1 - .../test/test.ts | 25 +-------- 4 files changed, 3 insertions(+), 76 deletions(-) diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md index 70fd97b81..6e924c1e4 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md @@ -70,54 +70,3 @@ cdk destroy --all Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: MIT-0 - -Outputs: -AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack.GraphQLApiKey = da2-367hvetdp5f6flyii7xnlafgki -AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack.GraphQLApiUrl = https://3xbko6bpk5awvahhtvo3htsfpm.appsync-api.us-east-1.amazonaws.com/graphql - - -# Sample Queries to test from App Sync Console - -1. Trigger subscription: -subscription OnReceiveChunkBroadcast { - onReceiveChunkBroadcast { - conversationId - chunk - } -} - -2. - -Mutation trigger -mutation StartConversation { - startConversation(input: "tell me a long joke") { - conversationId - status - } -} -====== - -Based on the search results and architecture diagram, I need to correct some misconceptions about AWS AppSync's Event mode Lambda invocations: -The Issue -The article's proposed solution won't work as intended because: - - AppSync's Event mode invocation doesn't maintain the Lambda execution context after returning the initial response2 - - The Lambda function will still terminate after returning the response, regardless of any background processing attempts6 - -Why It Doesn't Work -The fundamental problems are: - - AppSync's Event mode is designed for fire-and-forget scenarios, not for maintaining long-running connections3 - The Lambda execution context cannot be kept alive after the initial response is sent back to AppSync10 - The WebSocket connection through AppSync subscriptions needs a continuous execution context to send updates11 - -Correct Implementation -For long-running AI model invocations with streaming responses, you should instead use one of these patterns: - - Split the process into two Lambda functions - one for immediate response and another for processing4 - Use a queue-based architecture with SQS to handle the long-running process2 - - For streaming responses under 10 seconds, use AppSync's direct Bedrock integration9 - -The article's proposed architecture, while theoretically sound, doesn't account for Lambda's execution context limitations in practice. \ No newline at end of file diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json b/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json index 266c3515b..22d7a682b 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json @@ -24,7 +24,7 @@ "typescript": "~5.2.2" }, "dependencies": { - "@aws-sdk/client-bedrock-runtime": "^3.428.0", + "@aws-sdk/client-bedrock-runtime": "^3.496.0", "aws-amplify": "^6.0.12", "aws-cdk-lib": "^2.99.1", "constructs": "^10.0.0", diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql b/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql index 6287ca0c5..411d2309b 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/schema.graphql @@ -1,5 +1,4 @@ type Mutation { - createConversation(conversationId: ID!): ConversationResponse! startConversation(input: StartConversationInput!): ConversationResponse! sendChunk(conversationId: ID!, chunk: String!): ChunkResponse! completeStream(conversationId: ID!): CompletionResponse! diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts index 33320e82a..6387d2ae2 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts @@ -20,24 +20,6 @@ Amplify.configure({ // Generate Amplify client const client = generateClient(); -async function createConversation(conversationId: string) { - const mutation = ` - mutation CreateConversation($conversationId: ID!) { - createConversation(conversationId: $conversationId) { - conversationId - status - } - } - `; - - console.log('Creating conversation...'); - const response = await client.graphql({ - query: mutation, - variables: { conversationId } - }); - console.log('CreateConversation response:', response); -} - async function startConversation(prompt: string, conversationId: string) { const mutation = ` mutation StartConversation($input: StartConversationInput!) { @@ -94,13 +76,10 @@ async function main() { const TEST_CONVERSATION_ID = '123e4567-e89b-12d3-a456-426614174000'; const TEST_PROMPT = 'Tell me a joke'; - // Step 1: Create a conversation - await createConversation(TEST_CONVERSATION_ID); - - // Step 2: Subscribe to chunks + // Step 1: Subscribe to chunks const subscription = subscribeToChunks(TEST_CONVERSATION_ID); - // Step 3: Start the long-running invocation (start conversation) + // Step 2: Start the long-running invocation (start conversation) setTimeout(async () => { await startConversation(TEST_PROMPT, TEST_CONVERSATION_ID); }, 2000); From 3008459ce18c10a2a1a1483fb0de5fbf6d32becc Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Thu, 19 Dec 2024 22:54:39 -0500 Subject: [PATCH 07/15] st3 --- ...bda-bedrock-async-stream-subscription-cdk-stack.ts | 11 +++++++++-- .../lib/lambda/invocation/index.ts | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts index d6e54f281..44f5b2f3f 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts @@ -50,9 +50,16 @@ export class AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack extends cdk.Sta // Add Bedrock permissions to Lambda invocationHandler.addToRolePolicy(new iam.PolicyStatement({ actions: ['bedrock:InvokeModelWithResponseStream'], - resources: ['arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2'], + resources: ['arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-20240620-v1:0'], + })); + invocationHandler.addToRolePolicy(new iam.PolicyStatement({ + actions: ['bedrock:InvokeModelWithResponseStream'], + resources: ['arn:aws:bedrock:us-east-1:275631959608:inference-profile/us.anthropic.claude-3-5-sonnet-20240620-v1:0'], + })); + invocationHandler.addToRolePolicy(new iam.PolicyStatement({ + actions: ['bedrock:InvokeModelWithResponseStream'], + resources: ['arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-3-5-sonnet-20240620-v1:0'], })); - // Add AppSync permissions to Lambda invocationHandler.addToRolePolicy(new iam.PolicyStatement({ actions: ['appsync:GraphQL'], diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts index 3f880b826..f38fdc324 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts @@ -50,7 +50,7 @@ async function processBedrockStream( conversationId: string ): Promise { const params = { - modelId: 'anthropic.claude-v2', + modelId: 'us.anthropic.claude-3-5-sonnet-20240620-v1:0', contentType: 'application/json', accept: 'application/json', body: JSON.stringify({ From 75d1907b6f0ef191ac25d2f0320d61bef098547e Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Fri, 20 Dec 2024 10:57:16 -0500 Subject: [PATCH 08/15] st5 --- .../README.md | 42 +++++- .../image.png | Bin 0 -> 217889 bytes .../invocation/index async func.ts.back | 125 ------------------ .../invocation/index sync sub func.ts.back | 113 ---------------- .../lib/lambda/invocation/index.ts | 76 ++++++----- 5 files changed, 78 insertions(+), 278 deletions(-) create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/image.png delete mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index async func.ts.back delete mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index sync sub func.ts.back diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md index 6e924c1e4..47c2c412a 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md @@ -1,6 +1,6 @@ -# AppSync Lambda Bedrock Streaming Pattern +# AppSync Lambda Bedrock Streaming Pattern for Long-running Invocations -This pattern demonstrates how to implement long-running invocations with Amazon Bedrock using AWS AppSync subscriptions and AWS Lambda, following the official AWS AppSync documentation pattern. +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, 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 @@ -17,14 +17,16 @@ Important: this application uses various AWS services and there are costs associ ## How it works -The pattern implements an asynchronous streaming architecture where: +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 -3. Lambda function streams responses from Bedrock using InvokeModelWithResponseStream +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) + This pattern is ideal for: - Long-running AI model invocations - Real-time streaming responses @@ -52,14 +54,40 @@ npm install npm run deploy ``` - ## Testing -Run tests +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 + +Run the test script using: ```sh -npm run test + node test/test.js ``` +You should see output similar to: +```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!' +} +``` ## Cleanup 1. Delete the stack diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/image.png b/appsync-lambda-bedrock-async-stream-subscription-cdk/image.png new file mode 100644 index 0000000000000000000000000000000000000000..78960960f52d8a46415f5da02c860b465cbc35b7 GIT binary patch literal 217889 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⋙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 Ya)cB+ccE0P9sz$6BC>Bvg!KLX510i8X#fBK literal 0 HcmV?d00001 diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index async func.ts.back b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index async func.ts.back deleted file mode 100644 index 4f759751b..000000000 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index async func.ts.back +++ /dev/null @@ -1,125 +0,0 @@ -import { BedrockRuntimeClient, InvokeModelWithResponseStreamCommand } from "@aws-sdk/client-bedrock-runtime"; -import { GraphQLClient } from 'graphql-request'; -import { v4 as uuidv4 } from 'uuid'; - -interface AppSyncEvent { - arguments: { - input: string; - }; -} - -export const handler = async (event: AppSyncEvent, context: any) => { - console.log('Step 2: Lambda received event:', JSON.stringify(event, null, 2)); - - // Prevent Lambda from terminating early - context.callbackWaitsForEmptyEventLoop = false; - - const bedrockClient = new BedrockRuntimeClient(); - const graphQLClient = new GraphQLClient(process.env.APPSYNC_ENDPOINT!, { - headers: { - 'x-api-key': process.env.APPSYNC_API_KEY! - }, - }); - - try { - const input = event.arguments.input; - if (!input) { - throw new Error('Input is required'); - } - - // Generate a unique conversationId - const conversationId = uuidv4(); - console.log('Generated conversationId:', conversationId); - - // Start processing Bedrock stream in the background - processBedrockStream(bedrockClient, graphQLClient, input, conversationId); - - // Return immediate response to AppSync mutation caller - return { - conversationId, - status: "PROCESSING" - }; - - } catch (error) { - console.error('Lambda execution error:', error); - if (error instanceof Error) { - return { - conversationId: 'error', - status: error.message - }; - } - return { - conversationId: 'error', - status: 'Unknown error occurred' - }; - } -}; - -// Function to handle Bedrock streaming in the background -async function processBedrockStream( - bedrockClient: BedrockRuntimeClient, - graphQLClient: GraphQLClient, - input: string, - conversationId: string -) { - try { - const params = { - modelId: "anthropic.claude-v2", - contentType: "application/json", - accept: "application/json", - body: JSON.stringify({ - anthropic_version: "bedrock-2023-05-31", - max_tokens: 4096, - messages: [{ - role: "user", - content: input - }] - }) - }; - - console.log('Invoking Bedrock with params:', JSON.stringify(params, null, 2)); - const command = new InvokeModelWithResponseStreamCommand(params); - const stream = await bedrockClient.send(command); - - if (stream.body) { - let accumulatedText = ''; - - for await (const chunk of stream.body) { - if (chunk.chunk?.bytes) { - const parsed = JSON.parse( - Buffer.from(chunk.chunk.bytes).toString("utf-8") - ); - console.log('Received chunk from Bedrock:', parsed); - - if (parsed.delta?.text) { - accumulatedText += parsed.delta.text; - - // Send chunk update via AppSync mutation - const mutation = ` - mutation SendChunk($conversationId: ID!, $chunk: String!) { - sendChunk(conversationId: $conversationId, chunk: $chunk) { - conversationId - chunk - } - } - `; - - try { - await graphQLClient.request(mutation, { - conversationId, - chunk: parsed.delta.text - }); - console.log('Chunk sent to subscription:', parsed.delta.text); - } catch (error) { - console.error('Error sending chunk to subscription:', error); - } - } - } - } - - console.log('Completed streaming. Total accumulated text:', accumulatedText); - } - } catch (error) { - console.error('Error during Bedrock streaming:', error); - } -} diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index sync sub func.ts.back b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index sync sub func.ts.back deleted file mode 100644 index 599017135..000000000 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index sync sub func.ts.back +++ /dev/null @@ -1,113 +0,0 @@ -import { BedrockRuntimeClient, InvokeModelWithResponseStreamCommand } from "@aws-sdk/client-bedrock-runtime"; -import { GraphQLClient } from 'graphql-request'; -import { v4 as uuidv4 } from 'uuid'; - -interface AppSyncEvent { - arguments: { - input: string; - }; -} - -export const handler = async (event: AppSyncEvent) => { - console.log('Step 2: Lambda received event:', JSON.stringify(event, null, 2)); - - const bedrockClient = new BedrockRuntimeClient(); - const graphQLClient = new GraphQLClient(process.env.APPSYNC_ENDPOINT!, { - headers: { - 'x-api-key': process.env.APPSYNC_API_KEY! - }, - }); - - try { - const input = event.arguments.input; - if (!input) { - throw new Error('Input is required'); - } - - const conversationId = uuidv4(); - console.log('Generated conversationId:', conversationId); - - // Initial response to client - const response = { - conversationId, - status: "PROCESSING" - }; - - // Invoke Bedrock with streaming - const params = { - modelId: "anthropic.claude-v2", - contentType: "application/json", - accept: "application/json", - body: JSON.stringify({ - anthropic_version: "bedrock-2023-05-31", - max_tokens: 4096, - messages: [{ - role: "user", - content: input - }] - }) - }; - - console.log('Step 3: Invoking Bedrock with params:', JSON.stringify(params, null, 2)); - const command = new InvokeModelWithResponseStreamCommand(params); - const stream = await bedrockClient.send(command); - - // Process stream and send updates via mutations - if (stream.body) { - let accumulatedText = ''; - - for await (const chunk of stream.body) { - if (chunk.chunk?.bytes) { - const parsed = JSON.parse( - Buffer.from(chunk.chunk.bytes).toString("utf-8") - ); - console.log('Received chunk from Bedrock:', parsed); - - if (parsed.delta?.text) { - accumulatedText += parsed.delta.text; - - // Send mutation to AppSync - const mutation = ` - mutation SendChunk($conversationId: ID!, $chunk: String!) { - sendChunk(conversationId: $conversationId, chunk: $chunk) { - conversationId - chunk - } - } - `; - - try { - const mutationResponse = await graphQLClient.request(mutation, { - conversationId, - chunk: parsed.delta.text - }); - console.log('Step 4: Mutation sent to AppSync:', mutationResponse); - } catch (error) { - console.error('Error sending mutation to AppSync:', error); - if (error instanceof Error) { - console.error(error.message); - } - } - } - } - } - - console.log('Step 4: Completed streaming. Total response:', accumulatedText); - } - - return response; - - } catch (error) { - console.error('Lambda execution error:', error); - if (error instanceof Error) { - return { - conversationId: 'error', - status: error.message - }; - } - return { - conversationId: 'error', - status: 'Unknown error occurred' - }; - } -}; diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts index f38fdc324..ee729401f 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts @@ -1,4 +1,4 @@ -import { BedrockRuntimeClient, InvokeModelWithResponseStreamCommand } from '@aws-sdk/client-bedrock-runtime'; +import { BedrockRuntimeClient, ConverseStreamCommand, ConverseStreamCommandInput } from '@aws-sdk/client-bedrock-runtime'; import { GraphQLClient } from 'graphql-request'; interface Event { @@ -10,11 +10,11 @@ interface Event { function sanitizeGraphQLString(text: string): string { return text - .replace(/[\n\r]/g, ' ') // Replace newlines with spaces - .replace(/\\/g, '\\\\') // Escape backslashes - .replace(/"/g, '\\"') // Escape double quotes - .replace(/\t/g, ' ') // Replace tabs with spaces - .trim(); // Remove leading/trailing whitespace + .replace(/[\n\r]/g, ' ') + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\t/g, ' ') + .trim(); } export const handler = async (event: Event) => { @@ -49,47 +49,56 @@ async function processBedrockStream( input: string, conversationId: string ): Promise { - const params = { + const params: ConverseStreamCommandInput = { modelId: 'us.anthropic.claude-3-5-sonnet-20240620-v1:0', - contentType: 'application/json', - accept: 'application/json', - body: JSON.stringify({ - anthropic_version: 'bedrock-2023-05-31', - max_tokens: 4096, - messages: [{ role: 'user', content: input }], - }), + messages: [ + { + role: 'user', + content: [ + { + text: input + } + ] + } + ], + inferenceConfig: { + temperature: 0.7, + maxTokens: 4096, + topP: 1 + } }; + - const command = new InvokeModelWithResponseStreamCommand(params); - const stream = await bedrockClient.send(command); + const command = new ConverseStreamCommand(params); + const response = await bedrockClient.send(command); - if (!stream.body) { + if (!response.stream) { throw new Error('No response stream received from Bedrock'); } + let buffer = ''; - const chunkSize = 100; // Adjust based on your needs + const chunkSize = 100; try { - for await (const chunk of stream.body) { - if (!chunk.chunk?.bytes) continue; - - const parsed = JSON.parse(Buffer.from(chunk.chunk.bytes).toString('utf-8')); - if (!parsed.delta?.text) continue; - - buffer += parsed.delta.text; - - // Send chunks when buffer reaches certain size or contains complete sentences - if (buffer.length >= chunkSize || buffer.match(/[.!?]\s/)) { - const sanitizedChunk = sanitizeGraphQLString(buffer); - if (sanitizedChunk) { - await sendChunkToAppSync(graphQLClient, conversationId, sanitizedChunk); + for await (const event of response.stream) { + if (event.contentBlockDelta && event.contentBlockDelta.delta) { + const delta = event.contentBlockDelta.delta.text; + if (delta) { + buffer += delta; + + if (buffer.length >= chunkSize || buffer.match(/[.!?]\s/)) { + const sanitizedChunk = sanitizeGraphQLString(buffer); + if (sanitizedChunk) { + await sendChunkToAppSync(graphQLClient, conversationId, sanitizedChunk); + } + buffer = ''; + } } - buffer = ''; } } + - // Send any remaining text in buffer if (buffer) { const sanitizedChunk = sanitizeGraphQLString(buffer); if (sanitizedChunk) { @@ -104,6 +113,7 @@ async function processBedrockStream( } } + async function sendChunkToAppSync( graphQLClient: GraphQLClient, conversationId: string, From 03338d7987714932d352e8db90a740f7b28e83dd Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Fri, 20 Dec 2024 11:08:23 -0500 Subject: [PATCH 09/15] st6 --- .../README.md | 2 +- .../lib/lambda/invocation/index.ts | 3 +-- .../package.json | 8 +++----- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md index 47c2c412a..da282324b 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md @@ -64,7 +64,7 @@ After deployment, you can test the Bedrock streaming integration using the provi Run the test script using: ```sh - node test/test.js +npx tsx test/test.ts ``` You should see output similar to: diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts index ee729401f..419dad027 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts @@ -96,8 +96,7 @@ async function processBedrockStream( } } } - } - + } if (buffer) { const sanitizedChunk = sanitizeGraphQLString(buffer); diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json b/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json index 22d7a682b..53a4c7113 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/package.json @@ -7,21 +7,19 @@ "scripts": { "build": "tsc", "watch": "tsc -w", - "test": "jest", + "test": "tsx test/test.ts", "cdk": "cdk", "deploy": "cdk deploy" }, "devDependencies": { "@types/cookie": "^0.6.0", - "@types/jest": "^29.5.5", "@types/node": "^20.7.1", "aws-cdk": "^2.99.1", "cookie": "^0.5.0", "esbuild": "^0.19.4", - "jest": "^29.7.0", - "ts-jest": "^29.1.1", "ts-node": "^10.9.1", - "typescript": "~5.2.2" + "typescript": "~5.2.2", + "tsx": "^3.12.7" }, "dependencies": { "@aws-sdk/client-bedrock-runtime": "^3.496.0", From 1716f80440dbbf4a9237aef610b9762b01441c20 Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Fri, 20 Dec 2024 11:18:56 -0500 Subject: [PATCH 10/15] Adding AppSync Async Bedrock Streaming with Lambda Event Mode Pattern --- .../README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md index da282324b..8f2f0e336 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md @@ -1,6 +1,6 @@ -# AppSync Lambda Bedrock Streaming Pattern for Long-running Invocations +# AppSync Async Bedrock Streaming with Lambda Event Mode -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, following the official AWS AppSync documentation pattern. +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 @@ -20,18 +20,18 @@ Important: this application uses various AWS services and there are costs associ 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 +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) -This pattern is ideal for: -- Long-running AI model invocations -- Real-time streaming responses -- Asynchronous processing patterns -- Progressive UI updates +**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 From 8746d7dc72811bbfdabe9830853b620491c5ecbe Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Fri, 20 Dec 2024 17:41:23 -0500 Subject: [PATCH 11/15] final - AppSync Async Bedrock Streaming with Lambda Event Mode --- .../README.md | 2 +- ...edrock-async-stream-subscription-cdk-stack.ts | 16 +++++++--------- .../lib/lambda/invocation/index.ts | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md index 8f2f0e336..cc5eca965 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md @@ -13,7 +13,7 @@ Important: this application uses various AWS services and there are costs associ * [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 -* Make sure to enable the **Anthropic - Claude V2** model on the [Bedrock console](https://console.aws.amazon.com/bedrock/home#/modelaccess). +* Make sure to enable the **Anthropic - Claude Sonnet 3.5 V2** model on the [Bedrock console](https://console.aws.amazon.com/bedrock/home#/modelaccess). ## How it works diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts index 44f5b2f3f..8f1261e04 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts @@ -50,16 +50,14 @@ export class AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack extends cdk.Sta // Add Bedrock permissions to Lambda invocationHandler.addToRolePolicy(new iam.PolicyStatement({ actions: ['bedrock:InvokeModelWithResponseStream'], - resources: ['arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-20240620-v1:0'], - })); - invocationHandler.addToRolePolicy(new iam.PolicyStatement({ - actions: ['bedrock:InvokeModelWithResponseStream'], - resources: ['arn:aws:bedrock:us-east-1:275631959608:inference-profile/us.anthropic.claude-3-5-sonnet-20240620-v1:0'], - })); - invocationHandler.addToRolePolicy(new iam.PolicyStatement({ - actions: ['bedrock:InvokeModelWithResponseStream'], - resources: ['arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-3-5-sonnet-20240620-v1:0'], + resources: [ + 'arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0', + 'arn:aws:bedrock:us-east-2::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0', + 'arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0', + 'arn:aws:bedrock:us-east-1:275631959608:inference-profile/us.anthropic.claude-3-5-sonnet-20241022-v2:0' + ] })); + // Add AppSync permissions to Lambda invocationHandler.addToRolePolicy(new iam.PolicyStatement({ actions: ['appsync:GraphQL'], diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts index 419dad027..723bf7d12 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts @@ -50,7 +50,7 @@ async function processBedrockStream( conversationId: string ): Promise { const params: ConverseStreamCommandInput = { - modelId: 'us.anthropic.claude-3-5-sonnet-20240620-v1:0', + modelId: 'us.anthropic.claude-3-5-sonnet-20241022-v2:0', messages: [ { role: 'user', From f82e87338b8453aa9db4d9e2fe012af76aa96b3c Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Mon, 30 Dec 2024 13:46:41 -0500 Subject: [PATCH 12/15] Addressing review comments --- .../README.md | 37 +++++++++++++++---- ...ock-async-stream-subscription-cdk-stack.ts | 18 +-------- .../test/test.ts | 8 ++-- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md index cc5eca965..e4771cb48 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md @@ -1,4 +1,4 @@ -# AppSync Async Bedrock Streaming with Lambda Event Mode +# 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. @@ -35,25 +35,33 @@ The pattern implements an asynchronous [streaming architecture](https://docs.aws ## Deployment Instructions -1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: +1. Clone the repository: ```sh git clone https://github.com/aws-samples/serverless-patterns ``` -2. Change directory to the pattern directory: +2. Navigate to pattern directory: ```sh cd appsync-lambda-bedrock-async-stream-subscription-cdk ``` -3. Install the required dependencies: +3. Install dependencies: ```sh npm install ``` -4. Deploy the stack to your default AWS account and region: +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: @@ -62,12 +70,20 @@ After deployment, you can test the Bedrock streaming integration using the provi - Real-time streaming chunks display - Graceful cleanup on exit -Run the test script using: +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 + Replace the REGION with your AWS Account Region +``` + +2. Run the test: ```sh npx tsx test/test.ts ``` -You should see output similar to: +3. Expected Output: ```sh Starting subscription... Starting conversation... @@ -88,6 +104,13 @@ conversationId: '123e4567-e89b-12d3-a456-426614174000', chunk: 'up everything!' } ``` + +4. Stop the test: +```sh +Press Ctrl+C to terminate the process +``` + + ## Cleanup 1. Delete the stack diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts index 8f1261e04..fb6cecb5d 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts @@ -13,7 +13,7 @@ export class AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack extends cdk.Sta const api = new appsync.GraphqlApi(this, 'Api', { name: 'bedrock-streaming-api', - schema: appsync.SchemaFile.fromAsset('schema.graphql'), + definition: appsync.Definition.fromFile(path.join(__dirname, '..', 'schema.graphql')), authorizationConfig: { defaultAuthorization: { authorizationType: appsync.AuthorizationType.API_KEY, @@ -100,22 +100,6 @@ export class AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack extends cdk.Sta }); const noneDS = api.addNoneDataSource('NoneDataSource'); - noneDS.createResolver('CreateConversationResolver', { - typeName: 'Mutation', - fieldName: 'createConversation', - requestMappingTemplate: appsync.MappingTemplate.fromString(` - { - "version": "2018-05-29", - "payload": { - "conversationId": "$context.arguments.conversationId", - "status": "CREATED" - } - } - `), - responseMappingTemplate: appsync.MappingTemplate.fromString(` - $util.toJson($context.result) - `), - }); noneDS.createResolver('SendChunkResolver', { typeName: 'Mutation', diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts index 6387d2ae2..d14b124f6 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts @@ -2,15 +2,17 @@ import { Amplify } from 'aws-amplify'; import { generateClient } from 'aws-amplify/api'; // AppSync API Configuration -const APPSYNC_API_URL = 'https://utgypaxdjjcq3lo4y4jvk5v5pi.appsync-api.us-east-1.amazonaws.com/graphql'; -const APPSYNC_API_KEY = 'da2-qkexqqvs4bhhpigpaodgqdhgwu'; + +const APPSYNC_API_URL = ''; +const APPSYNC_API_KEY = ''; +const REGION = '' // Configure Amplify Amplify.configure({ API: { GraphQL: { endpoint: APPSYNC_API_URL, - region: 'us-east-1', + region: REGION, defaultAuthMode: 'apiKey', apiKey: APPSYNC_API_KEY } From 257146e695c8f523dcbb2c8f0dbbcc82765971ab Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Mon, 30 Dec 2024 14:43:32 -0500 Subject: [PATCH 13/15] Adding example-pattern.json --- .../README.md | 6 ++ .../example.pattern.json | 58 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/example.pattern.json diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md index e4771cb48..0a6ad478a 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md @@ -117,6 +117,12 @@ Press Ctrl+C to terminate the process ```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. diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/example.pattern.json b/appsync-lambda-bedrock-async-stream-subscription-cdk/example.pattern.json new file mode 100644 index 000000000..651074020 --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/example.pattern.json @@ -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: cdk destroy --all." + ] + }, + "authors": [ + { + "name": "Kaustav Dey", + "image": "https://avatars.githubusercontent.com/u/13236519", + "bio": "Solution Architect at AWS", + "linkedin": "https://www.linkedin.com/in/kaustavbecs/" + } + ] + } + \ No newline at end of file From 8ec21130abd0062c140d0f45f11ffe09f3951d5c Mon Sep 17 00:00:00 2001 From: Kaustav Dey Date: Tue, 31 Dec 2024 16:03:16 -0500 Subject: [PATCH 14/15] Making the code AWS region agnostic --- .../README.md | 8 +++++--- ...rock-async-stream-subscription-cdk-stack.ts | 18 +++++++++++++----- .../lib/lambda/invocation/index.ts | 5 ++++- .../test/test.ts | 2 -- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md index 0a6ad478a..148672c83 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/README.md @@ -2,7 +2,7 @@ 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 +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. @@ -13,7 +13,7 @@ Important: this application uses various AWS services and there are costs associ * [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 -* Make sure to enable the **Anthropic - Claude Sonnet 3.5 V2** model on the [Bedrock console](https://console.aws.amazon.com/bedrock/home#/modelaccess). +* 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 @@ -75,7 +75,6 @@ After deployment, you can test the Bedrock streaming integration using the provi 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 - Replace the REGION with your AWS Account Region ``` 2. Run the test: @@ -105,6 +104,8 @@ 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 @@ -127,3 +128,4 @@ Solution Architect Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: MIT-0 + diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts index fb6cecb5d..59a25375e 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/appsync-lambda-bedrock-async-stream-subscription-cdk-stack.ts @@ -9,7 +9,13 @@ import * as path from 'path'; export class AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { - super(scope, id, props); + super(scope, id, { + ...props, + env: { + account: process.env.CDK_DEFAULT_ACCOUNT, + region: process.env.CDK_DEFAULT_REGION, + }, + }); const api = new appsync.GraphqlApi(this, 'Api', { name: 'bedrock-streaming-api', @@ -47,14 +53,14 @@ export class AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack extends cdk.Sta }); - // Add Bedrock permissions to Lambda + // Add Bedrock permissions to Lambda. Add IAM policies for all regions covered under the Inference profile invocationHandler.addToRolePolicy(new iam.PolicyStatement({ actions: ['bedrock:InvokeModelWithResponseStream'], resources: [ 'arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0', 'arn:aws:bedrock:us-east-2::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0', 'arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0', - 'arn:aws:bedrock:us-east-1:275631959608:inference-profile/us.anthropic.claude-3-5-sonnet-20241022-v2:0' + `arn:aws:bedrock:us-east-1:${this.account}:inference-profile/us.anthropic.claude-3-5-sonnet-20241022-v2:0` ] })); @@ -67,12 +73,14 @@ export class AppsyncLambdaBedrockAsyncStreamSubscriptionCdkStack extends cdk.Sta // Add CloudWatch Logs permissions to Lambda invocationHandler.addToRolePolicy(new iam.PolicyStatement({ actions: [ - 'logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents' ], - resources: ['*'] + resources: [ + `arn:aws:logs:${this.region}:${this.account}:log-group:/aws/lambda/*` + ] })); + const invocationDS = api.addLambdaDataSource('InvocationDataSource', invocationHandler); diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts index 723bf7d12..cdfdfb7d3 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/lib/lambda/invocation/index.ts @@ -29,7 +29,10 @@ export const handler = async (event: Event) => { console.log(`Starting Bedrock stream for conversationId: ${conversationId}`); - const bedrockClient = new BedrockRuntimeClient({}); + // Using the us-east-1 Bedrock Inference Profile for Claude Sonnet 3.5 V2 + const bedrockClient = new BedrockRuntimeClient({ + region: 'us-east-1' + }); const graphQLClient = new GraphQLClient(process.env.APPSYNC_ENDPOINT!, { headers: { 'x-api-key': process.env.APPSYNC_API_KEY! }, }); diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts b/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts index d14b124f6..e030be89c 100644 --- a/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/test/test.ts @@ -5,14 +5,12 @@ import { generateClient } from 'aws-amplify/api'; const APPSYNC_API_URL = ''; const APPSYNC_API_KEY = ''; -const REGION = '' // Configure Amplify Amplify.configure({ API: { GraphQL: { endpoint: APPSYNC_API_URL, - region: REGION, defaultAuthMode: 'apiKey', apiKey: APPSYNC_API_KEY } From 9c7faa066eaf8a31e06b0f3ff7dfb40ebf3645b8 Mon Sep 17 00:00:00 2001 From: Marco Date: Thu, 2 Jan 2025 10:57:28 +0100 Subject: [PATCH 15/15] Create appsync-lambda-bedrock-async-stream-subscription-cdk.json add pattern json file --- ...bedrock-async-stream-subscription-cdk.json | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 appsync-lambda-bedrock-async-stream-subscription-cdk/appsync-lambda-bedrock-async-stream-subscription-cdk.json diff --git a/appsync-lambda-bedrock-async-stream-subscription-cdk/appsync-lambda-bedrock-async-stream-subscription-cdk.json b/appsync-lambda-bedrock-async-stream-subscription-cdk/appsync-lambda-bedrock-async-stream-subscription-cdk.json new file mode 100644 index 000000000..26cec414c --- /dev/null +++ b/appsync-lambda-bedrock-async-stream-subscription-cdk/appsync-lambda-bedrock-async-stream-subscription-cdk.json @@ -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: cdk destroy --all."] + }, + "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": "" + } + } +}