@aws/nx-plugin 1.0.0-rc.71 → 1.0.0-rc.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/migrations.json +6 -1
- package/package.json +1 -1
- package/src/agentcore-harness/generator.d.ts +1 -1
- package/src/infra/app/generator.d.ts +1 -1
- package/src/init/generator.d.ts +1 -1
- package/src/license/dependency-check/evaluator.js +55 -10
- package/src/license/dependency-check/evaluator.js.map +1 -1
- package/src/migrations/latest/py-agent-session-management-support/metadata.json +3 -0
- package/src/migrations/latest/py-agent-session-management-support/migration.d.ts +2 -0
- package/src/migrations/latest/py-agent-session-management-support/migration.js +308 -0
- package/src/migrations/latest/py-agent-session-management-support/migration.js.map +1 -0
- package/src/preset/generator.d.ts +1 -1
- package/src/py/agent/__snapshots__/generator.constructs.spec.ts.snap +615 -6
- package/src/py/agent/__snapshots__/generator.core.spec.ts.snap +186 -0
- package/src/py/agent/__snapshots__/generator.frameworks.spec.ts.snap +47 -3
- package/src/py/agent/__snapshots__/generator.protocols.spec.ts.snap +667 -2
- package/src/py/agent/files/langchain/common/__init__.py.template +0 -0
- package/src/py/agent/files/langchain/common/agent.py.template +3 -3
- package/src/py/agent/files/langchain/common/session.py.template +61 -0
- package/src/py/agent/files/strands/ag-ui/main.py.template +5 -1
- package/src/py/agent/files/strands/common/agent.py.template +9 -2
- package/src/py/agent/files/strands/common/session.py.template +36 -0
- package/src/py/agent/generator.d.ts +11 -1
- package/src/py/agent/generator.js +78 -28
- package/src/py/agent/generator.js.map +1 -1
- package/src/py/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +2 -0
- package/src/py/agent/react-connection/generator.d.ts +1 -1
- package/src/py/agent/schema.d.js.map +1 -1
- package/src/py/agent/schema.d.ts +1 -1
- package/src/py/agent/schema.json +3 -3
- package/src/py/fast-api/react/generator.d.ts +1 -1
- package/src/smithy/project/generator.d.ts +1 -1
- package/src/smithy/react-connection/generator.d.ts +1 -1
- package/src/terraform/project/generator.d.ts +1 -1
- package/src/trpc/react/generator.d.ts +1 -1
- package/src/ts/agent/__snapshots__/generator.spec.ts.snap +27 -6
- package/src/ts/agent/a2a-connection/generator.d.ts +1 -1
- package/src/ts/agent/gateway-connection/generator.d.ts +1 -1
- package/src/ts/agent/mcp-connection/generator.d.ts +1 -1
- package/src/ts/agent/react-connection/generator.d.ts +1 -1
- package/src/ts/astro-docs/generator.d.ts +1 -1
- package/src/ts/dcr-proxy/generator.d.ts +1 -1
- package/src/ts/dynamodb/generator.d.ts +1 -1
- package/src/ts/lambda-function/generator.d.ts +1 -1
- package/src/ts/lib/generator.d.ts +1 -1
- package/src/ts/nx-generator/generator.d.ts +1 -1
- package/src/ts/nx-migration/generator.d.ts +1 -1
- package/src/ts/nx-plugin/generator.d.ts +1 -1
- package/src/ts/rdb/generator.d.ts +1 -1
- package/src/ts/react-website/agui/generator.d.ts +1 -1
- package/src/utils/agent-connection/agent-connection.d.ts +10 -0
- package/src/utils/agent-connection/agent-connection.js +15 -0
- package/src/utils/agent-connection/agent-connection.js.map +1 -1
- package/src/utils/agent-connection/files/core-runtime-config/runtime-config.ts.template +5 -3
- package/src/utils/agent-connection/files/py-core-langchain/s3/s3_checkpoint_saver_langchain.py.template +360 -0
- package/src/utils/agent-connection/files/py-core-runtime-config/runtime_config.py.template +2 -4
- package/src/utils/agent-connection/files/py-core-strands/base/tool_errors_strands.py.template +24 -0
- package/src/utils/agent-core-constructs/agent-core-constructs.d.ts +1 -1
- package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
- package/src/utils/agent-core-constructs/files/cdk/app/agent-core/__nameKebabCase__/__nameKebabCase__.ts.template +113 -13
- package/src/utils/agent-core-constructs/files/terraform/app/agent-core/__nameKebabCase__/__nameKebabCase__.tf.template +76 -7
- package/src/utils/versions.d.ts +3 -0
- package/src/utils/versions.js +3 -0
- package/src/utils/versions.js.map +1 -1
|
@@ -1,5 +1,328 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
+
exports[`py#agent generator > should create DynamoDB checkpoint table and S3 offload bucket infrastructure when session is dynamodb-s3 > agent-construct.ts (dynamodb-s3) 1`] = `
|
|
4
|
+
"import { Fn, Lazy, Names, RemovalPolicy, Stack } from 'aws-cdk-lib';
|
|
5
|
+
import { Platform } from 'aws-cdk-lib/aws-ecr-assets';
|
|
6
|
+
import { Connections, IConnectable } from 'aws-cdk-lib/aws-ec2';
|
|
7
|
+
import {
|
|
8
|
+
BlockPublicAccess,
|
|
9
|
+
Bucket,
|
|
10
|
+
BucketEncryption,
|
|
11
|
+
} from 'aws-cdk-lib/aws-s3';
|
|
12
|
+
import { Key } from 'aws-cdk-lib/aws-kms';
|
|
13
|
+
import {
|
|
14
|
+
CfnDelivery,
|
|
15
|
+
CfnDeliveryDestination,
|
|
16
|
+
CfnDeliverySource,
|
|
17
|
+
LogGroup,
|
|
18
|
+
RetentionDays,
|
|
19
|
+
} from 'aws-cdk-lib/aws-logs';
|
|
20
|
+
import {
|
|
21
|
+
AttributeType,
|
|
22
|
+
BillingMode,
|
|
23
|
+
Table,
|
|
24
|
+
TableEncryption,
|
|
25
|
+
} from 'aws-cdk-lib/aws-dynamodb';
|
|
26
|
+
import { Construct } from 'constructs';
|
|
27
|
+
import * as path from 'path';
|
|
28
|
+
import * as url from 'url';
|
|
29
|
+
import {
|
|
30
|
+
AgentRuntimeArtifact,
|
|
31
|
+
ProtocolType,
|
|
32
|
+
Runtime,
|
|
33
|
+
RuntimeProps,
|
|
34
|
+
} from 'aws-cdk-lib/aws-bedrockagentcore';
|
|
35
|
+
import {
|
|
36
|
+
PolicyStatement,
|
|
37
|
+
Effect,
|
|
38
|
+
ServicePrincipal,
|
|
39
|
+
IGrantable,
|
|
40
|
+
IPrincipal,
|
|
41
|
+
Grant,
|
|
42
|
+
} from 'aws-cdk-lib/aws-iam';
|
|
43
|
+
import { suppressRules } from '../../../core/checkov.js';
|
|
44
|
+
import { RuntimeConfig } from '../../../core/runtime-config.js';
|
|
45
|
+
import { findWorkspaceRoot } from '../../../core/workspace.js';
|
|
46
|
+
|
|
47
|
+
export type DynamodbAgentProps = Omit<
|
|
48
|
+
RuntimeProps,
|
|
49
|
+
| 'runtimeName'
|
|
50
|
+
| 'protocolConfiguration'
|
|
51
|
+
| 'agentRuntimeArtifact'
|
|
52
|
+
| 'authorizerConfiguration'
|
|
53
|
+
> & {
|
|
54
|
+
/**
|
|
55
|
+
* Removal policy for the session bucket holding the agent's conversation
|
|
56
|
+
* history. Defaults to retaining it so a stack \`destroy\` doesn't silently
|
|
57
|
+
* delete session data — set to \`RemovalPolicy.DESTROY\` for sandbox/CI teardown.
|
|
58
|
+
*
|
|
59
|
+
* @default RemovalPolicy.RETAIN
|
|
60
|
+
*/
|
|
61
|
+
readonly sessionBucketRemovalPolicy?: RemovalPolicy;
|
|
62
|
+
/**
|
|
63
|
+
* Whether to enable deletion protection on the session checkpoint table.
|
|
64
|
+
* Set to \`false\` to allow a stack \`destroy\` to remove it (e.g. for
|
|
65
|
+
* sandbox/CI teardown).
|
|
66
|
+
*
|
|
67
|
+
* @default true
|
|
68
|
+
*/
|
|
69
|
+
readonly sessionTableDeletionProtection?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Removal policy for the session checkpoint table.
|
|
72
|
+
*
|
|
73
|
+
* @default RemovalPolicy.RETAIN
|
|
74
|
+
*/
|
|
75
|
+
readonly sessionTableRemovalPolicy?: RemovalPolicy;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export class DynamodbAgent
|
|
79
|
+
extends Construct
|
|
80
|
+
implements IGrantable, IConnectable
|
|
81
|
+
{
|
|
82
|
+
public readonly dockerImage: AgentRuntimeArtifact;
|
|
83
|
+
public readonly agentCoreRuntime: Runtime;
|
|
84
|
+
/** Default Gateway target name for this agent. */
|
|
85
|
+
public readonly agentName = 'dynamodb-agent';
|
|
86
|
+
/** Inbound auth — a fronting Gateway uses this to pick its outbound credential. */
|
|
87
|
+
public readonly auth = 'iam';
|
|
88
|
+
|
|
89
|
+
constructor(scope: Construct, id: string, props?: DynamodbAgentProps) {
|
|
90
|
+
super(scope, id);
|
|
91
|
+
|
|
92
|
+
const rc = RuntimeConfig.ensure(this);
|
|
93
|
+
|
|
94
|
+
// Resolve the bundle output directory containing the Dockerfile and built artifacts
|
|
95
|
+
const bundleDir = path.join(
|
|
96
|
+
findWorkspaceRoot(url.fileURLToPath(new URL(import.meta.url))),
|
|
97
|
+
'dist/apps/test-project/docker/dynamodb-agent',
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
this.dockerImage = AgentRuntimeArtifact.fromAsset(bundleDir, {
|
|
101
|
+
platform: Platform.LINUX_ARM64,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const {
|
|
105
|
+
sessionBucketRemovalPolicy = RemovalPolicy.RETAIN,
|
|
106
|
+
sessionTableDeletionProtection = true,
|
|
107
|
+
sessionTableRemovalPolicy = RemovalPolicy.RETAIN,
|
|
108
|
+
...restProps
|
|
109
|
+
} = props ?? {};
|
|
110
|
+
|
|
111
|
+
const sessionKey = new Key(this, 'SessionKey', {
|
|
112
|
+
enableKeyRotation: true,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Allow CloudWatch Logs to use the session key for server access log delivery.
|
|
116
|
+
const stack = Stack.of(this);
|
|
117
|
+
sessionKey.addToResourcePolicy(
|
|
118
|
+
new PolicyStatement({
|
|
119
|
+
effect: Effect.ALLOW,
|
|
120
|
+
principals: [
|
|
121
|
+
new ServicePrincipal(\`logs.\${stack.region}.amazonaws.com\`),
|
|
122
|
+
],
|
|
123
|
+
actions: [
|
|
124
|
+
'kms:Encrypt',
|
|
125
|
+
'kms:Decrypt',
|
|
126
|
+
'kms:ReEncrypt*',
|
|
127
|
+
'kms:GenerateDataKey*',
|
|
128
|
+
'kms:DescribeKey',
|
|
129
|
+
],
|
|
130
|
+
resources: ['*'],
|
|
131
|
+
conditions: {
|
|
132
|
+
ArnLike: {
|
|
133
|
+
'kms:EncryptionContext:aws:logs:arn': \`arn:aws:logs:\${stack.region}:\${stack.account}:log-group:*\`,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const sessionAccessLogs = new LogGroup(this, 'SessionAccessLogs', {
|
|
140
|
+
retention: RetentionDays.ONE_YEAR,
|
|
141
|
+
encryptionKey: sessionKey,
|
|
142
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const sessionBucket = new Bucket(this, 'SessionBucket', {
|
|
146
|
+
enforceSSL: true,
|
|
147
|
+
removalPolicy: sessionBucketRemovalPolicy,
|
|
148
|
+
encryption: BucketEncryption.KMS,
|
|
149
|
+
encryptionKey: sessionKey,
|
|
150
|
+
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
|
|
151
|
+
});
|
|
152
|
+
suppressRules(
|
|
153
|
+
sessionBucket,
|
|
154
|
+
['CKV_AWS_21'],
|
|
155
|
+
'Session data does not need versioning enabled',
|
|
156
|
+
);
|
|
157
|
+
suppressRules(
|
|
158
|
+
sessionBucket,
|
|
159
|
+
['CKV2_AWS_61'],
|
|
160
|
+
'Lifecycle configuration not required for session data',
|
|
161
|
+
);
|
|
162
|
+
suppressRules(
|
|
163
|
+
sessionBucket,
|
|
164
|
+
['CKV_AWS_144'],
|
|
165
|
+
'Cross-region replication not required for session data',
|
|
166
|
+
);
|
|
167
|
+
suppressRules(
|
|
168
|
+
sessionBucket,
|
|
169
|
+
['CKV2_AWS_62'],
|
|
170
|
+
'Event notifications not required for session data',
|
|
171
|
+
);
|
|
172
|
+
suppressRules(
|
|
173
|
+
sessionBucket,
|
|
174
|
+
['CKV_AWS_18'],
|
|
175
|
+
'Server access logs are delivered to CloudWatch Logs',
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const sessionAccessLogsSource: CfnDeliverySource = new CfnDeliverySource(
|
|
179
|
+
this,
|
|
180
|
+
'SessionAccessLogsSource',
|
|
181
|
+
{
|
|
182
|
+
name: Lazy.string({
|
|
183
|
+
produce: () =>
|
|
184
|
+
Names.uniqueResourceName(sessionAccessLogsSource, {
|
|
185
|
+
maxLength: 60,
|
|
186
|
+
}),
|
|
187
|
+
}),
|
|
188
|
+
logType: 'S3_SERVER_ACCESS_LOGS',
|
|
189
|
+
resourceArn: sessionBucket.bucketArn,
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
const sessionBucketPolicy = sessionBucket.policy;
|
|
193
|
+
if (sessionBucketPolicy) {
|
|
194
|
+
sessionAccessLogsSource.node.addDependency(sessionBucketPolicy);
|
|
195
|
+
}
|
|
196
|
+
const sessionAccessLogsDestination: CfnDeliveryDestination =
|
|
197
|
+
new CfnDeliveryDestination(this, 'SessionAccessLogsDestination', {
|
|
198
|
+
name: Lazy.string({
|
|
199
|
+
produce: () =>
|
|
200
|
+
Names.uniqueResourceName(sessionAccessLogsDestination, {
|
|
201
|
+
maxLength: 60,
|
|
202
|
+
}),
|
|
203
|
+
}),
|
|
204
|
+
destinationResourceArn: sessionAccessLogs.logGroupArn,
|
|
205
|
+
});
|
|
206
|
+
const sessionAccessLogsDelivery = new CfnDelivery(
|
|
207
|
+
this,
|
|
208
|
+
'SessionAccessLogsDelivery',
|
|
209
|
+
{
|
|
210
|
+
deliverySourceName: sessionAccessLogsSource.name,
|
|
211
|
+
deliveryDestinationArn: sessionAccessLogsDestination.attrArn,
|
|
212
|
+
},
|
|
213
|
+
);
|
|
214
|
+
sessionAccessLogsDelivery.addDependency(sessionAccessLogsSource);
|
|
215
|
+
|
|
216
|
+
// Unified checkpoints/writes table, matching the schema
|
|
217
|
+
// langgraph_checkpoint_aws.DynamoDBSaver expects (PK/SK, TTL on \`ttl\`).
|
|
218
|
+
const sessionTable = new Table(this, 'SessionTable', {
|
|
219
|
+
partitionKey: { name: 'PK', type: AttributeType.STRING },
|
|
220
|
+
sortKey: { name: 'SK', type: AttributeType.STRING },
|
|
221
|
+
billingMode: BillingMode.PAY_PER_REQUEST,
|
|
222
|
+
pointInTimeRecoverySpecification: { pointInTimeRecoveryEnabled: true },
|
|
223
|
+
timeToLiveAttribute: 'ttl',
|
|
224
|
+
encryption: TableEncryption.CUSTOMER_MANAGED,
|
|
225
|
+
encryptionKey: sessionKey,
|
|
226
|
+
deletionProtection: sessionTableDeletionProtection,
|
|
227
|
+
removalPolicy: sessionTableRemovalPolicy,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
this.agentCoreRuntime = new Runtime(this, 'DynamodbAgent', {
|
|
231
|
+
runtimeName: Lazy.string({
|
|
232
|
+
produce: () =>
|
|
233
|
+
Names.uniqueResourceName(this.agentCoreRuntime, { maxLength: 40 }),
|
|
234
|
+
}),
|
|
235
|
+
protocolConfiguration: ProtocolType.HTTP,
|
|
236
|
+
agentRuntimeArtifact: this.dockerImage,
|
|
237
|
+
...restProps,
|
|
238
|
+
environmentVariables: {
|
|
239
|
+
RUNTIME_CONFIG_APP_ID: rc.appConfigApplicationId,
|
|
240
|
+
...restProps?.environmentVariables,
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Grant access for the agent to invoke bedrock models
|
|
245
|
+
this.agentCoreRuntime.addToRolePolicy(
|
|
246
|
+
new PolicyStatement({
|
|
247
|
+
actions: [
|
|
248
|
+
'bedrock:InvokeModel',
|
|
249
|
+
'bedrock:InvokeModelWithResponseStream',
|
|
250
|
+
],
|
|
251
|
+
resources: [
|
|
252
|
+
'arn:aws:bedrock:*:*:foundation-model/*',
|
|
253
|
+
'arn:aws:bedrock:*:*:inference-profile/*',
|
|
254
|
+
],
|
|
255
|
+
}),
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
sessionTable.grantReadWriteData(this.agentCoreRuntime);
|
|
259
|
+
sessionBucket.grantReadWrite(this.agentCoreRuntime);
|
|
260
|
+
|
|
261
|
+
rc.grantReadAppConfig(this.agentCoreRuntime);
|
|
262
|
+
|
|
263
|
+
rc.set('agentcore', 'agentRuntimes', {
|
|
264
|
+
...rc.get('agentcore').agentRuntimes,
|
|
265
|
+
DynamodbAgent: {
|
|
266
|
+
arn: this.agentCoreRuntime.agentRuntimeArn,
|
|
267
|
+
session: {
|
|
268
|
+
tableName: sessionTable.tableName,
|
|
269
|
+
bucketName: sessionBucket.bucketName,
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* The principal to grant permissions to.
|
|
277
|
+
*/
|
|
278
|
+
public get grantPrincipal(): IPrincipal {
|
|
279
|
+
return this.agentCoreRuntime.grantPrincipal;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Network connections for this agent runtime.
|
|
284
|
+
*/
|
|
285
|
+
public get connections(): Connections {
|
|
286
|
+
return this.agentCoreRuntime.connections;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* The HTTPS invocation URL of the runtime.
|
|
291
|
+
*/
|
|
292
|
+
public get invocationUrl(): string {
|
|
293
|
+
// The URL must URL-encode the runtime ARN (':' -> '%3A', '/' -> '%2F').
|
|
294
|
+
// The ARN is a CDK token, so encode at deploy time via Fn.join/Fn.split.
|
|
295
|
+
const encodedArn = Fn.join(
|
|
296
|
+
'%2F',
|
|
297
|
+
Fn.split(
|
|
298
|
+
'/',
|
|
299
|
+
Fn.join('%3A', Fn.split(':', this.agentCoreRuntime.agentRuntimeArn)),
|
|
300
|
+
),
|
|
301
|
+
);
|
|
302
|
+
return \`https://bedrock-agentcore.\${Stack.of(this).region}.amazonaws.com/runtimes/\${encodedArn}/invocations?qualifier=DEFAULT\`;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Grants IAM permissions to invoke this agent runtime.
|
|
307
|
+
*
|
|
308
|
+
* @param grantee - The IAM principal to grant permissions to
|
|
309
|
+
*/
|
|
310
|
+
public grantInvokeAccess(grantee: IGrantable) {
|
|
311
|
+
this.agentCoreRuntime.grantInvoke(grantee);
|
|
312
|
+
|
|
313
|
+
Grant.addToPrincipal({
|
|
314
|
+
grantee,
|
|
315
|
+
actions: ['bedrock-agentcore:InvokeAgentRuntimeWithWebSocketStream'],
|
|
316
|
+
resourceArns: [
|
|
317
|
+
this.agentCoreRuntime.agentRuntimeArn,
|
|
318
|
+
\`\${this.agentCoreRuntime.agentRuntimeArn}/*\`,
|
|
319
|
+
],
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
"
|
|
324
|
+
`;
|
|
325
|
+
|
|
3
326
|
exports[`py#agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > agent-Dockerfile 1`] = `
|
|
4
327
|
"FROM public.ecr.aws/docker/library/python:3.14-slim
|
|
5
328
|
|
|
@@ -27,9 +350,22 @@ CMD ["python", "bin/opentelemetry-instrument", "python", "-m", "proj_test_projec
|
|
|
27
350
|
`;
|
|
28
351
|
|
|
29
352
|
exports[`py#agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > agent-construct.ts 1`] = `
|
|
30
|
-
"import { Fn, Lazy, Names, Stack } from 'aws-cdk-lib';
|
|
353
|
+
"import { Fn, Lazy, Names, RemovalPolicy, Stack } from 'aws-cdk-lib';
|
|
31
354
|
import { Platform } from 'aws-cdk-lib/aws-ecr-assets';
|
|
32
355
|
import { Connections, IConnectable } from 'aws-cdk-lib/aws-ec2';
|
|
356
|
+
import {
|
|
357
|
+
BlockPublicAccess,
|
|
358
|
+
Bucket,
|
|
359
|
+
BucketEncryption,
|
|
360
|
+
} from 'aws-cdk-lib/aws-s3';
|
|
361
|
+
import { Key } from 'aws-cdk-lib/aws-kms';
|
|
362
|
+
import {
|
|
363
|
+
CfnDelivery,
|
|
364
|
+
CfnDeliveryDestination,
|
|
365
|
+
CfnDeliverySource,
|
|
366
|
+
LogGroup,
|
|
367
|
+
RetentionDays,
|
|
368
|
+
} from 'aws-cdk-lib/aws-logs';
|
|
33
369
|
import { Construct } from 'constructs';
|
|
34
370
|
import * as path from 'path';
|
|
35
371
|
import * as url from 'url';
|
|
@@ -41,10 +377,13 @@ import {
|
|
|
41
377
|
} from 'aws-cdk-lib/aws-bedrockagentcore';
|
|
42
378
|
import {
|
|
43
379
|
PolicyStatement,
|
|
380
|
+
Effect,
|
|
381
|
+
ServicePrincipal,
|
|
44
382
|
IGrantable,
|
|
45
383
|
IPrincipal,
|
|
46
384
|
Grant,
|
|
47
385
|
} from 'aws-cdk-lib/aws-iam';
|
|
386
|
+
import { suppressRules } from '../../../core/checkov.js';
|
|
48
387
|
import { RuntimeConfig } from '../../../core/runtime-config.js';
|
|
49
388
|
import { findWorkspaceRoot } from '../../../core/workspace.js';
|
|
50
389
|
|
|
@@ -54,7 +393,16 @@ export type SnapshotBedrockAgentProps = Omit<
|
|
|
54
393
|
| 'protocolConfiguration'
|
|
55
394
|
| 'agentRuntimeArtifact'
|
|
56
395
|
| 'authorizerConfiguration'
|
|
57
|
-
|
|
396
|
+
> & {
|
|
397
|
+
/**
|
|
398
|
+
* Removal policy for the session bucket holding the agent's conversation
|
|
399
|
+
* history. Defaults to retaining it so a stack \`destroy\` doesn't silently
|
|
400
|
+
* delete session data — set to \`RemovalPolicy.DESTROY\` for sandbox/CI teardown.
|
|
401
|
+
*
|
|
402
|
+
* @default RemovalPolicy.RETAIN
|
|
403
|
+
*/
|
|
404
|
+
readonly sessionBucketRemovalPolicy?: RemovalPolicy;
|
|
405
|
+
};
|
|
58
406
|
|
|
59
407
|
export class SnapshotBedrockAgent
|
|
60
408
|
extends Construct
|
|
@@ -82,6 +430,114 @@ export class SnapshotBedrockAgent
|
|
|
82
430
|
platform: Platform.LINUX_ARM64,
|
|
83
431
|
});
|
|
84
432
|
|
|
433
|
+
const { sessionBucketRemovalPolicy = RemovalPolicy.RETAIN, ...restProps } =
|
|
434
|
+
props ?? {};
|
|
435
|
+
|
|
436
|
+
const sessionKey = new Key(this, 'SessionKey', {
|
|
437
|
+
enableKeyRotation: true,
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// Allow CloudWatch Logs to use the session key for server access log delivery.
|
|
441
|
+
const stack = Stack.of(this);
|
|
442
|
+
sessionKey.addToResourcePolicy(
|
|
443
|
+
new PolicyStatement({
|
|
444
|
+
effect: Effect.ALLOW,
|
|
445
|
+
principals: [
|
|
446
|
+
new ServicePrincipal(\`logs.\${stack.region}.amazonaws.com\`),
|
|
447
|
+
],
|
|
448
|
+
actions: [
|
|
449
|
+
'kms:Encrypt',
|
|
450
|
+
'kms:Decrypt',
|
|
451
|
+
'kms:ReEncrypt*',
|
|
452
|
+
'kms:GenerateDataKey*',
|
|
453
|
+
'kms:DescribeKey',
|
|
454
|
+
],
|
|
455
|
+
resources: ['*'],
|
|
456
|
+
conditions: {
|
|
457
|
+
ArnLike: {
|
|
458
|
+
'kms:EncryptionContext:aws:logs:arn': \`arn:aws:logs:\${stack.region}:\${stack.account}:log-group:*\`,
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
}),
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
const sessionAccessLogs = new LogGroup(this, 'SessionAccessLogs', {
|
|
465
|
+
retention: RetentionDays.ONE_YEAR,
|
|
466
|
+
encryptionKey: sessionKey,
|
|
467
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
const sessionBucket = new Bucket(this, 'SessionBucket', {
|
|
471
|
+
enforceSSL: true,
|
|
472
|
+
removalPolicy: sessionBucketRemovalPolicy,
|
|
473
|
+
encryption: BucketEncryption.KMS,
|
|
474
|
+
encryptionKey: sessionKey,
|
|
475
|
+
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
|
|
476
|
+
});
|
|
477
|
+
suppressRules(
|
|
478
|
+
sessionBucket,
|
|
479
|
+
['CKV_AWS_21'],
|
|
480
|
+
'Session data does not need versioning enabled',
|
|
481
|
+
);
|
|
482
|
+
suppressRules(
|
|
483
|
+
sessionBucket,
|
|
484
|
+
['CKV2_AWS_61'],
|
|
485
|
+
'Lifecycle configuration not required for session data',
|
|
486
|
+
);
|
|
487
|
+
suppressRules(
|
|
488
|
+
sessionBucket,
|
|
489
|
+
['CKV_AWS_144'],
|
|
490
|
+
'Cross-region replication not required for session data',
|
|
491
|
+
);
|
|
492
|
+
suppressRules(
|
|
493
|
+
sessionBucket,
|
|
494
|
+
['CKV2_AWS_62'],
|
|
495
|
+
'Event notifications not required for session data',
|
|
496
|
+
);
|
|
497
|
+
suppressRules(
|
|
498
|
+
sessionBucket,
|
|
499
|
+
['CKV_AWS_18'],
|
|
500
|
+
'Server access logs are delivered to CloudWatch Logs',
|
|
501
|
+
);
|
|
502
|
+
|
|
503
|
+
const sessionAccessLogsSource: CfnDeliverySource = new CfnDeliverySource(
|
|
504
|
+
this,
|
|
505
|
+
'SessionAccessLogsSource',
|
|
506
|
+
{
|
|
507
|
+
name: Lazy.string({
|
|
508
|
+
produce: () =>
|
|
509
|
+
Names.uniqueResourceName(sessionAccessLogsSource, {
|
|
510
|
+
maxLength: 60,
|
|
511
|
+
}),
|
|
512
|
+
}),
|
|
513
|
+
logType: 'S3_SERVER_ACCESS_LOGS',
|
|
514
|
+
resourceArn: sessionBucket.bucketArn,
|
|
515
|
+
},
|
|
516
|
+
);
|
|
517
|
+
const sessionBucketPolicy = sessionBucket.policy;
|
|
518
|
+
if (sessionBucketPolicy) {
|
|
519
|
+
sessionAccessLogsSource.node.addDependency(sessionBucketPolicy);
|
|
520
|
+
}
|
|
521
|
+
const sessionAccessLogsDestination: CfnDeliveryDestination =
|
|
522
|
+
new CfnDeliveryDestination(this, 'SessionAccessLogsDestination', {
|
|
523
|
+
name: Lazy.string({
|
|
524
|
+
produce: () =>
|
|
525
|
+
Names.uniqueResourceName(sessionAccessLogsDestination, {
|
|
526
|
+
maxLength: 60,
|
|
527
|
+
}),
|
|
528
|
+
}),
|
|
529
|
+
destinationResourceArn: sessionAccessLogs.logGroupArn,
|
|
530
|
+
});
|
|
531
|
+
const sessionAccessLogsDelivery = new CfnDelivery(
|
|
532
|
+
this,
|
|
533
|
+
'SessionAccessLogsDelivery',
|
|
534
|
+
{
|
|
535
|
+
deliverySourceName: sessionAccessLogsSource.name,
|
|
536
|
+
deliveryDestinationArn: sessionAccessLogsDestination.attrArn,
|
|
537
|
+
},
|
|
538
|
+
);
|
|
539
|
+
sessionAccessLogsDelivery.addDependency(sessionAccessLogsSource);
|
|
540
|
+
|
|
85
541
|
this.agentCoreRuntime = new Runtime(this, 'SnapshotBedrockAgent', {
|
|
86
542
|
runtimeName: Lazy.string({
|
|
87
543
|
produce: () =>
|
|
@@ -89,10 +545,10 @@ export class SnapshotBedrockAgent
|
|
|
89
545
|
}),
|
|
90
546
|
protocolConfiguration: ProtocolType.HTTP,
|
|
91
547
|
agentRuntimeArtifact: this.dockerImage,
|
|
92
|
-
...
|
|
548
|
+
...restProps,
|
|
93
549
|
environmentVariables: {
|
|
94
550
|
RUNTIME_CONFIG_APP_ID: rc.appConfigApplicationId,
|
|
95
|
-
...
|
|
551
|
+
...restProps?.environmentVariables,
|
|
96
552
|
},
|
|
97
553
|
});
|
|
98
554
|
|
|
@@ -110,12 +566,17 @@ export class SnapshotBedrockAgent
|
|
|
110
566
|
}),
|
|
111
567
|
);
|
|
112
568
|
|
|
569
|
+
sessionBucket.grantReadWrite(this.agentCoreRuntime);
|
|
570
|
+
|
|
113
571
|
rc.grantReadAppConfig(this.agentCoreRuntime);
|
|
114
572
|
|
|
115
573
|
rc.set('agentcore', 'agentRuntimes', {
|
|
116
574
|
...rc.get('agentcore').agentRuntimes,
|
|
117
575
|
SnapshotBedrockAgent: {
|
|
118
576
|
arn: this.agentCoreRuntime.agentRuntimeArn,
|
|
577
|
+
session: {
|
|
578
|
+
bucketName: sessionBucket.bucketName,
|
|
579
|
+
},
|
|
119
580
|
},
|
|
120
581
|
});
|
|
121
582
|
}
|
|
@@ -195,10 +656,12 @@ exports[`py#agent generator > should match snapshot for generated files > agent-
|
|
|
195
656
|
exports[`py#agent generator > should match snapshot for generated files > agent-agent.py 1`] = `
|
|
196
657
|
"from contextlib import contextmanager
|
|
197
658
|
|
|
198
|
-
from proj_agent_connection import log_model_errors
|
|
659
|
+
from proj_agent_connection import log_model_errors, log_tool_errors
|
|
199
660
|
from strands import Agent, tool
|
|
200
661
|
from strands_tools import current_time
|
|
201
662
|
|
|
663
|
+
from .session import get_session_manager
|
|
664
|
+
|
|
202
665
|
|
|
203
666
|
@tool
|
|
204
667
|
def subtract(a: int, b: int) -> int:
|
|
@@ -216,7 +679,8 @@ Use your tools for mathematical tasks.
|
|
|
216
679
|
Refer to tools as your 'spellbook'.
|
|
217
680
|
""",
|
|
218
681
|
tools=[subtract, current_time],
|
|
219
|
-
hooks=[log_model_errors],
|
|
682
|
+
hooks=[log_model_errors, log_tool_errors],
|
|
683
|
+
session_manager=get_session_manager(),
|
|
220
684
|
)
|
|
221
685
|
"
|
|
222
686
|
`;
|
|
@@ -324,3 +788,148 @@ dev-dependencies = [ ]
|
|
|
324
788
|
workspace = true
|
|
325
789
|
"
|
|
326
790
|
`;
|
|
791
|
+
|
|
792
|
+
exports[`py#agent generator > should not create session bucket infrastructure when session is in-memory > agent-construct.ts (in-memory) 1`] = `
|
|
793
|
+
"import { Fn, Lazy, Names, Stack } from 'aws-cdk-lib';
|
|
794
|
+
import { Platform } from 'aws-cdk-lib/aws-ecr-assets';
|
|
795
|
+
import { Connections, IConnectable } from 'aws-cdk-lib/aws-ec2';
|
|
796
|
+
import { Construct } from 'constructs';
|
|
797
|
+
import * as path from 'path';
|
|
798
|
+
import * as url from 'url';
|
|
799
|
+
import {
|
|
800
|
+
AgentRuntimeArtifact,
|
|
801
|
+
ProtocolType,
|
|
802
|
+
Runtime,
|
|
803
|
+
RuntimeProps,
|
|
804
|
+
} from 'aws-cdk-lib/aws-bedrockagentcore';
|
|
805
|
+
import {
|
|
806
|
+
PolicyStatement,
|
|
807
|
+
IGrantable,
|
|
808
|
+
IPrincipal,
|
|
809
|
+
Grant,
|
|
810
|
+
} from 'aws-cdk-lib/aws-iam';
|
|
811
|
+
import { RuntimeConfig } from '../../../core/runtime-config.js';
|
|
812
|
+
import { findWorkspaceRoot } from '../../../core/workspace.js';
|
|
813
|
+
|
|
814
|
+
export type InMemoryAgentProps = Omit<
|
|
815
|
+
RuntimeProps,
|
|
816
|
+
| 'runtimeName'
|
|
817
|
+
| 'protocolConfiguration'
|
|
818
|
+
| 'agentRuntimeArtifact'
|
|
819
|
+
| 'authorizerConfiguration'
|
|
820
|
+
>;
|
|
821
|
+
|
|
822
|
+
export class InMemoryAgent
|
|
823
|
+
extends Construct
|
|
824
|
+
implements IGrantable, IConnectable
|
|
825
|
+
{
|
|
826
|
+
public readonly dockerImage: AgentRuntimeArtifact;
|
|
827
|
+
public readonly agentCoreRuntime: Runtime;
|
|
828
|
+
/** Default Gateway target name for this agent. */
|
|
829
|
+
public readonly agentName = 'in-memory-agent';
|
|
830
|
+
/** Inbound auth — a fronting Gateway uses this to pick its outbound credential. */
|
|
831
|
+
public readonly auth = 'iam';
|
|
832
|
+
|
|
833
|
+
constructor(scope: Construct, id: string, props?: InMemoryAgentProps) {
|
|
834
|
+
super(scope, id);
|
|
835
|
+
|
|
836
|
+
const rc = RuntimeConfig.ensure(this);
|
|
837
|
+
|
|
838
|
+
// Resolve the bundle output directory containing the Dockerfile and built artifacts
|
|
839
|
+
const bundleDir = path.join(
|
|
840
|
+
findWorkspaceRoot(url.fileURLToPath(new URL(import.meta.url))),
|
|
841
|
+
'dist/apps/test-project/docker/in-memory-agent',
|
|
842
|
+
);
|
|
843
|
+
|
|
844
|
+
this.dockerImage = AgentRuntimeArtifact.fromAsset(bundleDir, {
|
|
845
|
+
platform: Platform.LINUX_ARM64,
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
this.agentCoreRuntime = new Runtime(this, 'InMemoryAgent', {
|
|
849
|
+
runtimeName: Lazy.string({
|
|
850
|
+
produce: () =>
|
|
851
|
+
Names.uniqueResourceName(this.agentCoreRuntime, { maxLength: 40 }),
|
|
852
|
+
}),
|
|
853
|
+
protocolConfiguration: ProtocolType.HTTP,
|
|
854
|
+
agentRuntimeArtifact: this.dockerImage,
|
|
855
|
+
...props,
|
|
856
|
+
environmentVariables: {
|
|
857
|
+
RUNTIME_CONFIG_APP_ID: rc.appConfigApplicationId,
|
|
858
|
+
...props?.environmentVariables,
|
|
859
|
+
},
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
// Grant access for the agent to invoke bedrock models
|
|
863
|
+
this.agentCoreRuntime.addToRolePolicy(
|
|
864
|
+
new PolicyStatement({
|
|
865
|
+
actions: [
|
|
866
|
+
'bedrock:InvokeModel',
|
|
867
|
+
'bedrock:InvokeModelWithResponseStream',
|
|
868
|
+
],
|
|
869
|
+
resources: [
|
|
870
|
+
'arn:aws:bedrock:*:*:foundation-model/*',
|
|
871
|
+
'arn:aws:bedrock:*:*:inference-profile/*',
|
|
872
|
+
],
|
|
873
|
+
}),
|
|
874
|
+
);
|
|
875
|
+
|
|
876
|
+
rc.grantReadAppConfig(this.agentCoreRuntime);
|
|
877
|
+
|
|
878
|
+
rc.set('agentcore', 'agentRuntimes', {
|
|
879
|
+
...rc.get('agentcore').agentRuntimes,
|
|
880
|
+
InMemoryAgent: {
|
|
881
|
+
arn: this.agentCoreRuntime.agentRuntimeArn,
|
|
882
|
+
},
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* The principal to grant permissions to.
|
|
888
|
+
*/
|
|
889
|
+
public get grantPrincipal(): IPrincipal {
|
|
890
|
+
return this.agentCoreRuntime.grantPrincipal;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Network connections for this agent runtime.
|
|
895
|
+
*/
|
|
896
|
+
public get connections(): Connections {
|
|
897
|
+
return this.agentCoreRuntime.connections;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* The HTTPS invocation URL of the runtime.
|
|
902
|
+
*/
|
|
903
|
+
public get invocationUrl(): string {
|
|
904
|
+
// The URL must URL-encode the runtime ARN (':' -> '%3A', '/' -> '%2F').
|
|
905
|
+
// The ARN is a CDK token, so encode at deploy time via Fn.join/Fn.split.
|
|
906
|
+
const encodedArn = Fn.join(
|
|
907
|
+
'%2F',
|
|
908
|
+
Fn.split(
|
|
909
|
+
'/',
|
|
910
|
+
Fn.join('%3A', Fn.split(':', this.agentCoreRuntime.agentRuntimeArn)),
|
|
911
|
+
),
|
|
912
|
+
);
|
|
913
|
+
return \`https://bedrock-agentcore.\${Stack.of(this).region}.amazonaws.com/runtimes/\${encodedArn}/invocations?qualifier=DEFAULT\`;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* Grants IAM permissions to invoke this agent runtime.
|
|
918
|
+
*
|
|
919
|
+
* @param grantee - The IAM principal to grant permissions to
|
|
920
|
+
*/
|
|
921
|
+
public grantInvokeAccess(grantee: IGrantable) {
|
|
922
|
+
this.agentCoreRuntime.grantInvoke(grantee);
|
|
923
|
+
|
|
924
|
+
Grant.addToPrincipal({
|
|
925
|
+
grantee,
|
|
926
|
+
actions: ['bedrock-agentcore:InvokeAgentRuntimeWithWebSocketStream'],
|
|
927
|
+
resourceArns: [
|
|
928
|
+
this.agentCoreRuntime.agentRuntimeArn,
|
|
929
|
+
\`\${this.agentCoreRuntime.agentRuntimeArn}/*\`,
|
|
930
|
+
],
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
"
|
|
935
|
+
`;
|