@aws/nx-plugin 1.0.0-rc.12 → 1.0.0-rc.13
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/LICENSE-THIRD-PARTY +81 -3
- package/package.json +2 -2
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +9 -9
- package/src/preset/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/py/agent/__snapshots__/generator.spec.ts.snap +574 -5
- package/src/py/agent/files/http/init.py.template +5 -23
- package/src/py/agent/generator.js +27 -22
- package/src/py/agent/generator.js.map +1 -1
- package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +5 -22
- package/src/py/fast-api/files/app/__name__/init.py.template +5 -22
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/agent/__snapshots__/generator.spec.ts.snap +564 -0
- package/src/ts/agent/generator.js +23 -19
- package/src/ts/agent/generator.js.map +1 -1
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +86 -39
- package/src/utils/agent-chat/agent-chat.d.ts +31 -0
- package/src/utils/agent-chat/agent-chat.js +30 -0
- package/src/utils/agent-chat/agent-chat.js.map +1 -0
- package/src/utils/agent-chat/files/a2a/chat.ts.template +33 -0
- package/src/utils/agent-chat/files/ag-ui/chat.ts.template +17 -0
- package/src/utils/agent-chat/files/common/agentcore.ts.template +81 -0
- package/src/utils/agent-chat/files/http-py/chat.ts.template +43 -0
- package/src/utils/agent-chat/files/http-ts/chat.ts.template +54 -0
- package/src/utils/versions.d.ts +44 -44
- package/src/utils/versions.js +43 -43
- package/src/utils/versions.js.map +1 -1
- package/src/py/agent/scripts/http/chat.ts.template +0 -38
- package/src/ts/agent/scripts/http/chat.ts.template +0 -35
|
@@ -1,5 +1,574 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
+
exports[`py#agent generator > chat scripts for a2a protocol > should match snapshot for chat scripts with cognito auth > agentcore.ts (a2a, cognito) 1`] = `
|
|
4
|
+
"// Resolves the deployed TestProjectAgent agent from runtime config and authenticates requests to it.
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';
|
|
7
|
+
|
|
8
|
+
const SESSION_HEADER = 'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id';
|
|
9
|
+
|
|
10
|
+
// AgentCore session ids must be at least 33 characters.
|
|
11
|
+
export const SESSION_ID = randomUUID().replaceAll('-', '').padEnd(33, '0');
|
|
12
|
+
|
|
13
|
+
export interface RemoteAgent {
|
|
14
|
+
/** ARN of the deployed Bedrock AgentCore runtime. */
|
|
15
|
+
arn: string;
|
|
16
|
+
/** AWS region parsed from the runtime ARN. */
|
|
17
|
+
region: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Returns the deployed agent when \`RUNTIME_CONFIG_APP_ID\` is set, otherwise \`undefined\` to chat locally.
|
|
21
|
+
export const resolveRemoteAgent = async (): Promise<
|
|
22
|
+
RemoteAgent | undefined
|
|
23
|
+
> => {
|
|
24
|
+
const application = process.env.RUNTIME_CONFIG_APP_ID;
|
|
25
|
+
if (!application) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const config = (await getAppConfig('agentcore', {
|
|
29
|
+
application,
|
|
30
|
+
environment: 'default',
|
|
31
|
+
transform: 'json',
|
|
32
|
+
})) as { agentRuntimes?: Record<string, string> };
|
|
33
|
+
const arn = config?.agentRuntimes?.['TestProjectAgent'];
|
|
34
|
+
if (!arn) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
\`No deployed agent named 'TestProjectAgent' found in runtime configuration (application \${application}).\`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return { arn, region: arn.split(':')[3] };
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** The Cognito access token used to authenticate with the deployed agent. */
|
|
43
|
+
export const getAccessToken = (): string => {
|
|
44
|
+
const accessToken = process.env.AGENT_ACCESS_TOKEN;
|
|
45
|
+
if (!accessToken) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
'AGENT_ACCESS_TOKEN is not set. Provide a Cognito access token to chat with the deployed agent.',
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return accessToken;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// A \`fetch\` that authenticates requests to the deployed agent and forwards the session id.
|
|
54
|
+
export const createAgentCoreFetch = (): typeof fetch => {
|
|
55
|
+
const accessToken = getAccessToken();
|
|
56
|
+
return (input, init) => {
|
|
57
|
+
const headers = new Headers(init?.headers);
|
|
58
|
+
headers.set(SESSION_HEADER, SESSION_ID);
|
|
59
|
+
headers.set('Authorization', \`Bearer \${accessToken}\`);
|
|
60
|
+
return fetch(input, { ...init, headers });
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
"
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
exports[`py#agent generator > chat scripts for a2a protocol > should match snapshot for chat scripts with cognito auth > chat.ts (a2a, cognito) 1`] = `
|
|
67
|
+
"// Chat CLI for TestProjectAgent (A2A protocol). Connects to the local
|
|
68
|
+
// \`serve-local\` server, or the deployed agent when \`RUNTIME_CONFIG_APP_ID\` is set.
|
|
69
|
+
import {
|
|
70
|
+
ClientFactory,
|
|
71
|
+
ClientFactoryOptions,
|
|
72
|
+
DefaultAgentCardResolver,
|
|
73
|
+
JsonRpcTransportFactory,
|
|
74
|
+
} from '@a2a-js/sdk/client';
|
|
75
|
+
import { A2AChatAdapter, chatLoop } from 'agent-chat-cli';
|
|
76
|
+
import { createAgentCoreFetch, resolveRemoteAgent } from './agentcore.js';
|
|
77
|
+
|
|
78
|
+
const remote = await resolveRemoteAgent();
|
|
79
|
+
|
|
80
|
+
const url = remote
|
|
81
|
+
? \`https://bedrock-agentcore.\${remote.region}.amazonaws.com/runtimes/\${encodeURIComponent(remote.arn)}/invocations/\`
|
|
82
|
+
: process.env.URL!;
|
|
83
|
+
|
|
84
|
+
// Remote requests are authenticated via a custom fetch; locally the default client talks plain HTTP.
|
|
85
|
+
const clientFactory = remote
|
|
86
|
+
? new ClientFactory({
|
|
87
|
+
...ClientFactoryOptions.default,
|
|
88
|
+
transports: [
|
|
89
|
+
new JsonRpcTransportFactory({
|
|
90
|
+
fetchImpl: createAgentCoreFetch(),
|
|
91
|
+
}),
|
|
92
|
+
],
|
|
93
|
+
cardResolver: new DefaultAgentCardResolver({
|
|
94
|
+
fetchImpl: createAgentCoreFetch(),
|
|
95
|
+
}),
|
|
96
|
+
})
|
|
97
|
+
: undefined;
|
|
98
|
+
|
|
99
|
+
await chatLoop(new A2AChatAdapter({ clientFactory }), url);
|
|
100
|
+
"
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
exports[`py#agent generator > chat scripts for a2a protocol > should match snapshot for chat scripts with iam auth > agentcore.ts (a2a, iam) 1`] = `
|
|
104
|
+
"// Resolves the deployed TestProjectAgent agent from runtime config and authenticates requests to it.
|
|
105
|
+
import { randomUUID } from 'node:crypto';
|
|
106
|
+
import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';
|
|
107
|
+
import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
|
|
108
|
+
import { AwsClient } from 'aws4fetch';
|
|
109
|
+
|
|
110
|
+
const SESSION_HEADER = 'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id';
|
|
111
|
+
|
|
112
|
+
// AgentCore session ids must be at least 33 characters.
|
|
113
|
+
export const SESSION_ID = randomUUID().replaceAll('-', '').padEnd(33, '0');
|
|
114
|
+
|
|
115
|
+
export interface RemoteAgent {
|
|
116
|
+
/** ARN of the deployed Bedrock AgentCore runtime. */
|
|
117
|
+
arn: string;
|
|
118
|
+
/** AWS region parsed from the runtime ARN. */
|
|
119
|
+
region: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Returns the deployed agent when \`RUNTIME_CONFIG_APP_ID\` is set, otherwise \`undefined\` to chat locally.
|
|
123
|
+
export const resolveRemoteAgent = async (): Promise<
|
|
124
|
+
RemoteAgent | undefined
|
|
125
|
+
> => {
|
|
126
|
+
const application = process.env.RUNTIME_CONFIG_APP_ID;
|
|
127
|
+
if (!application) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
const config = (await getAppConfig('agentcore', {
|
|
131
|
+
application,
|
|
132
|
+
environment: 'default',
|
|
133
|
+
transform: 'json',
|
|
134
|
+
})) as { agentRuntimes?: Record<string, string> };
|
|
135
|
+
const arn = config?.agentRuntimes?.['TestProjectAgent'];
|
|
136
|
+
if (!arn) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
\`No deployed agent named 'TestProjectAgent' found in runtime configuration (application \${application}).\`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
return { arn, region: arn.split(':')[3] };
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// A \`fetch\` that authenticates requests to the deployed agent and forwards the session id.
|
|
145
|
+
export const createAgentCoreFetch = (region: string): typeof fetch => {
|
|
146
|
+
const credentialProvider = fromNodeProviderChain();
|
|
147
|
+
return async (input, init) => {
|
|
148
|
+
const headers = new Headers(init?.headers);
|
|
149
|
+
headers.set(SESSION_HEADER, SESSION_ID);
|
|
150
|
+
const client = new AwsClient({
|
|
151
|
+
...(await credentialProvider()),
|
|
152
|
+
service: 'bedrock-agentcore',
|
|
153
|
+
region,
|
|
154
|
+
});
|
|
155
|
+
return client.fetch(input, { ...init, headers });
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
"
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
exports[`py#agent generator > chat scripts for a2a protocol > should match snapshot for chat scripts with iam auth > chat.ts (a2a, iam) 1`] = `
|
|
162
|
+
"// Chat CLI for TestProjectAgent (A2A protocol). Connects to the local
|
|
163
|
+
// \`serve-local\` server, or the deployed agent when \`RUNTIME_CONFIG_APP_ID\` is set.
|
|
164
|
+
import {
|
|
165
|
+
ClientFactory,
|
|
166
|
+
ClientFactoryOptions,
|
|
167
|
+
DefaultAgentCardResolver,
|
|
168
|
+
JsonRpcTransportFactory,
|
|
169
|
+
} from '@a2a-js/sdk/client';
|
|
170
|
+
import { A2AChatAdapter, chatLoop } from 'agent-chat-cli';
|
|
171
|
+
import { createAgentCoreFetch, resolveRemoteAgent } from './agentcore.js';
|
|
172
|
+
|
|
173
|
+
const remote = await resolveRemoteAgent();
|
|
174
|
+
|
|
175
|
+
const url = remote
|
|
176
|
+
? \`https://bedrock-agentcore.\${remote.region}.amazonaws.com/runtimes/\${encodeURIComponent(remote.arn)}/invocations/\`
|
|
177
|
+
: process.env.URL!;
|
|
178
|
+
|
|
179
|
+
// Remote requests are authenticated via a custom fetch; locally the default client talks plain HTTP.
|
|
180
|
+
const clientFactory = remote
|
|
181
|
+
? new ClientFactory({
|
|
182
|
+
...ClientFactoryOptions.default,
|
|
183
|
+
transports: [
|
|
184
|
+
new JsonRpcTransportFactory({
|
|
185
|
+
fetchImpl: createAgentCoreFetch(remote.region),
|
|
186
|
+
}),
|
|
187
|
+
],
|
|
188
|
+
cardResolver: new DefaultAgentCardResolver({
|
|
189
|
+
fetchImpl: createAgentCoreFetch(remote.region),
|
|
190
|
+
}),
|
|
191
|
+
})
|
|
192
|
+
: undefined;
|
|
193
|
+
|
|
194
|
+
await chatLoop(new A2AChatAdapter({ clientFactory }), url);
|
|
195
|
+
"
|
|
196
|
+
`;
|
|
197
|
+
|
|
198
|
+
exports[`py#agent generator > chat scripts for ag-ui protocol > should match snapshot for chat scripts with cognito auth > agentcore.ts (ag-ui, cognito) 1`] = `
|
|
199
|
+
"// Resolves the deployed TestProjectAgent agent from runtime config and authenticates requests to it.
|
|
200
|
+
import { randomUUID } from 'node:crypto';
|
|
201
|
+
import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';
|
|
202
|
+
|
|
203
|
+
const SESSION_HEADER = 'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id';
|
|
204
|
+
|
|
205
|
+
// AgentCore session ids must be at least 33 characters.
|
|
206
|
+
export const SESSION_ID = randomUUID().replaceAll('-', '').padEnd(33, '0');
|
|
207
|
+
|
|
208
|
+
export interface RemoteAgent {
|
|
209
|
+
/** ARN of the deployed Bedrock AgentCore runtime. */
|
|
210
|
+
arn: string;
|
|
211
|
+
/** AWS region parsed from the runtime ARN. */
|
|
212
|
+
region: string;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Returns the deployed agent when \`RUNTIME_CONFIG_APP_ID\` is set, otherwise \`undefined\` to chat locally.
|
|
216
|
+
export const resolveRemoteAgent = async (): Promise<
|
|
217
|
+
RemoteAgent | undefined
|
|
218
|
+
> => {
|
|
219
|
+
const application = process.env.RUNTIME_CONFIG_APP_ID;
|
|
220
|
+
if (!application) {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
const config = (await getAppConfig('agentcore', {
|
|
224
|
+
application,
|
|
225
|
+
environment: 'default',
|
|
226
|
+
transform: 'json',
|
|
227
|
+
})) as { agentRuntimes?: Record<string, string> };
|
|
228
|
+
const arn = config?.agentRuntimes?.['TestProjectAgent'];
|
|
229
|
+
if (!arn) {
|
|
230
|
+
throw new Error(
|
|
231
|
+
\`No deployed agent named 'TestProjectAgent' found in runtime configuration (application \${application}).\`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
return { arn, region: arn.split(':')[3] };
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
/** The Cognito access token used to authenticate with the deployed agent. */
|
|
238
|
+
export const getAccessToken = (): string => {
|
|
239
|
+
const accessToken = process.env.AGENT_ACCESS_TOKEN;
|
|
240
|
+
if (!accessToken) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
'AGENT_ACCESS_TOKEN is not set. Provide a Cognito access token to chat with the deployed agent.',
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
return accessToken;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// A \`fetch\` that authenticates requests to the deployed agent and forwards the session id.
|
|
249
|
+
export const createAgentCoreFetch = (): typeof fetch => {
|
|
250
|
+
const accessToken = getAccessToken();
|
|
251
|
+
return (input, init) => {
|
|
252
|
+
const headers = new Headers(init?.headers);
|
|
253
|
+
headers.set(SESSION_HEADER, SESSION_ID);
|
|
254
|
+
headers.set('Authorization', \`Bearer \${accessToken}\`);
|
|
255
|
+
return fetch(input, { ...init, headers });
|
|
256
|
+
};
|
|
257
|
+
};
|
|
258
|
+
"
|
|
259
|
+
`;
|
|
260
|
+
|
|
261
|
+
exports[`py#agent generator > chat scripts for ag-ui protocol > should match snapshot for chat scripts with cognito auth > chat.ts (ag-ui, cognito) 1`] = `
|
|
262
|
+
"// Chat CLI for TestProjectAgent (AG-UI protocol). Connects to the local
|
|
263
|
+
// \`serve-local\` server, or the deployed agent when \`RUNTIME_CONFIG_APP_ID\` is set.
|
|
264
|
+
import { AGUIChatAdapter, chatLoop } from 'agent-chat-cli';
|
|
265
|
+
import { createAgentCoreFetch, resolveRemoteAgent } from './agentcore.js';
|
|
266
|
+
|
|
267
|
+
const remote = await resolveRemoteAgent();
|
|
268
|
+
|
|
269
|
+
const url = remote
|
|
270
|
+
? \`https://bedrock-agentcore.\${remote.region}.amazonaws.com/runtimes/\${encodeURIComponent(remote.arn)}/invocations?qualifier=DEFAULT\`
|
|
271
|
+
: process.env.URL!;
|
|
272
|
+
|
|
273
|
+
// Remote requests are authenticated via a custom fetch; locally the default agent talks plain HTTP.
|
|
274
|
+
const fetchImpl = remote ? createAgentCoreFetch() : undefined;
|
|
275
|
+
|
|
276
|
+
await chatLoop(new AGUIChatAdapter({ fetch: fetchImpl }), url);
|
|
277
|
+
"
|
|
278
|
+
`;
|
|
279
|
+
|
|
280
|
+
exports[`py#agent generator > chat scripts for ag-ui protocol > should match snapshot for chat scripts with iam auth > agentcore.ts (ag-ui, iam) 1`] = `
|
|
281
|
+
"// Resolves the deployed TestProjectAgent agent from runtime config and authenticates requests to it.
|
|
282
|
+
import { randomUUID } from 'node:crypto';
|
|
283
|
+
import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';
|
|
284
|
+
import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
|
|
285
|
+
import { AwsClient } from 'aws4fetch';
|
|
286
|
+
|
|
287
|
+
const SESSION_HEADER = 'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id';
|
|
288
|
+
|
|
289
|
+
// AgentCore session ids must be at least 33 characters.
|
|
290
|
+
export const SESSION_ID = randomUUID().replaceAll('-', '').padEnd(33, '0');
|
|
291
|
+
|
|
292
|
+
export interface RemoteAgent {
|
|
293
|
+
/** ARN of the deployed Bedrock AgentCore runtime. */
|
|
294
|
+
arn: string;
|
|
295
|
+
/** AWS region parsed from the runtime ARN. */
|
|
296
|
+
region: string;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Returns the deployed agent when \`RUNTIME_CONFIG_APP_ID\` is set, otherwise \`undefined\` to chat locally.
|
|
300
|
+
export const resolveRemoteAgent = async (): Promise<
|
|
301
|
+
RemoteAgent | undefined
|
|
302
|
+
> => {
|
|
303
|
+
const application = process.env.RUNTIME_CONFIG_APP_ID;
|
|
304
|
+
if (!application) {
|
|
305
|
+
return undefined;
|
|
306
|
+
}
|
|
307
|
+
const config = (await getAppConfig('agentcore', {
|
|
308
|
+
application,
|
|
309
|
+
environment: 'default',
|
|
310
|
+
transform: 'json',
|
|
311
|
+
})) as { agentRuntimes?: Record<string, string> };
|
|
312
|
+
const arn = config?.agentRuntimes?.['TestProjectAgent'];
|
|
313
|
+
if (!arn) {
|
|
314
|
+
throw new Error(
|
|
315
|
+
\`No deployed agent named 'TestProjectAgent' found in runtime configuration (application \${application}).\`,
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
return { arn, region: arn.split(':')[3] };
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// A \`fetch\` that authenticates requests to the deployed agent and forwards the session id.
|
|
322
|
+
export const createAgentCoreFetch = (region: string): typeof fetch => {
|
|
323
|
+
const credentialProvider = fromNodeProviderChain();
|
|
324
|
+
return async (input, init) => {
|
|
325
|
+
const headers = new Headers(init?.headers);
|
|
326
|
+
headers.set(SESSION_HEADER, SESSION_ID);
|
|
327
|
+
const client = new AwsClient({
|
|
328
|
+
...(await credentialProvider()),
|
|
329
|
+
service: 'bedrock-agentcore',
|
|
330
|
+
region,
|
|
331
|
+
});
|
|
332
|
+
return client.fetch(input, { ...init, headers });
|
|
333
|
+
};
|
|
334
|
+
};
|
|
335
|
+
"
|
|
336
|
+
`;
|
|
337
|
+
|
|
338
|
+
exports[`py#agent generator > chat scripts for ag-ui protocol > should match snapshot for chat scripts with iam auth > chat.ts (ag-ui, iam) 1`] = `
|
|
339
|
+
"// Chat CLI for TestProjectAgent (AG-UI protocol). Connects to the local
|
|
340
|
+
// \`serve-local\` server, or the deployed agent when \`RUNTIME_CONFIG_APP_ID\` is set.
|
|
341
|
+
import { AGUIChatAdapter, chatLoop } from 'agent-chat-cli';
|
|
342
|
+
import { createAgentCoreFetch, resolveRemoteAgent } from './agentcore.js';
|
|
343
|
+
|
|
344
|
+
const remote = await resolveRemoteAgent();
|
|
345
|
+
|
|
346
|
+
const url = remote
|
|
347
|
+
? \`https://bedrock-agentcore.\${remote.region}.amazonaws.com/runtimes/\${encodeURIComponent(remote.arn)}/invocations?qualifier=DEFAULT\`
|
|
348
|
+
: process.env.URL!;
|
|
349
|
+
|
|
350
|
+
// Remote requests are authenticated via a custom fetch; locally the default agent talks plain HTTP.
|
|
351
|
+
const fetchImpl = remote ? createAgentCoreFetch(remote.region) : undefined;
|
|
352
|
+
|
|
353
|
+
await chatLoop(new AGUIChatAdapter({ fetch: fetchImpl }), url);
|
|
354
|
+
"
|
|
355
|
+
`;
|
|
356
|
+
|
|
357
|
+
exports[`py#agent generator > chat scripts for http protocol > should match snapshot for chat scripts with cognito auth > agentcore.ts (http, cognito) 1`] = `
|
|
358
|
+
"// Resolves the deployed TestProjectAgent agent from runtime config and authenticates requests to it.
|
|
359
|
+
import { randomUUID } from 'node:crypto';
|
|
360
|
+
import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';
|
|
361
|
+
|
|
362
|
+
const SESSION_HEADER = 'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id';
|
|
363
|
+
|
|
364
|
+
// AgentCore session ids must be at least 33 characters.
|
|
365
|
+
export const SESSION_ID = randomUUID().replaceAll('-', '').padEnd(33, '0');
|
|
366
|
+
|
|
367
|
+
export interface RemoteAgent {
|
|
368
|
+
/** ARN of the deployed Bedrock AgentCore runtime. */
|
|
369
|
+
arn: string;
|
|
370
|
+
/** AWS region parsed from the runtime ARN. */
|
|
371
|
+
region: string;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Returns the deployed agent when \`RUNTIME_CONFIG_APP_ID\` is set, otherwise \`undefined\` to chat locally.
|
|
375
|
+
export const resolveRemoteAgent = async (): Promise<
|
|
376
|
+
RemoteAgent | undefined
|
|
377
|
+
> => {
|
|
378
|
+
const application = process.env.RUNTIME_CONFIG_APP_ID;
|
|
379
|
+
if (!application) {
|
|
380
|
+
return undefined;
|
|
381
|
+
}
|
|
382
|
+
const config = (await getAppConfig('agentcore', {
|
|
383
|
+
application,
|
|
384
|
+
environment: 'default',
|
|
385
|
+
transform: 'json',
|
|
386
|
+
})) as { agentRuntimes?: Record<string, string> };
|
|
387
|
+
const arn = config?.agentRuntimes?.['TestProjectAgent'];
|
|
388
|
+
if (!arn) {
|
|
389
|
+
throw new Error(
|
|
390
|
+
\`No deployed agent named 'TestProjectAgent' found in runtime configuration (application \${application}).\`,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
return { arn, region: arn.split(':')[3] };
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
/** The Cognito access token used to authenticate with the deployed agent. */
|
|
397
|
+
export const getAccessToken = (): string => {
|
|
398
|
+
const accessToken = process.env.AGENT_ACCESS_TOKEN;
|
|
399
|
+
if (!accessToken) {
|
|
400
|
+
throw new Error(
|
|
401
|
+
'AGENT_ACCESS_TOKEN is not set. Provide a Cognito access token to chat with the deployed agent.',
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
return accessToken;
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
// A \`fetch\` that authenticates requests to the deployed agent and forwards the session id.
|
|
408
|
+
export const createAgentCoreFetch = (): typeof fetch => {
|
|
409
|
+
const accessToken = getAccessToken();
|
|
410
|
+
return (input, init) => {
|
|
411
|
+
const headers = new Headers(init?.headers);
|
|
412
|
+
headers.set(SESSION_HEADER, SESSION_ID);
|
|
413
|
+
headers.set('Authorization', \`Bearer \${accessToken}\`);
|
|
414
|
+
return fetch(input, { ...init, headers });
|
|
415
|
+
};
|
|
416
|
+
};
|
|
417
|
+
"
|
|
418
|
+
`;
|
|
419
|
+
|
|
420
|
+
exports[`py#agent generator > chat scripts for http protocol > should match snapshot for chat scripts with cognito auth > chat.ts (http, cognito) 1`] = `
|
|
421
|
+
"// Chat CLI for TestProjectAgent (Python FastAPI / JSONL streaming), using the
|
|
422
|
+
// generated client. Connects to the local \`serve-local\` server, or the deployed
|
|
423
|
+
// agent when \`RUNTIME_CONFIG_APP_ID\` is set.
|
|
424
|
+
import { chatLoop, type ChatAdapter } from 'agent-chat-cli';
|
|
425
|
+
import { TestProjectAgent } from './generated/client.gen.js';
|
|
426
|
+
import {
|
|
427
|
+
createAgentCoreFetch,
|
|
428
|
+
resolveRemoteAgent,
|
|
429
|
+
SESSION_ID,
|
|
430
|
+
} from './agentcore.js';
|
|
431
|
+
|
|
432
|
+
const SESSION_ID_HEADER = 'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id';
|
|
433
|
+
|
|
434
|
+
const remote = await resolveRemoteAgent();
|
|
435
|
+
|
|
436
|
+
class TestProjectAgentAdapter implements ChatAdapter {
|
|
437
|
+
private client!: TestProjectAgent;
|
|
438
|
+
|
|
439
|
+
async connect(url: string) {
|
|
440
|
+
this.client = remote
|
|
441
|
+
? new TestProjectAgent({
|
|
442
|
+
url: \`https://bedrock-agentcore.\${remote.region}.amazonaws.com/runtimes/\${encodeURIComponent(remote.arn)}\`,
|
|
443
|
+
fetch: createAgentCoreFetch(),
|
|
444
|
+
})
|
|
445
|
+
: new TestProjectAgent({
|
|
446
|
+
url,
|
|
447
|
+
fetch: (input, init) => {
|
|
448
|
+
const headers = new Headers(init?.headers);
|
|
449
|
+
headers.set(SESSION_ID_HEADER, SESSION_ID);
|
|
450
|
+
return fetch(input, { ...init, headers });
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
return { agentName: 'TestProjectAgent' };
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async *sendMessage(text: string): AsyncIterable<string> {
|
|
457
|
+
for await (const chunk of this.client.invoke({ message: text })) {
|
|
458
|
+
if (typeof chunk.content === 'string') yield chunk.content;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
await chatLoop(new TestProjectAgentAdapter(), process.env.URL ?? '');
|
|
464
|
+
"
|
|
465
|
+
`;
|
|
466
|
+
|
|
467
|
+
exports[`py#agent generator > chat scripts for http protocol > should match snapshot for chat scripts with iam auth > agentcore.ts (http, iam) 1`] = `
|
|
468
|
+
"// Resolves the deployed TestProjectAgent agent from runtime config and authenticates requests to it.
|
|
469
|
+
import { randomUUID } from 'node:crypto';
|
|
470
|
+
import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';
|
|
471
|
+
import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
|
|
472
|
+
import { AwsClient } from 'aws4fetch';
|
|
473
|
+
|
|
474
|
+
const SESSION_HEADER = 'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id';
|
|
475
|
+
|
|
476
|
+
// AgentCore session ids must be at least 33 characters.
|
|
477
|
+
export const SESSION_ID = randomUUID().replaceAll('-', '').padEnd(33, '0');
|
|
478
|
+
|
|
479
|
+
export interface RemoteAgent {
|
|
480
|
+
/** ARN of the deployed Bedrock AgentCore runtime. */
|
|
481
|
+
arn: string;
|
|
482
|
+
/** AWS region parsed from the runtime ARN. */
|
|
483
|
+
region: string;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Returns the deployed agent when \`RUNTIME_CONFIG_APP_ID\` is set, otherwise \`undefined\` to chat locally.
|
|
487
|
+
export const resolveRemoteAgent = async (): Promise<
|
|
488
|
+
RemoteAgent | undefined
|
|
489
|
+
> => {
|
|
490
|
+
const application = process.env.RUNTIME_CONFIG_APP_ID;
|
|
491
|
+
if (!application) {
|
|
492
|
+
return undefined;
|
|
493
|
+
}
|
|
494
|
+
const config = (await getAppConfig('agentcore', {
|
|
495
|
+
application,
|
|
496
|
+
environment: 'default',
|
|
497
|
+
transform: 'json',
|
|
498
|
+
})) as { agentRuntimes?: Record<string, string> };
|
|
499
|
+
const arn = config?.agentRuntimes?.['TestProjectAgent'];
|
|
500
|
+
if (!arn) {
|
|
501
|
+
throw new Error(
|
|
502
|
+
\`No deployed agent named 'TestProjectAgent' found in runtime configuration (application \${application}).\`,
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
return { arn, region: arn.split(':')[3] };
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
// A \`fetch\` that authenticates requests to the deployed agent and forwards the session id.
|
|
509
|
+
export const createAgentCoreFetch = (region: string): typeof fetch => {
|
|
510
|
+
const credentialProvider = fromNodeProviderChain();
|
|
511
|
+
return async (input, init) => {
|
|
512
|
+
const headers = new Headers(init?.headers);
|
|
513
|
+
headers.set(SESSION_HEADER, SESSION_ID);
|
|
514
|
+
const client = new AwsClient({
|
|
515
|
+
...(await credentialProvider()),
|
|
516
|
+
service: 'bedrock-agentcore',
|
|
517
|
+
region,
|
|
518
|
+
});
|
|
519
|
+
return client.fetch(input, { ...init, headers });
|
|
520
|
+
};
|
|
521
|
+
};
|
|
522
|
+
"
|
|
523
|
+
`;
|
|
524
|
+
|
|
525
|
+
exports[`py#agent generator > chat scripts for http protocol > should match snapshot for chat scripts with iam auth > chat.ts (http, iam) 1`] = `
|
|
526
|
+
"// Chat CLI for TestProjectAgent (Python FastAPI / JSONL streaming), using the
|
|
527
|
+
// generated client. Connects to the local \`serve-local\` server, or the deployed
|
|
528
|
+
// agent when \`RUNTIME_CONFIG_APP_ID\` is set.
|
|
529
|
+
import { chatLoop, type ChatAdapter } from 'agent-chat-cli';
|
|
530
|
+
import { TestProjectAgent } from './generated/client.gen.js';
|
|
531
|
+
import {
|
|
532
|
+
createAgentCoreFetch,
|
|
533
|
+
resolveRemoteAgent,
|
|
534
|
+
SESSION_ID,
|
|
535
|
+
} from './agentcore.js';
|
|
536
|
+
|
|
537
|
+
const SESSION_ID_HEADER = 'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id';
|
|
538
|
+
|
|
539
|
+
const remote = await resolveRemoteAgent();
|
|
540
|
+
|
|
541
|
+
class TestProjectAgentAdapter implements ChatAdapter {
|
|
542
|
+
private client!: TestProjectAgent;
|
|
543
|
+
|
|
544
|
+
async connect(url: string) {
|
|
545
|
+
this.client = remote
|
|
546
|
+
? new TestProjectAgent({
|
|
547
|
+
url: \`https://bedrock-agentcore.\${remote.region}.amazonaws.com/runtimes/\${encodeURIComponent(remote.arn)}\`,
|
|
548
|
+
fetch: createAgentCoreFetch(remote.region),
|
|
549
|
+
})
|
|
550
|
+
: new TestProjectAgent({
|
|
551
|
+
url,
|
|
552
|
+
fetch: (input, init) => {
|
|
553
|
+
const headers = new Headers(init?.headers);
|
|
554
|
+
headers.set(SESSION_ID_HEADER, SESSION_ID);
|
|
555
|
+
return fetch(input, { ...init, headers });
|
|
556
|
+
},
|
|
557
|
+
});
|
|
558
|
+
return { agentName: 'TestProjectAgent' };
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async *sendMessage(text: string): AsyncIterable<string> {
|
|
562
|
+
for await (const chunk of this.client.invoke({ message: text })) {
|
|
563
|
+
if (typeof chunk.content === 'string') yield chunk.content;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
await chatLoop(new TestProjectAgentAdapter(), process.env.URL ?? '');
|
|
569
|
+
"
|
|
570
|
+
`;
|
|
571
|
+
|
|
3
572
|
exports[`py#agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > agent-Dockerfile 1`] = `
|
|
4
573
|
"FROM public.ecr.aws/docker/library/python:3.14-slim
|
|
5
574
|
|
|
@@ -802,17 +1371,17 @@ dependencies = [
|
|
|
802
1371
|
"proj.agent_connection",
|
|
803
1372
|
"aws-lambda-powertools==3.29.0",
|
|
804
1373
|
"aws-opentelemetry-distro==0.17.1",
|
|
805
|
-
"bedrock-agentcore==1.14.
|
|
806
|
-
"boto3==1.43.
|
|
807
|
-
"fastapi==0.
|
|
1374
|
+
"bedrock-agentcore==1.14.1",
|
|
1375
|
+
"boto3==1.43.29",
|
|
1376
|
+
"fastapi==0.137.1",
|
|
808
1377
|
"mcp==1.27.2",
|
|
809
|
-
"strands-agents==1.
|
|
1378
|
+
"strands-agents==1.43.0",
|
|
810
1379
|
"strands-agents-tools==0.8.0",
|
|
811
1380
|
"uvicorn==0.49.0"
|
|
812
1381
|
]
|
|
813
1382
|
|
|
814
1383
|
[dependency-groups]
|
|
815
|
-
dev = [ "fastapi[standard]==0.
|
|
1384
|
+
dev = [ "fastapi[standard]==0.137.1" ]
|
|
816
1385
|
|
|
817
1386
|
[tool.uv]
|
|
818
1387
|
dev-dependencies = [ ]
|
|
@@ -4,9 +4,7 @@ from typing import Any
|
|
|
4
4
|
|
|
5
5
|
from fastapi import FastAPI
|
|
6
6
|
from fastapi.middleware.cors import CORSMiddleware
|
|
7
|
-
from fastapi.openapi.utils import get_openapi
|
|
8
7
|
from fastapi.responses import JSONResponse, StreamingResponse
|
|
9
|
-
from fastapi.routing import APIRoute
|
|
10
8
|
from pydantic import BaseModel
|
|
11
9
|
from starlette.middleware.exceptions import ExceptionMiddleware
|
|
12
10
|
|
|
@@ -62,7 +60,11 @@ class JsonStreamingResponse(StreamingResponse):
|
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
|
|
65
|
-
app = FastAPI(
|
|
63
|
+
app = FastAPI(
|
|
64
|
+
title="<%= agentNameClassName %>",
|
|
65
|
+
responses={500: {"model": InternalServerErrorDetails}},
|
|
66
|
+
generate_unique_id_function=lambda route: route.name,
|
|
67
|
+
)
|
|
66
68
|
|
|
67
69
|
# Add cors middleware
|
|
68
70
|
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
|
|
@@ -77,23 +79,3 @@ async def unhandled_exception_handler(request, err):
|
|
|
77
79
|
return JSONResponse(
|
|
78
80
|
status_code=500, content=InternalServerErrorDetails(detail="Internal Server Error").model_dump()
|
|
79
81
|
)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def custom_openapi() -> dict[str, Any]:
|
|
83
|
-
if app.openapi_schema:
|
|
84
|
-
return app.openapi_schema
|
|
85
|
-
for route in app.routes:
|
|
86
|
-
if isinstance(route, APIRoute):
|
|
87
|
-
route.operation_id = route.name
|
|
88
|
-
openapi_schema = get_openapi(
|
|
89
|
-
title=app.title,
|
|
90
|
-
version=app.version,
|
|
91
|
-
openapi_version=app.openapi_version,
|
|
92
|
-
description=app.description,
|
|
93
|
-
routes=app.routes,
|
|
94
|
-
)
|
|
95
|
-
app.openapi_schema = openapi_schema
|
|
96
|
-
return app.openapi_schema
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
app.openapi = custom_openapi # ty: ignore[invalid-assignment]
|