diff --git a/src/cdk-lib/bedrock/agent.ts b/src/cdk-lib/bedrock/agent.ts index aa095222..c24881ff 100644 --- a/src/cdk-lib/bedrock/agent.ts +++ b/src/cdk-lib/bedrock/agent.ts @@ -11,29 +11,28 @@ * and limitations under the License. */ -import * as cdk from 'aws-cdk-lib'; -import { aws_bedrock as bedrock } from 'aws-cdk-lib'; -import * as iam from 'aws-cdk-lib/aws-iam'; -import * as kms from 'aws-cdk-lib/aws-kms'; -import { Construct } from 'constructs'; +import * as cdk from "aws-cdk-lib"; +import { aws_bedrock as bedrock } from "aws-cdk-lib"; +import * as iam from "aws-cdk-lib/aws-iam"; +import * as kms from "aws-cdk-lib/aws-kms"; +import { Construct } from "constructs"; -import { AgentActionGroup } from './agent-action-group'; -import { AgentAlias } from './agent-alias'; -import { Guardrail } from './guardrails'; -import { KnowledgeBase } from './knowledge-base'; -import { BedrockFoundationModel } from './models'; - -import { generatePhysicalNameV2 } from '../../common/helpers/utils'; +import { AgentActionGroup } from "./agent-action-group"; +import { AgentAlias } from "./agent-alias"; +import { Guardrail } from "./guardrails"; +import { KnowledgeBase } from "./knowledge-base"; +import { BedrockInvokableModelType, IInvokableModel } from "./models"; +import { generatePhysicalNameV2 } from "../../common/helpers/utils"; /** * The step in the agent sequence that this prompt configuration applies to. */ export enum PromptType { - PRE_PROCESSING = 'PRE_PROCESSING', - ORCHESTRATION = 'ORCHESTRATION', - POST_PROCESSING = 'POST_PROCESSING', - KNOWLEDGE_BASE_RESPONSE_GENERATION = 'KNOWLEDGE_BASE_RESPONSE_GENERATION' + PRE_PROCESSING = "PRE_PROCESSING", + ORCHESTRATION = "ORCHESTRATION", + POST_PROCESSING = "POST_PROCESSING", + KNOWLEDGE_BASE_RESPONSE_GENERATION = "KNOWLEDGE_BASE_RESPONSE_GENERATION", } /** @@ -44,8 +43,8 @@ export enum PromptType { * with the ARN of a Lambda function. */ export enum ParserMode { - DEFAULT = 'DEFAULT', - OVERRIDDEN = 'OVERRIDDEN' + DEFAULT = "DEFAULT", + OVERRIDDEN = "OVERRIDDEN", } /** @@ -55,8 +54,8 @@ export enum ParserMode { * uses a default prompt template. */ export enum PromptCreationMode { - DEFAULT = 'DEFAULT', - OVERRIDDEN = 'OVERRIDDEN' + DEFAULT = "DEFAULT", + OVERRIDDEN = "OVERRIDDEN", } /** @@ -70,8 +69,8 @@ export enum PromptCreationMode { * POST_PROCESSING – DISABLED */ export enum PromptState { - ENABLED = 'ENABLED', - DISABLED = 'DISABLED' + ENABLED = "ENABLED", + DISABLED = "DISABLED", } /** Details about the guardrail associated with the agent. */ @@ -214,7 +213,7 @@ export interface AgentProps { /** * The Bedrock text foundation model for the agent to use. */ - readonly foundationModel: BedrockFoundationModel; + readonly model: IInvokableModel; /** * The name of the agent. * @@ -258,7 +257,7 @@ export interface AgentProps { * the guardrails. If you want the permissions to be configured on your behalf, * use the addGuardrail method. * @default - No guardrails associated to the agent. - */ + */ readonly guardrailConfiguration?: GuardrailConfiguration; /** * Select whether the agent can prompt additional @@ -388,65 +387,53 @@ export class Agent extends Construct { */ public knowledgeBases: bedrock.CfnAgent.AgentKnowledgeBaseProperty[] = []; - constructor(scope: Construct, id: string, props: AgentProps) { super(scope, id); validatePromptOverrideConfiguration(props.promptOverrideConfiguration); - validateModel(props.foundationModel); + validateModel(props.model); - this.name = props.name ?? generatePhysicalNameV2( - this, - 'bedrock-agent', - { maxLength: 32, lower: true, separator: '-' }); + this.name = + props.name ?? + generatePhysicalNameV2(this, "bedrock-agent", { + maxLength: 32, + lower: true, + separator: "-", + }); if (props.existingRole) { this.role = props.existingRole; } else { - this.role = new iam.Role(this, 'Role', { - assumedBy: new iam.ServicePrincipal('bedrock.amazonaws.com'), - roleName: generatePhysicalNameV2( - this, - 'AmazonBedrockExecutionRoleForAgents_', - { maxLength: 64, lower: false }), - }); - - this.role.assumeRolePolicy!.addStatements( - new iam.PolicyStatement({ - actions: ['sts:AssumeRole'], - principals: [new iam.ServicePrincipal('bedrock.amazonaws.com')], - conditions: { - StringEquals: { - 'aws:SourceAccount': cdk.Stack.of(this).account, - }, - ArnLike: { - 'aws:SourceArn': cdk.Stack.of(this).formatArn({ - service: 'bedrock', - resource: 'agent', - resourceName: '*', - arnFormat: cdk.ArnFormat.SLASH_RESOURCE_NAME, - }), - }, + this.role = new iam.Role(this, "Role", { + assumedBy: new iam.ServicePrincipal("bedrock.amazonaws.com").withConditions({ + StringEquals: { + "aws:SourceAccount": cdk.Stack.of(this).account, + }, + ArnLike: { + "aws:SourceArn": cdk.Stack.of(this).formatArn({ + service: "bedrock", + resource: "agent", + resourceName: "*", + arnFormat: cdk.ArnFormat.SLASH_RESOURCE_NAME, + }), }, }), - ); + roleName: generatePhysicalNameV2(this, "AmazonBedrockExecutionRoleForAgents_", { + maxLength: 64, + lower: false, + }), + }); - new iam.Policy(this, 'AgentFMPolicy', { + new iam.Policy(this, "AgentInvokeModelPolicy", { roles: [this.role], - statements: [ - new iam.PolicyStatement({ - actions: ['bedrock:InvokeModel'], - resources: [props.foundationModel.asArn(this)], - }), - ], + statements: this.generateInvokeModelPolicyStatements(props.model), }); } - const agent = new bedrock.CfnAgent(this, 'Agent', { - + const agent = new bedrock.CfnAgent(this, "Agent", { agentName: this.name, - foundationModel: String(props.foundationModel), + foundationModel: props.model.modelId, instruction: props.instruction, description: props.description, idleSessionTtlInSeconds: props.idleSessionTTL?.toSeconds(), @@ -489,14 +476,15 @@ export class Agent extends Construct { } // To allow your agent to request the user for additional information // when trying to complete a task, add this action group - this.addActionGroup(new AgentActionGroup(this, 'userInputEnabledActionGroup', { - actionGroupName: 'UserInputAction', - parentActionGroupSignature: 'AMAZON.UserInput', - actionGroupState: props.enableUserInput ? 'ENABLED' : 'DISABLED', - })); + this.addActionGroup( + new AgentActionGroup(this, "userInputEnabledActionGroup", { + actionGroupName: "UserInputAction", + parentActionGroupSignature: "AMAZON.UserInput", + actionGroupState: props.enableUserInput ? "ENABLED" : "DISABLED", + }) + ); } - /** * Add an alias to the agent. */ @@ -525,16 +513,13 @@ export class Agent extends Construct { */ public addKnowledgeBase(knowledgeBase: KnowledgeBase) { if (!knowledgeBase.instruction) { - throw new Error('Agent Knowledge Bases require instructions.'); + throw new Error("Agent Knowledge Bases require instructions."); } new iam.Policy(this, `AgentKBPolicy-${knowledgeBase.name}`, { roles: [this.role], statements: [ new iam.PolicyStatement({ - actions: [ - 'bedrock:UpdateKnowledgeBase', - 'bedrock:Retrieve', - ], + actions: ["bedrock:UpdateKnowledgeBase", "bedrock:Retrieve"], resources: [knowledgeBase.knowledgeBaseArn], }), ], @@ -545,25 +530,33 @@ export class Agent extends Construct { knowledgeBaseState: knowledgeBase.knowledgeBaseState, }; - if (!this.agentInstance.knowledgeBases || !Array.isArray(this.agentInstance.knowledgeBases)) { + if ( + !this.agentInstance.knowledgeBases || + !Array.isArray(this.agentInstance.knowledgeBases) + ) { this.agentInstance.knowledgeBases = [agentKnowledgeBaseProperty]; } else { (this.agentInstance.knowledgeBases as any).push(agentKnowledgeBaseProperty); } } - /** * Add action group to the agent. */ public addActionGroup(actionGroup: AgentActionGroup) { - actionGroup.actionGroupExecutor?.lambda?.addPermission('AgentLambdaInvocationPolicy', { - principal: new iam.ServicePrincipal('bedrock.amazonaws.com'), - sourceArn: this.agentArn, - sourceAccount: cdk.Stack.of(this).account, - }); - - if (!this.agentInstance.actionGroups || !Array.isArray(this.agentInstance.actionGroups)) { + actionGroup.actionGroupExecutor?.lambda?.addPermission( + "AgentLambdaInvocationPolicy", + { + principal: new iam.ServicePrincipal("bedrock.amazonaws.com"), + sourceArn: this.agentArn, + sourceAccount: cdk.Stack.of(this).account, + } + ); + + if ( + !this.agentInstance.actionGroups || + !Array.isArray(this.agentInstance.actionGroups) + ) { this.agentInstance.actionGroups = [actionGroup.actionGroupProperty]; } else { (this.agentInstance.actionGroups as any).push(actionGroup.actionGroupProperty); @@ -587,19 +580,17 @@ export class Agent extends Construct { roles: [this.role], statements: [ new iam.PolicyStatement({ - actions: [ - 'bedrock:ApplyGuardrail', + actions: ["bedrock:ApplyGuardrail"], + resources: [ + `arn:${cdk.Aws.PARTITION}:bedrock:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:guardrail/${guardrail.guardrailId}`, ], - resources: [`arn:${cdk.Aws.PARTITION}:bedrock:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:guardrail/${guardrail.guardrailId}`], }), new iam.PolicyStatement({ - actions: [ - 'kms:Decrypt', - ], + actions: ["kms:Decrypt"], resources: [guardrail.kmsKeyArn], conditions: { StringEquals: { - 'aws:ResourceAccount': cdk.Aws.ACCOUNT_ID, + "aws:ResourceAccount": cdk.Aws.ACCOUNT_ID, }, }, }), @@ -624,6 +615,37 @@ export class Agent extends Construct { this.resourceUpdates.push(updatedAt); } } + + /** + * Generates the policy statements needed to invoke the model + */ + public generateInvokeModelPolicyStatements( + model: IInvokableModel + ): iam.PolicyStatement[] { + let statements: iam.PolicyStatement[] = []; + + // Ability to get Inference Profile + if (model.modelType == BedrockInvokableModelType.INFERENCE_PROFILE) { + statements.push( + new iam.PolicyStatement({ + actions: ["bedrock:GetInferenceProfile"], + resources: [model.asArn(this)], + }), + new iam.PolicyStatement({ + actions: ["bedrock:ListInferenceProfiles"], + resources: ["*"], + }) + ); + } + // Ability to invoke model + statements.push( + new iam.PolicyStatement({ + actions: ["bedrock:InvokeModel"], + resources: [model.asArn(this)], + }) + ); + return statements; + } } /** @@ -631,9 +653,9 @@ export class Agent extends Construct { * * @internal This is an internal core function and should not be called directly. */ -function validateModel(foundationModel: BedrockFoundationModel) { - if (!foundationModel.supportsAgents) { - throw new Error(`The model ${foundationModel} is not supported by Bedrock Agents.`); +function validateModel(model: IInvokableModel) { + if (!model.supportsAgents) { + throw new Error(`The model ${model} is not supported by Bedrock Agents.`); } } @@ -642,33 +664,38 @@ function validateModel(foundationModel: BedrockFoundationModel) { * * @internal This is an internal core function and should not be called directly. */ -export function validateInferenceConfiguration(inferenceConfiguration: InferenceConfiguration) { +export function validateInferenceConfiguration( + inferenceConfiguration: InferenceConfiguration +) { if (inferenceConfiguration.topK < 0 || inferenceConfiguration.topK > 500) { - throw new Error('topK must be between 0 and 500'); + throw new Error("topK must be between 0 and 500"); } if (!Number.isInteger(inferenceConfiguration.topK)) { - throw new Error('topK must be an integer'); + throw new Error("topK must be an integer"); } if (inferenceConfiguration.stopSequences.length > 4) { - throw new Error('stopSequences cannot contain more than 4 elements'); + throw new Error("stopSequences cannot contain more than 4 elements"); } - if (inferenceConfiguration.maximumLength < 0 || inferenceConfiguration.maximumLength > 4096) { - throw new Error('maximumLength must be between 0 and 4096'); + if ( + inferenceConfiguration.maximumLength < 0 || + inferenceConfiguration.maximumLength > 4096 + ) { + throw new Error("maximumLength must be between 0 and 4096"); } if (!Number.isInteger(inferenceConfiguration.maximumLength)) { - throw new Error('maximumLength must be an integer'); + throw new Error("maximumLength must be an integer"); } if (inferenceConfiguration.topP < 0 || inferenceConfiguration.topP > 1) { - throw new Error('topP must be between 0 and 1'); + throw new Error("topP must be between 0 and 1"); } if (inferenceConfiguration.temperature < 0 || inferenceConfiguration.temperature > 1) { - throw new Error('temperature must be between 0 and 1'); + throw new Error("temperature must be between 0 and 1"); } } @@ -677,7 +704,9 @@ export function validateInferenceConfiguration(inferenceConfiguration: Inference * * @internal This is an internal core function and should not be called directly. */ -export function validatePromptOverrideConfiguration(promptOverrideConfiguration: PromptOverrideConfiguration | undefined) { +export function validatePromptOverrideConfiguration( + promptOverrideConfiguration: PromptOverrideConfiguration | undefined +) { if (!promptOverrideConfiguration) { return; } @@ -685,24 +714,29 @@ export function validatePromptOverrideConfiguration(promptOverrideConfiguration: if ( promptOverrideConfiguration.overrideLambda && promptOverrideConfiguration.promptConfigurations.some( - pc => pc.parserMode !== ParserMode.OVERRIDDEN, - )) { - throw new Error('overrideLambda can only be used if all promptConfigurations have a parserMode value of OVERRIDDEN'); + (pc) => pc.parserMode !== ParserMode.OVERRIDDEN + ) + ) { + throw new Error( + "overrideLambda can only be used if all promptConfigurations have a parserMode value of OVERRIDDEN" + ); } if ( !promptOverrideConfiguration.overrideLambda && promptOverrideConfiguration.promptConfigurations.some( - pc => pc.parserMode === ParserMode.OVERRIDDEN, - )) { - throw new Error('At least one promptConfiguration has a parserMode value of OVERRIDDEN, but no overrideLambda is specified'); + (pc) => pc.parserMode === ParserMode.OVERRIDDEN + ) + ) { + throw new Error( + "At least one promptConfiguration has a parserMode value of OVERRIDDEN, but no overrideLambda is specified" + ); } // check inferenceConfiguration number types - Object.values(promptOverrideConfiguration.promptConfigurations).forEach(pc => { + Object.values(promptOverrideConfiguration.promptConfigurations).forEach((pc) => { validateInferenceConfiguration(pc.inferenceConfiguration); }); return; } - diff --git a/src/cdk-lib/bedrock/data-sources/parsing.ts b/src/cdk-lib/bedrock/data-sources/parsing.ts index 1599382e..af38e9cc 100644 --- a/src/cdk-lib/bedrock/data-sources/parsing.ts +++ b/src/cdk-lib/bedrock/data-sources/parsing.ts @@ -11,9 +11,9 @@ * and limitations under the License. */ -import { CfnDataSource, IModel } from 'aws-cdk-lib/aws-bedrock'; -import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; -import { DEFAULT_PARSING_PROMPT } from './default-parsing-prompt'; +import { CfnDataSource, IModel } from "aws-cdk-lib/aws-bedrock"; +import { PolicyStatement } from "aws-cdk-lib/aws-iam"; +import { DEFAULT_PARSING_PROMPT } from "./default-parsing-prompt"; /** * Enum representing the types of parsing strategies available for Amazon Bedrock Knowledge Bases. @@ -22,7 +22,7 @@ enum ParsingStategyType { /** * Uses a Bedrock Foundation Model for advanced parsing of non-textual information from documents. */ - FOUNDATION_MODEL = 'BEDROCK_FOUNDATION_MODEL' + FOUNDATION_MODEL = "BEDROCK_FOUNDATION_MODEL", } /** @@ -41,7 +41,6 @@ export interface FoundationModelParsingStategyProps { * @default - Uses the default instruction prompt as provided in the AWS Console. */ readonly parsingPrompt?: string; - } /** @@ -49,7 +48,6 @@ export interface FoundationModelParsingStategyProps { * @see https://docs.aws.amazon.com/bedrock/latest/userguide/kb-chunking-parsing.html#kb-advanced-parsing */ export abstract class ParsingStategy { - // ------------------------------------------------------ // FM Parsing Strategy // ------------------------------------------------------ @@ -60,7 +58,9 @@ export abstract class ParsingStategy { * - There are limits on file types (PDF) and total data that can be parsed using advanced parsing. * @see https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base-ds.html#kb-ds-supported-doc-formats-limits */ - public static foundationModel(props: FoundationModelParsingStategyProps): ParsingStategy { + public static foundationModel( + props: FoundationModelParsingStategyProps + ): ParsingStategy { class FoundationModelTransformation extends ParsingStategy { /** The CloudFormation property representation of this configuration */ public readonly configuration = { @@ -74,12 +74,14 @@ export abstract class ParsingStategy { }; public generatePolicyStatements(): PolicyStatement[] { - return [new PolicyStatement({ - actions: ['bedrock:InvokeModel'], - resources: [props.parsingModel.modelArn], - })]; + return [ + new PolicyStatement({ + actions: ["bedrock:InvokeModel"], + resources: [props.parsingModel.modelArn], + }), + ]; } - }; + } return new FoundationModelTransformation(); } @@ -90,7 +92,4 @@ export abstract class ParsingStategy { public abstract configuration: CfnDataSource.ParsingConfigurationProperty; public abstract generatePolicyStatements(): PolicyStatement[]; - - } - diff --git a/src/cdk-lib/bedrock/index.ts b/src/cdk-lib/bedrock/index.ts index 1df04c57..257a7139 100644 --- a/src/cdk-lib/bedrock/index.ts +++ b/src/cdk-lib/bedrock/index.ts @@ -11,25 +11,26 @@ * and limitations under the License. */ -export * from './models'; -export * from './knowledge-base'; -export * from './agent'; -export * from './agent-alias'; -export * from './agent-action-group'; -export * from './api-schema'; -export * from './guardrails'; -export * from './guardrail-version'; -export * from './topic-list'; -export * from './pii-list'; -export * from './content-policy'; -export * from './prompt'; -export * from './prompt-version'; -export * from './data-sources/base-data-source'; -export * from './data-sources/chunking'; -export * from './data-sources/parsing'; -export * from './data-sources/custom-transformation'; -export * from './data-sources/web-crawler-data-source'; -export * from './data-sources/sharepoint-data-source'; -export * from './data-sources/confluence-data-source'; -export * from './data-sources/salesforce-data-source'; -export * from './data-sources/s3-data-source'; \ No newline at end of file +export * from "./models"; +export * from "./knowledge-base"; +export * from "./agent"; +export * from "./agent-alias"; +export * from "./agent-action-group"; +export * from "./api-schema"; +export * from "./guardrails"; +export * from "./guardrail-version"; +export * from "./topic-list"; +export * from "./pii-list"; +export * from "./content-policy"; +export * from "./prompt"; +export * from "./prompt-version"; +export * from "./data-sources/base-data-source"; +export * from "./data-sources/chunking"; +export * from "./data-sources/parsing"; +export * from "./data-sources/custom-transformation"; +export * from "./data-sources/web-crawler-data-source"; +export * from "./data-sources/sharepoint-data-source"; +export * from "./data-sources/confluence-data-source"; +export * from "./data-sources/salesforce-data-source"; +export * from "./data-sources/s3-data-source"; +export * from "./inference-profile"; diff --git a/src/cdk-lib/bedrock/inference-profile.ts b/src/cdk-lib/bedrock/inference-profile.ts new file mode 100644 index 00000000..28b594c4 --- /dev/null +++ b/src/cdk-lib/bedrock/inference-profile.ts @@ -0,0 +1,77 @@ +import { Arn, ArnFormat, Stack } from "aws-cdk-lib"; +import { + BedrockFoundationModel, + BedrockInvokableModelType, + IInvokableModel, +} from "./models"; +import { IConstruct } from "constructs"; + +export enum InferenceProfileRegion { + /** + * EU: Frankfurt (eu-central-1), Ireland (eu-west-1), Paris (eu-west-3) + */ + EU = "eu", + /** + * US: N. Virginia (us-east-1), Oregon (us-west-2) + */ + US = "us", +} + +export interface InferenceProfileProps { + /** + * The geo region where the traffic is going to be distributed. + */ + readonly region: InferenceProfileRegion; + /** + * A model supporting cross-region inference. + * @see https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference-support.html + */ + readonly model: BedrockFoundationModel; +} + +/** + * Represents an inference profile. + * NOTE: You must have enabled the model in all the regions + */ +export class InferenceProfile implements IInvokableModel { + /** + * @example 'us.anthropic.claude-3-5-sonnet-20240620-v1:0' + */ + public readonly modelId: string; + public readonly modelType: BedrockInvokableModelType; + public readonly supportsAgents: boolean; + public readonly supportsKnowledgeBase: boolean; + public readonly region: InferenceProfileRegion; + public readonly supportsDataParsing: boolean; + + constructor(props: InferenceProfileProps) { + // Check if inference profile is supported. + if (!props.model.supportsInferenceProfile) { + throw new Error( + `Selected model ${props.model.modelId} does not support inference profiles.` + ); + } + this.modelId = `${props.region}.${props.model.modelId}`; + this.modelType = BedrockInvokableModelType.INFERENCE_PROFILE; + this.region = props.region; + this.supportsAgents = props.model.supportsAgents; + this.supportsKnowledgeBase = props.model.supportsKnowledgeBase; + this.supportsDataParsing = props.model.supportsDataParsing; + } + + /** + * Returns the ARN of the inference profile. + * @example 'arn:aws:bedrock:us-east-1:123456789012:inference-profile/us.anthropic.claude-3-5-sonnet-20240620-v1:0' + */ + asArn(scope: IConstruct): string { + return Arn.format( + { + service: "bedrock", + resource: "inference-profile", + resourceName: this.modelId, + arnFormat: ArnFormat.SLASH_RESOURCE_NAME, + }, + Stack.of(scope) + ); + } +} diff --git a/src/cdk-lib/bedrock/models.ts b/src/cdk-lib/bedrock/models.ts index 53e5d757..79b5c12a 100644 --- a/src/cdk-lib/bedrock/models.ts +++ b/src/cdk-lib/bedrock/models.ts @@ -11,10 +11,58 @@ * and limitations under the License. */ +import { Stack } from "aws-cdk-lib"; +import { IModel } from "aws-cdk-lib/aws-bedrock"; +import { IConstruct } from "constructs"; -import { Stack } from 'aws-cdk-lib'; -import { IModel } from 'aws-cdk-lib/aws-bedrock'; -import { IConstruct } from 'constructs'; +/** + * The type of Bedrock Invokable model. + */ +export enum BedrockInvokableModelType { + FOUNDATION_MODEL = "foundation-model", + INFERENCE_PROFILE = "inference-profile", + CUSTOM_MODEL = "custom-model", + PROVISIONED_MODEL = "provisioned-model", + IMPORTED_MODEL = "imported-model", +} + +/** + * Properties common to all invokable models. + */ +export interface IInvokableModel { + /** + * The Identifier for this model. + */ + readonly modelId: string; + /** + * The type of Bedrock Model. + */ + readonly modelType: BedrockInvokableModelType; + /** + * Bedrock Agents can use this model. + * + * @see https://docs.aws.amazon.com/bedrock/latest/userguide/agents-supported.html + */ + readonly supportsAgents: boolean; + /** + * Whether Bedrock Knowledge Bases can use this model. + * + * @see https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base-supported.html + */ + readonly supportsKnowledgeBase: boolean; + /** + * Whether this model can be used for advanced data parsing techniques on data sources + * for Knowledge Bases for Amazon Bedrock. + * + * @see https://docs.aws.amazon.com/bedrock/latest/userguide/kb-chunking-parsing.html#kb-advanced-parsing + */ + readonly supportsDataParsing: boolean; + + /** + * Method to retrieve the ARN + */ + asArn(scope: IConstruct): string; +} export interface BedrockFoundationModelProps { /** @@ -29,6 +77,18 @@ export interface BedrockFoundationModelProps { * @default - false */ readonly supportsKnowledgeBase?: boolean; + /** + * Whether this model supports inference profiles / cross-region inference. + * + * @see https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference-support.html + * @default - false + */ + readonly supportsInferenceProfile?: boolean; + /** + * Whether this model can be used for advanced data parsing techniques on data sources + * for Amazon Bedrock Knowledge Bases. + */ + readonly supportsDataParsing?: boolean; /** * Embedding models have different vector dimensions. * Only applicable for embedding models. @@ -42,79 +102,122 @@ export interface BedrockFoundationModelProps { * If you need to use a model name that doesn't exist as a static member, you * can instantiate a `BedrockFoundationModel` object, e.g: `new BedrockFoundationModel('my-model')`. */ -export class BedrockFoundationModel { +export class BedrockFoundationModel implements IInvokableModel { + // ------------------------------------------------------ + // Text & Multimodal Models + // ------------------------------------------------------ + // ANTHROPIC CLAUDE MODELS public static readonly ANTHROPIC_CLAUDE_V2 = new BedrockFoundationModel( - 'anthropic.claude-v2', - { supportsAgents: true }, + "anthropic.claude-v2", + { supportsAgents: true } ); public static readonly ANTHROPIC_CLAUDE_V2_1 = new BedrockFoundationModel( - 'anthropic.claude-v2:1', - { supportsAgents: true }, + "anthropic.claude-v2:1", + { supportsAgents: true } ); public static readonly ANTHROPIC_CLAUDE_INSTANT_V1_2 = new BedrockFoundationModel( - 'anthropic.claude-instant-v1', - { supportsAgents: true }, + "anthropic.claude-instant-v1", + { supportsAgents: true } ); public static readonly AMAZON_TITAN_TEXT_EXPRESS_V1 = new BedrockFoundationModel( - 'amazon.titan-text-express-v1', - { supportsAgents: true }, + "amazon.titan-text-express-v1", + { supportsAgents: true } ); public static readonly ANTHROPIC_CLAUDE_3_5_SONNET_V1_0 = new BedrockFoundationModel( - 'anthropic.claude-3-5-sonnet-20240620-v1:0', - { supportsAgents: true }, + "anthropic.claude-3-5-sonnet-20240620-v1:0", + { supportsAgents: true, supportsInferenceProfile: true } ); public static readonly ANTHROPIC_CLAUDE_OPUS_V1_0 = new BedrockFoundationModel( - 'anthropic.claude-3-opus-20240229-v1:0', - { supportsAgents: true }, + "anthropic.claude-3-opus-20240229-v1:0", + { supportsAgents: true, supportsInferenceProfile: true } ); public static readonly ANTHROPIC_CLAUDE_SONNET_V1_0 = new BedrockFoundationModel( - 'anthropic.claude-3-sonnet-20240229-v1:0', - { supportsAgents: true }, + "anthropic.claude-3-sonnet-20240229-v1:0", + { supportsAgents: true, supportsInferenceProfile: true, supportsDataParsing: true } ); public static readonly ANTHROPIC_CLAUDE_HAIKU_V1_0 = new BedrockFoundationModel( - 'anthropic.claude-3-haiku-20240307-v1:0', - { supportsAgents: true }, + "anthropic.claude-3-haiku-20240307-v1:0", + { supportsAgents: true, supportsInferenceProfile: true, supportsDataParsing: true } ); + // AMAZON TITAN MODELS public static readonly AMAZON_TITAN_PREMIER_V1_0 = new BedrockFoundationModel( - 'amazon.titan-text-premier-v1:0', - { supportsAgents: true }, + "amazon.titan-text-premier-v1:0", + { supportsAgents: true } ); + // META LLAMA + public static readonly META_LLAMA_V3_2_1B_INSTRUCT = new BedrockFoundationModel( + "meta.llama3-2-1b-instruct-v1:0", + { supportsAgents: true, supportsInferenceProfile: true } + ); + public static readonly META_LLAMA_V3_2_3B_INSTRUCT = new BedrockFoundationModel( + "meta.llama3-2-3b-instruct-v1:0", + { supportsAgents: true, supportsInferenceProfile: true } + ); + public static readonly META_LLAMA_V3_2_11B_VISION_INSTRUCT = new BedrockFoundationModel( + "meta.llama3-2-11b-instruct-v1:0", + { supportsAgents: true, supportsInferenceProfile: true } + ); + public static readonly META_LLAMA_V3_2_90B_VISION_INSTRUCT = new BedrockFoundationModel( + "meta.llama3-2-90b-instruct-v1:0", + { supportsAgents: true, supportsInferenceProfile: true } + ); + + // ------------------------------------------------------ + // Embedding Models + // ------------------------------------------------------ + // TITAN TEXT EMBEDDINGS V1 public static readonly TITAN_EMBED_TEXT_V1 = new BedrockFoundationModel( - 'amazon.titan-embed-text-v1', - { supportsKnowledgeBase: true, vectorDimensions: 1536 }, + "amazon.titan-embed-text-v1", + { supportsKnowledgeBase: true, vectorDimensions: 1536 } ); - public static readonly TITAN_EMBED_TEXT_V2_1024 = new BedrockFoundationModel( - 'amazon.titan-embed-text-v2:0', - { supportsKnowledgeBase: true, vectorDimensions: 1024 }, + // TITAN TEXT EMBEDDINGS V2 + public static readonly TITAN_EMBED_TEXT_V2_256 = new BedrockFoundationModel( + "amazon.titan-embed-text-v2:0", + { supportsKnowledgeBase: true, vectorDimensions: 256 } ); public static readonly TITAN_EMBED_TEXT_V2_512 = new BedrockFoundationModel( - 'amazon.titan-embed-text-v2:0', - { supportsKnowledgeBase: true, vectorDimensions: 512 }, + "amazon.titan-embed-text-v2:0", + { supportsKnowledgeBase: true, vectorDimensions: 512 } ); - public static readonly TITAN_EMBED_TEXT_V2_256 = new BedrockFoundationModel( - 'amazon.titan-embed-text-v2:0', - { supportsKnowledgeBase: true, vectorDimensions: 256 }, + public static readonly TITAN_EMBED_TEXT_V2_1024 = new BedrockFoundationModel( + "amazon.titan-embed-text-v2:0", + { supportsKnowledgeBase: true, vectorDimensions: 1024 } ); + // COHERE EMBED V3 public static readonly COHERE_EMBED_ENGLISH_V3 = new BedrockFoundationModel( - 'cohere.embed-english-v3', - { supportsKnowledgeBase: true, vectorDimensions: 1024 }, + "cohere.embed-english-v3", + { supportsKnowledgeBase: true, vectorDimensions: 1024 } ); public static readonly COHERE_EMBED_MULTILINGUAL_V3 = new BedrockFoundationModel( - 'cohere.embed-multilingual-v3', - { supportsKnowledgeBase: true, vectorDimensions: 1024 }, + "cohere.embed-multilingual-v3", + { supportsKnowledgeBase: true, vectorDimensions: 1024 } ); + // ------------------------------------------------------ + // Embedding Models + // ------------------------------------------------------ public readonly modelId: string; public readonly supportsAgents: boolean; public readonly vectorDimensions?: number; public readonly supportsKnowledgeBase: boolean; + public readonly supportsInferenceProfile: boolean; + public readonly supportsDataParsing: boolean; + public readonly modelType: BedrockInvokableModelType; + constructor(value: string, props: BedrockFoundationModelProps = {}) { this.modelId = value; this.supportsAgents = props.supportsAgents ?? false; this.vectorDimensions = props.vectorDimensions; this.supportsKnowledgeBase = props.supportsKnowledgeBase ?? false; + this.supportsInferenceProfile = props.supportsInferenceProfile ?? false; + this.supportsDataParsing = props.supportsDataParsing ?? false; + this.modelType = BedrockInvokableModelType.FOUNDATION_MODEL; } + // ------------------------------------------------------ + // Methods + // ------------------------------------------------------ + /** Returns the modelId of this Model */ toString(): string { return this.modelId; } @@ -128,6 +231,7 @@ export class BedrockFoundationModel { return `arn:aws:bedrock:${region}::foundation-model/${this.modelId}`; } + /** Returns this class as an IModel */ asIModel(construct: IConstruct): IModel { return { modelArn: this.asArn(construct) }; } diff --git a/test/cdk-lib/bedrock/agent.test.ts b/test/cdk-lib/bedrock/agent.test.ts index 4a970233..6adbf995 100644 --- a/test/cdk-lib/bedrock/agent.test.ts +++ b/test/cdk-lib/bedrock/agent.test.ts @@ -11,27 +11,27 @@ * and limitations under the License. */ -import * as cdk from 'aws-cdk-lib'; -import { Annotations, Match, Template } from 'aws-cdk-lib/assertions'; -import * as lambda from 'aws-cdk-lib/aws-lambda'; -import * as s3 from 'aws-cdk-lib/aws-s3'; -import { NagSuppressions } from 'cdk-nag'; -import * as bedrock from '../../../src/cdk-lib/bedrock'; +import * as cdk from "aws-cdk-lib"; +import { Annotations, Match, Template } from "aws-cdk-lib/assertions"; +import * as lambda from "aws-cdk-lib/aws-lambda"; +import * as s3 from "aws-cdk-lib/aws-s3"; +import { NagSuppressions } from "cdk-nag"; +import * as bedrock from "../../../src/cdk-lib/bedrock"; // mock lambda.Code.fromDockerBuild() -jest.mock('aws-cdk-lib/aws-lambda', () => { - const actualLambda = jest.requireActual('aws-cdk-lib/aws-lambda'); +jest.mock("aws-cdk-lib/aws-lambda", () => { + const actualLambda = jest.requireActual("aws-cdk-lib/aws-lambda"); return { ...actualLambda, Code: { ...actualLambda.Code, - fromDockerBuild: jest.fn(() => actualLambda.Code.fromInline('mockCode')), - fromAsset: jest.fn(() => actualLambda.Code.fromInline('mockCode')), + fromDockerBuild: jest.fn(() => actualLambda.Code.fromInline("mockCode")), + fromAsset: jest.fn(() => actualLambda.Code.fromInline("mockCode")), }, }; }); -describe('Agent with guardrails through addGuardrail', () => { +describe("Agent with guardrails through addGuardrail", () => { let app: cdk.App; let stack: cdk.Stack; let kb: bedrock.KnowledgeBase; @@ -40,29 +40,29 @@ describe('Agent with guardrails through addGuardrail', () => { beforeAll(() => { app = new cdk.App(); - stack = new cdk.Stack(app, 'test-stack', { + stack = new cdk.Stack(app, "test-stack", { env: { - account: '123456789012', - region: 'us-east-1', + account: "123456789012", + region: "us-east-1", }, }); - const bucket = new s3.Bucket(stack, 'DocBucket'); - kb = new bedrock.KnowledgeBase(stack, 'KB', { + const bucket = new s3.Bucket(stack, "DocBucket"); + kb = new bedrock.KnowledgeBase(stack, "KB", { embeddingsModel: bedrock.BedrockFoundationModel.TITAN_EMBED_TEXT_V1, - description: 'Documentation about CDK constructs.', - instruction: 'Documentation about CDK constructs.', + description: "Documentation about CDK constructs.", + instruction: "Documentation about CDK constructs.", }); - new bedrock.S3DataSource(stack, 'DataSource', { + new bedrock.S3DataSource(stack, "DataSource", { bucket, knowledgeBase: kb, - dataSourceName: 'test-docs', + dataSourceName: "test-docs", }); const preprocessingPrompt: bedrock.PromptConfiguration = { promptType: bedrock.PromptType.PRE_PROCESSING, promptState: bedrock.PromptState.DISABLED, promptCreationMode: bedrock.PromptCreationMode.OVERRIDDEN, - basePromptTemplate: 'This prompt is disabled.', + basePromptTemplate: "This prompt is disabled.", inferenceConfiguration: { temperature: 0, topP: 1.0, @@ -76,62 +76,64 @@ describe('Agent with guardrails through addGuardrail', () => { promptType: bedrock.PromptType.ORCHESTRATION, promptState: bedrock.PromptState.ENABLED, promptCreationMode: bedrock.PromptCreationMode.OVERRIDDEN, - basePromptTemplate: 'This prompt is enabled.', + basePromptTemplate: "This prompt is enabled.", inferenceConfiguration: { temperature: 0, topP: 1.0, topK: 250, maximumLength: 2048, - stopSequences: ['', '', ''], + stopSequences: ["", "", ""], }, }; - actionGroupFunction = new lambda.Function(stack, 'ActionGroupFunction', { - code: lambda.Code.fromAsset('test/path'), + actionGroupFunction = new lambda.Function(stack, "ActionGroupFunction", { + code: lambda.Code.fromAsset("test/path"), runtime: lambda.Runtime.NODEJS_LATEST, - handler: 'index.handler', + handler: "index.handler", }); NagSuppressions.addResourceSuppressions( actionGroupFunction, [ { - id: 'AwsSolutions-IAM4', - reason: 'ActionGroup Lambda uses the AWSLambdaBasicExecutionRole AWS Managed Policy.', + id: "AwsSolutions-IAM4", + reason: + "ActionGroup Lambda uses the AWSLambdaBasicExecutionRole AWS Managed Policy.", }, ], - true, + true ); - const apiSchemaBucket = new s3.Bucket(stack, 'TestBucket'); - const actiongroup = new bedrock.AgentActionGroup(stack, 'actionGroups', { - actionGroupName: 'test-action-group', - description: 'Use these functions to get information about the books in the Project Gutenburg library.', - actionGroupState: 'ENABLED', + const apiSchemaBucket = new s3.Bucket(stack, "TestBucket"); + const actiongroup = new bedrock.AgentActionGroup(stack, "actionGroups", { + actionGroupName: "test-action-group", + description: + "Use these functions to get information about the books in the Project Gutenburg library.", + actionGroupState: "ENABLED", actionGroupExecutor: { lambda: actionGroupFunction, }, - apiSchema: bedrock.ApiSchema.fromBucket(apiSchemaBucket, 'test/api.yaml'), + apiSchema: bedrock.ApiSchema.fromBucket(apiSchemaBucket, "test/api.yaml"), }); - agent = new bedrock.Agent(stack, 'Agent', { - foundationModel: bedrock.BedrockFoundationModel.ANTHROPIC_CLAUDE_V2_1, - instruction: 'You provide support for developers working with CDK constructs.', + agent = new bedrock.Agent(stack, "Agent", { + model: bedrock.BedrockFoundationModel.ANTHROPIC_CLAUDE_V2_1, + instruction: "You provide support for developers working with CDK constructs.", knowledgeBases: [kb], idleSessionTTL: cdk.Duration.minutes(30), promptOverrideConfiguration: { promptConfigurations: [preprocessingPrompt, orchestrationPrompt], }, - aliasName: 'prod', + aliasName: "prod", enableUserInput: true, }); agent.addActionGroups([actiongroup]); - const guardrail = new bedrock.Guardrail(stack, 'MyGuardrail', { - name: 'my-custom-guardrail', - blockedInputMessaging: 'Blocked input message', - blockedOutputsMessaging: 'Blocked output message', + const guardrail = new bedrock.Guardrail(stack, "MyGuardrail", { + name: "my-custom-guardrail", + blockedInputMessaging: "Blocked input message", + blockedOutputsMessaging: "Blocked output message", filtersConfig: [ { filtersConfigType: bedrock.FiltersConfigType.HATE, @@ -139,63 +141,55 @@ describe('Agent with guardrails through addGuardrail', () => { outputStrength: bedrock.FiltersConfigStrength.HIGH, }, ], - kmsKeyArn: 'arn:aws:kms:region:XXXXX:key/12345678-1234-1234-1234-123456789012', + kmsKeyArn: "arn:aws:kms:region:XXXXX:key/12345678-1234-1234-1234-123456789012", }); agent.addGuardrail(guardrail); - - }); - test('Knowledge Base is created', () => { - Template.fromStack(stack).hasResourceProperties('AWS::Bedrock::KnowledgeBase', { - Description: 'Documentation about CDK constructs.', - Name: Match.stringLikeRegexp('^KB'), + test("Knowledge Base is created", () => { + Template.fromStack(stack).hasResourceProperties("AWS::Bedrock::KnowledgeBase", { + Description: "Documentation about CDK constructs.", + Name: Match.stringLikeRegexp("^KB"), KnowledgeBaseConfiguration: { - Type: 'VECTOR', + Type: "VECTOR", VectorKnowledgeBaseConfiguration: { - EmbeddingModelArn: 'arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v1', + EmbeddingModelArn: + "arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v1", }, }, }); }); - test('Data Source is created', () => { - Template.fromStack(stack).hasResourceProperties('AWS::Bedrock::DataSource', { - Name: 'test-docs', + test("Data Source is created", () => { + Template.fromStack(stack).hasResourceProperties("AWS::Bedrock::DataSource", { + Name: "test-docs", KnowledgeBaseId: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('^KB'), - 'KnowledgeBaseId', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("^KB"), "KnowledgeBaseId"], }, DataSourceConfiguration: { - Type: 'S3', + Type: "S3", S3Configuration: { BucketArn: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('^DocBucket'), - 'Arn', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("^DocBucket"), "Arn"], }, }, }, - }); }); - test('Agent is created', () => { - Template.fromStack(stack).hasResourceProperties('AWS::Bedrock::Agent', { - FoundationModel: 'anthropic.claude-v2:1', - Instruction: 'You provide support for developers working with CDK constructs.', + test("Agent is created", () => { + Template.fromStack(stack).hasResourceProperties("AWS::Bedrock::Agent", { + FoundationModel: "anthropic.claude-v2:1", + Instruction: "You provide support for developers working with CDK constructs.", IdleSessionTTLInSeconds: 1800, PromptOverrideConfiguration: { PromptConfigurations: [ { - PromptType: 'PRE_PROCESSING', - PromptState: 'DISABLED', - PromptCreationMode: 'OVERRIDDEN', - BasePromptTemplate: 'This prompt is disabled.', + PromptType: "PRE_PROCESSING", + PromptState: "DISABLED", + PromptCreationMode: "OVERRIDDEN", + BasePromptTemplate: "This prompt is disabled.", InferenceConfiguration: { Temperature: 0, TopP: 1.0, @@ -205,135 +199,117 @@ describe('Agent with guardrails through addGuardrail', () => { }, }, { - PromptType: 'ORCHESTRATION', - PromptState: 'ENABLED', - PromptCreationMode: 'OVERRIDDEN', - BasePromptTemplate: 'This prompt is enabled.', + PromptType: "ORCHESTRATION", + PromptState: "ENABLED", + PromptCreationMode: "OVERRIDDEN", + BasePromptTemplate: "This prompt is enabled.", InferenceConfiguration: { Temperature: 0, TopP: 1.0, TopK: 250, MaximumLength: 2048, - StopSequences: ['', '', ''], + StopSequences: ["", "", ""], }, }, ], }, GuardrailConfiguration: { GuardrailIdentifier: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('MyGuardrail'), 'GuardrailId', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("MyGuardrail"), "GuardrailId"], }, GuardrailVersion: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('MyGuardrail'), 'Version', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("MyGuardrail"), "Version"], }, }, }); }); - test('Agent is created with one knowledge base', () => { + test("Agent is created with one knowledge base", () => { const template = Template.fromStack(stack); - template.resourceCountIs('AWS::Bedrock::KnowledgeBase', 1); - template.hasResourceProperties('AWS::Bedrock::KnowledgeBase', { + template.resourceCountIs("AWS::Bedrock::KnowledgeBase", 1); + template.hasResourceProperties("AWS::Bedrock::KnowledgeBase", { KnowledgeBaseConfiguration: { - Type: Match.stringLikeRegexp('VECTOR'), + Type: Match.stringLikeRegexp("VECTOR"), }, - Name: Match.stringLikeRegexp('KBteststack'), + Name: Match.stringLikeRegexp("KBteststack"), RoleArn: { - 'Fn::GetAtt': - [Match.stringLikeRegexp('KBRole'), 'Arn'], - + "Fn::GetAtt": [Match.stringLikeRegexp("KBRole"), "Arn"], }, - Description: 'Documentation about CDK constructs.', + Description: "Documentation about CDK constructs.", }); }); - test('Agent action group and ApiSchema from S3', () => { + test("Agent action group and ApiSchema from S3", () => { const template = Template.fromStack(stack); - template.hasResourceProperties('AWS::Bedrock::Agent', { - + template.hasResourceProperties("AWS::Bedrock::Agent", { ActionGroups: [ { - ActionGroupName: 'UserInputAction', - ActionGroupState: 'ENABLED', - ParentActionGroupSignature: 'AMAZON.UserInput', + ActionGroupName: "UserInputAction", + ActionGroupState: "ENABLED", + ParentActionGroupSignature: "AMAZON.UserInput", SkipResourceInUseCheckOnDelete: false, }, { ActionGroupExecutor: { Lambda: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('ActionGroupFunction'), 'Arn', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("ActionGroupFunction"), "Arn"], }, }, - ActionGroupName: 'test-action-group', - ActionGroupState: 'ENABLED', + ActionGroupName: "test-action-group", + ActionGroupState: "ENABLED", ApiSchema: { S3: { S3BucketName: { - Ref: Match.stringLikeRegexp('^TestBucket'), + Ref: Match.stringLikeRegexp("^TestBucket"), }, - S3ObjectKey: 'test/api.yaml', + S3ObjectKey: "test/api.yaml", }, }, }, ], }); - }); - test('Guardrail is associated', () => { + test("Guardrail is associated", () => { const template = Template.fromStack(stack); console.log(template.toJSON()); - template.hasResourceProperties('AWS::Bedrock::Guardrail', { - BlockedInputMessaging: 'Blocked input message', - BlockedOutputsMessaging: 'Blocked output message', + template.hasResourceProperties("AWS::Bedrock::Guardrail", { + BlockedInputMessaging: "Blocked input message", + BlockedOutputsMessaging: "Blocked output message", ContentPolicyConfig: { FiltersConfig: [ { - Type: 'HATE', - InputStrength: 'HIGH', - OutputStrength: 'HIGH', + Type: "HATE", + InputStrength: "HIGH", + OutputStrength: "HIGH", }, ], }, - KmsKeyArn: 'arn:aws:kms:region:XXXXX:key/12345678-1234-1234-1234-123456789012', - Name: 'my-custom-guardrail', - }, - ); - + KmsKeyArn: "arn:aws:kms:region:XXXXX:key/12345678-1234-1234-1234-123456789012", + Name: "my-custom-guardrail", + }); }); - test('Agent Alias is created', () => { - Template.fromStack(stack).hasResourceProperties('AWS::Bedrock::AgentAlias', { + test("Agent Alias is created", () => { + Template.fromStack(stack).hasResourceProperties("AWS::Bedrock::AgentAlias", { AgentId: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('^Agent'), - 'AgentId', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("^Agent"), "AgentId"], }, - AgentAliasName: 'prod', + AgentAliasName: "prod", }); }); - - test('No unsuppressed Errors', () => { + test("No unsuppressed Errors", () => { const errors = Annotations.fromStack(stack).findError( - '*', - Match.stringLikeRegexp('AwsSolutions-.*'), + "*", + Match.stringLikeRegexp("AwsSolutions-.*") ); expect(errors).toHaveLength(0); }); - - }); -describe('Agent with guardrails through constructor', () => { +describe("Agent with guardrails through constructor", () => { let app: cdk.App; let stack: cdk.Stack; let kb: bedrock.KnowledgeBase; @@ -342,29 +318,29 @@ describe('Agent with guardrails through constructor', () => { beforeAll(() => { app = new cdk.App(); - stack = new cdk.Stack(app, 'test-stack', { + stack = new cdk.Stack(app, "test-stack", { env: { - account: '123456789012', - region: 'us-east-1', + account: "123456789012", + region: "us-east-1", }, }); - const bucket = new s3.Bucket(stack, 'DocBucket'); - kb = new bedrock.KnowledgeBase(stack, 'KB', { + const bucket = new s3.Bucket(stack, "DocBucket"); + kb = new bedrock.KnowledgeBase(stack, "KB", { embeddingsModel: bedrock.BedrockFoundationModel.TITAN_EMBED_TEXT_V1, - description: 'Documentation about CDK constructs.', - instruction: 'Documentation about CDK constructs.', + description: "Documentation about CDK constructs.", + instruction: "Documentation about CDK constructs.", }); - new bedrock.S3DataSource(stack, 'DataSource', { + new bedrock.S3DataSource(stack, "DataSource", { bucket, knowledgeBase: kb, - dataSourceName: 'test-docs', + dataSourceName: "test-docs", }); const preprocessingPrompt: bedrock.PromptConfiguration = { promptType: bedrock.PromptType.PRE_PROCESSING, promptState: bedrock.PromptState.DISABLED, promptCreationMode: bedrock.PromptCreationMode.OVERRIDDEN, - basePromptTemplate: 'This prompt is disabled.', + basePromptTemplate: "This prompt is disabled.", inferenceConfiguration: { temperature: 0, topP: 1.0, @@ -378,112 +354,107 @@ describe('Agent with guardrails through constructor', () => { promptType: bedrock.PromptType.ORCHESTRATION, promptState: bedrock.PromptState.ENABLED, promptCreationMode: bedrock.PromptCreationMode.OVERRIDDEN, - basePromptTemplate: 'This prompt is enabled.', + basePromptTemplate: "This prompt is enabled.", inferenceConfiguration: { temperature: 0, topP: 1.0, topK: 250, maximumLength: 2048, - stopSequences: ['', '', ''], + stopSequences: ["", "", ""], }, }; - actionGroupFunction = new lambda.Function(stack, 'ActionGroupFunction', { - code: lambda.Code.fromAsset('test/path'), + actionGroupFunction = new lambda.Function(stack, "ActionGroupFunction", { + code: lambda.Code.fromAsset("test/path"), runtime: lambda.Runtime.NODEJS_LATEST, - handler: 'index.handler', + handler: "index.handler", }); NagSuppressions.addResourceSuppressions( actionGroupFunction, [ { - id: 'AwsSolutions-IAM4', - reason: 'ActionGroup Lambda uses the AWSLambdaBasicExecutionRole AWS Managed Policy.', + id: "AwsSolutions-IAM4", + reason: + "ActionGroup Lambda uses the AWSLambdaBasicExecutionRole AWS Managed Policy.", }, ], - true, + true ); - const apiSchemaBucket = new s3.Bucket(stack, 'TestBucket'); - const actiongroup = new bedrock.AgentActionGroup(stack, 'actionGroups', { - actionGroupName: 'test-action-group', - description: 'Use these functions to get information about the books in the Project Gutenburg library.', - actionGroupState: 'ENABLED', + const apiSchemaBucket = new s3.Bucket(stack, "TestBucket"); + const actiongroup = new bedrock.AgentActionGroup(stack, "actionGroups", { + actionGroupName: "test-action-group", + description: + "Use these functions to get information about the books in the Project Gutenburg library.", + actionGroupState: "ENABLED", actionGroupExecutor: { lambda: actionGroupFunction, }, - apiSchema: bedrock.ApiSchema.fromBucket(apiSchemaBucket, 'test/api.yaml'), + apiSchema: bedrock.ApiSchema.fromBucket(apiSchemaBucket, "test/api.yaml"), }); - agent = new bedrock.Agent(stack, 'Agent', { - foundationModel: bedrock.BedrockFoundationModel.ANTHROPIC_CLAUDE_V2_1, - instruction: 'You provide support for developers working with CDK constructs.', + agent = new bedrock.Agent(stack, "Agent", { + model: bedrock.BedrockFoundationModel.ANTHROPIC_CLAUDE_V2_1, + instruction: "You provide support for developers working with CDK constructs.", knowledgeBases: [kb], idleSessionTTL: cdk.Duration.minutes(30), promptOverrideConfiguration: { promptConfigurations: [preprocessingPrompt, orchestrationPrompt], }, guardrailConfiguration: { - guardrailId: 'testId', - guardrailVersion: 'version1', + guardrailId: "testId", + guardrailVersion: "version1", }, - aliasName: 'prod', + aliasName: "prod", }); agent.addActionGroups([actiongroup]); - }); - test('Knowledge Base is created', () => { - Template.fromStack(stack).hasResourceProperties('AWS::Bedrock::KnowledgeBase', { - Description: 'Documentation about CDK constructs.', - Name: Match.stringLikeRegexp('^KB'), + test("Knowledge Base is created", () => { + Template.fromStack(stack).hasResourceProperties("AWS::Bedrock::KnowledgeBase", { + Description: "Documentation about CDK constructs.", + Name: Match.stringLikeRegexp("^KB"), KnowledgeBaseConfiguration: { - Type: 'VECTOR', + Type: "VECTOR", VectorKnowledgeBaseConfiguration: { - EmbeddingModelArn: 'arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v1', + EmbeddingModelArn: + "arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v1", }, }, }); }); - test('Data Source is created', () => { - Template.fromStack(stack).hasResourceProperties('AWS::Bedrock::DataSource', { - Name: 'test-docs', + test("Data Source is created", () => { + Template.fromStack(stack).hasResourceProperties("AWS::Bedrock::DataSource", { + Name: "test-docs", KnowledgeBaseId: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('^KB'), - 'KnowledgeBaseId', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("^KB"), "KnowledgeBaseId"], }, DataSourceConfiguration: { - Type: 'S3', + Type: "S3", S3Configuration: { BucketArn: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('^DocBucket'), - 'Arn', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("^DocBucket"), "Arn"], }, }, }, - }); }); - test('Agent is created', () => { - Template.fromStack(stack).hasResourceProperties('AWS::Bedrock::Agent', { - FoundationModel: 'anthropic.claude-v2:1', - Instruction: 'You provide support for developers working with CDK constructs.', + test("Agent is created", () => { + Template.fromStack(stack).hasResourceProperties("AWS::Bedrock::Agent", { + FoundationModel: "anthropic.claude-v2:1", + Instruction: "You provide support for developers working with CDK constructs.", IdleSessionTTLInSeconds: 1800, PromptOverrideConfiguration: { PromptConfigurations: [ { - PromptType: 'PRE_PROCESSING', - PromptState: 'DISABLED', - PromptCreationMode: 'OVERRIDDEN', - BasePromptTemplate: 'This prompt is disabled.', + PromptType: "PRE_PROCESSING", + PromptState: "DISABLED", + PromptCreationMode: "OVERRIDDEN", + BasePromptTemplate: "This prompt is disabled.", InferenceConfiguration: { Temperature: 0, TopP: 1.0, @@ -493,16 +464,16 @@ describe('Agent with guardrails through constructor', () => { }, }, { - PromptType: 'ORCHESTRATION', - PromptState: 'ENABLED', - PromptCreationMode: 'OVERRIDDEN', - BasePromptTemplate: 'This prompt is enabled.', + PromptType: "ORCHESTRATION", + PromptState: "ENABLED", + PromptCreationMode: "OVERRIDDEN", + BasePromptTemplate: "This prompt is enabled.", InferenceConfiguration: { Temperature: 0, TopP: 1.0, TopK: 250, MaximumLength: 2048, - StopSequences: ['', '', ''], + StopSequences: ["", "", ""], }, }, ], @@ -510,97 +481,83 @@ describe('Agent with guardrails through constructor', () => { }); }); - test('Agent is created with one knowledge base', () => { + test("Agent is created with one knowledge base", () => { const template = Template.fromStack(stack); - template.resourceCountIs('AWS::Bedrock::KnowledgeBase', 1); - template.hasResourceProperties('AWS::Bedrock::KnowledgeBase', { + template.resourceCountIs("AWS::Bedrock::KnowledgeBase", 1); + template.hasResourceProperties("AWS::Bedrock::KnowledgeBase", { KnowledgeBaseConfiguration: { - Type: Match.stringLikeRegexp('VECTOR'), + Type: Match.stringLikeRegexp("VECTOR"), }, - Name: Match.stringLikeRegexp('KBteststack'), + Name: Match.stringLikeRegexp("KBteststack"), RoleArn: { - 'Fn::GetAtt': - [Match.stringLikeRegexp('KBRole'), 'Arn'], - + "Fn::GetAtt": [Match.stringLikeRegexp("KBRole"), "Arn"], }, - Description: 'Documentation about CDK constructs.', + Description: "Documentation about CDK constructs.", }); }); - test('Agent action group and ApiSchema from S3', () => { + test("Agent action group and ApiSchema from S3", () => { const template = Template.fromStack(stack); - template.hasResourceProperties('AWS::Bedrock::Agent', { - + template.hasResourceProperties("AWS::Bedrock::Agent", { ActionGroups: [ { - ActionGroupName: 'UserInputAction', - ActionGroupState: 'DISABLED', - ParentActionGroupSignature: 'AMAZON.UserInput', + ActionGroupName: "UserInputAction", + ActionGroupState: "DISABLED", + ParentActionGroupSignature: "AMAZON.UserInput", SkipResourceInUseCheckOnDelete: false, }, { ActionGroupExecutor: { Lambda: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('ActionGroupFunction'), 'Arn', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("ActionGroupFunction"), "Arn"], }, }, - ActionGroupName: 'test-action-group', - ActionGroupState: 'ENABLED', + ActionGroupName: "test-action-group", + ActionGroupState: "ENABLED", ApiSchema: { S3: { S3BucketName: { - Ref: Match.stringLikeRegexp('^TestBucket'), + Ref: Match.stringLikeRegexp("^TestBucket"), }, - S3ObjectKey: 'test/api.yaml', + S3ObjectKey: "test/api.yaml", }, }, }, ], }); - }); - test('Guardrail is associated', () => { + test("Guardrail is associated", () => { const template = Template.fromStack(stack); console.log(template.toJSON()); - template.hasResourceProperties('AWS::Bedrock::Agent', { + template.hasResourceProperties("AWS::Bedrock::Agent", { GuardrailConfiguration: { - GuardrailIdentifier: 'testId', - GuardrailVersion: 'version1', + GuardrailIdentifier: "testId", + GuardrailVersion: "version1", }, - }, - ); - + }); }); - test('Agent Alias is created', () => { - Template.fromStack(stack).hasResourceProperties('AWS::Bedrock::AgentAlias', { + test("Agent Alias is created", () => { + Template.fromStack(stack).hasResourceProperties("AWS::Bedrock::AgentAlias", { AgentId: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('^Agent'), - 'AgentId', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("^Agent"), "AgentId"], }, - AgentAliasName: 'prod', + AgentAliasName: "prod", }); }); - - test('No unsuppressed Errors', () => { + test("No unsuppressed Errors", () => { const errors = Annotations.fromStack(stack).findError( - '*', - Match.stringLikeRegexp('AwsSolutions-.*'), + "*", + Match.stringLikeRegexp("AwsSolutions-.*") ); expect(errors).toHaveLength(0); }); - - }); -describe('Agent without guardrails', () => { +describe("Agent without guardrails", () => { let app: cdk.App; let stack: cdk.Stack; let kb: bedrock.KnowledgeBase; @@ -609,29 +566,29 @@ describe('Agent without guardrails', () => { beforeAll(() => { app = new cdk.App(); - stack = new cdk.Stack(app, 'test-stack', { + stack = new cdk.Stack(app, "test-stack", { env: { - account: '123456789012', - region: 'us-east-1', + account: "123456789012", + region: "us-east-1", }, }); - const bucket = new s3.Bucket(stack, 'DocBucket'); - kb = new bedrock.KnowledgeBase(stack, 'KB', { + const bucket = new s3.Bucket(stack, "DocBucket"); + kb = new bedrock.KnowledgeBase(stack, "KB", { embeddingsModel: bedrock.BedrockFoundationModel.TITAN_EMBED_TEXT_V1, - description: 'Documentation about CDK constructs.', - instruction: 'Documentation about CDK constructs.', + description: "Documentation about CDK constructs.", + instruction: "Documentation about CDK constructs.", }); - new bedrock.S3DataSource(stack, 'DataSource', { + new bedrock.S3DataSource(stack, "DataSource", { bucket, knowledgeBase: kb, - dataSourceName: 'test-docs', + dataSourceName: "test-docs", }); const preprocessingPrompt: bedrock.PromptConfiguration = { promptType: bedrock.PromptType.PRE_PROCESSING, promptState: bedrock.PromptState.DISABLED, promptCreationMode: bedrock.PromptCreationMode.OVERRIDDEN, - basePromptTemplate: 'This prompt is disabled.', + basePromptTemplate: "This prompt is disabled.", inferenceConfiguration: { temperature: 0, topP: 1.0, @@ -645,108 +602,103 @@ describe('Agent without guardrails', () => { promptType: bedrock.PromptType.ORCHESTRATION, promptState: bedrock.PromptState.ENABLED, promptCreationMode: bedrock.PromptCreationMode.OVERRIDDEN, - basePromptTemplate: 'This prompt is enabled.', + basePromptTemplate: "This prompt is enabled.", inferenceConfiguration: { temperature: 0, topP: 1.0, topK: 250, maximumLength: 2048, - stopSequences: ['', '', ''], + stopSequences: ["", "", ""], }, }; - actionGroupFunction = new lambda.Function(stack, 'ActionGroupFunction', { - code: lambda.Code.fromAsset('test/path'), + actionGroupFunction = new lambda.Function(stack, "ActionGroupFunction", { + code: lambda.Code.fromAsset("test/path"), runtime: lambda.Runtime.NODEJS_LATEST, - handler: 'index.handler', + handler: "index.handler", }); NagSuppressions.addResourceSuppressions( actionGroupFunction, [ { - id: 'AwsSolutions-IAM4', - reason: 'ActionGroup Lambda uses the AWSLambdaBasicExecutionRole AWS Managed Policy.', + id: "AwsSolutions-IAM4", + reason: + "ActionGroup Lambda uses the AWSLambdaBasicExecutionRole AWS Managed Policy.", }, ], - true, + true ); - const apiSchemaBucket = new s3.Bucket(stack, 'TestBucket'); - const actiongroup = new bedrock.AgentActionGroup(stack, 'actionGroups', { - actionGroupName: 'test-action-group', - description: 'Use these functions to get information about the books in the Project Gutenburg library.', - actionGroupState: 'ENABLED', + const apiSchemaBucket = new s3.Bucket(stack, "TestBucket"); + const actiongroup = new bedrock.AgentActionGroup(stack, "actionGroups", { + actionGroupName: "test-action-group", + description: + "Use these functions to get information about the books in the Project Gutenburg library.", + actionGroupState: "ENABLED", actionGroupExecutor: { lambda: actionGroupFunction, }, - apiSchema: bedrock.ApiSchema.fromBucket(apiSchemaBucket, 'test/api.yaml'), + apiSchema: bedrock.ApiSchema.fromBucket(apiSchemaBucket, "test/api.yaml"), }); - agent = new bedrock.Agent(stack, 'Agent', { - foundationModel: bedrock.BedrockFoundationModel.ANTHROPIC_CLAUDE_V2_1, - instruction: 'You provide support for developers working with CDK constructs.', + agent = new bedrock.Agent(stack, "Agent", { + model: bedrock.BedrockFoundationModel.ANTHROPIC_CLAUDE_V2_1, + instruction: "You provide support for developers working with CDK constructs.", knowledgeBases: [kb], idleSessionTTL: cdk.Duration.minutes(30), promptOverrideConfiguration: { promptConfigurations: [preprocessingPrompt, orchestrationPrompt], }, - aliasName: 'prod', + aliasName: "prod", }); agent.addActionGroups([actiongroup]); - }); - test('Knowledge Base is created', () => { - Template.fromStack(stack).hasResourceProperties('AWS::Bedrock::KnowledgeBase', { - Description: 'Documentation about CDK constructs.', - Name: Match.stringLikeRegexp('^KB'), + test("Knowledge Base is created", () => { + Template.fromStack(stack).hasResourceProperties("AWS::Bedrock::KnowledgeBase", { + Description: "Documentation about CDK constructs.", + Name: Match.stringLikeRegexp("^KB"), KnowledgeBaseConfiguration: { - Type: 'VECTOR', + Type: "VECTOR", VectorKnowledgeBaseConfiguration: { - EmbeddingModelArn: 'arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v1', + EmbeddingModelArn: + "arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v1", }, }, }); }); - test('Data Source is created', () => { - Template.fromStack(stack).hasResourceProperties('AWS::Bedrock::DataSource', { - Name: 'test-docs', + test("Data Source is created", () => { + Template.fromStack(stack).hasResourceProperties("AWS::Bedrock::DataSource", { + Name: "test-docs", KnowledgeBaseId: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('^KB'), - 'KnowledgeBaseId', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("^KB"), "KnowledgeBaseId"], }, DataSourceConfiguration: { - Type: 'S3', + Type: "S3", S3Configuration: { BucketArn: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('^DocBucket'), - 'Arn', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("^DocBucket"), "Arn"], }, }, }, - }); }); - test('Agent is created', () => { - Template.fromStack(stack).hasResourceProperties('AWS::Bedrock::Agent', { - FoundationModel: 'anthropic.claude-v2:1', - Instruction: 'You provide support for developers working with CDK constructs.', + test("Agent is created", () => { + Template.fromStack(stack).hasResourceProperties("AWS::Bedrock::Agent", { + FoundationModel: "anthropic.claude-v2:1", + Instruction: "You provide support for developers working with CDK constructs.", IdleSessionTTLInSeconds: 1800, PromptOverrideConfiguration: { PromptConfigurations: [ { - PromptType: 'PRE_PROCESSING', - PromptState: 'DISABLED', - PromptCreationMode: 'OVERRIDDEN', - BasePromptTemplate: 'This prompt is disabled.', + PromptType: "PRE_PROCESSING", + PromptState: "DISABLED", + PromptCreationMode: "OVERRIDDEN", + BasePromptTemplate: "This prompt is disabled.", InferenceConfiguration: { Temperature: 0, TopP: 1.0, @@ -756,16 +708,16 @@ describe('Agent without guardrails', () => { }, }, { - PromptType: 'ORCHESTRATION', - PromptState: 'ENABLED', - PromptCreationMode: 'OVERRIDDEN', - BasePromptTemplate: 'This prompt is enabled.', + PromptType: "ORCHESTRATION", + PromptState: "ENABLED", + PromptCreationMode: "OVERRIDDEN", + BasePromptTemplate: "This prompt is enabled.", InferenceConfiguration: { Temperature: 0, TopP: 1.0, TopK: 250, MaximumLength: 2048, - StopSequences: ['', '', ''], + StopSequences: ["", "", ""], }, }, ], @@ -773,104 +725,98 @@ describe('Agent without guardrails', () => { }); }); - test('Agent is created with one knowledge base', () => { + test("Agent is created with one knowledge base", () => { const template = Template.fromStack(stack); - template.resourceCountIs('AWS::Bedrock::KnowledgeBase', 1); - template.hasResourceProperties('AWS::Bedrock::KnowledgeBase', { + template.resourceCountIs("AWS::Bedrock::KnowledgeBase", 1); + template.hasResourceProperties("AWS::Bedrock::KnowledgeBase", { KnowledgeBaseConfiguration: { - Type: Match.stringLikeRegexp('VECTOR'), + Type: Match.stringLikeRegexp("VECTOR"), }, - Name: Match.stringLikeRegexp('KBteststack'), + Name: Match.stringLikeRegexp("KBteststack"), RoleArn: { - 'Fn::GetAtt': - [Match.stringLikeRegexp('KBRole'), 'Arn'], - + "Fn::GetAtt": [Match.stringLikeRegexp("KBRole"), "Arn"], }, - Description: 'Documentation about CDK constructs.', + Description: "Documentation about CDK constructs.", }); }); - test('Agent action group and ApiSchema from S3', () => { + test("Agent action group and ApiSchema from S3", () => { const template = Template.fromStack(stack); - template.hasResourceProperties('AWS::Bedrock::Agent', { - + template.hasResourceProperties("AWS::Bedrock::Agent", { ActionGroups: [ { - ActionGroupName: 'UserInputAction', - ActionGroupState: 'DISABLED', - ParentActionGroupSignature: 'AMAZON.UserInput', + ActionGroupName: "UserInputAction", + ActionGroupState: "DISABLED", + ParentActionGroupSignature: "AMAZON.UserInput", SkipResourceInUseCheckOnDelete: false, }, { ActionGroupExecutor: { Lambda: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('ActionGroupFunction'), 'Arn', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("ActionGroupFunction"), "Arn"], }, }, - ActionGroupName: 'test-action-group', - ActionGroupState: 'ENABLED', + ActionGroupName: "test-action-group", + ActionGroupState: "ENABLED", ApiSchema: { S3: { S3BucketName: { - Ref: Match.stringLikeRegexp('^TestBucket'), + Ref: Match.stringLikeRegexp("^TestBucket"), }, - S3ObjectKey: 'test/api.yaml', + S3ObjectKey: "test/api.yaml", }, }, }, ], }); - }); - test('Guardrail should not be associated', () => { + test("Guardrail should not be associated", () => { const template = Template.fromStack(stack); - expect(template.toJSON()).not.toHaveProperty('AWS::Bedrock::Guardrail'); + expect(template.toJSON()).not.toHaveProperty("AWS::Bedrock::Guardrail"); }); - test('Agent Alias is created', () => { - Template.fromStack(stack).hasResourceProperties('AWS::Bedrock::AgentAlias', { + test("Agent Alias is created", () => { + Template.fromStack(stack).hasResourceProperties("AWS::Bedrock::AgentAlias", { AgentId: { - 'Fn::GetAtt': [ - Match.stringLikeRegexp('^Agent'), - 'AgentId', - ], + "Fn::GetAtt": [Match.stringLikeRegexp("^Agent"), "AgentId"], }, - AgentAliasName: 'prod', + AgentAliasName: "prod", }); }); - - test('No unsuppressed Errors', () => { + test("No unsuppressed Errors", () => { const errors = Annotations.fromStack(stack).findError( - '*', - Match.stringLikeRegexp('AwsSolutions-.*'), + "*", + Match.stringLikeRegexp("AwsSolutions-.*") ); expect(errors).toHaveLength(0); }); }); -describe('Imports', () => { +describe("Imports", () => { let app: cdk.App; let stack: cdk.Stack; beforeEach(() => { app = new cdk.App(); - stack = new cdk.Stack(app, 'TestStack'); + stack = new cdk.Stack(app, "TestStack"); }); - test('Agent Alias Import', () => { + test("Agent Alias Import", () => { // GIVEN - const agentAlias = bedrock.AgentAlias.fromAliasArn(stack, 'alias', - 'arn:aws:bedrock:us-east-1:123456789012:agent-alias/DNCJJYQKSU/TCLCITFZTN', + const agentAlias = bedrock.AgentAlias.fromAliasArn( + stack, + "alias", + "arn:aws:bedrock:us-east-1:123456789012:agent-alias/DNCJJYQKSU/TCLCITFZTN" ); //THEN - expect(agentAlias.agentId).toEqual('DNCJJYQKSU'); - expect(agentAlias.aliasId).toEqual('TCLCITFZTN'); - expect(agentAlias.aliasArn).toEqual('arn:aws:bedrock:us-east-1:123456789012:agent-alias/DNCJJYQKSU/TCLCITFZTN'); + expect(agentAlias.agentId).toEqual("DNCJJYQKSU"); + expect(agentAlias.aliasId).toEqual("TCLCITFZTN"); + expect(agentAlias.aliasArn).toEqual( + "arn:aws:bedrock:us-east-1:123456789012:agent-alias/DNCJJYQKSU/TCLCITFZTN" + ); }); }); diff --git a/test/cdk-lib/bedrock/inference-profiles.test.ts b/test/cdk-lib/bedrock/inference-profiles.test.ts new file mode 100644 index 00000000..d175f92d --- /dev/null +++ b/test/cdk-lib/bedrock/inference-profiles.test.ts @@ -0,0 +1,80 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import * as cdk from "aws-cdk-lib"; +import { + Agent, + BedrockFoundationModel, + InferenceProfile, + InferenceProfileRegion, +} from "../../../src/cdk-lib/bedrock"; +import { Match, Template } from "aws-cdk-lib/assertions"; + +describe("Inference Profile", () => { + let app: cdk.App; + let stack: cdk.Stack; + + beforeEach(() => { + app = new cdk.App(); + stack = new cdk.Stack(app, "TestStack", { env: { region: "us-west-2" } }); + }); + + test("Inference Profile is created", () => { + const inferenceProfileEU = new InferenceProfile({ + model: BedrockFoundationModel.ANTHROPIC_CLAUDE_3_5_SONNET_V1_0, + region: InferenceProfileRegion.EU, + }); + const inferenceProfileUS = new InferenceProfile({ + model: BedrockFoundationModel.ANTHROPIC_CLAUDE_3_5_SONNET_V1_0, + region: InferenceProfileRegion.US, + }); + + expect(inferenceProfileEU.modelType).toEqual("inference-profile"); + expect(inferenceProfileEU.modelId).toEqual( + "eu.anthropic.claude-3-5-sonnet-20240620-v1:0" + ); + expect(inferenceProfileUS.modelId).toEqual( + "us.anthropic.claude-3-5-sonnet-20240620-v1:0" + ); + }); + + test("Inference Profile - Agent", () => { + const inferenceProfileEU = new InferenceProfile({ + model: BedrockFoundationModel.ANTHROPIC_CLAUDE_3_5_SONNET_V1_0, + region: InferenceProfileRegion.EU, + }); + + new Agent(stack, "id", { + model: inferenceProfileEU, + instruction: "You provide support for developers working with CDK constructs.", + }); + + Template.fromStack(stack).hasResourceProperties("AWS::Bedrock::Agent", { + Instruction: "You provide support for developers working with CDK constructs.", + }); + + Template.fromStack(stack).hasResourceProperties("AWS::IAM::Policy", { + PolicyDocument: { + Statement: [ + { + Action: "bedrock:InvokeModel", + Effect: "Allow", + Resource: [ + "arn:aws:bedrock:us-west-2::inference-profile/anthropic.claude-3-5-sonnet-20240620-v1:0", + ], + }, + ], + }, + }); + }); +});