From a0924a66a05eed9ab253ac88664ed40b5955b85d Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Mon, 4 Dec 2023 20:51:07 -0800 Subject: [PATCH 1/6] feat(stepfunctions-tasks): support for the Step Functions optimized integration for Bedrock InvokeModel API --- ...efaultTestDeployAssert9C0D2DFC.assets.json | 19 + ...aultTestDeployAssert9C0D2DFC.template.json | 36 ++ ...sks-bedrock-invoke-model-integ.assets.json | 19 + ...s-bedrock-invoke-model-integ.template.json | 140 +++++++ .../integ.invoke-model.js.snapshot/cdk.out | 1 + .../integ.invoke-model.js.snapshot/integ.json | 12 + .../manifest.json | 125 ++++++ .../integ.invoke-model.js.snapshot/tree.json | 275 +++++++++++++ .../test/bedrock/integ.invoke-model.ts | 66 ++++ packages/aws-cdk-lib/aws-bedrock/.jsiirc.json | 13 + packages/aws-cdk-lib/aws-bedrock/README.md | 32 ++ packages/aws-cdk-lib/aws-bedrock/index.ts | 1 + .../aws-bedrock/lib/foundation-model.ts | 112 ++++++ packages/aws-cdk-lib/aws-bedrock/lib/index.ts | 3 + .../aws-cdk-lib/aws-bedrock/lib/model-base.ts | 13 + .../aws-bedrock/lib/provisioned-model.ts | 30 ++ .../aws-bedrock/test/model.test.ts | 30 ++ .../aws-stepfunctions-tasks/README.md | 38 ++ .../lib/bedrock/invoke-model.ts | 218 +++++++++++ .../aws-stepfunctions-tasks/lib/index.ts | 1 + .../test/bedrock/invoke-model.test.ts | 363 ++++++++++++++++++ packages/aws-cdk-lib/index.ts | 1 + packages/aws-cdk-lib/package.json | 1 + 23 files changed, 1549 insertions(+) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/InvokeModelDefaultTestDeployAssert9C0D2DFC.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/InvokeModelDefaultTestDeployAssert9C0D2DFC.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/aws-stepfunctions-tasks-bedrock-invoke-model-integ.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/aws-stepfunctions-tasks-bedrock-invoke-model-integ.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.ts create mode 100644 packages/aws-cdk-lib/aws-bedrock/.jsiirc.json create mode 100644 packages/aws-cdk-lib/aws-bedrock/README.md create mode 100644 packages/aws-cdk-lib/aws-bedrock/index.ts create mode 100644 packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts create mode 100644 packages/aws-cdk-lib/aws-bedrock/lib/index.ts create mode 100644 packages/aws-cdk-lib/aws-bedrock/lib/model-base.ts create mode 100644 packages/aws-cdk-lib/aws-bedrock/lib/provisioned-model.ts create mode 100644 packages/aws-cdk-lib/aws-bedrock/test/model.test.ts create mode 100644 packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/bedrock/invoke-model.ts create mode 100644 packages/aws-cdk-lib/aws-stepfunctions-tasks/test/bedrock/invoke-model.test.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/InvokeModelDefaultTestDeployAssert9C0D2DFC.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/InvokeModelDefaultTestDeployAssert9C0D2DFC.assets.json new file mode 100644 index 0000000000000..ec22c34fcf613 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/InvokeModelDefaultTestDeployAssert9C0D2DFC.assets.json @@ -0,0 +1,19 @@ +{ + "version": "35.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "InvokeModelDefaultTestDeployAssert9C0D2DFC.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/InvokeModelDefaultTestDeployAssert9C0D2DFC.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/InvokeModelDefaultTestDeployAssert9C0D2DFC.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/InvokeModelDefaultTestDeployAssert9C0D2DFC.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/aws-stepfunctions-tasks-bedrock-invoke-model-integ.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/aws-stepfunctions-tasks-bedrock-invoke-model-integ.assets.json new file mode 100644 index 0000000000000..2d7004e16caf5 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/aws-stepfunctions-tasks-bedrock-invoke-model-integ.assets.json @@ -0,0 +1,19 @@ +{ + "version": "35.0.0", + "files": { + "19db222d8d51351d1127c4b099aa6545a4c1ddd9425a2e0f78c328f39ff74edf": { + "source": { + "path": "aws-stepfunctions-tasks-bedrock-invoke-model-integ.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "19db222d8d51351d1127c4b099aa6545a4c1ddd9425a2e0f78c328f39ff74edf.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/aws-stepfunctions-tasks-bedrock-invoke-model-integ.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/aws-stepfunctions-tasks-bedrock-invoke-model-integ.template.json new file mode 100644 index 0000000000000..ce41a5e96cc20 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/aws-stepfunctions-tasks-bedrock-invoke-model-integ.template.json @@ -0,0 +1,140 @@ +{ + "Resources": { + "StateMachineRoleB840431D": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "states.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "StateMachineRoleDefaultPolicyDF1E6607": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "bedrock:InvokeModel", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":bedrock:", + { + "Ref": "AWS::Region" + }, + "::foundation-model/amazon.titan-text-express-v1" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "StateMachineRoleDefaultPolicyDF1E6607", + "Roles": [ + { + "Ref": "StateMachineRoleB840431D" + } + ] + } + }, + "StateMachine2E01A3A5": { + "Type": "AWS::StepFunctions::StateMachine", + "Properties": { + "DefinitionString": { + "Fn::Join": [ + "", + [ + "{\"StartAt\":\"Prompt1\",\"States\":{\"Prompt1\":{\"Next\":\"Prompt2\",\"Type\":\"Task\",\"ResultPath\":\"$\",\"ResultSelector\":{\"names.$\":\"$.Body.results[0].outputText\"},\"Resource\":\"arn:", + { + "Ref": "AWS::Partition" + }, + ":states:::bedrock:invokeModel\",\"Parameters\":{\"ModelId\":\"arn:", + { + "Ref": "AWS::Partition" + }, + ":bedrock:", + { + "Ref": "AWS::Region" + }, + "::foundation-model/amazon.titan-text-express-v1\",\"Body\":{\"inputText\":\"Generate a list of five first names.\",\"textGenerationConfig\":{\"maxTokenCount\":100,\"temperature\":1}}}},\"Prompt2\":{\"End\":true,\"Type\":\"Task\",\"ResultPath\":\"$\",\"ResultSelector\":{\"names.$\":\"$.Body.results[0].outputText\"},\"Resource\":\"arn:", + { + "Ref": "AWS::Partition" + }, + ":states:::bedrock:invokeModel\",\"Parameters\":{\"ModelId\":\"arn:", + { + "Ref": "AWS::Partition" + }, + ":bedrock:", + { + "Ref": "AWS::Region" + }, + "::foundation-model/amazon.titan-text-express-v1\",\"Body\":{\"inputText.$\":\"States.Format('Alphabetize this list of first names:\\n{}', $.names)\",\"textGenerationConfig\":{\"maxTokenCount\":100,\"temperature\":1}}}}},\"TimeoutSeconds\":30}" + ] + ] + }, + "RoleArn": { + "Fn::GetAtt": [ + "StateMachineRoleB840431D", + "Arn" + ] + } + }, + "DependsOn": [ + "StateMachineRoleDefaultPolicyDF1E6607", + "StateMachineRoleB840431D" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/cdk.out new file mode 100644 index 0000000000000..c5cb2e5de6344 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"35.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/integ.json new file mode 100644 index 0000000000000..5eb622d1d7b82 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "35.0.0", + "testCases": { + "InvokeModel/DefaultTest": { + "stacks": [ + "aws-stepfunctions-tasks-bedrock-invoke-model-integ" + ], + "assertionStack": "InvokeModel/DefaultTest/DeployAssert", + "assertionStackName": "InvokeModelDefaultTestDeployAssert9C0D2DFC" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/manifest.json new file mode 100644 index 0000000000000..60de1b8de8ab7 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/manifest.json @@ -0,0 +1,125 @@ +{ + "version": "35.0.0", + "artifacts": { + "aws-stepfunctions-tasks-bedrock-invoke-model-integ.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-stepfunctions-tasks-bedrock-invoke-model-integ.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-stepfunctions-tasks-bedrock-invoke-model-integ": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-stepfunctions-tasks-bedrock-invoke-model-integ.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/19db222d8d51351d1127c4b099aa6545a4c1ddd9425a2e0f78c328f39ff74edf.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-stepfunctions-tasks-bedrock-invoke-model-integ.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-stepfunctions-tasks-bedrock-invoke-model-integ.assets" + ], + "metadata": { + "/aws-stepfunctions-tasks-bedrock-invoke-model-integ/StateMachine/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "StateMachineRoleB840431D" + } + ], + "/aws-stepfunctions-tasks-bedrock-invoke-model-integ/StateMachine/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "StateMachineRoleDefaultPolicyDF1E6607" + } + ], + "/aws-stepfunctions-tasks-bedrock-invoke-model-integ/StateMachine/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "StateMachine2E01A3A5" + } + ], + "/aws-stepfunctions-tasks-bedrock-invoke-model-integ/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-stepfunctions-tasks-bedrock-invoke-model-integ/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-stepfunctions-tasks-bedrock-invoke-model-integ" + }, + "InvokeModelDefaultTestDeployAssert9C0D2DFC.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "InvokeModelDefaultTestDeployAssert9C0D2DFC.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "InvokeModelDefaultTestDeployAssert9C0D2DFC": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "InvokeModelDefaultTestDeployAssert9C0D2DFC.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "InvokeModelDefaultTestDeployAssert9C0D2DFC.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "InvokeModelDefaultTestDeployAssert9C0D2DFC.assets" + ], + "metadata": { + "/InvokeModel/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/InvokeModel/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "InvokeModel/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/tree.json new file mode 100644 index 0000000000000..b3a2882dbd6fb --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.js.snapshot/tree.json @@ -0,0 +1,275 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-stepfunctions-tasks-bedrock-invoke-model-integ": { + "id": "aws-stepfunctions-tasks-bedrock-invoke-model-integ", + "path": "aws-stepfunctions-tasks-bedrock-invoke-model-integ", + "children": { + "Prompt1": { + "id": "Prompt1", + "path": "aws-stepfunctions-tasks-bedrock-invoke-model-integ/Prompt1", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_stepfunctions_tasks.BedrockInvokeModel", + "version": "0.0.0" + } + }, + "Prompt2": { + "id": "Prompt2", + "path": "aws-stepfunctions-tasks-bedrock-invoke-model-integ/Prompt2", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_stepfunctions_tasks.BedrockInvokeModel", + "version": "0.0.0" + } + }, + "StateMachine": { + "id": "StateMachine", + "path": "aws-stepfunctions-tasks-bedrock-invoke-model-integ/StateMachine", + "children": { + "Role": { + "id": "Role", + "path": "aws-stepfunctions-tasks-bedrock-invoke-model-integ/StateMachine/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "aws-stepfunctions-tasks-bedrock-invoke-model-integ/StateMachine/Role/ImportRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-stepfunctions-tasks-bedrock-invoke-model-integ/StateMachine/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "states.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-stepfunctions-tasks-bedrock-invoke-model-integ/StateMachine/Role/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-stepfunctions-tasks-bedrock-invoke-model-integ/StateMachine/Role/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "bedrock:InvokeModel", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":bedrock:", + { + "Ref": "AWS::Region" + }, + "::foundation-model/amazon.titan-text-express-v1" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "StateMachineRoleDefaultPolicyDF1E6607", + "roles": [ + { + "Ref": "StateMachineRoleB840431D" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-stepfunctions-tasks-bedrock-invoke-model-integ/StateMachine/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::StepFunctions::StateMachine", + "aws:cdk:cloudformation:props": { + "definitionString": { + "Fn::Join": [ + "", + [ + "{\"StartAt\":\"Prompt1\",\"States\":{\"Prompt1\":{\"Next\":\"Prompt2\",\"Type\":\"Task\",\"ResultPath\":\"$\",\"ResultSelector\":{\"names.$\":\"$.Body.results[0].outputText\"},\"Resource\":\"arn:", + { + "Ref": "AWS::Partition" + }, + ":states:::bedrock:invokeModel\",\"Parameters\":{\"ModelId\":\"arn:", + { + "Ref": "AWS::Partition" + }, + ":bedrock:", + { + "Ref": "AWS::Region" + }, + "::foundation-model/amazon.titan-text-express-v1\",\"Body\":{\"inputText\":\"Generate a list of five first names.\",\"textGenerationConfig\":{\"maxTokenCount\":100,\"temperature\":1}}}},\"Prompt2\":{\"End\":true,\"Type\":\"Task\",\"ResultPath\":\"$\",\"ResultSelector\":{\"names.$\":\"$.Body.results[0].outputText\"},\"Resource\":\"arn:", + { + "Ref": "AWS::Partition" + }, + ":states:::bedrock:invokeModel\",\"Parameters\":{\"ModelId\":\"arn:", + { + "Ref": "AWS::Partition" + }, + ":bedrock:", + { + "Ref": "AWS::Region" + }, + "::foundation-model/amazon.titan-text-express-v1\",\"Body\":{\"inputText.$\":\"States.Format('Alphabetize this list of first names:\\n{}', $.names)\",\"textGenerationConfig\":{\"maxTokenCount\":100,\"temperature\":1}}}}},\"TimeoutSeconds\":30}" + ] + ] + }, + "roleArn": { + "Fn::GetAtt": [ + "StateMachineRoleB840431D", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_stepfunctions.CfnStateMachine", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_stepfunctions.StateMachine", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-stepfunctions-tasks-bedrock-invoke-model-integ/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-stepfunctions-tasks-bedrock-invoke-model-integ/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "InvokeModel": { + "id": "InvokeModel", + "path": "InvokeModel", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "InvokeModel/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "InvokeModel/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "InvokeModel/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "InvokeModel/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "InvokeModel/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.ts new file mode 100644 index 0000000000000..95860f09e624e --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions-tasks/test/bedrock/integ.invoke-model.ts @@ -0,0 +1,66 @@ +import * as bedrock from 'aws-cdk-lib/aws-bedrock'; +import * as sfn from 'aws-cdk-lib/aws-stepfunctions'; +import * as cdk from 'aws-cdk-lib'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import { BedrockInvokeModel } from 'aws-cdk-lib/aws-stepfunctions-tasks'; + +/* + * Stack verification steps: + * * aws stepfunctions start-execution --state-machine-arn : should return execution arn + * * aws stepfunctions describe-execution --execution-arn : should return status as SUCCEEDED + * This integ test does not actually verify a Step Functions execution, as not all AWS accounts have Bedrock model access. + */ +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-stepfunctions-tasks-bedrock-invoke-model-integ'); + +const model = bedrock.FoundationModel.fromFoundationModelId(stack, 'Model', bedrock.FoundationModelIdentifier.AMAZON_TITAN_TEXT_G1_EXPRESS_V1); + +const prompt1 = new BedrockInvokeModel(stack, 'Prompt1', { + model, + body: sfn.TaskInput.fromObject( + { + inputText: 'Generate a list of five first names.', + textGenerationConfig: { + maxTokenCount: 100, + temperature: 1, + }, + }, + ), + resultSelector: { + names: sfn.JsonPath.stringAt('$.Body.results[0].outputText'), + }, + resultPath: '$', +}); + +const prompt2 = new BedrockInvokeModel(stack, 'Prompt2', { + model, + body: sfn.TaskInput.fromObject( + { + inputText: sfn.JsonPath.format( + 'Alphabetize this list of first names:\n{}', + sfn.JsonPath.stringAt('$.names'), + ), + textGenerationConfig: { + maxTokenCount: 100, + temperature: 1, + }, + }, + ), + resultSelector: { + names: sfn.JsonPath.stringAt('$.Body.results[0].outputText'), + }, + resultPath: '$', +}); + +const chain = sfn.Chain.start(prompt1).next(prompt2); + +new sfn.StateMachine(stack, 'StateMachine', { + definitionBody: sfn.DefinitionBody.fromChainable(chain), + timeout: cdk.Duration.seconds(30), +}); + +new IntegTest(app, 'InvokeModel', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/aws-cdk-lib/aws-bedrock/.jsiirc.json b/packages/aws-cdk-lib/aws-bedrock/.jsiirc.json new file mode 100644 index 0000000000000..5d9d8fdd12efe --- /dev/null +++ b/packages/aws-cdk-lib/aws-bedrock/.jsiirc.json @@ -0,0 +1,13 @@ +{ + "targets": { + "java": { + "package": "software.amazon.awscdk.services.bedrock" + }, + "dotnet": { + "namespace": "Amazon.CDK.AWS.Bedrock" + }, + "python": { + "module": "aws_cdk.aws_bedrock" + } + } +} diff --git a/packages/aws-cdk-lib/aws-bedrock/README.md b/packages/aws-cdk-lib/aws-bedrock/README.md new file mode 100644 index 0000000000000..cc353def5f1eb --- /dev/null +++ b/packages/aws-cdk-lib/aws-bedrock/README.md @@ -0,0 +1,32 @@ +# Amazon Bedrock Construct Library + +Amazon Bedrock is a fully managed service that offers a choice of foundation models (FMs) +along with a broad set of capabilities for building generative AI applications. + +CloudFormation does not currently support any Bedrock resource types. +This construct library is a collection of constructs that can look up existing Bedrock models +for use with other services' CDK constructs, such as AWS Step Functions. + +To look up a Bedrock base foundation model: + +```ts +import * as bedrock from 'aws-cdk-lib/aws-bedrock'; + +bedrock.FoundationModel.fromFoundationModelId( + this, + 'Model', + bedrock.FoundationModelIdentifier.ANTHROPIC_CLAUDE_V2, +); +``` + +To look up a Bedrock provisioned throughput model: + +```ts +import * as bedrock from 'aws-cdk-lib/aws-bedrock'; + +bedrock.ProvisionedModel.fromProvisionedModelArn( + this, + 'Model', + 'arn:aws:bedrock:us-east-2:123456789012:provisioned-model/abc-123', +); +``` diff --git a/packages/aws-cdk-lib/aws-bedrock/index.ts b/packages/aws-cdk-lib/aws-bedrock/index.ts new file mode 100644 index 0000000000000..f41a696fd204d --- /dev/null +++ b/packages/aws-cdk-lib/aws-bedrock/index.ts @@ -0,0 +1 @@ +export * from './lib'; diff --git a/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts b/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts new file mode 100644 index 0000000000000..37932c64e41ba --- /dev/null +++ b/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts @@ -0,0 +1,112 @@ +import { Construct } from 'constructs'; +import { IModel } from './model-base'; +import { ArnFormat, Stack } from '../../core'; + +/** + * The model identifiers for the Bedrock base foundation models. + * + * @see https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids-arns.html + */ +export enum FoundationModelIdentifier { + /** Base model "ai21.j2-mid-v1". */ + AI21_LABS_JURASSIC_2_MID_V1 = 'ai21.j2-mid-v1', + + /** Base model "ai21.j2-ultra-v1". */ + AI21_LABS_JURASSIC_2_ULTRA_V1 = 'ai21.j2-ultra-v1', + + /** Base model "amazon.titan-embed-text-v1". */ + AMAZON_TITAN_EMBEDDINGS_G1_TEXT_V1 = 'amazon.titan-embed-text-v1', + + /** Base model "amazon.titan-text-express-v1". */ + AMAZON_TITAN_TEXT_G1_EXPRESS_V1 = 'amazon.titan-text-express-v1', + + /** Base model "amazon.titan-embed-image-v1". */ + AMAZON_TITAN_MULTIMODAL_EMBEDDINGS_G1_V1 = 'amazon.titan-embed-image-v1', + + /** Base model "amazon.titan-image-generator-v1". */ + AMAZON_TITAN_IMAGE_GENERATOR_G1_V1 = 'amazon.titan-image-generator-v1', + + /** Base model "anthropic.claude-v1". */ + ANTHROPIC_CLAUDE_V1 = 'anthropic.claude-v1', + + /** Base model "anthropic.claude-v2". */ + ANTHROPIC_CLAUDE_V2 = 'anthropic.claude-v2', + + /** Base model "anthropic.claude-v2:1". */ + ANTHROPIC_CLAUDE_V2_1 = 'anthropic.claude-v2:1', + + /** Base model "anthropic.claude-instant-v1". */ + ANTHROPIC_CLAUDE_INSTANT_V1 = 'anthropic.claude-instant-v1', + + /** Base model "cohere.command-text-v14". */ + COHERE_COMMAND_V14 = 'cohere.command-text-v14', + + /** Base model "cohere.command-light-text-v14". */ + COHERE_COMMAND_LIGHT_V14 = 'cohere.command-light-text-v14', + + /** Base model "cohere.embed-english-v3". */ + COHERE_EMBED_ENGLISH_V3 = 'cohere.embed-english-v3', + + /** Base model "cohere.embed-multilingual-v3". */ + COHERE_EMBED_MULTILINGUAL_V3 = 'cohere.embed-multilingual-v3', + + /** Base model "meta.llama2-13b-chat-v1". */ + META_LLAMA_2_CHAT_13B_V1 = 'meta.llama2-13b-chat-v1', + + /** Base model "meta.llama2-70b-chat-v1". */ + META_LLAMA_2_CHAT_70B_V1 = 'meta.llama2-70b-chat-v1', +} + +/** + * Construction properties of `FoundationModel`. + * Module-private, as the constructor of `FoundationModel` is private. + */ +interface FoundationModelProps { + readonly modelId: string; + readonly modelArn: string; +} + +/** + * A Bedrock base foundation model. + * + * @see https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html + */ +export class FoundationModel implements IModel { + /** + * Construct a Bedrock base foundation model given the model identifier. + * + * @see https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids-arns.html + * + * @param id The model identifier + * @example 'amazon.titan-text-express-v1' + * @returns A Bedrock base foundation model. + */ + public static fromFoundationModelId(scope: Construct, _id: string, foundationModelId: FoundationModelIdentifier): FoundationModel { + return new FoundationModel({ + modelId: foundationModelId, + modelArn: Stack.of(scope).formatArn({ + service: 'bedrock', + account: '', + resource: 'foundation-model', + resourceName: foundationModelId, + arnFormat: ArnFormat.SLASH_RESOURCE_NAME, + }), + }); + } + + /** + * The foundation model ID. + * @example 'amazon.titan-text-express-v1' + */ + public readonly modelId: string; + + /** + * The foundation model ARN. + */ + public readonly modelArn: string; + + private constructor(props: FoundationModelProps) { + this.modelId = props.modelId; + this.modelArn = props.modelArn; + } +} diff --git a/packages/aws-cdk-lib/aws-bedrock/lib/index.ts b/packages/aws-cdk-lib/aws-bedrock/lib/index.ts new file mode 100644 index 0000000000000..4249e058d34c5 --- /dev/null +++ b/packages/aws-cdk-lib/aws-bedrock/lib/index.ts @@ -0,0 +1,3 @@ +export * from './foundation-model'; +export * from './model-base'; +export * from './provisioned-model'; diff --git a/packages/aws-cdk-lib/aws-bedrock/lib/model-base.ts b/packages/aws-cdk-lib/aws-bedrock/lib/model-base.ts new file mode 100644 index 0000000000000..97ef32bb7f08e --- /dev/null +++ b/packages/aws-cdk-lib/aws-bedrock/lib/model-base.ts @@ -0,0 +1,13 @@ +/** + * Represents a Bedrock model. + * The model could be a foundation model, a custom model, or a provisioned model. + */ +export interface IModel { + /** + * The ARN of the model + * + * @see https://docs.aws.amazon.com/bedrock/latest/userguide/api-methods-run.html + * @see https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonbedrock.html#amazonbedrock-actions-as-permissions + */ + readonly modelArn: string; +} diff --git a/packages/aws-cdk-lib/aws-bedrock/lib/provisioned-model.ts b/packages/aws-cdk-lib/aws-bedrock/lib/provisioned-model.ts new file mode 100644 index 0000000000000..4c1a72e2a0252 --- /dev/null +++ b/packages/aws-cdk-lib/aws-bedrock/lib/provisioned-model.ts @@ -0,0 +1,30 @@ +import { Construct } from 'constructs'; +import { IModel } from './model-base'; + +/** + * A Bedrock provisioned model + * + * Note: CloudFormation does not currently support creating Bedrock Provisioned Throughput + * resources outside of a custom resource. You can import provisioned models created by + * provisioning throughput in Bedrock outside the CDK or via a custom resource with + * {@link ProvisionedModel#fromProvisionedModelArn}. + * + * @see https://docs.aws.amazon.com/bedrock/latest/userguide/prov-throughput.html + */ +export class ProvisionedModel implements IModel { + /** + * Import an provisioned model given an ARN + */ + public static fromProvisionedModelArn(_scope: Construct, _id: string, provisionedModelArn: string): IModel { + return new ProvisionedModel(provisionedModelArn); + } + + /** + * The ARN of the provisioned model. + */ + public readonly modelArn: string; + + private constructor(provisionedModelArn: string) { + this.modelArn = provisionedModelArn; + } +} diff --git a/packages/aws-cdk-lib/aws-bedrock/test/model.test.ts b/packages/aws-cdk-lib/aws-bedrock/test/model.test.ts new file mode 100644 index 0000000000000..a5cc2eb362227 --- /dev/null +++ b/packages/aws-cdk-lib/aws-bedrock/test/model.test.ts @@ -0,0 +1,30 @@ +import * as cdk from '../../core'; +import * as bedrock from '../lib'; + +/* eslint-disable quote-props */ + +describe('ProvisionedModel', () => { + test('fromProvisionedModelArn', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const model = bedrock.ProvisionedModel.fromProvisionedModelArn(stack, 'Model', 'arn:aws:bedrock:us-turbo-2:123456789012:provisioned-model/abc-123'); + + // THEN + expect(model.modelArn).toEqual('arn:aws:bedrock:us-turbo-2:123456789012:provisioned-model/abc-123'); + }); +}); + +describe('FoundationModel', () => { + test('fromFoundationModelId', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const model = bedrock.FoundationModel.fromFoundationModelId(stack, 'Model', bedrock.FoundationModelIdentifier.ANTHROPIC_CLAUDE_V2); + + // THEN + expect(stack.resolve(model.modelArn)).toEqual({ 'Fn::Join': ['', ['arn:', { 'Ref': 'AWS::Partition' }, ':bedrock:', { 'Ref': 'AWS::Region' }, '::foundation-model/anthropic.claude-v2']] }); + }); +}); diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/README.md b/packages/aws-cdk-lib/aws-stepfunctions-tasks/README.md index bf726ce12a802..8f32286484f39 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/README.md +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/README.md @@ -31,6 +31,8 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aw - [StopQueryExecution](#stopqueryexecution) - [Batch](#batch) - [SubmitJob](#submitjob) + - [Bedrock](#bedrock) + - [InvokeModel](#invokemodel) - [CodeBuild](#codebuild) - [StartBuild](#startbuild) - [DynamoDB](#dynamodb) @@ -311,6 +313,42 @@ const task = new tasks.BatchSubmitJob(this, 'Submit Job', { }); ``` +## Bedrock + +Step Functions supports [Bedrock](https://docs.aws.amazon.com/step-functions/latest/dg/connect-bedrock.html) through the service integration pattern. + +### InvokeModel + +The [InvokeModel](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_InvokeModel.html) API +invokes the specified Bedrock model to run inference using the input provided. +The format of the input body and the response body depend on the model selected. + +```ts +import * as bedrock from 'aws-cdk-lib/aws-bedrock'; + +const model = bedrock.FoundationModel.fromFoundationModelId( + this, + 'Model', + bedrock.FoundationModelIdentifier.AMAZON_TITAN_TEXT_G1_EXPRESS_V1, +); + +const task = new tasks.BedrockInvokeModel(this, 'Prompt Model', { + model, + body: sfn.TaskInput.fromObject( + { + inputText: 'Generate a list of five first names.', + textGenerationConfig: { + maxTokenCount: 100, + temperature: 1, + }, + }, + ), + resultSelector: { + names: sfn.JsonPath.stringAt('$.Body.results[0].outputText'), + }, +}); +``` + ## CodeBuild Step Functions supports [CodeBuild](https://docs.aws.amazon.com/step-functions/latest/dg/connect-codebuild.html) through the service integration pattern. diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/bedrock/invoke-model.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/bedrock/invoke-model.ts new file mode 100644 index 0000000000000..d844ecfc0a509 --- /dev/null +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/bedrock/invoke-model.ts @@ -0,0 +1,218 @@ +import { Construct } from 'constructs'; +import * as bedrock from '../../../aws-bedrock'; +import * as iam from '../../../aws-iam'; +import * as s3 from '../../../aws-s3'; +import * as sfn from '../../../aws-stepfunctions'; +import { Stack } from '../../../core'; +import { integrationResourceArn, validatePatternSupported } from '../private/task-utils'; + +/** + * Location to retrieve the input data, prior to calling Bedrock InvokeModel. + * + * @see https://docs.aws.amazon.com/step-functions/latest/dg/connect-bedrock.html + */ +export interface BedrockInvokeModelInputProps { + + /** + * S3 object to retrieve the input data from. + * + * If the S3 location is not set, then the Body must be set. + * + * @default Input data is retrieved from the `body` field + */ + readonly s3Location?: s3.Location; +} + +/** + * Location where the Bedrock InvokeModel API response is written. + * + * @see https://docs.aws.amazon.com/step-functions/latest/dg/connect-bedrock.html + */ +export interface BedrockInvokeModelOutputProps { + + /** + * S3 object where the Bedrock InvokeModel API response is written. + * + * If you specify this field, the API response body is replaced with + * a reference to the Amazon S3 location of the original output. + * + * @default Response body is returned in the task result + */ + readonly s3Location?: s3.Location; +} + +/** + * Properties for invoking a Bedrock Model + */ +export interface BedrockInvokeModelProps extends sfn.TaskStateBaseProps { + + /** + * The Bedrock model that the task will invoke. + * + * @see https://docs.aws.amazon.com/bedrock/latest/userguide/api-methods-run.html + */ + readonly model: bedrock.IModel; + + /** + * The input data for the Bedrock model invocation. + * + * The inference parameters contained in the body depend on the Bedrock model being used. + * + * The body must be in the format specified in the `contentType` field. + * For example, if the content type is `application/json`, the body must be + * JSON formatted. + * + * The body must be up to 256 KB in size. For input data that exceeds 256 KB, + * use `input` instead to retrieve the input data from S3. + * + * You must specify either the `body` or the `input` field, but not both. + * + * @see https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters.html + * + * @default Input data is retrieved from the location specified in the `input` field + */ + readonly body?: sfn.TaskInput; + + /** + * The desired MIME type of the inference body in the response. + * + * @see https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_InvokeModel.html + * @default 'application/json' + */ + readonly accept?: string; + + /** + * The MIME type of the input data in the request. + * + * @see https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_InvokeModel.html + * @default 'application/json' + */ + readonly contentType?: string; + + /** + * The source location to retrieve the input data from. + * + * @default Input data is retrieved from the `body` field + */ + readonly input?: BedrockInvokeModelInputProps; + + /** + * The destination location where the API response is written. + * + * If you specify this field, the API response body is replaced with a reference to the + * output location. + * + * @default The API response body is returned in the result. + */ + readonly output?: BedrockInvokeModelOutputProps; +} + +/** + * A Step Functions Task to invoke a model in Bedrock. + * + */ +export class BedrockInvokeModel extends sfn.TaskStateBase { + + private static readonly SUPPORTED_INTEGRATION_PATTERNS: sfn.IntegrationPattern[] = [ + sfn.IntegrationPattern.REQUEST_RESPONSE, + ]; + + protected readonly taskMetrics: sfn.TaskMetricsConfig | undefined; + protected readonly taskPolicies: iam.PolicyStatement[] | undefined; + + private readonly integrationPattern: sfn.IntegrationPattern; + + constructor(scope: Construct, id: string, private readonly props: BedrockInvokeModelProps) { + super(scope, id, props); + this.integrationPattern = props.integrationPattern ?? sfn.IntegrationPattern.REQUEST_RESPONSE; + + validatePatternSupported(this.integrationPattern, BedrockInvokeModel.SUPPORTED_INTEGRATION_PATTERNS); + + const isBodySpecified = props.body !== undefined; + const isInputSpecified = props.input !== undefined && props.input.s3Location !== undefined; + + if (isBodySpecified && isInputSpecified) { + throw new Error('Either `body` or `input` must be specified, but not both.'); + } + if (!isBodySpecified && !isInputSpecified) { + throw new Error('Either `body` or `input` must be specified.'); + } + if (props.input?.s3Location?.objectVersion !== undefined) { + throw new Error('Input S3 object version is not supported.'); + } + if (props.output?.s3Location?.objectVersion !== undefined) { + throw new Error('Output S3 object version is not supported.'); + } + + this.taskPolicies = this.renderPolicyStatements(); + } + + private renderPolicyStatements(): iam.PolicyStatement[] { + const policyStatements = [ + new iam.PolicyStatement({ + actions: ['bedrock:InvokeModel'], + resources: [this.props.model.modelArn], + }), + ]; + + if (this.props.input !== undefined && this.props.input.s3Location !== undefined) { + policyStatements.push( + new iam.PolicyStatement({ + actions: ['s3:GetObject'], + resources: [ + Stack.of(this).formatArn({ + region: '', + account: '', + service: 's3', + resource: this.props.input?.s3Location?.bucketName, + resourceName: this.props.input?.s3Location?.objectKey, + }), + ], + }), + ); + } + + if (this.props.output !== undefined && this.props.output.s3Location !== undefined) { + policyStatements.push( + new iam.PolicyStatement({ + actions: ['s3:PutObject'], + resources: [ + Stack.of(this).formatArn({ + region: '', + account: '', + service: 's3', + resource: this.props.output?.s3Location?.bucketName, + resourceName: this.props.output?.s3Location?.objectKey, + }), + ], + }), + ); + } + + return policyStatements; + } + + /** + * Provides the Bedrock InvokeModel service integration task configuration + */ + /** + * @internal + */ + protected _renderTask(): any { + return { + Resource: integrationResourceArn('bedrock', 'invokeModel'), + Parameters: sfn.FieldUtils.renderObject({ + ModelId: this.props.model.modelArn, + Accept: this.props.accept, + ContentType: this.props.contentType, + Body: this.props.body?.value, + Input: this.props.input?.s3Location ? { + S3Uri: `s3://${this.props.input.s3Location.bucketName}/${this.props.input.s3Location.objectKey}`, + } : undefined, + Output: this.props.output?.s3Location ? { + S3Uri: `s3://${this.props.output.s3Location.bucketName}/${this.props.output.s3Location.objectKey}`, + } : undefined, + }), + }; + } +} diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/index.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/index.ts index 04fd1846f1d08..575464d22dcf0 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/index.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/index.ts @@ -51,3 +51,4 @@ export * from './eks/call'; export * from './apigateway'; export * from './eventbridge/put-events'; export * from './aws-sdk/call-aws-service'; +export * from './bedrock/invoke-model'; diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/test/bedrock/invoke-model.test.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/test/bedrock/invoke-model.test.ts new file mode 100644 index 0000000000000..32c636cf679cf --- /dev/null +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/test/bedrock/invoke-model.test.ts @@ -0,0 +1,363 @@ +import { Template, Match } from '../../../assertions'; +import * as bedrock from '../../../aws-bedrock'; +import * as sfn from '../../../aws-stepfunctions'; +import * as cdk from '../../../core'; +import { BedrockInvokeModel } from '../../lib/bedrock/invoke-model'; + +describe('Invoke Model', () => { + + test('default settings', () => { + // GIVEN + const stack = new cdk.Stack(undefined, 'Stack1', { env: { account: '12345678', region: 'us-turbo-1' } }); + + // WHEN + const task = new BedrockInvokeModel(stack, 'Invoke', { + model: bedrock.FoundationModel.fromFoundationModelId(stack, 'Model', bedrock.FoundationModelIdentifier.ANTHROPIC_CLAUDE_INSTANT_V1), + body: sfn.TaskInput.fromObject( + { + prompt: 'Hello world', + }, + ), + }); + + // THEN + expect(stack.resolve(task.toStateJson())).toEqual({ + Type: 'Task', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':states:::bedrock:invokeModel', + ], + ], + }, + End: true, + Parameters: { + ModelId: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':bedrock:us-turbo-1::foundation-model/anthropic.claude-instant-v1', + ], + ], + }, + Body: { + prompt: 'Hello world', + }, + }, + }); + }); + + test('InvokeModel permissions are created in generated policy', () => { + // GIVEN + const stack = new cdk.Stack(undefined, 'Stack1', { env: { account: '12345678', region: 'us-turbo-1' } }); + + // WHEN + const task = new BedrockInvokeModel(stack, 'Invoke', { + model: bedrock.FoundationModel.fromFoundationModelId(stack, 'Model', bedrock.FoundationModelIdentifier.ANTHROPIC_CLAUDE_INSTANT_V1), + body: sfn.TaskInput.fromObject( + { + prompt: 'Hello world', + }, + ), + }); + + new sfn.StateMachine(stack, 'StateMachine', { + definitionBody: sfn.DefinitionBody.fromChainable(task), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: Match.objectLike({ + Statement: Match.arrayWith([ + { + Action: 'bedrock:InvokeModel', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':bedrock:us-turbo-1::foundation-model/anthropic.claude-instant-v1', + ], + ], + }, + }, + ]), + }), + }); + }); + + test('MIME type configurations', () => { + // GIVEN + const stack = new cdk.Stack(undefined, 'Stack1', { env: { account: '12345678', region: 'us-turbo-1' } }); + + // WHEN + const task = new BedrockInvokeModel(stack, 'Invoke', { + model: bedrock.FoundationModel.fromFoundationModelId(stack, 'Model', bedrock.FoundationModelIdentifier.ANTHROPIC_CLAUDE_INSTANT_V1), + body: sfn.TaskInput.fromObject( + { + prompt: 'Hello world', + }, + ), + accept: 'image/png', + contentType: 'text/plain', + }); + + // THEN + expect(stack.resolve(task.toStateJson())).toEqual({ + Type: 'Task', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':states:::bedrock:invokeModel', + ], + ], + }, + End: true, + Parameters: { + ModelId: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':bedrock:us-turbo-1::foundation-model/anthropic.claude-instant-v1', + ], + ], + }, + Body: { + prompt: 'Hello world', + }, + Accept: 'image/png', + ContentType: 'text/plain', + }, + }); + }); + + test('input and output configurations', () => { + // GIVEN + const stack = new cdk.Stack(undefined, 'Stack1', { env: { account: '12345678', region: 'us-turbo-1' } }); + const model = bedrock.ProvisionedModel.fromProvisionedModelArn(stack, 'Imported', 'arn:aws:bedrock:us-turbo-2:123456789012:provisioned-model/abc-123'); + + // WHEN + const task = new BedrockInvokeModel(stack, 'Invoke', { + model, + input: { + s3Location: { + bucketName: 'input-bucket', + objectKey: 'input-key', + }, + }, + output: { + s3Location: { + bucketName: 'output-bucket', + objectKey: 'output-key', + }, + }, + }); + + // THEN + expect(stack.resolve(task.toStateJson())).toEqual({ + Type: 'Task', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':states:::bedrock:invokeModel', + ], + ], + }, + End: true, + Parameters: { + ModelId: 'arn:aws:bedrock:us-turbo-2:123456789012:provisioned-model/abc-123', + Input: { + S3Uri: 's3://input-bucket/input-key', + }, + Output: { + S3Uri: 's3://output-bucket/output-key', + }, + }, + }); + }); + + test('S3 permissions are created in generated policy when input and output locations are specified', () => { + // GIVEN + const stack = new cdk.Stack(); + const model = bedrock.ProvisionedModel.fromProvisionedModelArn(stack, 'Imported', 'arn:aws:bedrock:us-turbo-2:123456789012:provisioned-model/abc-123'); + + // WHEN + const task = new BedrockInvokeModel(stack, 'Invoke', { + model, + input: { + s3Location: { + bucketName: 'input-bucket', + objectKey: 'input-key', + }, + }, + output: { + s3Location: { + bucketName: 'output-bucket', + objectKey: 'output-key', + }, + }, + }); + + new sfn.StateMachine(stack, 'StateMachine', { + definitionBody: sfn.DefinitionBody.fromChainable(task), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: Match.objectLike({ + Statement: Match.arrayWith([ + { + Action: 'bedrock:InvokeModel', + Effect: 'Allow', + Resource: 'arn:aws:bedrock:us-turbo-2:123456789012:provisioned-model/abc-123', + }, + { + Action: 's3:GetObject', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':s3:::input-bucket/input-key', + ], + ], + }, + }, + { + Action: 's3:PutObject', + Effect: 'Allow', + Resource: { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':s3:::output-bucket/output-key', + ], + ], + }, + }, + ]), + }), + }); + }); + + test('fails on neither input nor body set', () => { + // GIVEN + const stack = new cdk.Stack(); + const model = bedrock.ProvisionedModel.fromProvisionedModelArn(stack, 'Imported', 'arn:aws:bedrock:us-turbo-2:123456789012:provisioned-model/abc-123'); + + expect(() => { + // WHEN + new BedrockInvokeModel(stack, 'Invoke', { + model, + }); + // THEN + }).toThrow(/Either `body` or `input` must be specified./); + }); + + test('fails on both input and body set', () => { + // GIVEN + const stack = new cdk.Stack(); + const model = bedrock.ProvisionedModel.fromProvisionedModelArn(stack, 'Imported', 'arn:aws:bedrock:us-turbo-2:123456789012:provisioned-model/abc-123'); + + expect(() => { + // WHEN + new BedrockInvokeModel(stack, 'Invoke', { + model, + body: sfn.TaskInput.fromObject( + { + prompt: 'Hello world', + }, + ), + input: { + s3Location: { + bucketName: 'hello', + objectKey: 'world', + }, + }, + }); + // THEN + }).toThrow(/Either `body` or `input` must be specified, but not both./); + }); + + test('fails on S3 object version in input', () => { + // GIVEN + const stack = new cdk.Stack(); + const model = bedrock.ProvisionedModel.fromProvisionedModelArn(stack, 'Imported', 'arn:aws:bedrock:us-turbo-2:123456789012:provisioned-model/abc-123'); + + expect(() => { + // WHEN + new BedrockInvokeModel(stack, 'Invoke', { + model, + input: { + s3Location: { + bucketName: 'hello', + objectKey: 'world', + objectVersion: '123', + }, + }, + }); + // THEN + }).toThrow(/Input S3 object version is not supported./); + }); + + test('fails on S3 object version in output', () => { + // GIVEN + const stack = new cdk.Stack(); + const model = bedrock.ProvisionedModel.fromProvisionedModelArn(stack, 'Imported', 'arn:aws:bedrock:us-turbo-2:123456789012:provisioned-model/abc-123'); + + expect(() => { + // WHEN + new BedrockInvokeModel(stack, 'Invoke', { + model, + body: sfn.TaskInput.fromObject( + { + prompt: 'Hello world', + }, + ), + output: { + s3Location: { + bucketName: 'hello', + objectKey: 'world', + objectVersion: '123', + }, + }, + }); + // THEN + }).toThrow(/Output S3 object version is not supported./); + }); +}); diff --git a/packages/aws-cdk-lib/index.ts b/packages/aws-cdk-lib/index.ts index 2a77270f03d99..739ab80ae4644 100644 --- a/packages/aws-cdk-lib/index.ts +++ b/packages/aws-cdk-lib/index.ts @@ -30,6 +30,7 @@ export * as aws_autoscalingplans from './aws-autoscalingplans'; export * as aws_backup from './aws-backup'; export * as aws_backupgateway from './aws-backupgateway'; export * as aws_batch from './aws-batch'; +export * as aws_bedrock from './aws-bedrock'; export * as aws_billingconductor from './aws-billingconductor'; export * as aws_budgets from './aws-budgets'; export * as aws_cassandra from './aws-cassandra'; diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index e701dbc7a05a6..57226dd89aff2 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -241,6 +241,7 @@ "./aws-backup": "./aws-backup/index.js", "./aws-backupgateway": "./aws-backupgateway/index.js", "./aws-batch": "./aws-batch/index.js", + "./aws-bedrock": "./aws-bedrock/index.js", "./aws-billingconductor": "./aws-billingconductor/index.js", "./aws-budgets": "./aws-budgets/index.js", "./aws-cassandra": "./aws-cassandra/index.js", From d1e1abde8c6db8a702414a826e65e170681b0ba5 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Thu, 7 Dec 2023 13:30:26 -0800 Subject: [PATCH 2/6] Fix comments Co-authored-by: Kaizen Conroy <36202692+kaizencc@users.noreply.github.com> --- .../aws-stepfunctions-tasks/lib/bedrock/invoke-model.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/bedrock/invoke-model.ts b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/bedrock/invoke-model.ts index d844ecfc0a509..f123ca00ac271 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/bedrock/invoke-model.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/bedrock/invoke-model.ts @@ -194,8 +194,7 @@ export class BedrockInvokeModel extends sfn.TaskStateBase { /** * Provides the Bedrock InvokeModel service integration task configuration - */ - /** + * * @internal */ protected _renderTask(): any { From ded8a4721098e407cfb8c9e8a158803dccc92517 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Thu, 7 Dec 2023 13:34:05 -0800 Subject: [PATCH 3/6] Fix fromFoundationModelId docstrings --- packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts b/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts index 37932c64e41ba..bb10cd0714b6c 100644 --- a/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts +++ b/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts @@ -77,8 +77,9 @@ export class FoundationModel implements IModel { * * @see https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids-arns.html * - * @param id The model identifier - * @example 'amazon.titan-text-express-v1' + * @param scope The parent construct + * @param _id The name of the model construct + * @param foundationModelId The model identifier such as 'amazon.titan-text-express-v1' * @returns A Bedrock base foundation model. */ public static fromFoundationModelId(scope: Construct, _id: string, foundationModelId: FoundationModelIdentifier): FoundationModel { From 3804b041374872813efabe5806f096f95b092ad9 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Fri, 8 Dec 2023 08:06:12 -0800 Subject: [PATCH 4/6] Move from enum to enum-like class for foundation model ID --- .../aws-bedrock/lib/foundation-model.ts | 40 ++++++++++--------- .../aws-bedrock/test/model.test.ts | 11 +++++ 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts b/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts index bb10cd0714b6c..261ff0b03ee79 100644 --- a/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts +++ b/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts @@ -7,54 +7,56 @@ import { ArnFormat, Stack } from '../../core'; * * @see https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids-arns.html */ -export enum FoundationModelIdentifier { +export class FoundationModelIdentifier { /** Base model "ai21.j2-mid-v1". */ - AI21_LABS_JURASSIC_2_MID_V1 = 'ai21.j2-mid-v1', + public static AI21_LABS_JURASSIC_2_MID_V1 = new FoundationModelIdentifier('ai21.j2-mid-v1'); /** Base model "ai21.j2-ultra-v1". */ - AI21_LABS_JURASSIC_2_ULTRA_V1 = 'ai21.j2-ultra-v1', + public static AI21_LABS_JURASSIC_2_ULTRA_V1 = new FoundationModelIdentifier('ai21.j2-ultra-v1'); /** Base model "amazon.titan-embed-text-v1". */ - AMAZON_TITAN_EMBEDDINGS_G1_TEXT_V1 = 'amazon.titan-embed-text-v1', + public static AMAZON_TITAN_EMBEDDINGS_G1_TEXT_V1 = new FoundationModelIdentifier('amazon.titan-embed-text-v1'); /** Base model "amazon.titan-text-express-v1". */ - AMAZON_TITAN_TEXT_G1_EXPRESS_V1 = 'amazon.titan-text-express-v1', + public static AMAZON_TITAN_TEXT_G1_EXPRESS_V1 = new FoundationModelIdentifier('amazon.titan-text-express-v1'); /** Base model "amazon.titan-embed-image-v1". */ - AMAZON_TITAN_MULTIMODAL_EMBEDDINGS_G1_V1 = 'amazon.titan-embed-image-v1', + public static AMAZON_TITAN_MULTIMODAL_EMBEDDINGS_G1_V1 = new FoundationModelIdentifier('amazon.titan-embed-image-v1'); /** Base model "amazon.titan-image-generator-v1". */ - AMAZON_TITAN_IMAGE_GENERATOR_G1_V1 = 'amazon.titan-image-generator-v1', + public static AMAZON_TITAN_IMAGE_GENERATOR_G1_V1 = new FoundationModelIdentifier('amazon.titan-image-generator-v1'); /** Base model "anthropic.claude-v1". */ - ANTHROPIC_CLAUDE_V1 = 'anthropic.claude-v1', + public static ANTHROPIC_CLAUDE_V1 = new FoundationModelIdentifier('anthropic.claude-v1'); /** Base model "anthropic.claude-v2". */ - ANTHROPIC_CLAUDE_V2 = 'anthropic.claude-v2', + public static ANTHROPIC_CLAUDE_V2 = new FoundationModelIdentifier('anthropic.claude-v2'); /** Base model "anthropic.claude-v2:1". */ - ANTHROPIC_CLAUDE_V2_1 = 'anthropic.claude-v2:1', + public static ANTHROPIC_CLAUDE_V2_1 = new FoundationModelIdentifier('anthropic.claude-v2:1'); /** Base model "anthropic.claude-instant-v1". */ - ANTHROPIC_CLAUDE_INSTANT_V1 = 'anthropic.claude-instant-v1', + public static ANTHROPIC_CLAUDE_INSTANT_V1 = new FoundationModelIdentifier('anthropic.claude-instant-v1'); /** Base model "cohere.command-text-v14". */ - COHERE_COMMAND_V14 = 'cohere.command-text-v14', + public static COHERE_COMMAND_V14 = new FoundationModelIdentifier('cohere.command-text-v14'); /** Base model "cohere.command-light-text-v14". */ - COHERE_COMMAND_LIGHT_V14 = 'cohere.command-light-text-v14', + public static COHERE_COMMAND_LIGHT_V14 = new FoundationModelIdentifier('cohere.command-light-text-v14'); /** Base model "cohere.embed-english-v3". */ - COHERE_EMBED_ENGLISH_V3 = 'cohere.embed-english-v3', + public static COHERE_EMBED_ENGLISH_V3 = new FoundationModelIdentifier('cohere.embed-english-v3'); /** Base model "cohere.embed-multilingual-v3". */ - COHERE_EMBED_MULTILINGUAL_V3 = 'cohere.embed-multilingual-v3', + public static COHERE_EMBED_MULTILINGUAL_V3 = new FoundationModelIdentifier('cohere.embed-multilingual-v3'); /** Base model "meta.llama2-13b-chat-v1". */ - META_LLAMA_2_CHAT_13B_V1 = 'meta.llama2-13b-chat-v1', + public static META_LLAMA_2_CHAT_13B_V1 = new FoundationModelIdentifier('meta.llama2-13b-chat-v1'); /** Base model "meta.llama2-70b-chat-v1". */ - META_LLAMA_2_CHAT_70B_V1 = 'meta.llama2-70b-chat-v1', + public static META_LLAMA_2_CHAT_70B_V1 = new FoundationModelIdentifier('meta.llama2-70b-chat-v1'); + + public constructor(public readonly modelId: string) { } } /** @@ -84,12 +86,12 @@ export class FoundationModel implements IModel { */ public static fromFoundationModelId(scope: Construct, _id: string, foundationModelId: FoundationModelIdentifier): FoundationModel { return new FoundationModel({ - modelId: foundationModelId, + modelId: foundationModelId.modelId, modelArn: Stack.of(scope).formatArn({ service: 'bedrock', account: '', resource: 'foundation-model', - resourceName: foundationModelId, + resourceName: foundationModelId.modelId, arnFormat: ArnFormat.SLASH_RESOURCE_NAME, }), }); diff --git a/packages/aws-cdk-lib/aws-bedrock/test/model.test.ts b/packages/aws-cdk-lib/aws-bedrock/test/model.test.ts index a5cc2eb362227..f5887cd4203cb 100644 --- a/packages/aws-cdk-lib/aws-bedrock/test/model.test.ts +++ b/packages/aws-cdk-lib/aws-bedrock/test/model.test.ts @@ -27,4 +27,15 @@ describe('FoundationModel', () => { // THEN expect(stack.resolve(model.modelArn)).toEqual({ 'Fn::Join': ['', ['arn:', { 'Ref': 'AWS::Partition' }, ':bedrock:', { 'Ref': 'AWS::Region' }, '::foundation-model/anthropic.claude-v2']] }); }); + + test('fromFoundationModelId with newer model ID', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const model = bedrock.FoundationModel.fromFoundationModelId(stack, 'Model', new bedrock.FoundationModelIdentifier('new-base-model')); + + // THEN + expect(stack.resolve(model.modelArn)).toEqual({ 'Fn::Join': ['', ['arn:', { 'Ref': 'AWS::Partition' }, ':bedrock:', { 'Ref': 'AWS::Region' }, '::foundation-model/new-base-model']] }); + }); }); From 74c7757ebb1d8092c979e3aa1815121c0615adff Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Fri, 8 Dec 2023 08:24:42 -0800 Subject: [PATCH 5/6] Add missing readonly --- .../aws-bedrock/lib/foundation-model.ts | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts b/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts index 261ff0b03ee79..1ce1a6c24e40a 100644 --- a/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts +++ b/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts @@ -9,52 +9,52 @@ import { ArnFormat, Stack } from '../../core'; */ export class FoundationModelIdentifier { /** Base model "ai21.j2-mid-v1". */ - public static AI21_LABS_JURASSIC_2_MID_V1 = new FoundationModelIdentifier('ai21.j2-mid-v1'); + public static readonly AI21_LABS_JURASSIC_2_MID_V1 = new FoundationModelIdentifier('ai21.j2-mid-v1'); /** Base model "ai21.j2-ultra-v1". */ - public static AI21_LABS_JURASSIC_2_ULTRA_V1 = new FoundationModelIdentifier('ai21.j2-ultra-v1'); + public static readonly AI21_LABS_JURASSIC_2_ULTRA_V1 = new FoundationModelIdentifier('ai21.j2-ultra-v1'); /** Base model "amazon.titan-embed-text-v1". */ - public static AMAZON_TITAN_EMBEDDINGS_G1_TEXT_V1 = new FoundationModelIdentifier('amazon.titan-embed-text-v1'); + public static readonly AMAZON_TITAN_EMBEDDINGS_G1_TEXT_V1 = new FoundationModelIdentifier('amazon.titan-embed-text-v1'); /** Base model "amazon.titan-text-express-v1". */ - public static AMAZON_TITAN_TEXT_G1_EXPRESS_V1 = new FoundationModelIdentifier('amazon.titan-text-express-v1'); + public static readonly AMAZON_TITAN_TEXT_G1_EXPRESS_V1 = new FoundationModelIdentifier('amazon.titan-text-express-v1'); /** Base model "amazon.titan-embed-image-v1". */ - public static AMAZON_TITAN_MULTIMODAL_EMBEDDINGS_G1_V1 = new FoundationModelIdentifier('amazon.titan-embed-image-v1'); + public static readonly AMAZON_TITAN_MULTIMODAL_EMBEDDINGS_G1_V1 = new FoundationModelIdentifier('amazon.titan-embed-image-v1'); /** Base model "amazon.titan-image-generator-v1". */ - public static AMAZON_TITAN_IMAGE_GENERATOR_G1_V1 = new FoundationModelIdentifier('amazon.titan-image-generator-v1'); + public static readonly AMAZON_TITAN_IMAGE_GENERATOR_G1_V1 = new FoundationModelIdentifier('amazon.titan-image-generator-v1'); /** Base model "anthropic.claude-v1". */ - public static ANTHROPIC_CLAUDE_V1 = new FoundationModelIdentifier('anthropic.claude-v1'); + public static readonly ANTHROPIC_CLAUDE_V1 = new FoundationModelIdentifier('anthropic.claude-v1'); /** Base model "anthropic.claude-v2". */ - public static ANTHROPIC_CLAUDE_V2 = new FoundationModelIdentifier('anthropic.claude-v2'); + public static readonly ANTHROPIC_CLAUDE_V2 = new FoundationModelIdentifier('anthropic.claude-v2'); /** Base model "anthropic.claude-v2:1". */ - public static ANTHROPIC_CLAUDE_V2_1 = new FoundationModelIdentifier('anthropic.claude-v2:1'); + public static readonly ANTHROPIC_CLAUDE_V2_1 = new FoundationModelIdentifier('anthropic.claude-v2:1'); /** Base model "anthropic.claude-instant-v1". */ - public static ANTHROPIC_CLAUDE_INSTANT_V1 = new FoundationModelIdentifier('anthropic.claude-instant-v1'); + public static readonly ANTHROPIC_CLAUDE_INSTANT_V1 = new FoundationModelIdentifier('anthropic.claude-instant-v1'); /** Base model "cohere.command-text-v14". */ - public static COHERE_COMMAND_V14 = new FoundationModelIdentifier('cohere.command-text-v14'); + public static readonly COHERE_COMMAND_V14 = new FoundationModelIdentifier('cohere.command-text-v14'); /** Base model "cohere.command-light-text-v14". */ - public static COHERE_COMMAND_LIGHT_V14 = new FoundationModelIdentifier('cohere.command-light-text-v14'); + public static readonly COHERE_COMMAND_LIGHT_V14 = new FoundationModelIdentifier('cohere.command-light-text-v14'); /** Base model "cohere.embed-english-v3". */ - public static COHERE_EMBED_ENGLISH_V3 = new FoundationModelIdentifier('cohere.embed-english-v3'); + public static readonly COHERE_EMBED_ENGLISH_V3 = new FoundationModelIdentifier('cohere.embed-english-v3'); /** Base model "cohere.embed-multilingual-v3". */ - public static COHERE_EMBED_MULTILINGUAL_V3 = new FoundationModelIdentifier('cohere.embed-multilingual-v3'); + public static readonly COHERE_EMBED_MULTILINGUAL_V3 = new FoundationModelIdentifier('cohere.embed-multilingual-v3'); /** Base model "meta.llama2-13b-chat-v1". */ - public static META_LLAMA_2_CHAT_13B_V1 = new FoundationModelIdentifier('meta.llama2-13b-chat-v1'); + public static readonly META_LLAMA_2_CHAT_13B_V1 = new FoundationModelIdentifier('meta.llama2-13b-chat-v1'); /** Base model "meta.llama2-70b-chat-v1". */ - public static META_LLAMA_2_CHAT_70B_V1 = new FoundationModelIdentifier('meta.llama2-70b-chat-v1'); + public static readonly META_LLAMA_2_CHAT_70B_V1 = new FoundationModelIdentifier('meta.llama2-70b-chat-v1'); public constructor(public readonly modelId: string) { } } From 8f0de43d97e976ac9c8e3afaea03fd860e88762f Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Fri, 8 Dec 2023 08:46:03 -0800 Subject: [PATCH 6/6] Add missing docstring --- packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts b/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts index 1ce1a6c24e40a..b4e0a31d9c7fc 100644 --- a/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts +++ b/packages/aws-cdk-lib/aws-bedrock/lib/foundation-model.ts @@ -56,6 +56,10 @@ export class FoundationModelIdentifier { /** Base model "meta.llama2-70b-chat-v1". */ public static readonly META_LLAMA_2_CHAT_70B_V1 = new FoundationModelIdentifier('meta.llama2-70b-chat-v1'); + /** + * Constructor for foundation model identifier + * @param modelId the model identifier + */ public constructor(public readonly modelId: string) { } }