Repository: awslabs/generative-ai-cdk-constructs
Pull request #202 (2024-01-18 00:07:58)

chore(deps): upgrade dependencies
Upgrades project dependencies. See details in [workflow run].

[Workflow Run]: https://github.com/awslabs/generative-ai-cdk-constructs/actions/runs/7563342177

------

*Automatically created by projen via the "upgrade-main" workflow*

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/awslabs/generative-ai-cdk-constructs/pull/202?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=awslabs) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`fd546b8`)](https://app.codecov.io/gh/awslabs/generative-ai-cdk-constructs/commit/fd546b83101a8a87fb25b645e3270c5e08efc59e?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=awslabs) 93.61% compared to head [(`a4de44f`)](https://app.codecov.io/gh/awslabs/generative-ai-cdk-constructs/pull/202?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=awslabs) 93.61%.


<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main     #202   +/-   ##
=======================================
  Coverage   93.61%   93.61%           
=======================================
  Files          18       18           
  Lines        5624     5624           
  Branches      125      125           
=======================================
  Hits         5265     5265           
  Misses        359      359           
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/awslabs/generative-ai-cdk-constructs/pull/202?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=awslabs).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=awslabs).


Patch:
diff --git a/package.json b/package.json
index 413fcfc..e4bebb3 100644
--- a/package.json
+++ b/package.json
@@ -71,7 +71,7 @@
     "constructs": "^10.3.0"
   },
   "dependencies": {
-    "cdk-nag": "^2.28.10"
+    "cdk-nag": "^2.28.11"
   },
   "keywords": [
     "aws-cdk",
diff --git a/src/common/helpers/utils.ts b/src/common/helpers/utils.ts
index 1508229..9fd06bc 100644
--- a/src/common/helpers/utils.ts
+++ b/src/common/helpers/utils.ts
@@ -84,4 +84,21 @@ export function generatePhysicalName(
   }
 
   return prefix.toLowerCase() + (lower ? allParts.toLowerCase() : allParts) + '-' + uniqueStackIdPart;
+}
+
+export const maximumLambdaMemorySizeContextItem = 'maximumLambdaMemorySize';
+export const recommendedMaximumLambdaMemorySize = 7076;
+export function lambdaMemorySizeLimiter(construct: IConstruct, requestedMemorySizeInMegabytes: number) {
+  const maximumLambaMemorySize = construct.node.tryGetContext(maximumLambdaMemorySizeContextItem) === undefined ?
+    recommendedMaximumLambdaMemorySize :
+    parseInt(construct.node.tryGetContext(maximumLambdaMemorySizeContextItem));
+  if (maximumLambaMemorySize < recommendedMaximumLambdaMemorySize) {
+    console.warn(`Maximum Lambda memorySize, ${maximumLambaMemorySize}, is less than the recommended ${recommendedMaximumLambdaMemorySize}.`);
+  }
+  if (requestedMemorySizeInMegabytes > maximumLambaMemorySize) {
+    console.warn(`Reducing Lambda memorySize, ${requestedMemorySizeInMegabytes} to ${maximumLambaMemorySize} for ${construct.constructor.name}`);
+    return maximumLambaMemorySize;
+  } else {
+    return requestedMemorySizeInMegabytes;
+  }
 }
\ No newline at end of file
diff --git a/src/patterns/gen-ai/aws-rag-appsync-stepfn-opensearch/index.ts b/src/patterns/gen-ai/aws-rag-appsync-stepfn-opensearch/index.ts
index 3413377..f76b384 100644
--- a/src/patterns/gen-ai/aws-rag-appsync-stepfn-opensearch/index.ts
+++ b/src/patterns/gen-ai/aws-rag-appsync-stepfn-opensearch/index.ts
@@ -28,7 +28,7 @@ import * as stepfn_task from 'aws-cdk-lib/aws-stepfunctions-tasks';
 import { NagSuppressions } from 'cdk-nag';
 import { Construct } from 'constructs';
 import * as s3_bucket_helper from '../../../common/helpers/s3-bucket-helper';
-import { generatePhysicalName, version } from '../../../common/helpers/utils';
+import { generatePhysicalName, version, lambdaMemorySizeLimiter } from '../../../common/helpers/utils';
 import * as vpc_helper from '../../../common/helpers/vpc-helper';
 
 /**
@@ -431,7 +431,7 @@ export class RagAppsyncStepfnOpensearch extends Construct {
         tracing: lambda_tracing,
         vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
         securityGroups: [this.securityGroup],
-        memorySize: 1_769 * 4,
+        memorySize: lambdaMemorySizeLimiter(this, 1_769 * 4),
         timeout: Duration.minutes(15),
         environment: {
           GRAPHQL_URL: updateGraphQlApiEndpoint,
@@ -563,7 +563,7 @@ export class RagAppsyncStepfnOpensearch extends Construct {
         tracing: lambda_tracing,
         vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
         securityGroups: [this.securityGroup],
-        memorySize: 1_769 * 4,
+        memorySize: lambdaMemorySizeLimiter(this, 1_769 * 4),
         timeout: Duration.minutes(15),
         role: s3_transformer_job_function_role,
         environment: {
@@ -681,7 +681,7 @@ export class RagAppsyncStepfnOpensearch extends Construct {
         tracing: lambda_tracing,
         vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
         securityGroups: [this.securityGroup],
-        memorySize: 1_769 * 4,
+        memorySize: lambdaMemorySizeLimiter(this, 1_769 * 4),
         timeout: Duration.minutes(15),
         role: embeddings_job_function_role,
         environment: {
diff --git a/src/patterns/gen-ai/aws-summarization-appsync-stepfn/index.ts b/src/patterns/gen-ai/aws-summarization-appsync-stepfn/index.ts
index 08df83a..062c05c 100644
--- a/src/patterns/gen-ai/aws-summarization-appsync-stepfn/index.ts
+++ b/src/patterns/gen-ai/aws-summarization-appsync-stepfn/index.ts
@@ -30,7 +30,7 @@ import { Construct } from 'constructs';
 import * as eventBridge from '../../../common/helpers/eventbridge-helper';
 import * as redisHelper from '../../../common/helpers/redis-helper';
 import * as s3BucketHelper from '../../../common/helpers/s3-bucket-helper';
-import { generatePhysicalName, version } from '../../../common/helpers/utils';
+import { generatePhysicalName, version, lambdaMemorySizeLimiter } from '../../../common/helpers/utils';
 import * as vpcHelper from '../../../common/helpers/vpc-helper';
 
 export interface SummarizationAppsyncStepfnProps {
@@ -511,7 +511,7 @@ export class SummarizationAppsyncStepfn extends Construct {
         tracing: lambda_tracing,
         vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
         securityGroups: [this.securityGroup],
-        memorySize: 1_769 * 1,
+        memorySize: lambdaMemorySizeLimiter(this, 1_769 * 1),
         timeout: Duration.minutes(5),
         role: inputvalidatorLambdaRole,
         environment: {
@@ -593,7 +593,7 @@ export class SummarizationAppsyncStepfn extends Construct {
       vpc: this.vpc,
       vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
       securityGroups: [this.securityGroup],
-      memorySize: 1_769 * 1,
+      memorySize: lambdaMemorySizeLimiter(this, 1_769 * 1),
       tracing: lambda_tracing,
       timeout: Duration.minutes(5),
       role: documentReaderLambdaRole,
@@ -688,7 +688,7 @@ export class SummarizationAppsyncStepfn extends Construct {
       vpc: this.vpc,
       vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
       securityGroups: [this.securityGroup],
-      memorySize: 1_769 * 4,
+      memorySize: lambdaMemorySizeLimiter(this, 1_769 * 4),
       timeout: Duration.minutes(10),
       tracing: lambda_tracing,
       role: summaryGeneratorLambdaRole,
diff --git a/yarn.lock b/yarn.lock
index 38da27d..2588e71 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1012,16 +1012,16 @@
   integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==
 
 "@types/node@*":
-  version "20.11.4"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.4.tgz#c724a5d6723182af758b91b994209336f4439cb7"
-  integrity sha512-6I0fMH8Aoy2lOejL3s4LhyIYX34DPwY8bl5xlNjBvUEk8OHrcuzsFt+Ied4LvJihbtXPM+8zUqdydfIti86v9g==
+  version "20.11.5"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.5.tgz#be10c622ca7fcaa3cf226cf80166abc31389d86e"
+  integrity sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==
   dependencies:
     undici-types "~5.26.4"
 
 "@types/node@^18":
-  version "18.19.7"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.7.tgz#9a5f6ac7ec42a5dff68fe7faf2dd359710de4a12"
-  integrity sha512-IGRJfoNX10N/PfrReRZ1br/7SQ+2vF/tK3KXNwzXz82D32z5dMQEoOlFew18nLSN+vMNcLY4GrKfzwi/yWI8/w==
+  version "18.19.8"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.8.tgz#c1e42b165e5a526caf1f010747e0522cb2c9c36a"
+  integrity sha512-g1pZtPhsvGVTwmeVoexWZLTQaOvXwoSq//pTL0DHeNzUDrFnir4fgETdhjhIxjVnN+hKOuh98+E1eMLnUXstFg==
   dependencies:
     undici-types "~5.26.4"
 
@@ -1514,19 +1514,19 @@ camelcase@^6.2.0, camelcase@^6.3.0:
   integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
 
 caniuse-lite@^1.0.30001565:
-  version "1.0.30001577"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001577.tgz#a24991eb4ad67324ba8b96716340d53151f2f6f8"
-  integrity sha512-rs2ZygrG1PNXMfmncM0B5H1hndY5ZCC9b5TkFaVNfZ+AUlyqcMyVIQtc3fsezi0NUCk5XZfDf9WS6WxMxnfdrg==
+  version "1.0.30001578"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001578.tgz#11741580434ce60aae4b4a9abee9f9f8d7bf5be5"
+  integrity sha512-J/jkFgsQ3NEl4w2lCoM9ZPxrD+FoBNJ7uJUpGVjIg/j0OwJosWM36EPDv+Yyi0V4twBk9pPmlFS+PLykgEvUmg==
 
 case@1.6.3, case@^1.6.3:
   version "1.6.3"
   resolved "https://registry.yarnpkg.com/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9"
   integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==
 
-cdk-nag@^2.28.10:
-  version "2.28.10"
-  resolved "https://registry.yarnpkg.com/cdk-nag/-/cdk-nag-2.28.10.tgz#6ee002ab486fb22c1b64f7269bf8f07f88a2669b"
-  integrity sha512-979GFr11GHDekNXi+8REcjaGN3oTUPxvxwFw3litkNiv1BGHN4GyT/IR+/NXVFjBVi9Cikn92vbGJn19H06bCA==
+cdk-nag@^2.28.11:
+  version "2.28.11"
+  resolved "https://registry.yarnpkg.com/cdk-nag/-/cdk-nag-2.28.11.tgz#a43cbdc745054fbb339fd9712a658b977b8b2958"
+  integrity sha512-51v8bk6YrKCLkmQoUVP+zy7P8ACwViwIpsSHihQvib9JtVa19aVPkhOpdB3Omb6c/CiNo8XdbgcLZIEd/eecxQ==
 
 chalk@^2.4.2:
   version "2.4.2"
@@ -2080,9 +2080,9 @@ downlevel-dts@^0.11.0:
     typescript next
 
 electron-to-chromium@^1.4.601:
-  version "1.4.633"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.633.tgz#df8831d2fef994fe9c013e61db6cfb98eeebf52d"
-  integrity sha512-7BvxzXrHFliyQ1oZc6NRMjyEaKOO1Ma1NY98sFZofogWlm+klLWSgrDw7EhatiMgi4R4NV+iWxDdxuIKXtPbOw==
+  version "1.4.637"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.637.tgz#ed8775cf5e0c380c3e8452e9818a0e4b7a671ac4"
+  integrity sha512-G7j3UCOukFtxVO1vWrPQUoDk3kL70mtvjc/DC/k2o7lE0wAdq+Vwp1ipagOow+BH0uVztFysLWbkM/RTIrbK3w==
 
 emittery@^0.13.1:
   version "0.13.1"


Test patch:
diff --git a/test/common/helpers/utils.test.ts b/test/common/helpers/utils.test.ts
index 52ddacd..8a92183 100644
--- a/test/common/helpers/utils.test.ts
+++ b/test/common/helpers/utils.test.ts
@@ -11,8 +11,133 @@
  *  and limitations under the License.
  */
 import * as cdk from 'aws-cdk-lib';
-import { generatePhysicalName } from '../../../src/common/helpers/utils';
+import { Construct } from 'constructs';
+import { generatePhysicalName, lambdaMemorySizeLimiter, maximumLambdaMemorySizeContextItem, recommendedMaximumLambdaMemorySize } from '../../../src/common/helpers/utils';
 
+describe('lambdaMemorySizeLimiter', () => {
+  let testConstruct: TestConstruct;
+  let spyOnConsoleWarn: any;
+  beforeAll(() => {
+    spyOnConsoleWarn = jest.spyOn(console, 'warn');
+  });
+  afterAll(() => {
+    // console.warn = consoleWarn;
+    jest.restoreAllMocks();
+    console.log('Test "lambdaMemorySizeLimiter" completed');
+  });
+
+  test('above recommended', () => {
+    const app = new cdk.App({});
+    const stack = new cdk.Stack(app, 'TestStack', { env: { account: '012345678912', region: 'bermuda-triangle-1' } });
+    testConstruct = new TestConstruct(stack, 'TestConstruct');
+    const requestedMemory = recommendedMaximumLambdaMemorySize + 1;
+    const actualMemory = lambdaMemorySizeLimiter(testConstruct, requestedMemory);
+    expect(actualMemory).toEqual(recommendedMaximumLambdaMemorySize);
+    expect(spyOnConsoleWarn).toHaveBeenCalledTimes(1); // above recommended
+  });
+  test('equal to recommended', () => {
+    const app = new cdk.App({});
+    const stack = new cdk.Stack(app, 'TestStack', { env: { account: '012345678912', region: 'bermuda-triangle-1' } });
+    testConstruct = new TestConstruct(stack, 'TestConstruct');
+    const requestedMemory = recommendedMaximumLambdaMemorySize;
+    const actualMemory = lambdaMemorySizeLimiter(testConstruct, requestedMemory);
+    expect(actualMemory).toEqual(requestedMemory);
+    expect(spyOnConsoleWarn).toHaveBeenCalledTimes(0);
+  });
+  test('below recommended', () => {
+    const app = new cdk.App({});
+    const stack = new cdk.Stack(app, 'TestStack', { env: { account: '012345678912', region: 'bermuda-triangle-1' } });
+    testConstruct = new TestConstruct(stack, 'TestConstruct');
+    const requestedMemory = recommendedMaximumLambdaMemorySize - 1;
+    const actualMemory = lambdaMemorySizeLimiter(testConstruct, requestedMemory);
+    expect(actualMemory).toEqual(requestedMemory);
+    expect(spyOnConsoleWarn).toHaveBeenCalledTimes(0);
+  });
+  test('above minimum context', () => {
+    const contextLambdaMemorySize = 3008;
+    const app = new cdk.App({
+      context: {
+        [maximumLambdaMemorySizeContextItem]: contextLambdaMemorySize.toString(),
+      },
+    });
+    const stack = new cdk.Stack(app, 'TestStack', { env: { account: '012345678912', region: 'bermuda-triangle-1' } });
+    testConstruct = new TestConstruct(stack, 'TestConstruct');
+    const requestedMemory = contextLambdaMemorySize + 1;
+    const actualMemory = lambdaMemorySizeLimiter(testConstruct, requestedMemory);
+    expect(actualMemory).toEqual(contextLambdaMemorySize);
+    expect(spyOnConsoleWarn).toHaveBeenCalledTimes(2); // warning below recommended and below context
+  });
+  test('at minimum context', () => {
+    const contextLambdaMemorySize = 3008;
+    const app = new cdk.App({
+      context: {
+        [maximumLambdaMemorySizeContextItem]: contextLambdaMemorySize.toString(),
+      },
+    });
+    const stack = new cdk.Stack(app, 'TestStack', { env: { account: '012345678912', region: 'bermuda-triangle-1' } });
+    testConstruct = new TestConstruct(stack, 'TestConstruct');
+    const requestedMemory = contextLambdaMemorySize;
+    const actualMemory = lambdaMemorySizeLimiter(testConstruct, requestedMemory);
+    expect(actualMemory).toEqual(requestedMemory);
+    expect(spyOnConsoleWarn).toHaveBeenCalledTimes(1); // warning below recommended
+  });
+  test('below minimum context', () => {
+    const contextLambdaMemorySize = 3008;
+    const app = new cdk.App({
+      context: {
+        [maximumLambdaMemorySizeContextItem]: contextLambdaMemorySize.toString(),
+      },
+    });
+    const stack = new cdk.Stack(app, 'TestStack', { env: { account: '012345678912', region: 'bermuda-triangle-1' } });
+    testConstruct = new TestConstruct(stack, 'TestConstruct');
+    const requestedMemory = contextLambdaMemorySize - 1;
+    const actualMemory = lambdaMemorySizeLimiter(testConstruct, requestedMemory);
+    expect(actualMemory).toEqual(requestedMemory);
+    expect(spyOnConsoleWarn).toHaveBeenCalledTimes(1); // warning below recommended.
+  });
+  test('above high context', () => {
+    const contextLambdaMemorySize = recommendedMaximumLambdaMemorySize * 2;
+    const app = new cdk.App({
+      context: {
+        [maximumLambdaMemorySizeContextItem]: contextLambdaMemorySize.toString(),
+      },
+    });
+    const stack = new cdk.Stack(app, 'TestStack', { env: { account: '012345678912', region: 'bermuda-triangle-1' } });
+    testConstruct = new TestConstruct(stack, 'TestConstruct');
+    const requestedMemory = contextLambdaMemorySize + 1;
+    const actualMemory = lambdaMemorySizeLimiter(testConstruct, requestedMemory);
+    expect(actualMemory).toEqual(contextLambdaMemorySize);
+    expect(spyOnConsoleWarn).toHaveBeenCalledTimes(1); // below context
+  });
+  test('at high context', () => {
+    const contextLambdaMemorySize = recommendedMaximumLambdaMemorySize * 2;
+    const app = new cdk.App({
+      context: {
+        [maximumLambdaMemorySizeContextItem]: contextLambdaMemorySize.toString(),
+      },
+    });
+    const stack = new cdk.Stack(app, 'TestStack', { env: { account: '012345678912', region: 'bermuda-triangle-1' } });
+    testConstruct = new TestConstruct(stack, 'TestConstruct');
+    const requestedMemory = contextLambdaMemorySize;
+    const actualMemory = lambdaMemorySizeLimiter(testConstruct, requestedMemory);
+    expect(actualMemory).toEqual(requestedMemory);
+    expect(spyOnConsoleWarn).toHaveBeenCalledTimes(0);
+  });
+  test('below high context', () => {
+    const contextLambdaMemorySize = recommendedMaximumLambdaMemorySize * 2;
+    const app = new cdk.App({
+      context: {
+        [maximumLambdaMemorySizeContextItem]: contextLambdaMemorySize.toString(),
+      },
+    });
+    const stack = new cdk.Stack(app, 'TestStack', { env: { account: '012345678912', region: 'bermuda-triangle-1' } });
+    testConstruct = new TestConstruct(stack, 'TestConstruct');
+    const requestedMemory = contextLambdaMemorySize - 1;
+    const actualMemory = lambdaMemorySizeLimiter(testConstruct, requestedMemory);
+    expect(actualMemory).toEqual(requestedMemory);
+    expect(spyOnConsoleWarn).toHaveBeenCalledTimes(0);
+  });
+});
 
 describe('generatePhysicalName', () => {
   let testResourceA: TestResource;
@@ -20,7 +145,7 @@ describe('generatePhysicalName', () => {
   let testStack: cdk.Stack;
 
   afterAll(() => {
-    console.log('Test completed');
+    console.log('Test "generatePhysicalName" completed');
   });
 
   beforeAll(() => {
@@ -105,4 +230,5 @@ describe('generatePhysicalName', () => {
 });
 
 
-class TestResource extends cdk.Resource {}
\ No newline at end of file
+class TestResource extends cdk.Resource {}
+class TestConstruct extends Construct {}
\ No newline at end of file