@aws/nx-plugin 0.42.1 → 0.44.0
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/README.md +1 -0
- package/generators.json +6 -0
- package/package.json +7 -7
- package/sdk/py.d.ts +2 -0
- package/sdk/py.js +3 -1
- package/sdk/py.js.map +1 -1
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +14 -14
- package/src/license/__snapshots__/config.spec.ts.snap +3 -3
- package/src/license/config.js +5 -3
- package/src/license/config.js.map +1 -1
- package/src/mcp-server/tools/create-workspace-command.js +1 -1
- package/src/preset/__snapshots__/generator.spec.ts.snap +5 -3
- package/src/py/fast-api/generator.js +2 -7
- package/src/py/fast-api/generator.js.map +1 -1
- package/src/py/fast-api/react/generator.js +2 -2
- package/src/py/fast-api/react/generator.js.map +1 -1
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +27 -2
- package/src/py/mcp-server/files/Dockerfile.template +1 -5
- package/src/py/mcp-server/generator.js +10 -6
- package/src/py/mcp-server/generator.js.map +1 -1
- package/src/py/mcp-server/schema.json +3 -3
- package/src/py/project/generator.js +16 -1
- package/src/py/project/generator.js.map +1 -1
- package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +375 -0
- package/src/py/strands-agent/files/Dockerfile.template +14 -0
- package/src/py/strands-agent/files/__init__.py.template +0 -0
- package/src/py/strands-agent/files/agent.py.template +22 -0
- package/src/py/strands-agent/files/agentcore_mcp_client.py.template +99 -0
- package/src/py/strands-agent/files/main.py.template +24 -0
- package/src/py/strands-agent/generator.d.ts +10 -0
- package/src/py/strands-agent/generator.js +137 -0
- package/src/py/strands-agent/generator.js.map +1 -0
- package/src/py/strands-agent/schema.d.ts +12 -0
- package/src/py/strands-agent/schema.json +33 -0
- package/src/terraform/project/files/application/src/main.tf.template +5 -0
- package/src/terraform/project/generator.js +13 -5
- package/src/terraform/project/generator.js.map +1 -1
- package/src/trpc/backend/generator.js +27 -33
- package/src/trpc/backend/generator.js.map +1 -1
- package/src/trpc/react/generator.js +2 -2
- package/src/trpc/react/generator.js.map +1 -1
- package/src/ts/lib/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +22 -1
- package/src/ts/mcp-server/generator.js +4 -3
- package/src/ts/mcp-server/generator.js.map +1 -1
- package/src/ts/mcp-server/schema.json +3 -3
- package/src/ts/nx-plugin/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/nx-plugin/files/mcp-server/tools/create-workspace-command.ts.template +1 -1
- package/src/ts/nx-plugin/generator.js +4 -1
- package/src/ts/nx-plugin/generator.js.map +1 -1
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +33 -484
- package/src/ts/react-website/cognito-auth/generator.js +0 -25
- package/src/ts/react-website/cognito-auth/generator.js.map +1 -1
- package/src/ts/react-website/runtime-config/__snapshots__/generator.spec.ts.snap +6 -5
- package/src/ts/react-website/runtime-config/files/app/components/RuntimeConfig/index.tsx.template +3 -1
- package/src/ts/react-website/runtime-config/files/app/hooks/useRuntimeConfig.tsx.template +1 -2
- package/src/utils/__snapshots__/shared-constructs.spec.ts.snap +3 -15
- package/src/utils/agent-core-constructs/agent-core-constructs.d.ts +16 -0
- package/src/utils/agent-core-constructs/agent-core-constructs.js +33 -8
- package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
- package/src/utils/agent-core-constructs/files/app/{mcp-servers/__mcpServerNameKebabCase__/__mcpServerNameKebabCase__.ts.template → agent-core/__nameKebabCase__/__nameKebabCase__.ts.template} +5 -5
- package/src/utils/agent-core-constructs/files/core/agent-core/runtime.ts.template +22 -1
- package/src/utils/api-constructs/api-constructs.js +0 -30
- package/src/utils/api-constructs/api-constructs.js.map +1 -1
- package/src/utils/files/common/constructs/src/core/runtime-config.ts.template +3 -4
- package/src/utils/files/terraform/src/metrics/metrics.tf.template +19 -0
- package/src/utils/metrics.d.ts +3 -1
- package/src/utils/metrics.js +36 -3
- package/src/utils/metrics.js.map +1 -1
- package/src/utils/port.d.ts +4 -6
- package/src/utils/port.js +32 -12
- package/src/utils/port.js.map +1 -1
- package/src/utils/shared-constructs-constants.d.ts +0 -2
- package/src/utils/shared-constructs-constants.js +1 -3
- package/src/utils/shared-constructs-constants.js.map +1 -1
- package/src/utils/shared-constructs.d.ts +4 -1
- package/src/utils/shared-constructs.js +49 -45
- package/src/utils/shared-constructs.js.map +1 -1
- package/src/utils/versions.d.ts +7 -2
- package/src/utils/versions.js +6 -1
- package/src/utils/versions.js.map +1 -1
- package/src/utils/files/common/types/src/index.ts.template +0 -1
- package/src/utils/files/common/types/src/runtime-config.ts.template +0 -2
- /package/src/utils/agent-core-constructs/files/app/{mcp-servers/__mcpServerNameKebabCase__ → agent-core/__nameKebabCase__}/Dockerfile.template +0 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`py#strands-agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > agent-Dockerfile 1`] = `
|
|
4
|
+
"FROM public.ecr.aws/docker/library/python:3.12-slim
|
|
5
|
+
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
# Copy bundled package
|
|
9
|
+
COPY --from=workspace dist/apps/test-project/bundle /app
|
|
10
|
+
|
|
11
|
+
EXPOSE 8080
|
|
12
|
+
|
|
13
|
+
ENV PYTHONPATH=/app
|
|
14
|
+
|
|
15
|
+
# Auto-instrument with AWS Distro for OpenTelemetry
|
|
16
|
+
# https://aws-otel.github.io/docs/getting-started/python-sdk/auto-instr
|
|
17
|
+
CMD ["python", "bin/opentelemetry-instrument", "python", "-m", "proj_test_project.snapshot_bedrock_agent.main"]
|
|
18
|
+
"
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
exports[`py#strands-agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > agent-construct.ts 1`] = `
|
|
22
|
+
"import { DockerImageAsset, Platform } from 'aws-cdk-lib/aws-ecr-assets';
|
|
23
|
+
import { Construct } from 'constructs';
|
|
24
|
+
import { execSync } from 'child_process';
|
|
25
|
+
import * as path from 'path';
|
|
26
|
+
import * as url from 'url';
|
|
27
|
+
import {
|
|
28
|
+
AgentCoreRuntime,
|
|
29
|
+
AgentCoreRuntimeProps,
|
|
30
|
+
} from '../../../core/agent-core/runtime.js';
|
|
31
|
+
|
|
32
|
+
export type SnapshotBedrockAgentProps = Omit<
|
|
33
|
+
AgentCoreRuntimeProps,
|
|
34
|
+
'runtimeName' | 'serverProtocol' | 'containerUri'
|
|
35
|
+
>;
|
|
36
|
+
|
|
37
|
+
export class SnapshotBedrockAgent extends Construct {
|
|
38
|
+
public readonly dockerImage: DockerImageAsset;
|
|
39
|
+
public readonly agentCoreRuntime: AgentCoreRuntime;
|
|
40
|
+
|
|
41
|
+
constructor(scope: Construct, id: string, props?: SnapshotBedrockAgentProps) {
|
|
42
|
+
super(scope, id);
|
|
43
|
+
|
|
44
|
+
this.dockerImage = new DockerImageAsset(this, 'DockerImage', {
|
|
45
|
+
platform: Platform.LINUX_ARM64,
|
|
46
|
+
directory: path.dirname(url.fileURLToPath(new URL(import.meta.url))),
|
|
47
|
+
extraHash: execSync(
|
|
48
|
+
\`docker inspect proj-snapshot-bedrock-agent:latest --format '{{.Descriptor.digest}}'\`,
|
|
49
|
+
{ encoding: 'utf-8' },
|
|
50
|
+
).trim(),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
this.agentCoreRuntime = new AgentCoreRuntime(this, 'AgentCore', {
|
|
54
|
+
runtimeName: 'SnapshotBedrockAgent',
|
|
55
|
+
serverProtocol: 'HTTP',
|
|
56
|
+
containerUri: this.dockerImage.imageUri,
|
|
57
|
+
...props,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
"
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
exports[`py#strands-agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > agent-core-runtime.ts 1`] = `
|
|
65
|
+
"import {
|
|
66
|
+
Role,
|
|
67
|
+
ServicePrincipal,
|
|
68
|
+
PolicyStatement,
|
|
69
|
+
Effect,
|
|
70
|
+
PolicyDocument,
|
|
71
|
+
IGrantable,
|
|
72
|
+
Grant,
|
|
73
|
+
IPrincipal,
|
|
74
|
+
} from 'aws-cdk-lib/aws-iam';
|
|
75
|
+
import {
|
|
76
|
+
AwsCustomResource,
|
|
77
|
+
AwsCustomResourcePolicy,
|
|
78
|
+
PhysicalResourceId,
|
|
79
|
+
PhysicalResourceIdReference,
|
|
80
|
+
} from 'aws-cdk-lib/custom-resources';
|
|
81
|
+
import { Construct } from 'constructs';
|
|
82
|
+
import type { AuthorizerConfiguration } from '@aws-sdk/client-bedrock-agentcore-control';
|
|
83
|
+
import { Stack } from 'aws-cdk-lib';
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Options for the AgentCoreRuntime construct
|
|
87
|
+
*/
|
|
88
|
+
export interface AgentCoreRuntimeProps {
|
|
89
|
+
runtimeName: string;
|
|
90
|
+
description?: string;
|
|
91
|
+
containerUri: string;
|
|
92
|
+
serverProtocol: 'MCP' | 'HTTP';
|
|
93
|
+
environment?: Record<string, string>;
|
|
94
|
+
authorizerConfiguration?: AuthorizerConfiguration;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* A construct for creating a Bedrock AgentCore Runtime
|
|
99
|
+
*/
|
|
100
|
+
export class AgentCoreRuntime extends Construct implements IGrantable {
|
|
101
|
+
public readonly role: Role;
|
|
102
|
+
public readonly arn: string;
|
|
103
|
+
|
|
104
|
+
public readonly grantPrincipal: IPrincipal;
|
|
105
|
+
|
|
106
|
+
constructor(scope: Construct, id: string, props: AgentCoreRuntimeProps) {
|
|
107
|
+
super(scope, id);
|
|
108
|
+
|
|
109
|
+
const region = Stack.of(this).region;
|
|
110
|
+
const accountId = Stack.of(this).account;
|
|
111
|
+
|
|
112
|
+
this.role = new Role(this, 'AgentCoreRole', {
|
|
113
|
+
assumedBy: new ServicePrincipal('bedrock-agentcore.amazonaws.com'),
|
|
114
|
+
inlinePolicies: {
|
|
115
|
+
AgentCorePolicy: new PolicyDocument({
|
|
116
|
+
statements: [
|
|
117
|
+
new PolicyStatement({
|
|
118
|
+
sid: 'ECRImageAccess',
|
|
119
|
+
effect: Effect.ALLOW,
|
|
120
|
+
actions: ['ecr:BatchGetImage', 'ecr:GetDownloadUrlForLayer'],
|
|
121
|
+
resources: [\`arn:aws:ecr:\${region}:\${accountId}:repository/*\`],
|
|
122
|
+
}),
|
|
123
|
+
new PolicyStatement({
|
|
124
|
+
effect: Effect.ALLOW,
|
|
125
|
+
actions: ['logs:DescribeLogStreams', 'logs:CreateLogGroup'],
|
|
126
|
+
resources: [
|
|
127
|
+
\`arn:aws:logs:\${region}:\${accountId}:log-group:/aws/bedrock-agentcore/runtimes/*\`,
|
|
128
|
+
],
|
|
129
|
+
}),
|
|
130
|
+
new PolicyStatement({
|
|
131
|
+
effect: Effect.ALLOW,
|
|
132
|
+
actions: ['logs:DescribeLogGroups'],
|
|
133
|
+
resources: [\`arn:aws:logs:\${region}:\${accountId}:log-group:*\`],
|
|
134
|
+
}),
|
|
135
|
+
new PolicyStatement({
|
|
136
|
+
effect: Effect.ALLOW,
|
|
137
|
+
actions: ['logs:CreateLogStream', 'logs:PutLogEvents'],
|
|
138
|
+
resources: [
|
|
139
|
+
\`arn:aws:logs:\${region}:\${accountId}:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*\`,
|
|
140
|
+
],
|
|
141
|
+
}),
|
|
142
|
+
new PolicyStatement({
|
|
143
|
+
sid: 'ECRTokenAccess',
|
|
144
|
+
effect: Effect.ALLOW,
|
|
145
|
+
actions: ['ecr:GetAuthorizationToken'],
|
|
146
|
+
resources: ['*'],
|
|
147
|
+
}),
|
|
148
|
+
new PolicyStatement({
|
|
149
|
+
effect: Effect.ALLOW,
|
|
150
|
+
actions: [
|
|
151
|
+
'xray:PutTraceSegments',
|
|
152
|
+
'xray:PutTelemetryRecords',
|
|
153
|
+
'xray:GetSamplingRules',
|
|
154
|
+
'xray:GetSamplingTargets',
|
|
155
|
+
],
|
|
156
|
+
resources: ['*'],
|
|
157
|
+
}),
|
|
158
|
+
new PolicyStatement({
|
|
159
|
+
effect: Effect.ALLOW,
|
|
160
|
+
actions: ['cloudwatch:PutMetricData'],
|
|
161
|
+
resources: ['*'],
|
|
162
|
+
conditions: {
|
|
163
|
+
StringEquals: {
|
|
164
|
+
'cloudwatch:namespace': 'bedrock-agentcore',
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
}),
|
|
168
|
+
new PolicyStatement({
|
|
169
|
+
sid: 'GetAgentAccessToken',
|
|
170
|
+
effect: Effect.ALLOW,
|
|
171
|
+
actions: [
|
|
172
|
+
'bedrock-agentcore:GetWorkloadAccessToken',
|
|
173
|
+
'bedrock-agentcore:GetWorkloadAccessTokenForJWT',
|
|
174
|
+
'bedrock-agentcore:GetWorkloadAccessTokenForUserId',
|
|
175
|
+
],
|
|
176
|
+
resources: [
|
|
177
|
+
\`arn:aws:bedrock-agentcore:\${region}:\${accountId}:workload-identity-directory/default\`,
|
|
178
|
+
\`arn:aws:bedrock-agentcore:\${region}:\${accountId}:workload-identity-directory/default/workload-identity/*\`,
|
|
179
|
+
],
|
|
180
|
+
}),
|
|
181
|
+
new PolicyStatement({
|
|
182
|
+
sid: 'BedrockModelInvocation',
|
|
183
|
+
effect: Effect.ALLOW,
|
|
184
|
+
actions: [
|
|
185
|
+
'bedrock:InvokeModel',
|
|
186
|
+
'bedrock:InvokeModelWithResponseStream',
|
|
187
|
+
],
|
|
188
|
+
resources: [
|
|
189
|
+
'arn:aws:bedrock:*::foundation-model/*',
|
|
190
|
+
\`arn:aws:bedrock:\${region}:\${accountId}:*\`,
|
|
191
|
+
],
|
|
192
|
+
}),
|
|
193
|
+
],
|
|
194
|
+
}),
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
this.grantPrincipal = this.role.grantPrincipal;
|
|
198
|
+
|
|
199
|
+
const agentRuntime = new AwsCustomResource(this, 'MCPSeverRuntime', {
|
|
200
|
+
onCreate: {
|
|
201
|
+
service: 'bedrock-agentcore-control',
|
|
202
|
+
action: 'CreateAgentRuntime',
|
|
203
|
+
parameters: {
|
|
204
|
+
agentRuntimeName: props.runtimeName,
|
|
205
|
+
agentRuntimeArtifact: {
|
|
206
|
+
containerConfiguration: {
|
|
207
|
+
containerUri: props.containerUri,
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
description: props.description,
|
|
211
|
+
environmentVariables: props.environment,
|
|
212
|
+
networkConfiguration: {
|
|
213
|
+
networkMode: 'PUBLIC',
|
|
214
|
+
},
|
|
215
|
+
protocolConfiguration: {
|
|
216
|
+
serverProtocol: props.serverProtocol,
|
|
217
|
+
},
|
|
218
|
+
roleArn: this.role.roleArn,
|
|
219
|
+
authorizerConfiguration: props.authorizerConfiguration,
|
|
220
|
+
},
|
|
221
|
+
physicalResourceId: PhysicalResourceId.fromResponse('agentRuntimeId'),
|
|
222
|
+
},
|
|
223
|
+
onUpdate: {
|
|
224
|
+
service: 'bedrock-agentcore-control',
|
|
225
|
+
action: 'UpdateAgentRuntime',
|
|
226
|
+
parameters: {
|
|
227
|
+
agentRuntimeId: new PhysicalResourceIdReference(),
|
|
228
|
+
agentRuntimeName: props.runtimeName,
|
|
229
|
+
agentRuntimeArtifact: {
|
|
230
|
+
containerConfiguration: {
|
|
231
|
+
containerUri: props.containerUri,
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
description: props.description,
|
|
235
|
+
environmentVariables: props.environment,
|
|
236
|
+
networkConfiguration: {
|
|
237
|
+
networkMode: 'PUBLIC',
|
|
238
|
+
},
|
|
239
|
+
protocolConfiguration: {
|
|
240
|
+
serverProtocol: props.serverProtocol,
|
|
241
|
+
},
|
|
242
|
+
roleArn: this.role.roleArn,
|
|
243
|
+
authorizerConfiguration: props.authorizerConfiguration,
|
|
244
|
+
},
|
|
245
|
+
physicalResourceId: PhysicalResourceId.fromResponse('agentRuntimeId'),
|
|
246
|
+
},
|
|
247
|
+
onDelete: {
|
|
248
|
+
service: 'bedrock-agentcore-control',
|
|
249
|
+
action: 'DeleteAgentRuntime',
|
|
250
|
+
parameters: {
|
|
251
|
+
agentRuntimeId: new PhysicalResourceIdReference(),
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
policy: AwsCustomResourcePolicy.fromStatements([
|
|
255
|
+
new PolicyStatement({
|
|
256
|
+
actions: ['bedrock-agentcore:*'],
|
|
257
|
+
resources: ['*'],
|
|
258
|
+
}),
|
|
259
|
+
new PolicyStatement({
|
|
260
|
+
actions: ['iam:PassRole'],
|
|
261
|
+
resources: [this.role.roleArn],
|
|
262
|
+
}),
|
|
263
|
+
]),
|
|
264
|
+
installLatestAwsSdk: true,
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
this.arn = agentRuntime.getResponseField('agentRuntimeArn');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Grant permissions to invoke the agent runtime (if using IAM auth - not required for JWT auth)
|
|
272
|
+
*/
|
|
273
|
+
public grantInvoke = (grantee: IGrantable) => {
|
|
274
|
+
Grant.addToPrincipal({
|
|
275
|
+
grantee,
|
|
276
|
+
actions: ['bedrock-agentcore:InvokeAgentRuntime'],
|
|
277
|
+
resourceArns: [this.arn, \`\${this.arn}/*\`],
|
|
278
|
+
});
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
"
|
|
282
|
+
`;
|
|
283
|
+
|
|
284
|
+
exports[`py#strands-agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > agents-index.ts 1`] = `
|
|
285
|
+
"export * from './snapshot-bedrock-agent/snapshot-bedrock-agent.js';
|
|
286
|
+
"
|
|
287
|
+
`;
|
|
288
|
+
|
|
289
|
+
exports[`py#strands-agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > app-index.ts 1`] = `
|
|
290
|
+
"export * from './agents/index.js';
|
|
291
|
+
"
|
|
292
|
+
`;
|
|
293
|
+
|
|
294
|
+
exports[`py#strands-agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > core-index.ts 1`] = `
|
|
295
|
+
"export * from './app.js';
|
|
296
|
+
export * from './runtime-config.js';
|
|
297
|
+
"
|
|
298
|
+
`;
|
|
299
|
+
|
|
300
|
+
exports[`py#strands-agent generator > should match snapshot for generated files > strands-agent-__init__.py 1`] = `""`;
|
|
301
|
+
|
|
302
|
+
exports[`py#strands-agent generator > should match snapshot for generated files > strands-agent-agent.py 1`] = `
|
|
303
|
+
"from contextlib import contextmanager
|
|
304
|
+
|
|
305
|
+
from strands import Agent, tool
|
|
306
|
+
from strands_tools import current_time
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
# Define a custom tool
|
|
310
|
+
@tool
|
|
311
|
+
def add(a: int, b: int) -> int:
|
|
312
|
+
return a + b
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
@contextmanager
|
|
316
|
+
def get_agent(session_id: str):
|
|
317
|
+
yield Agent(
|
|
318
|
+
system_prompt="""
|
|
319
|
+
You are an addition wizard.
|
|
320
|
+
Use the 'add' tool for addition tasks.
|
|
321
|
+
Refer to tools as your 'spellbook'.
|
|
322
|
+
""",
|
|
323
|
+
tools=[add, current_time],
|
|
324
|
+
)
|
|
325
|
+
"
|
|
326
|
+
`;
|
|
327
|
+
|
|
328
|
+
exports[`py#strands-agent generator > should match snapshot for generated files > strands-agent-main.py 1`] = `
|
|
329
|
+
"from bedrock_agentcore.runtime import BedrockAgentCoreApp
|
|
330
|
+
|
|
331
|
+
from .agent import get_agent
|
|
332
|
+
|
|
333
|
+
app = BedrockAgentCoreApp()
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
@app.entrypoint
|
|
337
|
+
async def invoke(payload, context):
|
|
338
|
+
"""Handler for agent invocation"""
|
|
339
|
+
prompt = payload.get(
|
|
340
|
+
"prompt", "No prompt found in input, please guide the user "
|
|
341
|
+
"to create a json payload with prompt key"
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
with get_agent(session_id=context.session_id) as agent:
|
|
345
|
+
stream = agent.stream_async(prompt)
|
|
346
|
+
async for event in stream:
|
|
347
|
+
print(event)
|
|
348
|
+
yield (event)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
if __name__ == "__main__":
|
|
352
|
+
app.run()
|
|
353
|
+
"
|
|
354
|
+
`;
|
|
355
|
+
|
|
356
|
+
exports[`py#strands-agent generator > should match snapshot for generated files > updated-pyproject.toml 1`] = `
|
|
357
|
+
"[project]
|
|
358
|
+
name = "proj.test_project"
|
|
359
|
+
version = "0.1.0"
|
|
360
|
+
dependencies = [
|
|
361
|
+
"aws-opentelemetry-distro~=0.11.0",
|
|
362
|
+
"bedrock-agentcore~=0.1.2",
|
|
363
|
+
"boto3~=1.40.14",
|
|
364
|
+
"mcp~=1.13.0",
|
|
365
|
+
"strands-agents~=1.5.0",
|
|
366
|
+
"strands-agents-tools~=0.2.4"
|
|
367
|
+
]
|
|
368
|
+
|
|
369
|
+
[dependency-groups]
|
|
370
|
+
dev = [ ]
|
|
371
|
+
|
|
372
|
+
[tool.uv]
|
|
373
|
+
dev-dependencies = [ ]
|
|
374
|
+
"
|
|
375
|
+
`;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
FROM public.ecr.aws/docker/library/python:3.12-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# Copy bundled package
|
|
6
|
+
COPY --from=workspace <%- distDir %>/bundle /app
|
|
7
|
+
|
|
8
|
+
EXPOSE 8080
|
|
9
|
+
|
|
10
|
+
ENV PYTHONPATH=/app
|
|
11
|
+
|
|
12
|
+
# Auto-instrument with AWS Distro for OpenTelemetry
|
|
13
|
+
# https://aws-otel.github.io/docs/getting-started/python-sdk/auto-instr
|
|
14
|
+
CMD ["python", "bin/opentelemetry-instrument", "python", "-m", "<%- moduleName %>.<%- agentNameSnakeCase %>.main"]
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
|
|
3
|
+
from strands import Agent, tool
|
|
4
|
+
from strands_tools import current_time
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Define a custom tool
|
|
8
|
+
@tool
|
|
9
|
+
def add(a: int, b: int) -> int:
|
|
10
|
+
return a + b
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@contextmanager
|
|
14
|
+
def get_agent(session_id: str):
|
|
15
|
+
yield Agent(
|
|
16
|
+
system_prompt="""
|
|
17
|
+
You are an addition wizard.
|
|
18
|
+
Use the 'add' tool for addition tasks.
|
|
19
|
+
Refer to tools as your 'spellbook'.
|
|
20
|
+
""",
|
|
21
|
+
tools=[add, current_time],
|
|
22
|
+
)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
from collections.abc import Generator
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
from botocore.auth import SigV4Auth
|
|
7
|
+
from botocore.awsrequest import AWSRequest
|
|
8
|
+
from mcp.client.streamable_http import streamablehttp_client
|
|
9
|
+
from strands.tools.mcp.mcp_client import MCPClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SigV4HTTPXAuth(httpx.Auth):
|
|
13
|
+
"""HTTPX Auth class that signs requests with AWS SigV4."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, credentials: Any, region: str):
|
|
16
|
+
self.credentials = credentials
|
|
17
|
+
self.service = "bedrock-agentcore"
|
|
18
|
+
self.region = region
|
|
19
|
+
self.signer = SigV4Auth(credentials, self.service, region)
|
|
20
|
+
|
|
21
|
+
def auth_flow(
|
|
22
|
+
self, request: httpx.Request
|
|
23
|
+
) -> Generator[httpx.Request, httpx.Response, None]:
|
|
24
|
+
headers = dict(request.headers)
|
|
25
|
+
|
|
26
|
+
headers.pop("connection", None)
|
|
27
|
+
headers["x-amz-content-sha256"] = hashlib.sha256(
|
|
28
|
+
request.content if request.content else b""
|
|
29
|
+
).hexdigest()
|
|
30
|
+
|
|
31
|
+
aws_request = AWSRequest(
|
|
32
|
+
method=request.method,
|
|
33
|
+
url=str(request.url),
|
|
34
|
+
data=request.content,
|
|
35
|
+
headers=headers,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
self.signer.add_auth(aws_request)
|
|
39
|
+
|
|
40
|
+
request.headers.clear()
|
|
41
|
+
request.headers.update(dict(aws_request.headers))
|
|
42
|
+
|
|
43
|
+
yield request
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class AgentCoreMCPClient:
|
|
47
|
+
"""Factory for clients to call MCP servers hosted on Bedrock AgentCore Runtime"""
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def _create(
|
|
51
|
+
agent_runtime_arn: str,
|
|
52
|
+
region: str,
|
|
53
|
+
session_id: str,
|
|
54
|
+
headers: dict = None,
|
|
55
|
+
auth_handler: httpx.Auth = None,
|
|
56
|
+
):
|
|
57
|
+
# Build the URL
|
|
58
|
+
encoded_arn = agent_runtime_arn.replace(":", "%3A").replace("/", "%2F")
|
|
59
|
+
url = f"https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{encoded_arn}/invocations?qualifier=DEFAULT"
|
|
60
|
+
|
|
61
|
+
# Create and return the MCP client
|
|
62
|
+
return MCPClient(
|
|
63
|
+
lambda: streamablehttp_client(
|
|
64
|
+
url,
|
|
65
|
+
auth=auth_handler,
|
|
66
|
+
timeout=120,
|
|
67
|
+
terminate_on_close=False,
|
|
68
|
+
headers={
|
|
69
|
+
"X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": session_id,
|
|
70
|
+
**(headers if headers is not None else {}),
|
|
71
|
+
},
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def with_iam_auth(
|
|
77
|
+
agent_runtime_arn: str, credentials: Any, region: str, session_id: str
|
|
78
|
+
) -> MCPClient:
|
|
79
|
+
"""Create an MCP client with IAM (SigV4) authentication."""
|
|
80
|
+
return AgentCoreMCPClient._create(
|
|
81
|
+
agent_runtime_arn=agent_runtime_arn,
|
|
82
|
+
region=region,
|
|
83
|
+
session_id=session_id,
|
|
84
|
+
auth_handler=SigV4HTTPXAuth(credentials, region),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def with_jwt_auth(
|
|
89
|
+
agent_runtime_arn: str, access_token: str, region: str, session_id: str
|
|
90
|
+
) -> MCPClient:
|
|
91
|
+
"""Create an MCP client with JWT authentication."""
|
|
92
|
+
return AgentCoreMCPClient._create(
|
|
93
|
+
agent_runtime_arn=agent_runtime_arn,
|
|
94
|
+
region=region,
|
|
95
|
+
session_id=session_id,
|
|
96
|
+
headers={
|
|
97
|
+
"Authorization": f"Bearer {access_token}",
|
|
98
|
+
},
|
|
99
|
+
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from bedrock_agentcore.runtime import BedrockAgentCoreApp
|
|
2
|
+
|
|
3
|
+
from .agent import get_agent
|
|
4
|
+
|
|
5
|
+
app = BedrockAgentCoreApp()
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@app.entrypoint
|
|
9
|
+
async def invoke(payload, context):
|
|
10
|
+
"""Handler for agent invocation"""
|
|
11
|
+
prompt = payload.get(
|
|
12
|
+
"prompt", "No prompt found in input, please guide the user "
|
|
13
|
+
"to create a json payload with prompt key"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
with get_agent(session_id=context.session_id) as agent:
|
|
17
|
+
stream = agent.stream_async(prompt)
|
|
18
|
+
async for event in stream:
|
|
19
|
+
print(event)
|
|
20
|
+
yield (event)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if __name__ == "__main__":
|
|
24
|
+
app.run()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { GeneratorCallback, Tree } from '@nx/devkit';
|
|
6
|
+
import { PyStrandsAgentGeneratorSchema } from './schema';
|
|
7
|
+
import { NxGeneratorInfo } from '../../utils/nx';
|
|
8
|
+
export declare const PY_STRANDS_AGENT_GENERATOR_INFO: NxGeneratorInfo;
|
|
9
|
+
export declare const pyStrandsAgentGenerator: (tree: Tree, options: PyStrandsAgentGeneratorSchema) => Promise<GeneratorCallback>;
|
|
10
|
+
export default pyStrandsAgentGenerator;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pyStrandsAgentGenerator = exports.PY_STRANDS_AGENT_GENERATOR_INFO = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
/**
|
|
6
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
7
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
8
|
+
*/
|
|
9
|
+
const devkit_1 = require("@nx/devkit");
|
|
10
|
+
const nx_1 = require("../../utils/nx");
|
|
11
|
+
const metrics_1 = require("../../utils/metrics");
|
|
12
|
+
const format_1 = require("../../utils/format");
|
|
13
|
+
const names_1 = require("../../utils/names");
|
|
14
|
+
const py_1 = require("../../utils/py");
|
|
15
|
+
const agent_core_constructs_1 = require("../../utils/agent-core-constructs/agent-core-constructs");
|
|
16
|
+
const bundle_1 = require("../../utils/bundle");
|
|
17
|
+
const npm_scope_1 = require("../../utils/npm-scope");
|
|
18
|
+
const shared_constructs_1 = require("../../utils/shared-constructs");
|
|
19
|
+
const shared_constructs_constants_1 = require("../../utils/shared-constructs-constants");
|
|
20
|
+
const logger_1 = require("@nxlv/python/src/executors/utils/logger");
|
|
21
|
+
const provider_1 = require("@nxlv/python/src/provider/uv/provider");
|
|
22
|
+
exports.PY_STRANDS_AGENT_GENERATOR_INFO = (0, nx_1.getGeneratorInfo)(__filename);
|
|
23
|
+
const pyStrandsAgentGenerator = (tree, options) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
24
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
25
|
+
const project = (0, nx_1.readProjectConfigurationUnqualified)(tree, options.project);
|
|
26
|
+
const pyProjectPath = (0, devkit_1.joinPathFragments)(project.root, 'pyproject.toml');
|
|
27
|
+
// Check if the project has a pyproject.toml file
|
|
28
|
+
if (!pyProjectPath) {
|
|
29
|
+
throw new Error(`Unsupported project ${options.project}. Expected a Python project (with a pyproject.toml)`);
|
|
30
|
+
}
|
|
31
|
+
if (!project.sourceRoot) {
|
|
32
|
+
throw new Error(`This project does not have a source root. Please add a source root to the project configuration before running this generator.`);
|
|
33
|
+
}
|
|
34
|
+
// Module name is the last part of the source root,
|
|
35
|
+
const sourceParts = project.sourceRoot.split('/');
|
|
36
|
+
const moduleName = sourceParts[sourceParts.length - 1];
|
|
37
|
+
const name = (0, names_1.kebabCase)((_a = options.name) !== null && _a !== void 0 ? _a : `${(0, names_1.kebabCase)(project.name.split('.').pop())}-agent`);
|
|
38
|
+
const agentNameSnakeCase = (0, names_1.toSnakeCase)((_b = options.name) !== null && _b !== void 0 ? _b : 'agent');
|
|
39
|
+
const agentNameClassName = (0, names_1.toClassName)(name);
|
|
40
|
+
const targetSourceDir = (0, devkit_1.joinPathFragments)(project.sourceRoot, agentNameSnakeCase);
|
|
41
|
+
const distDir = (0, devkit_1.joinPathFragments)('dist', project.root);
|
|
42
|
+
const computeType = (_c = options.computeType) !== null && _c !== void 0 ? _c : 'BedrockAgentCoreRuntime';
|
|
43
|
+
// Generate example agent
|
|
44
|
+
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files'), targetSourceDir, {
|
|
45
|
+
name,
|
|
46
|
+
agentNameSnakeCase,
|
|
47
|
+
agentNameClassName,
|
|
48
|
+
moduleName,
|
|
49
|
+
distDir,
|
|
50
|
+
}, { overwriteStrategy: devkit_1.OverwriteStrategy.KeepExisting });
|
|
51
|
+
(0, py_1.addDependenciesToPyProjectToml)(tree, project.root, [
|
|
52
|
+
'aws-opentelemetry-distro',
|
|
53
|
+
'bedrock-agentcore',
|
|
54
|
+
'boto3',
|
|
55
|
+
'mcp',
|
|
56
|
+
'strands-agents',
|
|
57
|
+
'strands-agents-tools',
|
|
58
|
+
]);
|
|
59
|
+
if (computeType === 'BedrockAgentCoreRuntime') {
|
|
60
|
+
const dockerImageTag = `${(0, npm_scope_1.getNpmScope)(tree)}-${name}:latest`;
|
|
61
|
+
// Add bundle target
|
|
62
|
+
(0, bundle_1.addPythonBundleTarget)(project, {
|
|
63
|
+
pythonPlatform: 'aarch64-manylinux2014',
|
|
64
|
+
});
|
|
65
|
+
const dockerTargetName = `${name}-docker`;
|
|
66
|
+
// Add a docker target specific to this MCP server
|
|
67
|
+
project.targets[dockerTargetName] = {
|
|
68
|
+
cache: true,
|
|
69
|
+
executor: 'nx:run-commands',
|
|
70
|
+
options: {
|
|
71
|
+
commands: [
|
|
72
|
+
`docker build --platform linux/arm64 -t ${dockerImageTag} ${targetSourceDir} --build-context workspace=.`,
|
|
73
|
+
],
|
|
74
|
+
parallel: false,
|
|
75
|
+
},
|
|
76
|
+
dependsOn: ['bundle'],
|
|
77
|
+
};
|
|
78
|
+
project.targets.docker = Object.assign(Object.assign({}, project.targets.docker), { dependsOn: [
|
|
79
|
+
...((_e = (_d = project.targets.docker) === null || _d === void 0 ? void 0 : _d.dependsOn) !== null && _e !== void 0 ? _e : []).filter((t) => t !== dockerTargetName),
|
|
80
|
+
dockerTargetName,
|
|
81
|
+
] });
|
|
82
|
+
project.targets.build = Object.assign(Object.assign({}, project.targets.build), { dependsOn: [
|
|
83
|
+
...((_g = (_f = project.targets.build) === null || _f === void 0 ? void 0 : _f.dependsOn) !== null && _g !== void 0 ? _g : []).filter((t) => t !== 'docker'),
|
|
84
|
+
'docker',
|
|
85
|
+
] });
|
|
86
|
+
// Add shared constructs
|
|
87
|
+
yield (0, shared_constructs_1.sharedConstructsGenerator)(tree);
|
|
88
|
+
// Ensure common constructs builds after our agent project
|
|
89
|
+
(0, devkit_1.updateJson)(tree, (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_CONSTRUCTS_DIR, 'project.json'), (config) => {
|
|
90
|
+
var _a;
|
|
91
|
+
if (!config.targets) {
|
|
92
|
+
config.targets = {};
|
|
93
|
+
}
|
|
94
|
+
if (!config.targets.build) {
|
|
95
|
+
config.targets.build = {};
|
|
96
|
+
}
|
|
97
|
+
config.targets.build.dependsOn = [
|
|
98
|
+
...((_a = config.targets.build.dependsOn) !== null && _a !== void 0 ? _a : []).filter((t) => t !== `${project.name}:build`),
|
|
99
|
+
`${project.name}:build`,
|
|
100
|
+
];
|
|
101
|
+
return config;
|
|
102
|
+
});
|
|
103
|
+
// Add the construct to deploy the agent
|
|
104
|
+
(0, agent_core_constructs_1.addAgentConstruct)(tree, {
|
|
105
|
+
agentNameKebabCase: name,
|
|
106
|
+
agentNameClassName,
|
|
107
|
+
dockerImageTag,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// No Dockerfile needed for non-hosted Agent
|
|
112
|
+
tree.delete((0, devkit_1.joinPathFragments)(targetSourceDir, 'Dockerfile'));
|
|
113
|
+
}
|
|
114
|
+
(0, devkit_1.updateProjectConfiguration)(tree, project.name, Object.assign(Object.assign({}, project), { targets: Object.assign(Object.assign({}, project.targets), {
|
|
115
|
+
// TODO: Add hot-reload
|
|
116
|
+
[`${options.name ? name : 'agent'}-serve`]: {
|
|
117
|
+
executor: 'nx:run-commands',
|
|
118
|
+
options: {
|
|
119
|
+
commands: [
|
|
120
|
+
`uv run python -m ${moduleName}.${agentNameSnakeCase}.main`,
|
|
121
|
+
],
|
|
122
|
+
cwd: '{projectRoot}',
|
|
123
|
+
},
|
|
124
|
+
continuous: true,
|
|
125
|
+
} }) }));
|
|
126
|
+
yield (0, metrics_1.addGeneratorMetricsIfApplicable)(tree, [
|
|
127
|
+
exports.PY_STRANDS_AGENT_GENERATOR_INFO,
|
|
128
|
+
]);
|
|
129
|
+
yield (0, format_1.formatFilesInSubtree)(tree);
|
|
130
|
+
return () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
131
|
+
(0, devkit_1.installPackagesTask)(tree);
|
|
132
|
+
yield new provider_1.UVProvider(tree.root, new logger_1.Logger(), tree).install();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
exports.pyStrandsAgentGenerator = pyStrandsAgentGenerator;
|
|
136
|
+
exports.default = exports.pyStrandsAgentGenerator;
|
|
137
|
+
//# sourceMappingURL=generator.js.map
|