@aws/agentcore 0.9.1 → 1.0.0-preview.2
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/dist/agent-inspector/index.css +1 -1
- package/dist/agent-inspector/index.js +90 -72
- package/dist/assets/README.md +56 -31
- package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +1781 -259
- package/dist/assets/__tests__/__snapshots__/dockerfile-render.test.ts.snap +77 -0
- package/dist/assets/__tests__/dockerfile-render.test.ts +24 -0
- package/dist/assets/agents/AGENTS.md +84 -55
- package/dist/assets/cdk/bin/cdk.ts +40 -0
- package/dist/assets/cdk/cdk.json +1 -1
- package/dist/assets/cdk/lib/cdk-stack.ts +59 -2
- package/dist/assets/container/python/Dockerfile +4 -0
- package/dist/assets/evaluators/python-lambda/execution-role-policy.json +1 -1
- package/dist/assets/harness/invoke.py.template +74 -0
- package/dist/assets/python/a2a/googleadk/base/main.py +65 -3
- package/dist/assets/python/a2a/langchain_langgraph/base/main.py +64 -1
- package/dist/assets/python/a2a/strands/base/main.py +65 -2
- package/dist/assets/python/agui/googleadk/base/README.md +30 -0
- package/dist/assets/python/agui/googleadk/base/gitignore.template +41 -0
- package/dist/assets/python/agui/googleadk/base/main.py +31 -0
- package/dist/assets/python/agui/googleadk/base/model/__init__.py +1 -0
- package/dist/assets/python/agui/googleadk/base/model/load.py +41 -0
- package/dist/assets/python/agui/googleadk/base/pyproject.toml +24 -0
- package/dist/assets/python/agui/langchain_langgraph/base/README.md +22 -0
- package/dist/assets/python/agui/langchain_langgraph/base/gitignore.template +41 -0
- package/dist/assets/python/agui/langchain_langgraph/base/main.py +74 -0
- package/dist/assets/python/agui/langchain_langgraph/base/model/__init__.py +1 -0
- package/dist/assets/python/agui/langchain_langgraph/base/model/load.py +123 -0
- package/dist/assets/python/agui/langchain_langgraph/base/pyproject.toml +30 -0
- package/dist/assets/python/agui/strands/base/README.md +22 -0
- package/dist/assets/python/agui/strands/base/gitignore.template +41 -0
- package/dist/assets/python/agui/strands/base/main.py +43 -0
- package/dist/assets/python/agui/strands/base/model/__init__.py +1 -0
- package/dist/assets/python/agui/strands/base/model/load.py +123 -0
- package/dist/assets/python/agui/strands/base/pyproject.toml +27 -0
- package/dist/assets/python/agui/strands/capabilities/memory/__init__.py +1 -0
- package/dist/assets/python/agui/strands/capabilities/memory/session.py +38 -0
- package/dist/assets/python/http/autogen/base/main.py +61 -1
- package/dist/assets/python/http/googleadk/base/main.py +62 -2
- package/dist/assets/python/http/langchain_langgraph/base/main.py +61 -1
- package/dist/assets/python/http/openaiagents/base/main.py +70 -4
- package/dist/assets/python/http/strands/base/main.py +64 -6
- package/dist/cli/index.mjs +415 -377
- package/dist/lib/constants.d.ts +1 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +3 -1
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/errors/config.d.ts.map +1 -1
- package/dist/lib/errors/config.js +5 -2
- package/dist/lib/errors/config.js.map +1 -1
- package/dist/lib/schemas/io/config-io.d.ts +9 -1
- package/dist/lib/schemas/io/config-io.d.ts.map +1 -1
- package/dist/lib/schemas/io/config-io.js +14 -0
- package/dist/lib/schemas/io/config-io.js.map +1 -1
- package/dist/lib/schemas/io/path-resolver.d.ts +12 -0
- package/dist/lib/schemas/io/path-resolver.d.ts.map +1 -1
- package/dist/lib/schemas/io/path-resolver.js +18 -0
- package/dist/lib/schemas/io/path-resolver.js.map +1 -1
- package/dist/schema/constants.d.ts +1 -0
- package/dist/schema/constants.d.ts.map +1 -1
- package/dist/schema/constants.js +8 -1
- package/dist/schema/constants.js.map +1 -1
- package/dist/schema/schemas/agent-env.d.ts +20 -0
- package/dist/schema/schemas/agent-env.d.ts.map +1 -1
- package/dist/schema/schemas/agent-env.js +17 -2
- package/dist/schema/schemas/agent-env.js.map +1 -1
- package/dist/schema/schemas/agentcore-project.d.ts +17 -0
- package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
- package/dist/schema/schemas/agentcore-project.js +18 -1
- package/dist/schema/schemas/agentcore-project.js.map +1 -1
- package/dist/schema/schemas/aws-targets.d.ts +3 -0
- package/dist/schema/schemas/aws-targets.d.ts.map +1 -1
- package/dist/schema/schemas/aws-targets.js +1 -0
- package/dist/schema/schemas/aws-targets.js.map +1 -1
- package/dist/schema/schemas/deployed-state.d.ts +50 -0
- package/dist/schema/schemas/deployed-state.d.ts.map +1 -1
- package/dist/schema/schemas/deployed-state.js +15 -1
- package/dist/schema/schemas/deployed-state.js.map +1 -1
- package/dist/schema/schemas/primitives/harness.d.ts +287 -0
- package/dist/schema/schemas/primitives/harness.d.ts.map +1 -0
- package/dist/schema/schemas/primitives/harness.js +237 -0
- package/dist/schema/schemas/primitives/harness.js.map +1 -0
- package/dist/schema/schemas/primitives/index.d.ts +2 -0
- package/dist/schema/schemas/primitives/index.d.ts.map +1 -1
- package/dist/schema/schemas/primitives/index.js +14 -1
- package/dist/schema/schemas/primitives/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/bump-version.ts +7 -80
- package/scripts/bundle.mjs +57 -3
- package/dist/agent-inspector/index.js.map +0 -1
|
@@ -99,6 +99,45 @@ async function main() {
|
|
|
99
99
|
throw new Error('No deployment targets configured. Please define targets in agentcore/aws-targets.json');
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
// Read harness configs for role creation.
|
|
103
|
+
// Harness fields may not yet be on the AgentCoreProjectSpec type from @aws/agentcore-cdk,
|
|
104
|
+
// so we read them dynamically via specAny (same pattern as gateways above).
|
|
105
|
+
// Harness paths in agentcore.json are relative to the project root (parent of agentcore/).
|
|
106
|
+
const projectRoot = path.resolve(configRoot, '..');
|
|
107
|
+
const harnessConfigs: {
|
|
108
|
+
name: string;
|
|
109
|
+
executionRoleArn?: string;
|
|
110
|
+
memoryName?: string;
|
|
111
|
+
containerUri?: string;
|
|
112
|
+
hasDockerfile?: boolean;
|
|
113
|
+
dockerfileName?: string;
|
|
114
|
+
harnessDir?: string;
|
|
115
|
+
tools?: { type: string; name: string }[];
|
|
116
|
+
apiKeyArn?: string;
|
|
117
|
+
}[] = [];
|
|
118
|
+
for (const entry of specAny.harnesses ?? []) {
|
|
119
|
+
const harnessDir = path.resolve(projectRoot, entry.path);
|
|
120
|
+
const harnessPath = path.resolve(harnessDir, 'harness.json');
|
|
121
|
+
try {
|
|
122
|
+
const harnessSpec = JSON.parse(fs.readFileSync(harnessPath, 'utf-8'));
|
|
123
|
+
harnessConfigs.push({
|
|
124
|
+
name: entry.name,
|
|
125
|
+
executionRoleArn: harnessSpec.executionRoleArn,
|
|
126
|
+
memoryName: harnessSpec.memory?.name,
|
|
127
|
+
containerUri: harnessSpec.containerUri,
|
|
128
|
+
hasDockerfile: !!harnessSpec.dockerfile,
|
|
129
|
+
dockerfileName: harnessSpec.dockerfile,
|
|
130
|
+
harnessDir,
|
|
131
|
+
tools: harnessSpec.tools,
|
|
132
|
+
apiKeyArn: harnessSpec.model?.apiKeyArn,
|
|
133
|
+
});
|
|
134
|
+
} catch (err) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
\`Could not read harness.json for "\${entry.name}" at \${harnessPath}: \${err instanceof Error ? err.message : err}\`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
102
141
|
const app = new App();
|
|
103
142
|
|
|
104
143
|
for (const target of targets) {
|
|
@@ -118,6 +157,7 @@ async function main() {
|
|
|
118
157
|
spec,
|
|
119
158
|
mcpSpec,
|
|
120
159
|
credentials,
|
|
160
|
+
harnesses: harnessConfigs.length > 0 ? harnessConfigs : undefined,
|
|
121
161
|
env,
|
|
122
162
|
description: \`AgentCore stack for \${spec.name} deployed to \${target.name} (\${target.region})\`,
|
|
123
163
|
tags: {
|
|
@@ -149,7 +189,7 @@ exports[`Assets Directory Snapshots > CDK assets > cdk/cdk/cdk.json should match
|
|
|
149
189
|
"@aws-cdk/aws-ecs-patterns:secGroupsDisablesImplicitOpenListener": true,
|
|
150
190
|
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
|
|
151
191
|
"@aws-cdk/core:checkSecretUsage": true,
|
|
152
|
-
"@aws-cdk/core:target-partitions": ["aws", "aws-cn"],
|
|
192
|
+
"@aws-cdk/core:target-partitions": ["aws", "aws-cn", "aws-us-gov"],
|
|
153
193
|
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
|
|
154
194
|
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
|
|
155
195
|
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
|
|
@@ -261,10 +301,26 @@ exports[`Assets Directory Snapshots > CDK assets > cdk/cdk/lib/cdk-stack.ts shou
|
|
|
261
301
|
AgentCoreMcp,
|
|
262
302
|
type AgentCoreProjectSpec,
|
|
263
303
|
type AgentCoreMcpSpec,
|
|
304
|
+
ContainerSourceAssetFromPath,
|
|
305
|
+
AgentEcrRepository,
|
|
306
|
+
ContainerBuildProject,
|
|
307
|
+
ContainerImageBuilder,
|
|
264
308
|
} from '@aws/agentcore-cdk';
|
|
265
309
|
import { CfnOutput, Stack, type StackProps } from 'aws-cdk-lib';
|
|
266
310
|
import { Construct } from 'constructs';
|
|
267
311
|
|
|
312
|
+
export interface HarnessConfig {
|
|
313
|
+
name: string;
|
|
314
|
+
executionRoleArn?: string;
|
|
315
|
+
memoryName?: string;
|
|
316
|
+
containerUri?: string;
|
|
317
|
+
hasDockerfile?: boolean;
|
|
318
|
+
dockerfileName?: string;
|
|
319
|
+
harnessDir?: string;
|
|
320
|
+
tools?: { type: string; name: string }[];
|
|
321
|
+
apiKeyArn?: string;
|
|
322
|
+
}
|
|
323
|
+
|
|
268
324
|
export interface AgentCoreStackProps extends StackProps {
|
|
269
325
|
/**
|
|
270
326
|
* The AgentCore project specification containing agents, memories, and credentials.
|
|
@@ -278,6 +334,10 @@ export interface AgentCoreStackProps extends StackProps {
|
|
|
278
334
|
* Credential provider ARNs from deployed state, keyed by credential name.
|
|
279
335
|
*/
|
|
280
336
|
credentials?: Record<string, { credentialProviderArn: string; clientSecretArn?: string }>;
|
|
337
|
+
/**
|
|
338
|
+
* Harness role configurations. Each entry creates an IAM execution role for a harness.
|
|
339
|
+
*/
|
|
340
|
+
harnesses?: HarnessConfig[];
|
|
281
341
|
}
|
|
282
342
|
|
|
283
343
|
/**
|
|
@@ -293,11 +353,48 @@ export class AgentCoreStack extends Stack {
|
|
|
293
353
|
constructor(scope: Construct, id: string, props: AgentCoreStackProps) {
|
|
294
354
|
super(scope, id, props);
|
|
295
355
|
|
|
296
|
-
const { spec, mcpSpec, credentials } = props;
|
|
356
|
+
const { spec, mcpSpec, credentials, harnesses } = props;
|
|
357
|
+
|
|
358
|
+
// Build container images for harnesses that specify a dockerfile (no containerUri).
|
|
359
|
+
// Produces CDK outputs consumed by the imperative harness deployer.
|
|
360
|
+
const harnessesForCdk = harnesses ? [...harnesses] : [];
|
|
361
|
+
if (harnesses) {
|
|
362
|
+
for (let i = 0; i < harnesses.length; i++) {
|
|
363
|
+
const h = harnesses[i]!;
|
|
364
|
+
if (h.hasDockerfile && !h.containerUri && h.harnessDir) {
|
|
365
|
+
const pascalName = h.name.replace(/(^|_)([a-z])/g, (_: string, __: string, c: string) => c.toUpperCase());
|
|
366
|
+
const sourceAsset = new ContainerSourceAssetFromPath(this, \`Harness\${pascalName}SourceAsset\`, {
|
|
367
|
+
sourcePath: h.harnessDir,
|
|
368
|
+
});
|
|
369
|
+
const ecrRepo = new AgentEcrRepository(this, \`Harness\${pascalName}EcrRepo\`, {
|
|
370
|
+
projectName: spec.name,
|
|
371
|
+
agentName: \`harness-\${h.name}\`,
|
|
372
|
+
});
|
|
373
|
+
const buildProject = ContainerBuildProject.getOrCreate(this);
|
|
374
|
+
buildProject.grantPushTo(ecrRepo.repository);
|
|
375
|
+
sourceAsset.asset.grantRead(buildProject.role);
|
|
376
|
+
|
|
377
|
+
const builder = new ContainerImageBuilder(this, \`Harness\${pascalName}ContainerBuild\`, {
|
|
378
|
+
buildProject,
|
|
379
|
+
sourceAsset,
|
|
380
|
+
repository: ecrRepo,
|
|
381
|
+
dockerfile: h.dockerfileName ?? 'Dockerfile',
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
new CfnOutput(this, \`Harness\${pascalName}ContainerUriOutput\`, {
|
|
385
|
+
value: builder.containerUri,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
// Pass the built containerUri to the harness role construct so it gets ECR pull permissions
|
|
389
|
+
harnessesForCdk[i] = { ...h, containerUri: builder.containerUri };
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
297
393
|
|
|
298
|
-
// Create AgentCoreApplication with all agents
|
|
394
|
+
// Create AgentCoreApplication with all agents and harness roles
|
|
299
395
|
this.application = new AgentCoreApplication(this, 'Application', {
|
|
300
396
|
spec,
|
|
397
|
+
harnesses: harnessesForCdk.length > 0 ? harnessesForCdk : undefined,
|
|
301
398
|
});
|
|
302
399
|
|
|
303
400
|
// Create AgentCoreMcp if there are gateways configured
|
|
@@ -449,6 +546,7 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
|
|
|
449
546
|
"evaluators/python-lambda/execution-role-policy.json",
|
|
450
547
|
"evaluators/python-lambda/lambda_function.py",
|
|
451
548
|
"evaluators/python-lambda/pyproject.toml",
|
|
549
|
+
"harness/invoke.py.template",
|
|
452
550
|
"mcp/python-lambda/README.md",
|
|
453
551
|
"mcp/python-lambda/handler.py",
|
|
454
552
|
"mcp/python-lambda/pyproject.toml",
|
|
@@ -475,6 +573,26 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
|
|
|
475
573
|
"python/a2a/strands/base/pyproject.toml",
|
|
476
574
|
"python/a2a/strands/capabilities/memory/__init__.py",
|
|
477
575
|
"python/a2a/strands/capabilities/memory/session.py",
|
|
576
|
+
"python/agui/googleadk/base/README.md",
|
|
577
|
+
"python/agui/googleadk/base/gitignore.template",
|
|
578
|
+
"python/agui/googleadk/base/main.py",
|
|
579
|
+
"python/agui/googleadk/base/model/__init__.py",
|
|
580
|
+
"python/agui/googleadk/base/model/load.py",
|
|
581
|
+
"python/agui/googleadk/base/pyproject.toml",
|
|
582
|
+
"python/agui/langchain_langgraph/base/README.md",
|
|
583
|
+
"python/agui/langchain_langgraph/base/gitignore.template",
|
|
584
|
+
"python/agui/langchain_langgraph/base/main.py",
|
|
585
|
+
"python/agui/langchain_langgraph/base/model/__init__.py",
|
|
586
|
+
"python/agui/langchain_langgraph/base/model/load.py",
|
|
587
|
+
"python/agui/langchain_langgraph/base/pyproject.toml",
|
|
588
|
+
"python/agui/strands/base/README.md",
|
|
589
|
+
"python/agui/strands/base/gitignore.template",
|
|
590
|
+
"python/agui/strands/base/main.py",
|
|
591
|
+
"python/agui/strands/base/model/__init__.py",
|
|
592
|
+
"python/agui/strands/base/model/load.py",
|
|
593
|
+
"python/agui/strands/base/pyproject.toml",
|
|
594
|
+
"python/agui/strands/capabilities/memory/__init__.py",
|
|
595
|
+
"python/agui/strands/capabilities/memory/session.py",
|
|
478
596
|
"python/http/autogen/base/README.md",
|
|
479
597
|
"python/http/autogen/base/gitignore.template",
|
|
480
598
|
"python/http/autogen/base/main.py",
|
|
@@ -907,12 +1025,1033 @@ This agent implements the A2A protocol using Google's Agent Development Kit, ena
|
|
|
907
1025
|
|
|
908
1026
|
## Local Development
|
|
909
1027
|
|
|
910
|
-
\`\`\`bash
|
|
911
|
-
uv sync
|
|
912
|
-
uv run python main.py
|
|
1028
|
+
\`\`\`bash
|
|
1029
|
+
uv sync
|
|
1030
|
+
uv run python main.py
|
|
1031
|
+
\`\`\`
|
|
1032
|
+
|
|
1033
|
+
The agent starts on port 9000.
|
|
1034
|
+
|
|
1035
|
+
## Deploy
|
|
1036
|
+
|
|
1037
|
+
\`\`\`bash
|
|
1038
|
+
agentcore deploy
|
|
1039
|
+
\`\`\`
|
|
1040
|
+
"
|
|
1041
|
+
`;
|
|
1042
|
+
|
|
1043
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/gitignore.template should match snapshot 1`] = `
|
|
1044
|
+
"# Environment variables
|
|
1045
|
+
.env
|
|
1046
|
+
|
|
1047
|
+
# Python
|
|
1048
|
+
__pycache__/
|
|
1049
|
+
*.py[cod]
|
|
1050
|
+
*$py.class
|
|
1051
|
+
*.so
|
|
1052
|
+
.Python
|
|
1053
|
+
build/
|
|
1054
|
+
develop-eggs/
|
|
1055
|
+
dist/
|
|
1056
|
+
downloads/
|
|
1057
|
+
eggs/
|
|
1058
|
+
.eggs/
|
|
1059
|
+
lib/
|
|
1060
|
+
lib64/
|
|
1061
|
+
parts/
|
|
1062
|
+
sdist/
|
|
1063
|
+
var/
|
|
1064
|
+
wheels/
|
|
1065
|
+
*.egg-info/
|
|
1066
|
+
.installed.cfg
|
|
1067
|
+
*.egg
|
|
1068
|
+
|
|
1069
|
+
# Virtual environments
|
|
1070
|
+
.venv/
|
|
1071
|
+
venv/
|
|
1072
|
+
ENV/
|
|
1073
|
+
env/
|
|
1074
|
+
|
|
1075
|
+
# IDE
|
|
1076
|
+
.vscode/
|
|
1077
|
+
.idea/
|
|
1078
|
+
*.swp
|
|
1079
|
+
*.swo
|
|
1080
|
+
*~
|
|
1081
|
+
|
|
1082
|
+
# OS
|
|
1083
|
+
.DS_Store
|
|
1084
|
+
Thumbs.db
|
|
1085
|
+
"
|
|
1086
|
+
`;
|
|
1087
|
+
|
|
1088
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/main.py should match snapshot 1`] = `
|
|
1089
|
+
"import os
|
|
1090
|
+
from google.adk.agents import Agent
|
|
1091
|
+
from google.adk.a2a.executor.a2a_agent_executor import A2aAgentExecutor
|
|
1092
|
+
from google.adk.runners import Runner
|
|
1093
|
+
from google.adk.sessions import InMemorySessionService
|
|
1094
|
+
from a2a.types import AgentCapabilities, AgentCard, AgentSkill
|
|
1095
|
+
from bedrock_agentcore.runtime import serve_a2a
|
|
1096
|
+
from model.load import load_model
|
|
1097
|
+
|
|
1098
|
+
load_model() # Sets GOOGLE_API_KEY env var (returns None)
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
def add_numbers(a: int, b: int) -> int:
|
|
1102
|
+
"""Return the sum of two numbers."""
|
|
1103
|
+
return a + b
|
|
1104
|
+
|
|
1105
|
+
|
|
1106
|
+
tools = [add_numbers]
|
|
1107
|
+
|
|
1108
|
+
{{#if sessionStorageMountPath}}
|
|
1109
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
1110
|
+
|
|
1111
|
+
def _safe_resolve(path: str) -> str:
|
|
1112
|
+
"""Resolve path safely within the storage boundary."""
|
|
1113
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
1114
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
1115
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
1116
|
+
return resolved
|
|
1117
|
+
|
|
1118
|
+
def file_read(path: str) -> str:
|
|
1119
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
1120
|
+
try:
|
|
1121
|
+
full_path = _safe_resolve(path)
|
|
1122
|
+
with open(full_path) as f:
|
|
1123
|
+
return f.read()
|
|
1124
|
+
except ValueError as e:
|
|
1125
|
+
return str(e)
|
|
1126
|
+
except OSError as e:
|
|
1127
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
1128
|
+
|
|
1129
|
+
def file_write(path: str, content: str) -> str:
|
|
1130
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
1131
|
+
try:
|
|
1132
|
+
full_path = _safe_resolve(path)
|
|
1133
|
+
parent = os.path.dirname(full_path)
|
|
1134
|
+
if parent:
|
|
1135
|
+
os.makedirs(parent, exist_ok=True)
|
|
1136
|
+
with open(full_path, "w") as f:
|
|
1137
|
+
f.write(content)
|
|
1138
|
+
return f"Written to {path}"
|
|
1139
|
+
except ValueError as e:
|
|
1140
|
+
return str(e)
|
|
1141
|
+
except OSError as e:
|
|
1142
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
1143
|
+
|
|
1144
|
+
def list_files(directory: str = "") -> str:
|
|
1145
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
1146
|
+
try:
|
|
1147
|
+
target = _safe_resolve(directory)
|
|
1148
|
+
entries = os.listdir(target)
|
|
1149
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
1150
|
+
except ValueError as e:
|
|
1151
|
+
return str(e)
|
|
1152
|
+
except OSError as e:
|
|
1153
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
1154
|
+
|
|
1155
|
+
tools.extend([file_read, file_write, list_files])
|
|
1156
|
+
{{/if}}
|
|
1157
|
+
|
|
1158
|
+
AGENT_INSTRUCTION = """
|
|
1159
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
1160
|
+
{{#if sessionStorageMountPath}}
|
|
1161
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
1162
|
+
{{/if}}
|
|
1163
|
+
"""
|
|
1164
|
+
|
|
1165
|
+
agent = Agent(
|
|
1166
|
+
model="gemini-2.5-flash",
|
|
1167
|
+
name="{{ name }}",
|
|
1168
|
+
description="A helpful assistant that can use tools.",
|
|
1169
|
+
instruction=AGENT_INSTRUCTION,
|
|
1170
|
+
tools=tools,
|
|
1171
|
+
)
|
|
1172
|
+
|
|
1173
|
+
runner = Runner(
|
|
1174
|
+
app_name=agent.name,
|
|
1175
|
+
agent=agent,
|
|
1176
|
+
session_service=InMemorySessionService(),
|
|
1177
|
+
)
|
|
1178
|
+
|
|
1179
|
+
card = AgentCard(
|
|
1180
|
+
name=agent.name,
|
|
1181
|
+
description=agent.description,
|
|
1182
|
+
url="http://localhost:9000/",
|
|
1183
|
+
version="0.1.0",
|
|
1184
|
+
capabilities=AgentCapabilities(streaming=True),
|
|
1185
|
+
skills=[
|
|
1186
|
+
AgentSkill(
|
|
1187
|
+
id="tools",
|
|
1188
|
+
name="tools",
|
|
1189
|
+
description="Use tools to help answer questions",
|
|
1190
|
+
tags=["tools"],
|
|
1191
|
+
)
|
|
1192
|
+
],
|
|
1193
|
+
default_input_modes=["text"],
|
|
1194
|
+
default_output_modes=["text"],
|
|
1195
|
+
)
|
|
1196
|
+
|
|
1197
|
+
if __name__ == "__main__":
|
|
1198
|
+
serve_a2a(A2aAgentExecutor(runner=runner), card)
|
|
1199
|
+
"
|
|
1200
|
+
`;
|
|
1201
|
+
|
|
1202
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/model/__init__.py should match snapshot 1`] = `
|
|
1203
|
+
"# Package marker
|
|
1204
|
+
"
|
|
1205
|
+
`;
|
|
1206
|
+
|
|
1207
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/model/load.py should match snapshot 1`] = `
|
|
1208
|
+
"import os
|
|
1209
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1210
|
+
|
|
1211
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1212
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1213
|
+
|
|
1214
|
+
|
|
1215
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1216
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1217
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1218
|
+
return api_key
|
|
1219
|
+
|
|
1220
|
+
|
|
1221
|
+
def _get_api_key() -> str:
|
|
1222
|
+
"""
|
|
1223
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1224
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1225
|
+
"""
|
|
1226
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1227
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1228
|
+
if not api_key:
|
|
1229
|
+
raise RuntimeError(
|
|
1230
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1231
|
+
)
|
|
1232
|
+
return api_key
|
|
1233
|
+
return _agentcore_identity_api_key_provider()
|
|
1234
|
+
|
|
1235
|
+
|
|
1236
|
+
def load_model() -> None:
|
|
1237
|
+
"""
|
|
1238
|
+
Set up Gemini API key authentication.
|
|
1239
|
+
Uses AgentCore Identity for API key management in deployed environments,
|
|
1240
|
+
and falls back to .env file for local development.
|
|
1241
|
+
Sets the GOOGLE_API_KEY environment variable for the Google ADK.
|
|
1242
|
+
"""
|
|
1243
|
+
api_key = _get_api_key()
|
|
1244
|
+
# Use Google AI Studios API Key Authentication.
|
|
1245
|
+
# https://google.github.io/adk-docs/agents/models/#google-ai-studio
|
|
1246
|
+
os.environ["GOOGLE_API_KEY"] = api_key
|
|
1247
|
+
# Set to TRUE is using Google Vertex AI, Set to FALSE for Google AI Studio
|
|
1248
|
+
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "FALSE"
|
|
1249
|
+
"
|
|
1250
|
+
`;
|
|
1251
|
+
|
|
1252
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/pyproject.toml should match snapshot 1`] = `
|
|
1253
|
+
"[build-system]
|
|
1254
|
+
requires = ["hatchling"]
|
|
1255
|
+
build-backend = "hatchling.build"
|
|
1256
|
+
|
|
1257
|
+
[project]
|
|
1258
|
+
name = "{{ name }}"
|
|
1259
|
+
version = "0.1.0"
|
|
1260
|
+
description = "AgentCore A2A Agent using Google ADK"
|
|
1261
|
+
readme = "README.md"
|
|
1262
|
+
requires-python = ">=3.10"
|
|
1263
|
+
dependencies = [
|
|
1264
|
+
"a2a-sdk >= 0.2.0",
|
|
1265
|
+
"aws-opentelemetry-distro",
|
|
1266
|
+
"bedrock-agentcore[a2a] >= 1.0.3",
|
|
1267
|
+
"google-adk >= 1.0.0",
|
|
1268
|
+
"google-genai >= 1.0.0",
|
|
1269
|
+
]
|
|
1270
|
+
|
|
1271
|
+
[tool.hatch.build.targets.wheel]
|
|
1272
|
+
packages = ["."]
|
|
1273
|
+
"
|
|
1274
|
+
`;
|
|
1275
|
+
|
|
1276
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/README.md should match snapshot 1`] = `
|
|
1277
|
+
"# {{ name }}
|
|
1278
|
+
|
|
1279
|
+
An A2A (Agent-to-Agent) agent deployed on Amazon Bedrock AgentCore using LangChain + LangGraph.
|
|
1280
|
+
|
|
1281
|
+
## Overview
|
|
1282
|
+
|
|
1283
|
+
This agent implements the A2A protocol using LangGraph, enabling agent-to-agent communication.
|
|
1284
|
+
|
|
1285
|
+
## Local Development
|
|
1286
|
+
|
|
1287
|
+
\`\`\`bash
|
|
1288
|
+
uv sync
|
|
1289
|
+
uv run python main.py
|
|
1290
|
+
\`\`\`
|
|
1291
|
+
|
|
1292
|
+
The agent starts on port 9000.
|
|
1293
|
+
|
|
1294
|
+
## Deploy
|
|
1295
|
+
|
|
1296
|
+
\`\`\`bash
|
|
1297
|
+
agentcore deploy
|
|
1298
|
+
\`\`\`
|
|
1299
|
+
"
|
|
1300
|
+
`;
|
|
1301
|
+
|
|
1302
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/gitignore.template should match snapshot 1`] = `
|
|
1303
|
+
"# Environment variables
|
|
1304
|
+
.env
|
|
1305
|
+
|
|
1306
|
+
# Python
|
|
1307
|
+
__pycache__/
|
|
1308
|
+
*.py[cod]
|
|
1309
|
+
*$py.class
|
|
1310
|
+
*.so
|
|
1311
|
+
.Python
|
|
1312
|
+
build/
|
|
1313
|
+
develop-eggs/
|
|
1314
|
+
dist/
|
|
1315
|
+
downloads/
|
|
1316
|
+
eggs/
|
|
1317
|
+
.eggs/
|
|
1318
|
+
lib/
|
|
1319
|
+
lib64/
|
|
1320
|
+
parts/
|
|
1321
|
+
sdist/
|
|
1322
|
+
var/
|
|
1323
|
+
wheels/
|
|
1324
|
+
*.egg-info/
|
|
1325
|
+
.installed.cfg
|
|
1326
|
+
*.egg
|
|
1327
|
+
|
|
1328
|
+
# Virtual environments
|
|
1329
|
+
.venv/
|
|
1330
|
+
venv/
|
|
1331
|
+
ENV/
|
|
1332
|
+
env/
|
|
1333
|
+
|
|
1334
|
+
# IDE
|
|
1335
|
+
.vscode/
|
|
1336
|
+
.idea/
|
|
1337
|
+
*.swp
|
|
1338
|
+
*.swo
|
|
1339
|
+
*~
|
|
1340
|
+
|
|
1341
|
+
# OS
|
|
1342
|
+
.DS_Store
|
|
1343
|
+
Thumbs.db
|
|
1344
|
+
"
|
|
1345
|
+
`;
|
|
1346
|
+
|
|
1347
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/main.py should match snapshot 1`] = `
|
|
1348
|
+
"import os
|
|
1349
|
+
from langchain_core.tools import tool
|
|
1350
|
+
from langgraph.prebuilt import create_react_agent
|
|
1351
|
+
from opentelemetry.instrumentation.langchain import LangchainInstrumentor
|
|
1352
|
+
from a2a.server.agent_execution import AgentExecutor, RequestContext
|
|
1353
|
+
from a2a.server.events import EventQueue
|
|
1354
|
+
from a2a.server.tasks import TaskUpdater
|
|
1355
|
+
from a2a.types import AgentCapabilities, AgentCard, AgentSkill, Part, TextPart
|
|
1356
|
+
from a2a.utils import new_task
|
|
1357
|
+
from bedrock_agentcore.runtime import serve_a2a
|
|
1358
|
+
from model.load import load_model
|
|
1359
|
+
|
|
1360
|
+
LangchainInstrumentor().instrument()
|
|
1361
|
+
|
|
1362
|
+
|
|
1363
|
+
@tool
|
|
1364
|
+
def add_numbers(a: int, b: int) -> int:
|
|
1365
|
+
"""Return the sum of two numbers."""
|
|
1366
|
+
return a + b
|
|
1367
|
+
|
|
1368
|
+
|
|
1369
|
+
tools = [add_numbers]
|
|
1370
|
+
|
|
1371
|
+
{{#if sessionStorageMountPath}}
|
|
1372
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
1373
|
+
|
|
1374
|
+
def _safe_resolve(path: str) -> str:
|
|
1375
|
+
"""Resolve path safely within the storage boundary."""
|
|
1376
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
1377
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
1378
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
1379
|
+
return resolved
|
|
1380
|
+
|
|
1381
|
+
@tool
|
|
1382
|
+
def file_read(path: str) -> str:
|
|
1383
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
1384
|
+
try:
|
|
1385
|
+
full_path = _safe_resolve(path)
|
|
1386
|
+
with open(full_path) as f:
|
|
1387
|
+
return f.read()
|
|
1388
|
+
except ValueError as e:
|
|
1389
|
+
return str(e)
|
|
1390
|
+
except OSError as e:
|
|
1391
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
1392
|
+
|
|
1393
|
+
@tool
|
|
1394
|
+
def file_write(path: str, content: str) -> str:
|
|
1395
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
1396
|
+
try:
|
|
1397
|
+
full_path = _safe_resolve(path)
|
|
1398
|
+
parent = os.path.dirname(full_path)
|
|
1399
|
+
if parent:
|
|
1400
|
+
os.makedirs(parent, exist_ok=True)
|
|
1401
|
+
with open(full_path, "w") as f:
|
|
1402
|
+
f.write(content)
|
|
1403
|
+
return f"Written to {path}"
|
|
1404
|
+
except ValueError as e:
|
|
1405
|
+
return str(e)
|
|
1406
|
+
except OSError as e:
|
|
1407
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
1408
|
+
|
|
1409
|
+
@tool
|
|
1410
|
+
def list_files(directory: str = "") -> str:
|
|
1411
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
1412
|
+
try:
|
|
1413
|
+
target = _safe_resolve(directory)
|
|
1414
|
+
entries = os.listdir(target)
|
|
1415
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
1416
|
+
except ValueError as e:
|
|
1417
|
+
return str(e)
|
|
1418
|
+
except OSError as e:
|
|
1419
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
1420
|
+
|
|
1421
|
+
tools.extend([file_read, file_write, list_files])
|
|
1422
|
+
{{/if}}
|
|
1423
|
+
|
|
1424
|
+
SYSTEM_PROMPT = """
|
|
1425
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
1426
|
+
{{#if sessionStorageMountPath}}
|
|
1427
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
1428
|
+
{{/if}}
|
|
1429
|
+
"""
|
|
1430
|
+
|
|
1431
|
+
model = load_model()
|
|
1432
|
+
graph = create_react_agent(model, tools=tools, prompt=SYSTEM_PROMPT)
|
|
1433
|
+
|
|
1434
|
+
|
|
1435
|
+
class LangGraphA2AExecutor(AgentExecutor):
|
|
1436
|
+
"""Wraps a LangGraph CompiledGraph as an a2a-sdk AgentExecutor."""
|
|
1437
|
+
|
|
1438
|
+
def __init__(self, graph):
|
|
1439
|
+
self.graph = graph
|
|
1440
|
+
|
|
1441
|
+
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
1442
|
+
task = context.current_task or new_task(context.message)
|
|
1443
|
+
if not context.current_task:
|
|
1444
|
+
await event_queue.enqueue_event(task)
|
|
1445
|
+
updater = TaskUpdater(event_queue, task.id, task.context_id)
|
|
1446
|
+
|
|
1447
|
+
user_text = context.get_user_input()
|
|
1448
|
+
result = await self.graph.ainvoke({"messages": [("user", user_text)]})
|
|
1449
|
+
response = result["messages"][-1].content
|
|
1450
|
+
|
|
1451
|
+
await updater.add_artifact([Part(root=TextPart(text=response))])
|
|
1452
|
+
await updater.complete()
|
|
1453
|
+
|
|
1454
|
+
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
1455
|
+
pass
|
|
1456
|
+
|
|
1457
|
+
|
|
1458
|
+
card = AgentCard(
|
|
1459
|
+
name="{{ name }}",
|
|
1460
|
+
description="A LangGraph agent on Bedrock AgentCore",
|
|
1461
|
+
url="http://localhost:9000/",
|
|
1462
|
+
version="0.1.0",
|
|
1463
|
+
capabilities=AgentCapabilities(streaming=True),
|
|
1464
|
+
skills=[
|
|
1465
|
+
AgentSkill(
|
|
1466
|
+
id="tools",
|
|
1467
|
+
name="tools",
|
|
1468
|
+
description="Use tools to help answer questions",
|
|
1469
|
+
tags=["tools"],
|
|
1470
|
+
)
|
|
1471
|
+
],
|
|
1472
|
+
default_input_modes=["text"],
|
|
1473
|
+
default_output_modes=["text"],
|
|
1474
|
+
)
|
|
1475
|
+
|
|
1476
|
+
if __name__ == "__main__":
|
|
1477
|
+
serve_a2a(LangGraphA2AExecutor(graph), card)
|
|
1478
|
+
"
|
|
1479
|
+
`;
|
|
1480
|
+
|
|
1481
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/model/__init__.py should match snapshot 1`] = `
|
|
1482
|
+
"# Package marker
|
|
1483
|
+
"
|
|
1484
|
+
`;
|
|
1485
|
+
|
|
1486
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/model/load.py should match snapshot 1`] = `
|
|
1487
|
+
"{{#if (eq modelProvider "Bedrock")}}
|
|
1488
|
+
from langchain_aws import ChatBedrock
|
|
1489
|
+
|
|
1490
|
+
# Uses global inference profile for Claude Sonnet 4.5
|
|
1491
|
+
# https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html
|
|
1492
|
+
MODEL_ID = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
|
1493
|
+
|
|
1494
|
+
|
|
1495
|
+
def load_model() -> ChatBedrock:
|
|
1496
|
+
"""Get Bedrock model client using IAM credentials."""
|
|
1497
|
+
return ChatBedrock(model_id=MODEL_ID)
|
|
1498
|
+
{{/if}}
|
|
1499
|
+
{{#if (eq modelProvider "Anthropic")}}
|
|
1500
|
+
import os
|
|
1501
|
+
from langchain_anthropic import ChatAnthropic
|
|
1502
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1503
|
+
|
|
1504
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1505
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1506
|
+
|
|
1507
|
+
|
|
1508
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1509
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1510
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1511
|
+
return api_key
|
|
1512
|
+
|
|
1513
|
+
|
|
1514
|
+
def _get_api_key() -> str:
|
|
1515
|
+
"""
|
|
1516
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1517
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1518
|
+
"""
|
|
1519
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1520
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1521
|
+
if not api_key:
|
|
1522
|
+
raise RuntimeError(
|
|
1523
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1524
|
+
)
|
|
1525
|
+
return api_key
|
|
1526
|
+
return _agentcore_identity_api_key_provider()
|
|
1527
|
+
|
|
1528
|
+
|
|
1529
|
+
def load_model() -> ChatAnthropic:
|
|
1530
|
+
"""Get authenticated Anthropic model client."""
|
|
1531
|
+
return ChatAnthropic(
|
|
1532
|
+
model="claude-sonnet-4-5-20250929",
|
|
1533
|
+
api_key=_get_api_key()
|
|
1534
|
+
)
|
|
1535
|
+
{{/if}}
|
|
1536
|
+
{{#if (eq modelProvider "OpenAI")}}
|
|
1537
|
+
import os
|
|
1538
|
+
from langchain_openai import ChatOpenAI
|
|
1539
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1540
|
+
|
|
1541
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1542
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1543
|
+
|
|
1544
|
+
|
|
1545
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1546
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1547
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1548
|
+
return api_key
|
|
1549
|
+
|
|
1550
|
+
|
|
1551
|
+
def _get_api_key() -> str:
|
|
1552
|
+
"""
|
|
1553
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1554
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1555
|
+
"""
|
|
1556
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1557
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1558
|
+
if not api_key:
|
|
1559
|
+
raise RuntimeError(
|
|
1560
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1561
|
+
)
|
|
1562
|
+
return api_key
|
|
1563
|
+
return _agentcore_identity_api_key_provider()
|
|
1564
|
+
|
|
1565
|
+
|
|
1566
|
+
def load_model() -> ChatOpenAI:
|
|
1567
|
+
"""Get authenticated OpenAI model client."""
|
|
1568
|
+
return ChatOpenAI(
|
|
1569
|
+
model="gpt-4.1",
|
|
1570
|
+
api_key=_get_api_key()
|
|
1571
|
+
)
|
|
1572
|
+
{{/if}}
|
|
1573
|
+
{{#if (eq modelProvider "Gemini")}}
|
|
1574
|
+
import os
|
|
1575
|
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
|
1576
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1577
|
+
|
|
1578
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1579
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1580
|
+
|
|
1581
|
+
|
|
1582
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1583
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1584
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1585
|
+
return api_key
|
|
1586
|
+
|
|
1587
|
+
|
|
1588
|
+
def _get_api_key() -> str:
|
|
1589
|
+
"""
|
|
1590
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1591
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1592
|
+
"""
|
|
1593
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1594
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1595
|
+
if not api_key:
|
|
1596
|
+
raise RuntimeError(
|
|
1597
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1598
|
+
)
|
|
1599
|
+
return api_key
|
|
1600
|
+
return _agentcore_identity_api_key_provider()
|
|
1601
|
+
|
|
1602
|
+
|
|
1603
|
+
def load_model() -> ChatGoogleGenerativeAI:
|
|
1604
|
+
"""Get authenticated Gemini model client."""
|
|
1605
|
+
return ChatGoogleGenerativeAI(
|
|
1606
|
+
model="gemini-2.5-flash",
|
|
1607
|
+
api_key=_get_api_key()
|
|
1608
|
+
)
|
|
1609
|
+
{{/if}}
|
|
1610
|
+
"
|
|
1611
|
+
`;
|
|
1612
|
+
|
|
1613
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/pyproject.toml should match snapshot 1`] = `
|
|
1614
|
+
"[build-system]
|
|
1615
|
+
requires = ["hatchling"]
|
|
1616
|
+
build-backend = "hatchling.build"
|
|
1617
|
+
|
|
1618
|
+
[project]
|
|
1619
|
+
name = "{{ name }}"
|
|
1620
|
+
version = "0.1.0"
|
|
1621
|
+
description = "AgentCore A2A Agent using LangChain + LangGraph"
|
|
1622
|
+
readme = "README.md"
|
|
1623
|
+
requires-python = ">=3.10"
|
|
1624
|
+
dependencies = [
|
|
1625
|
+
"a2a-sdk >= 0.2.0",
|
|
1626
|
+
{{#if (eq modelProvider "Anthropic")}}"langchain-anthropic >= 0.3.0",
|
|
1627
|
+
{{/if}}{{#if (eq modelProvider "Bedrock")}}"langchain-aws >= 0.2.0",
|
|
1628
|
+
{{/if}}{{#if (eq modelProvider "Gemini")}}"langchain-google-genai >= 2.0.0",
|
|
1629
|
+
{{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
|
|
1630
|
+
{{/if}}"aws-opentelemetry-distro",
|
|
1631
|
+
"opentelemetry-instrumentation-langchain >= 0.59.0",
|
|
1632
|
+
"bedrock-agentcore[a2a] >= 1.0.3",
|
|
1633
|
+
"botocore[crt] >= 1.35.0",
|
|
1634
|
+
"langgraph >= 0.2.0",
|
|
1635
|
+
]
|
|
1636
|
+
|
|
1637
|
+
[tool.hatch.build.targets.wheel]
|
|
1638
|
+
packages = ["."]
|
|
1639
|
+
"
|
|
1640
|
+
`;
|
|
1641
|
+
|
|
1642
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/README.md should match snapshot 1`] = `
|
|
1643
|
+
"# {{ name }}
|
|
1644
|
+
|
|
1645
|
+
An A2A (Agent-to-Agent) agent deployed on Amazon Bedrock AgentCore using Strands SDK.
|
|
1646
|
+
|
|
1647
|
+
## Overview
|
|
1648
|
+
|
|
1649
|
+
This agent implements the A2A protocol, enabling agent-to-agent communication. Other agents can discover and interact with this agent via the \`/.well-known/agent-card.json\` endpoint.
|
|
1650
|
+
|
|
1651
|
+
## Local Development
|
|
1652
|
+
|
|
1653
|
+
\`\`\`bash
|
|
1654
|
+
uv sync
|
|
1655
|
+
uv run python main.py
|
|
1656
|
+
\`\`\`
|
|
1657
|
+
|
|
1658
|
+
The agent starts on port 9000.
|
|
1659
|
+
|
|
1660
|
+
## Deploy
|
|
1661
|
+
|
|
1662
|
+
\`\`\`bash
|
|
1663
|
+
agentcore deploy
|
|
1664
|
+
\`\`\`
|
|
1665
|
+
"
|
|
1666
|
+
`;
|
|
1667
|
+
|
|
1668
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/gitignore.template should match snapshot 1`] = `
|
|
1669
|
+
"# Environment variables
|
|
1670
|
+
.env
|
|
1671
|
+
|
|
1672
|
+
# Python
|
|
1673
|
+
__pycache__/
|
|
1674
|
+
*.py[cod]
|
|
1675
|
+
*$py.class
|
|
1676
|
+
*.so
|
|
1677
|
+
.Python
|
|
1678
|
+
build/
|
|
1679
|
+
develop-eggs/
|
|
1680
|
+
dist/
|
|
1681
|
+
downloads/
|
|
1682
|
+
eggs/
|
|
1683
|
+
.eggs/
|
|
1684
|
+
lib/
|
|
1685
|
+
lib64/
|
|
1686
|
+
parts/
|
|
1687
|
+
sdist/
|
|
1688
|
+
var/
|
|
1689
|
+
wheels/
|
|
1690
|
+
*.egg-info/
|
|
1691
|
+
.installed.cfg
|
|
1692
|
+
*.egg
|
|
1693
|
+
|
|
1694
|
+
# Virtual environments
|
|
1695
|
+
.venv/
|
|
1696
|
+
venv/
|
|
1697
|
+
ENV/
|
|
1698
|
+
env/
|
|
1699
|
+
|
|
1700
|
+
# IDE
|
|
1701
|
+
.vscode/
|
|
1702
|
+
.idea/
|
|
1703
|
+
*.swp
|
|
1704
|
+
*.swo
|
|
1705
|
+
*~
|
|
1706
|
+
|
|
1707
|
+
# OS
|
|
1708
|
+
.DS_Store
|
|
1709
|
+
Thumbs.db
|
|
1710
|
+
"
|
|
1711
|
+
`;
|
|
1712
|
+
|
|
1713
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/main.py should match snapshot 1`] = `
|
|
1714
|
+
"from strands import Agent, tool
|
|
1715
|
+
from strands.multiagent.a2a.executor import StrandsA2AExecutor
|
|
1716
|
+
from bedrock_agentcore.runtime import serve_a2a
|
|
1717
|
+
from model.load import load_model
|
|
1718
|
+
{{#if hasMemory}}
|
|
1719
|
+
from memory.session import get_memory_session_manager
|
|
1720
|
+
{{/if}}
|
|
1721
|
+
{{#if sessionStorageMountPath}}
|
|
1722
|
+
import os
|
|
1723
|
+
{{/if}}
|
|
1724
|
+
|
|
1725
|
+
|
|
1726
|
+
@tool
|
|
1727
|
+
def add_numbers(a: int, b: int) -> int:
|
|
1728
|
+
"""Return the sum of two numbers."""
|
|
1729
|
+
return a + b
|
|
1730
|
+
|
|
1731
|
+
|
|
1732
|
+
tools = [add_numbers]
|
|
1733
|
+
|
|
1734
|
+
{{#if sessionStorageMountPath}}
|
|
1735
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
1736
|
+
|
|
1737
|
+
def _safe_resolve(path: str) -> str:
|
|
1738
|
+
"""Resolve path safely within the storage boundary."""
|
|
1739
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
1740
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
1741
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
1742
|
+
return resolved
|
|
1743
|
+
|
|
1744
|
+
@tool
|
|
1745
|
+
def file_read(path: str) -> str:
|
|
1746
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
1747
|
+
try:
|
|
1748
|
+
full_path = _safe_resolve(path)
|
|
1749
|
+
with open(full_path) as f:
|
|
1750
|
+
return f.read()
|
|
1751
|
+
except ValueError as e:
|
|
1752
|
+
return str(e)
|
|
1753
|
+
except OSError as e:
|
|
1754
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
1755
|
+
|
|
1756
|
+
@tool
|
|
1757
|
+
def file_write(path: str, content: str) -> str:
|
|
1758
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
1759
|
+
try:
|
|
1760
|
+
full_path = _safe_resolve(path)
|
|
1761
|
+
parent = os.path.dirname(full_path)
|
|
1762
|
+
if parent:
|
|
1763
|
+
os.makedirs(parent, exist_ok=True)
|
|
1764
|
+
with open(full_path, "w") as f:
|
|
1765
|
+
f.write(content)
|
|
1766
|
+
return f"Written to {path}"
|
|
1767
|
+
except ValueError as e:
|
|
1768
|
+
return str(e)
|
|
1769
|
+
except OSError as e:
|
|
1770
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
1771
|
+
|
|
1772
|
+
@tool
|
|
1773
|
+
def list_files(directory: str = "") -> str:
|
|
1774
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
1775
|
+
try:
|
|
1776
|
+
target = _safe_resolve(directory)
|
|
1777
|
+
entries = os.listdir(target)
|
|
1778
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
1779
|
+
except ValueError as e:
|
|
1780
|
+
return str(e)
|
|
1781
|
+
except OSError as e:
|
|
1782
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
1783
|
+
|
|
1784
|
+
tools.extend([file_read, file_write, list_files])
|
|
1785
|
+
{{/if}}
|
|
1786
|
+
|
|
1787
|
+
SYSTEM_PROMPT = """
|
|
1788
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
1789
|
+
{{#if sessionStorageMountPath}}
|
|
1790
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
1791
|
+
{{/if}}
|
|
1792
|
+
"""
|
|
1793
|
+
|
|
1794
|
+
{{#if hasMemory}}
|
|
1795
|
+
def agent_factory():
|
|
1796
|
+
cache = {}
|
|
1797
|
+
def get_or_create_agent(session_id, user_id):
|
|
1798
|
+
key = f"{session_id}/{user_id}"
|
|
1799
|
+
if key not in cache:
|
|
1800
|
+
cache[key] = Agent(
|
|
1801
|
+
model=load_model(),
|
|
1802
|
+
session_manager=get_memory_session_manager(session_id, user_id),
|
|
1803
|
+
system_prompt=SYSTEM_PROMPT,
|
|
1804
|
+
tools=tools,
|
|
1805
|
+
)
|
|
1806
|
+
return cache[key]
|
|
1807
|
+
return get_or_create_agent
|
|
1808
|
+
|
|
1809
|
+
get_or_create_agent = agent_factory()
|
|
1810
|
+
agent = get_or_create_agent("default-session", "default-user")
|
|
1811
|
+
{{else}}
|
|
1812
|
+
agent = Agent(
|
|
1813
|
+
model=load_model(),
|
|
1814
|
+
system_prompt=SYSTEM_PROMPT,
|
|
1815
|
+
tools=tools,
|
|
1816
|
+
)
|
|
1817
|
+
{{/if}}
|
|
1818
|
+
|
|
1819
|
+
if __name__ == "__main__":
|
|
1820
|
+
serve_a2a(StrandsA2AExecutor(agent))
|
|
1821
|
+
"
|
|
1822
|
+
`;
|
|
1823
|
+
|
|
1824
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/model/__init__.py should match snapshot 1`] = `
|
|
1825
|
+
"# Package marker
|
|
1826
|
+
"
|
|
1827
|
+
`;
|
|
1828
|
+
|
|
1829
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/model/load.py should match snapshot 1`] = `
|
|
1830
|
+
"{{#if (eq modelProvider "Bedrock")}}
|
|
1831
|
+
from strands.models.bedrock import BedrockModel
|
|
1832
|
+
|
|
1833
|
+
|
|
1834
|
+
def load_model() -> BedrockModel:
|
|
1835
|
+
"""Get Bedrock model client using IAM credentials."""
|
|
1836
|
+
return BedrockModel(model_id="global.anthropic.claude-sonnet-4-5-20250929-v1:0")
|
|
1837
|
+
{{/if}}
|
|
1838
|
+
{{#if (eq modelProvider "Anthropic")}}
|
|
1839
|
+
import os
|
|
1840
|
+
|
|
1841
|
+
from strands.models.anthropic import AnthropicModel
|
|
1842
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1843
|
+
|
|
1844
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1845
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1846
|
+
|
|
1847
|
+
|
|
1848
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1849
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1850
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1851
|
+
return api_key
|
|
1852
|
+
|
|
1853
|
+
|
|
1854
|
+
def _get_api_key() -> str:
|
|
1855
|
+
"""
|
|
1856
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1857
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1858
|
+
"""
|
|
1859
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1860
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1861
|
+
if not api_key:
|
|
1862
|
+
raise RuntimeError(
|
|
1863
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1864
|
+
)
|
|
1865
|
+
return api_key
|
|
1866
|
+
return _agentcore_identity_api_key_provider()
|
|
1867
|
+
|
|
1868
|
+
|
|
1869
|
+
def load_model() -> AnthropicModel:
|
|
1870
|
+
"""Get authenticated Anthropic model client."""
|
|
1871
|
+
return AnthropicModel(
|
|
1872
|
+
client_args={"api_key": _get_api_key()},
|
|
1873
|
+
model_id="claude-sonnet-4-5-20250929",
|
|
1874
|
+
max_tokens=5000,
|
|
1875
|
+
)
|
|
1876
|
+
{{/if}}
|
|
1877
|
+
{{#if (eq modelProvider "OpenAI")}}
|
|
1878
|
+
import os
|
|
1879
|
+
|
|
1880
|
+
from strands.models.openai import OpenAIModel
|
|
1881
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1882
|
+
|
|
1883
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1884
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1885
|
+
|
|
1886
|
+
|
|
1887
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1888
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1889
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1890
|
+
return api_key
|
|
1891
|
+
|
|
1892
|
+
|
|
1893
|
+
def _get_api_key() -> str:
|
|
1894
|
+
"""
|
|
1895
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1896
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1897
|
+
"""
|
|
1898
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1899
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1900
|
+
if not api_key:
|
|
1901
|
+
raise RuntimeError(
|
|
1902
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1903
|
+
)
|
|
1904
|
+
return api_key
|
|
1905
|
+
return _agentcore_identity_api_key_provider()
|
|
1906
|
+
|
|
1907
|
+
|
|
1908
|
+
def load_model() -> OpenAIModel:
|
|
1909
|
+
"""Get authenticated OpenAI model client."""
|
|
1910
|
+
return OpenAIModel(
|
|
1911
|
+
client_args={"api_key": _get_api_key()},
|
|
1912
|
+
model_id="gpt-4.1",
|
|
1913
|
+
)
|
|
1914
|
+
{{/if}}
|
|
1915
|
+
{{#if (eq modelProvider "Gemini")}}
|
|
1916
|
+
import os
|
|
1917
|
+
|
|
1918
|
+
from strands.models.gemini import GeminiModel
|
|
1919
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1920
|
+
|
|
1921
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1922
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1923
|
+
|
|
1924
|
+
|
|
1925
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1926
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1927
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1928
|
+
return api_key
|
|
1929
|
+
|
|
1930
|
+
|
|
1931
|
+
def _get_api_key() -> str:
|
|
1932
|
+
"""
|
|
1933
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1934
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1935
|
+
"""
|
|
1936
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1937
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1938
|
+
if not api_key:
|
|
1939
|
+
raise RuntimeError(
|
|
1940
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1941
|
+
)
|
|
1942
|
+
return api_key
|
|
1943
|
+
return _agentcore_identity_api_key_provider()
|
|
1944
|
+
|
|
1945
|
+
|
|
1946
|
+
def load_model() -> GeminiModel:
|
|
1947
|
+
"""Get authenticated Gemini model client."""
|
|
1948
|
+
return GeminiModel(
|
|
1949
|
+
client_args={"api_key": _get_api_key()},
|
|
1950
|
+
model_id="gemini-2.5-flash",
|
|
1951
|
+
)
|
|
1952
|
+
{{/if}}
|
|
1953
|
+
"
|
|
1954
|
+
`;
|
|
1955
|
+
|
|
1956
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/pyproject.toml should match snapshot 1`] = `
|
|
1957
|
+
"[build-system]
|
|
1958
|
+
requires = ["hatchling"]
|
|
1959
|
+
build-backend = "hatchling.build"
|
|
1960
|
+
|
|
1961
|
+
[project]
|
|
1962
|
+
name = "{{ name }}"
|
|
1963
|
+
version = "0.1.0"
|
|
1964
|
+
description = "AgentCore A2A Agent using Strands SDK"
|
|
1965
|
+
readme = "README.md"
|
|
1966
|
+
requires-python = ">=3.10"
|
|
1967
|
+
dependencies = [
|
|
1968
|
+
{{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
|
|
1969
|
+
{{/if}}"a2a-sdk[all] >= 0.2.0",
|
|
1970
|
+
"aws-opentelemetry-distro",
|
|
1971
|
+
"bedrock-agentcore[a2a] >= 1.0.3",
|
|
1972
|
+
"botocore[crt] >= 1.35.0",
|
|
1973
|
+
{{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
|
|
1974
|
+
{{/if}}{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
|
|
1975
|
+
{{/if}}"strands-agents >= 1.13.0",
|
|
1976
|
+
]
|
|
1977
|
+
|
|
1978
|
+
[tool.hatch.build.targets.wheel]
|
|
1979
|
+
packages = ["."]
|
|
1980
|
+
"
|
|
1981
|
+
`;
|
|
1982
|
+
|
|
1983
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/capabilities/memory/__init__.py should match snapshot 1`] = `
|
|
1984
|
+
"# Package marker
|
|
1985
|
+
"
|
|
1986
|
+
`;
|
|
1987
|
+
|
|
1988
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/capabilities/memory/session.py should match snapshot 1`] = `
|
|
1989
|
+
"import os
|
|
1990
|
+
from typing import Optional
|
|
1991
|
+
|
|
1992
|
+
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig{{#if memoryProviders.[0].strategies.length}}, RetrievalConfig{{/if}}
|
|
1993
|
+
from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager
|
|
1994
|
+
|
|
1995
|
+
MEMORY_ID = os.getenv("{{memoryProviders.[0].envVarName}}")
|
|
1996
|
+
REGION = os.getenv("AWS_REGION")
|
|
1997
|
+
|
|
1998
|
+
def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
|
|
1999
|
+
if not MEMORY_ID:
|
|
2000
|
+
return None
|
|
2001
|
+
|
|
2002
|
+
{{#if memoryProviders.[0].strategies.length}}
|
|
2003
|
+
retrieval_config = {
|
|
2004
|
+
{{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}
|
|
2005
|
+
f"/users/{actor_id}/facts": RetrievalConfig(top_k=3, relevance_score=0.5),
|
|
2006
|
+
{{/if}}
|
|
2007
|
+
{{#if (includes memoryProviders.[0].strategies "USER_PREFERENCE")}}
|
|
2008
|
+
f"/users/{actor_id}/preferences": RetrievalConfig(top_k=3, relevance_score=0.5),
|
|
2009
|
+
{{/if}}
|
|
2010
|
+
{{#if (includes memoryProviders.[0].strategies "SUMMARIZATION")}}
|
|
2011
|
+
f"/summaries/{actor_id}/{session_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
|
|
2012
|
+
{{/if}}
|
|
2013
|
+
}
|
|
2014
|
+
{{/if}}
|
|
2015
|
+
|
|
2016
|
+
return AgentCoreMemorySessionManager(
|
|
2017
|
+
AgentCoreMemoryConfig(
|
|
2018
|
+
memory_id=MEMORY_ID,
|
|
2019
|
+
session_id=session_id,
|
|
2020
|
+
actor_id=actor_id,
|
|
2021
|
+
{{#if memoryProviders.[0].strategies.length}}
|
|
2022
|
+
retrieval_config=retrieval_config,
|
|
2023
|
+
{{/if}}
|
|
2024
|
+
),
|
|
2025
|
+
REGION
|
|
2026
|
+
)
|
|
2027
|
+
"
|
|
2028
|
+
`;
|
|
2029
|
+
|
|
2030
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/README.md should match snapshot 1`] = `
|
|
2031
|
+
"# {{ name }}
|
|
2032
|
+
|
|
2033
|
+
An AG-UI agent deployed on Amazon Bedrock AgentCore using Google ADK.
|
|
2034
|
+
|
|
2035
|
+
## Overview
|
|
2036
|
+
|
|
2037
|
+
This agent implements the AG-UI protocol using Google's Agent Development Kit, enabling rich agent-user interaction via the AG-UI event stream.
|
|
2038
|
+
|
|
2039
|
+
## Local Development
|
|
2040
|
+
|
|
2041
|
+
\`\`\`bash
|
|
2042
|
+
uv sync
|
|
2043
|
+
uv run python main.py
|
|
2044
|
+
\`\`\`
|
|
2045
|
+
|
|
2046
|
+
The agent starts on port 8080 and serves requests at \`/invocations\`.
|
|
2047
|
+
|
|
2048
|
+
## Health Check
|
|
2049
|
+
|
|
2050
|
+
\`\`\`
|
|
2051
|
+
GET /ping
|
|
913
2052
|
\`\`\`
|
|
914
2053
|
|
|
915
|
-
|
|
2054
|
+
Returns \`{"status": "healthy"}\`.
|
|
916
2055
|
|
|
917
2056
|
## Deploy
|
|
918
2057
|
|
|
@@ -922,7 +2061,7 @@ agentcore deploy
|
|
|
922
2061
|
"
|
|
923
2062
|
`;
|
|
924
2063
|
|
|
925
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2064
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/gitignore.template should match snapshot 1`] = `
|
|
926
2065
|
"# Environment variables
|
|
927
2066
|
.env
|
|
928
2067
|
|
|
@@ -967,64 +2106,47 @@ Thumbs.db
|
|
|
967
2106
|
"
|
|
968
2107
|
`;
|
|
969
2108
|
|
|
970
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
971
|
-
"
|
|
972
|
-
|
|
973
|
-
from google.adk.
|
|
974
|
-
from
|
|
975
|
-
from a2a.types import AgentCapabilities, AgentCard, AgentSkill
|
|
976
|
-
from bedrock_agentcore.runtime import serve_a2a
|
|
2109
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/main.py should match snapshot 1`] = `
|
|
2110
|
+
"import os
|
|
2111
|
+
import uvicorn
|
|
2112
|
+
from google.adk.agents import LlmAgent
|
|
2113
|
+
from ag_ui_adk import ADKAgent, AGUIToolset, create_adk_app
|
|
977
2114
|
from model.load import load_model
|
|
978
2115
|
|
|
2116
|
+
load_model()
|
|
979
2117
|
|
|
980
|
-
|
|
981
|
-
"""Return the sum of two numbers."""
|
|
982
|
-
return a + b
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
agent = Agent(
|
|
986
|
-
model=load_model(),
|
|
2118
|
+
agent = LlmAgent(
|
|
987
2119
|
name="{{ name }}",
|
|
988
|
-
|
|
989
|
-
instruction="You are a helpful assistant.
|
|
990
|
-
tools=[
|
|
2120
|
+
model="gemini-2.5-flash",
|
|
2121
|
+
instruction="You are a helpful assistant.",
|
|
2122
|
+
tools=[AGUIToolset()],
|
|
991
2123
|
)
|
|
992
2124
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
2125
|
+
adk_agent = ADKAgent(
|
|
2126
|
+
adk_agent=agent,
|
|
2127
|
+
app_name="{{ name }}",
|
|
2128
|
+
use_in_memory_services=True,
|
|
997
2129
|
)
|
|
998
2130
|
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
AgentSkill(
|
|
1007
|
-
id="tools",
|
|
1008
|
-
name="tools",
|
|
1009
|
-
description="Use tools to help answer questions",
|
|
1010
|
-
tags=["tools"],
|
|
1011
|
-
)
|
|
1012
|
-
],
|
|
1013
|
-
default_input_modes=["text"],
|
|
1014
|
-
default_output_modes=["text"],
|
|
1015
|
-
)
|
|
2131
|
+
app = create_adk_app(adk_agent, path="/invocations")
|
|
2132
|
+
|
|
2133
|
+
|
|
2134
|
+
@app.get("/ping")
|
|
2135
|
+
async def ping():
|
|
2136
|
+
return {"status": "healthy"}
|
|
2137
|
+
|
|
1016
2138
|
|
|
1017
2139
|
if __name__ == "__main__":
|
|
1018
|
-
|
|
2140
|
+
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))
|
|
1019
2141
|
"
|
|
1020
2142
|
`;
|
|
1021
2143
|
|
|
1022
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2144
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/model/__init__.py should match snapshot 1`] = `
|
|
1023
2145
|
"# Package marker
|
|
1024
2146
|
"
|
|
1025
2147
|
`;
|
|
1026
2148
|
|
|
1027
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2149
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/model/load.py should match snapshot 1`] = `
|
|
1028
2150
|
"import os
|
|
1029
2151
|
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1030
2152
|
|
|
@@ -1069,7 +2191,7 @@ def load_model() -> None:
|
|
|
1069
2191
|
"
|
|
1070
2192
|
`;
|
|
1071
2193
|
|
|
1072
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2194
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/pyproject.toml should match snapshot 1`] = `
|
|
1073
2195
|
"[build-system]
|
|
1074
2196
|
requires = ["hatchling"]
|
|
1075
2197
|
build-backend = "hatchling.build"
|
|
@@ -1077,15 +2199,19 @@ build-backend = "hatchling.build"
|
|
|
1077
2199
|
[project]
|
|
1078
2200
|
name = "{{ name }}"
|
|
1079
2201
|
version = "0.1.0"
|
|
1080
|
-
description = "AgentCore
|
|
2202
|
+
description = "AgentCore AG-UI Agent using Google ADK"
|
|
1081
2203
|
readme = "README.md"
|
|
1082
2204
|
requires-python = ">=3.10"
|
|
1083
2205
|
dependencies = [
|
|
1084
|
-
"
|
|
1085
|
-
"
|
|
1086
|
-
"bedrock-agentcore
|
|
1087
|
-
"
|
|
2206
|
+
"ag-ui-adk >= 0.6.0",
|
|
2207
|
+
"ag-ui-protocol >= 0.1.10",
|
|
2208
|
+
"bedrock-agentcore >= 1.0.3",
|
|
2209
|
+
"fastapi >= 0.115.12",
|
|
2210
|
+
"google-adk >= 1.16.0",
|
|
1088
2211
|
"google-genai >= 1.0.0",
|
|
2212
|
+
"opentelemetry-distro",
|
|
2213
|
+
"opentelemetry-exporter-otlp",
|
|
2214
|
+
"uvicorn >= 0.34.3",
|
|
1089
2215
|
]
|
|
1090
2216
|
|
|
1091
2217
|
[tool.hatch.build.targets.wheel]
|
|
@@ -1093,14 +2219,14 @@ packages = ["."]
|
|
|
1093
2219
|
"
|
|
1094
2220
|
`;
|
|
1095
2221
|
|
|
1096
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2222
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/README.md should match snapshot 1`] = `
|
|
1097
2223
|
"# {{ name }}
|
|
1098
2224
|
|
|
1099
|
-
An
|
|
2225
|
+
An AG-UI agent deployed on Amazon Bedrock AgentCore using LangChain + LangGraph.
|
|
1100
2226
|
|
|
1101
2227
|
## Overview
|
|
1102
2228
|
|
|
1103
|
-
This agent implements the
|
|
2229
|
+
This agent implements the AG-UI protocol using LangGraph, enabling seamless frontend-to-agent communication with support for streaming, tool calls, and frontend-injected tools.
|
|
1104
2230
|
|
|
1105
2231
|
## Local Development
|
|
1106
2232
|
|
|
@@ -1109,7 +2235,7 @@ uv sync
|
|
|
1109
2235
|
uv run python main.py
|
|
1110
2236
|
\`\`\`
|
|
1111
2237
|
|
|
1112
|
-
The agent starts on port
|
|
2238
|
+
The agent starts on port 8080.
|
|
1113
2239
|
|
|
1114
2240
|
## Deploy
|
|
1115
2241
|
|
|
@@ -1119,7 +2245,7 @@ agentcore deploy
|
|
|
1119
2245
|
"
|
|
1120
2246
|
`;
|
|
1121
2247
|
|
|
1122
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2248
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/gitignore.template should match snapshot 1`] = `
|
|
1123
2249
|
"# Environment variables
|
|
1124
2250
|
.env
|
|
1125
2251
|
|
|
@@ -1164,16 +2290,22 @@ Thumbs.db
|
|
|
1164
2290
|
"
|
|
1165
2291
|
`;
|
|
1166
2292
|
|
|
1167
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
1168
|
-
"
|
|
1169
|
-
|
|
2293
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/main.py should match snapshot 1`] = `
|
|
2294
|
+
"import os
|
|
2295
|
+
|
|
2296
|
+
os.environ["LANGGRAPH_FAST_API"] = "true"
|
|
2297
|
+
|
|
2298
|
+
import uvicorn
|
|
2299
|
+
from typing import Any, List
|
|
2300
|
+
from fastapi import FastAPI
|
|
2301
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
2302
|
+
from langgraph.graph import StateGraph, START
|
|
2303
|
+
from langgraph.graph.message import MessagesState
|
|
2304
|
+
from langgraph.checkpoint.memory import MemorySaver
|
|
2305
|
+
from langgraph.prebuilt import ToolNode, tools_condition
|
|
2306
|
+
from langchain_core.tools import tool
|
|
1170
2307
|
from opentelemetry.instrumentation.langchain import LangchainInstrumentor
|
|
1171
|
-
from
|
|
1172
|
-
from a2a.server.events import EventQueue
|
|
1173
|
-
from a2a.server.tasks import TaskUpdater
|
|
1174
|
-
from a2a.types import AgentCapabilities, AgentCard, AgentSkill, Part, TextPart
|
|
1175
|
-
from a2a.utils import new_task
|
|
1176
|
-
from bedrock_agentcore.runtime import serve_a2a
|
|
2308
|
+
from ag_ui_langgraph import LangGraphAgent, add_langgraph_fastapi_endpoint
|
|
1177
2309
|
from model.load import load_model
|
|
1178
2310
|
|
|
1179
2311
|
LangchainInstrumentor().instrument()
|
|
@@ -1185,62 +2317,63 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
1185
2317
|
return a + b
|
|
1186
2318
|
|
|
1187
2319
|
|
|
2320
|
+
backend_tools = [add_numbers]
|
|
1188
2321
|
model = load_model()
|
|
1189
|
-
graph = create_react_agent(model, tools=[add_numbers])
|
|
1190
2322
|
|
|
1191
2323
|
|
|
1192
|
-
class
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
def __init__(self, graph):
|
|
1196
|
-
self.graph = graph
|
|
1197
|
-
|
|
1198
|
-
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
1199
|
-
task = context.current_task or new_task(context.message)
|
|
1200
|
-
if not context.current_task:
|
|
1201
|
-
await event_queue.enqueue_event(task)
|
|
1202
|
-
updater = TaskUpdater(event_queue, task.id, task.context_id)
|
|
2324
|
+
class AgentState(MessagesState):
|
|
2325
|
+
tools: List[Any]
|
|
1203
2326
|
|
|
1204
|
-
user_text = context.get_user_input()
|
|
1205
|
-
result = await self.graph.ainvoke({"messages": [("user", user_text)]})
|
|
1206
|
-
response = result["messages"][-1].content
|
|
1207
2327
|
|
|
1208
|
-
|
|
1209
|
-
|
|
2328
|
+
def chat_node(state: AgentState):
|
|
2329
|
+
bound_model = model.bind_tools(
|
|
2330
|
+
[*state.get("tools", []), *backend_tools],
|
|
2331
|
+
)
|
|
2332
|
+
response = bound_model.invoke(state["messages"])
|
|
2333
|
+
return {"messages": [response]}
|
|
1210
2334
|
|
|
1211
|
-
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
1212
|
-
pass
|
|
1213
2335
|
|
|
2336
|
+
builder = StateGraph(AgentState)
|
|
2337
|
+
builder.add_node("chat", chat_node)
|
|
2338
|
+
builder.add_node("tools", ToolNode(tools=backend_tools))
|
|
2339
|
+
builder.add_edge(START, "chat")
|
|
2340
|
+
builder.add_conditional_edges("chat", tools_condition)
|
|
2341
|
+
builder.add_edge("tools", "chat")
|
|
2342
|
+
graph = builder.compile(checkpointer=MemorySaver())
|
|
1214
2343
|
|
|
1215
|
-
|
|
2344
|
+
agent = LangGraphAgent(
|
|
1216
2345
|
name="{{ name }}",
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
)
|
|
1228
|
-
],
|
|
1229
|
-
default_input_modes=["text"],
|
|
1230
|
-
default_output_modes=["text"],
|
|
2346
|
+
graph=graph,
|
|
2347
|
+
description="A helpful assistant",
|
|
2348
|
+
)
|
|
2349
|
+
|
|
2350
|
+
app = FastAPI()
|
|
2351
|
+
app.add_middleware(
|
|
2352
|
+
CORSMiddleware,
|
|
2353
|
+
allow_origins=["*"],
|
|
2354
|
+
allow_methods=["*"],
|
|
2355
|
+
allow_headers=["*"],
|
|
1231
2356
|
)
|
|
1232
2357
|
|
|
2358
|
+
add_langgraph_fastapi_endpoint(app=app, agent=agent, path="/invocations")
|
|
2359
|
+
|
|
2360
|
+
|
|
2361
|
+
@app.get("/ping")
|
|
2362
|
+
async def ping():
|
|
2363
|
+
return {"status": "healthy"}
|
|
2364
|
+
|
|
2365
|
+
|
|
1233
2366
|
if __name__ == "__main__":
|
|
1234
|
-
|
|
2367
|
+
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))
|
|
1235
2368
|
"
|
|
1236
2369
|
`;
|
|
1237
2370
|
|
|
1238
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2371
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/model/__init__.py should match snapshot 1`] = `
|
|
1239
2372
|
"# Package marker
|
|
1240
2373
|
"
|
|
1241
2374
|
`;
|
|
1242
2375
|
|
|
1243
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2376
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/model/load.py should match snapshot 1`] = `
|
|
1244
2377
|
"{{#if (eq modelProvider "Bedrock")}}
|
|
1245
2378
|
from langchain_aws import ChatBedrock
|
|
1246
2379
|
|
|
@@ -1367,7 +2500,7 @@ def load_model() -> ChatGoogleGenerativeAI:
|
|
|
1367
2500
|
"
|
|
1368
2501
|
`;
|
|
1369
2502
|
|
|
1370
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2503
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/pyproject.toml should match snapshot 1`] = `
|
|
1371
2504
|
"[build-system]
|
|
1372
2505
|
requires = ["hatchling"]
|
|
1373
2506
|
build-backend = "hatchling.build"
|
|
@@ -1375,20 +2508,25 @@ build-backend = "hatchling.build"
|
|
|
1375
2508
|
[project]
|
|
1376
2509
|
name = "{{ name }}"
|
|
1377
2510
|
version = "0.1.0"
|
|
1378
|
-
description = "AgentCore
|
|
2511
|
+
description = "AgentCore AG-UI Agent using LangChain + LangGraph"
|
|
1379
2512
|
readme = "README.md"
|
|
1380
2513
|
requires-python = ">=3.10"
|
|
1381
2514
|
dependencies = [
|
|
1382
|
-
"
|
|
2515
|
+
"ag-ui-langgraph >= 0.0.31",
|
|
2516
|
+
"ag-ui-protocol >= 0.1.10",
|
|
1383
2517
|
{{#if (eq modelProvider "Anthropic")}}"langchain-anthropic >= 0.3.0",
|
|
1384
2518
|
{{/if}}{{#if (eq modelProvider "Bedrock")}}"langchain-aws >= 0.2.0",
|
|
1385
2519
|
{{/if}}{{#if (eq modelProvider "Gemini")}}"langchain-google-genai >= 2.0.0",
|
|
1386
2520
|
{{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
|
|
1387
2521
|
{{/if}}"aws-opentelemetry-distro",
|
|
1388
2522
|
"opentelemetry-instrumentation-langchain >= 0.59.0",
|
|
1389
|
-
"bedrock-agentcore
|
|
2523
|
+
"bedrock-agentcore >= 1.0.3",
|
|
1390
2524
|
"botocore[crt] >= 1.35.0",
|
|
1391
|
-
"langgraph >= 0.
|
|
2525
|
+
"langgraph >= 0.3.25",
|
|
2526
|
+
"langchain >= 0.3.0",
|
|
2527
|
+
"langchain-core >= 0.3.0",
|
|
2528
|
+
"fastapi >= 0.115.12",
|
|
2529
|
+
"uvicorn >= 0.34.3",
|
|
1392
2530
|
]
|
|
1393
2531
|
|
|
1394
2532
|
[tool.hatch.build.targets.wheel]
|
|
@@ -1396,14 +2534,14 @@ packages = ["."]
|
|
|
1396
2534
|
"
|
|
1397
2535
|
`;
|
|
1398
2536
|
|
|
1399
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2537
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/README.md should match snapshot 1`] = `
|
|
1400
2538
|
"# {{ name }}
|
|
1401
2539
|
|
|
1402
|
-
An
|
|
2540
|
+
An AG-UI agent deployed on Amazon Bedrock AgentCore using Strands SDK.
|
|
1403
2541
|
|
|
1404
2542
|
## Overview
|
|
1405
2543
|
|
|
1406
|
-
This agent implements the
|
|
2544
|
+
This agent implements the AG-UI protocol, enabling streaming agent-to-UI communication. The agent exposes an \`/invocations\` endpoint that accepts AG-UI protocol requests and streams responses back to the client.
|
|
1407
2545
|
|
|
1408
2546
|
## Local Development
|
|
1409
2547
|
|
|
@@ -1412,7 +2550,7 @@ uv sync
|
|
|
1412
2550
|
uv run python main.py
|
|
1413
2551
|
\`\`\`
|
|
1414
2552
|
|
|
1415
|
-
The agent starts on port
|
|
2553
|
+
The agent starts on port 8080.
|
|
1416
2554
|
|
|
1417
2555
|
## Deploy
|
|
1418
2556
|
|
|
@@ -1422,7 +2560,7 @@ agentcore deploy
|
|
|
1422
2560
|
"
|
|
1423
2561
|
`;
|
|
1424
2562
|
|
|
1425
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2563
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/gitignore.template should match snapshot 1`] = `
|
|
1426
2564
|
"# Environment variables
|
|
1427
2565
|
.env
|
|
1428
2566
|
|
|
@@ -1467,10 +2605,16 @@ Thumbs.db
|
|
|
1467
2605
|
"
|
|
1468
2606
|
`;
|
|
1469
2607
|
|
|
1470
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
1471
|
-
"
|
|
1472
|
-
|
|
1473
|
-
|
|
2608
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/main.py should match snapshot 1`] = `
|
|
2609
|
+
"import os
|
|
2610
|
+
|
|
2611
|
+
# Suppress OpenTelemetry warnings during local development; remove for production
|
|
2612
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
2613
|
+
os.environ["OTEL_SDK_DISABLED"] = "true"
|
|
2614
|
+
|
|
2615
|
+
import uvicorn
|
|
2616
|
+
from strands import Agent, tool
|
|
2617
|
+
from ag_ui_strands import StrandsAgent, StrandsAgentConfig, create_strands_app
|
|
1474
2618
|
from model.load import load_model
|
|
1475
2619
|
{{#if hasMemory}}
|
|
1476
2620
|
from memory.session import get_memory_session_manager
|
|
@@ -1485,42 +2629,35 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
1485
2629
|
|
|
1486
2630
|
tools = [add_numbers]
|
|
1487
2631
|
|
|
1488
|
-
{{#if hasMemory}}
|
|
1489
|
-
def agent_factory():
|
|
1490
|
-
cache = {}
|
|
1491
|
-
def get_or_create_agent(session_id, user_id):
|
|
1492
|
-
key = f"{session_id}/{user_id}"
|
|
1493
|
-
if key not in cache:
|
|
1494
|
-
cache[key] = Agent(
|
|
1495
|
-
model=load_model(),
|
|
1496
|
-
session_manager=get_memory_session_manager(session_id, user_id),
|
|
1497
|
-
system_prompt="You are a helpful assistant. Use tools when appropriate.",
|
|
1498
|
-
tools=tools,
|
|
1499
|
-
)
|
|
1500
|
-
return cache[key]
|
|
1501
|
-
return get_or_create_agent
|
|
1502
|
-
|
|
1503
|
-
get_or_create_agent = agent_factory()
|
|
1504
|
-
agent = get_or_create_agent("default-session", "default-user")
|
|
1505
|
-
{{else}}
|
|
1506
2632
|
agent = Agent(
|
|
1507
2633
|
model=load_model(),
|
|
1508
2634
|
system_prompt="You are a helpful assistant. Use tools when appropriate.",
|
|
1509
2635
|
tools=tools,
|
|
1510
2636
|
)
|
|
2637
|
+
|
|
2638
|
+
{{#if hasMemory}}
|
|
2639
|
+
def session_manager_provider(input_data):
|
|
2640
|
+
return get_memory_session_manager(input_data.thread_id, "default-user")
|
|
2641
|
+
|
|
2642
|
+
config = StrandsAgentConfig(session_manager_provider=session_manager_provider)
|
|
2643
|
+
{{else}}
|
|
2644
|
+
config = StrandsAgentConfig()
|
|
1511
2645
|
{{/if}}
|
|
1512
2646
|
|
|
2647
|
+
agui_agent = StrandsAgent(agent=agent, name="{{ name }}", description="A helpful assistant", config=config)
|
|
2648
|
+
app = create_strands_app(agui_agent, path="/invocations", ping_path="/ping")
|
|
2649
|
+
|
|
1513
2650
|
if __name__ == "__main__":
|
|
1514
|
-
|
|
2651
|
+
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))
|
|
1515
2652
|
"
|
|
1516
2653
|
`;
|
|
1517
2654
|
|
|
1518
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2655
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/model/__init__.py should match snapshot 1`] = `
|
|
1519
2656
|
"# Package marker
|
|
1520
2657
|
"
|
|
1521
2658
|
`;
|
|
1522
2659
|
|
|
1523
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2660
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/model/load.py should match snapshot 1`] = `
|
|
1524
2661
|
"{{#if (eq modelProvider "Bedrock")}}
|
|
1525
2662
|
from strands.models.bedrock import BedrockModel
|
|
1526
2663
|
|
|
@@ -1647,7 +2784,7 @@ def load_model() -> GeminiModel:
|
|
|
1647
2784
|
"
|
|
1648
2785
|
`;
|
|
1649
2786
|
|
|
1650
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2787
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/pyproject.toml should match snapshot 1`] = `
|
|
1651
2788
|
"[build-system]
|
|
1652
2789
|
requires = ["hatchling"]
|
|
1653
2790
|
build-backend = "hatchling.build"
|
|
@@ -1655,18 +2792,22 @@ build-backend = "hatchling.build"
|
|
|
1655
2792
|
[project]
|
|
1656
2793
|
name = "{{ name }}"
|
|
1657
2794
|
version = "0.1.0"
|
|
1658
|
-
description = "AgentCore
|
|
2795
|
+
description = "AgentCore AG-UI Agent using Strands SDK"
|
|
1659
2796
|
readme = "README.md"
|
|
1660
|
-
requires-python = ">=3.
|
|
2797
|
+
requires-python = ">=3.12"
|
|
1661
2798
|
dependencies = [
|
|
1662
2799
|
{{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
|
|
1663
|
-
{{/if}}"
|
|
2800
|
+
{{/if}}"ag-ui-strands >= 0.1.7",
|
|
2801
|
+
"ag-ui-protocol >= 0.1.10",
|
|
1664
2802
|
"aws-opentelemetry-distro",
|
|
1665
|
-
"bedrock-agentcore
|
|
2803
|
+
"bedrock-agentcore >= 1.0.3",
|
|
1666
2804
|
"botocore[crt] >= 1.35.0",
|
|
2805
|
+
"fastapi >= 0.115.12",
|
|
1667
2806
|
{{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
|
|
1668
2807
|
{{/if}}{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
|
|
1669
|
-
{{/if}}"strands-agents >= 1.
|
|
2808
|
+
{{/if}}"strands-agents >= 1.15.0",
|
|
2809
|
+
"strands-agents-tools >= 0.2.14",
|
|
2810
|
+
"uvicorn >= 0.34.3",
|
|
1670
2811
|
]
|
|
1671
2812
|
|
|
1672
2813
|
[tool.hatch.build.targets.wheel]
|
|
@@ -1674,12 +2815,12 @@ packages = ["."]
|
|
|
1674
2815
|
"
|
|
1675
2816
|
`;
|
|
1676
2817
|
|
|
1677
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2818
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/capabilities/memory/__init__.py should match snapshot 1`] = `
|
|
1678
2819
|
"# Package marker
|
|
1679
2820
|
"
|
|
1680
2821
|
`;
|
|
1681
2822
|
|
|
1682
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2823
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/capabilities/memory/session.py should match snapshot 1`] = `
|
|
1683
2824
|
"import os
|
|
1684
2825
|
from typing import Optional
|
|
1685
2826
|
|
|
@@ -1834,6 +2975,66 @@ add_numbers_tool = FunctionTool(
|
|
|
1834
2975
|
# Define a collection of tools used by the model
|
|
1835
2976
|
tools = [add_numbers_tool]
|
|
1836
2977
|
|
|
2978
|
+
{{#if sessionStorageMountPath}}
|
|
2979
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
2980
|
+
|
|
2981
|
+
def _safe_resolve(path: str) -> str:
|
|
2982
|
+
"""Resolve path safely within the storage boundary."""
|
|
2983
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
2984
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
2985
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
2986
|
+
return resolved
|
|
2987
|
+
|
|
2988
|
+
def file_read(path: str) -> str:
|
|
2989
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
2990
|
+
try:
|
|
2991
|
+
full_path = _safe_resolve(path)
|
|
2992
|
+
with open(full_path) as f:
|
|
2993
|
+
return f.read()
|
|
2994
|
+
except ValueError as e:
|
|
2995
|
+
return str(e)
|
|
2996
|
+
except OSError as e:
|
|
2997
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
2998
|
+
|
|
2999
|
+
def file_write(path: str, content: str) -> str:
|
|
3000
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
3001
|
+
try:
|
|
3002
|
+
full_path = _safe_resolve(path)
|
|
3003
|
+
parent = os.path.dirname(full_path)
|
|
3004
|
+
if parent:
|
|
3005
|
+
os.makedirs(parent, exist_ok=True)
|
|
3006
|
+
with open(full_path, "w") as f:
|
|
3007
|
+
f.write(content)
|
|
3008
|
+
return f"Written to {path}"
|
|
3009
|
+
except ValueError as e:
|
|
3010
|
+
return str(e)
|
|
3011
|
+
except OSError as e:
|
|
3012
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
3013
|
+
|
|
3014
|
+
def list_files(directory: str = "") -> str:
|
|
3015
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
3016
|
+
try:
|
|
3017
|
+
target = _safe_resolve(directory)
|
|
3018
|
+
entries = os.listdir(target)
|
|
3019
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
3020
|
+
except ValueError as e:
|
|
3021
|
+
return str(e)
|
|
3022
|
+
except OSError as e:
|
|
3023
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
3024
|
+
|
|
3025
|
+
tools.extend([
|
|
3026
|
+
FunctionTool(file_read, description="Read a file from persistent storage. The path is relative to the storage root."),
|
|
3027
|
+
FunctionTool(file_write, description="Write content to a file in persistent storage. The path is relative to the storage root."),
|
|
3028
|
+
FunctionTool(list_files, description="List files in persistent storage. The directory is relative to the storage root."),
|
|
3029
|
+
])
|
|
3030
|
+
{{/if}}
|
|
3031
|
+
|
|
3032
|
+
SYSTEM_MESSAGE = """
|
|
3033
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
3034
|
+
{{#if sessionStorageMountPath}}
|
|
3035
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
3036
|
+
{{/if}}
|
|
3037
|
+
"""
|
|
1837
3038
|
|
|
1838
3039
|
@app.entrypoint
|
|
1839
3040
|
async def invoke(payload, context):
|
|
@@ -1847,7 +3048,7 @@ async def invoke(payload, context):
|
|
|
1847
3048
|
name="{{ name }}",
|
|
1848
3049
|
model_client=load_model(),
|
|
1849
3050
|
tools=tools + mcp_tools,
|
|
1850
|
-
system_message=
|
|
3051
|
+
system_message=SYSTEM_MESSAGE,
|
|
1851
3052
|
)
|
|
1852
3053
|
|
|
1853
3054
|
# Process the user prompt
|
|
@@ -2202,6 +3403,66 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
2202
3403
|
return a + b
|
|
2203
3404
|
|
|
2204
3405
|
|
|
3406
|
+
# Define a collection of tools used by the model
|
|
3407
|
+
tools = [add_numbers]
|
|
3408
|
+
|
|
3409
|
+
{{#if sessionStorageMountPath}}
|
|
3410
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
3411
|
+
|
|
3412
|
+
def _safe_resolve(path: str) -> str:
|
|
3413
|
+
"""Resolve path safely within the storage boundary."""
|
|
3414
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
3415
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
3416
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
3417
|
+
return resolved
|
|
3418
|
+
|
|
3419
|
+
def file_read(path: str) -> str:
|
|
3420
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
3421
|
+
try:
|
|
3422
|
+
full_path = _safe_resolve(path)
|
|
3423
|
+
with open(full_path) as f:
|
|
3424
|
+
return f.read()
|
|
3425
|
+
except ValueError as e:
|
|
3426
|
+
return str(e)
|
|
3427
|
+
except OSError as e:
|
|
3428
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
3429
|
+
|
|
3430
|
+
def file_write(path: str, content: str) -> str:
|
|
3431
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
3432
|
+
try:
|
|
3433
|
+
full_path = _safe_resolve(path)
|
|
3434
|
+
parent = os.path.dirname(full_path)
|
|
3435
|
+
if parent:
|
|
3436
|
+
os.makedirs(parent, exist_ok=True)
|
|
3437
|
+
with open(full_path, "w") as f:
|
|
3438
|
+
f.write(content)
|
|
3439
|
+
return f"Written to {path}"
|
|
3440
|
+
except ValueError as e:
|
|
3441
|
+
return str(e)
|
|
3442
|
+
except OSError as e:
|
|
3443
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
3444
|
+
|
|
3445
|
+
def list_files(directory: str = "") -> str:
|
|
3446
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
3447
|
+
try:
|
|
3448
|
+
target = _safe_resolve(directory)
|
|
3449
|
+
entries = os.listdir(target)
|
|
3450
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
3451
|
+
except ValueError as e:
|
|
3452
|
+
return str(e)
|
|
3453
|
+
except OSError as e:
|
|
3454
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
3455
|
+
|
|
3456
|
+
tools.extend([file_read, file_write, list_files])
|
|
3457
|
+
{{/if}}
|
|
3458
|
+
|
|
3459
|
+
AGENT_INSTRUCTION = """
|
|
3460
|
+
I can answer your questions using the knowledge I have!
|
|
3461
|
+
{{#if sessionStorageMountPath}}
|
|
3462
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
3463
|
+
{{/if}}
|
|
3464
|
+
"""
|
|
3465
|
+
|
|
2205
3466
|
# Get MCP Toolset
|
|
2206
3467
|
{{#if hasGateway}}
|
|
2207
3468
|
mcp_toolset = get_all_gateway_mcp_toolsets()
|
|
@@ -2224,8 +3485,8 @@ agent = Agent(
|
|
|
2224
3485
|
model=MODEL_ID,
|
|
2225
3486
|
name="{{ name }}",
|
|
2226
3487
|
description="Agent to answer questions",
|
|
2227
|
-
instruction=
|
|
2228
|
-
tools=mcp_toolset +
|
|
3488
|
+
instruction=AGENT_INSTRUCTION,
|
|
3489
|
+
tools=mcp_toolset + tools,
|
|
2229
3490
|
)
|
|
2230
3491
|
|
|
2231
3492
|
|
|
@@ -2563,6 +3824,66 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
2563
3824
|
# Define a collection of tools used by the model
|
|
2564
3825
|
tools = [add_numbers]
|
|
2565
3826
|
|
|
3827
|
+
{{#if sessionStorageMountPath}}
|
|
3828
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
3829
|
+
|
|
3830
|
+
def _safe_resolve(path: str) -> str:
|
|
3831
|
+
"""Resolve path safely within the storage boundary."""
|
|
3832
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
3833
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
3834
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
3835
|
+
return resolved
|
|
3836
|
+
|
|
3837
|
+
@tool
|
|
3838
|
+
def file_read(path: str) -> str:
|
|
3839
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
3840
|
+
try:
|
|
3841
|
+
full_path = _safe_resolve(path)
|
|
3842
|
+
with open(full_path) as f:
|
|
3843
|
+
return f.read()
|
|
3844
|
+
except ValueError as e:
|
|
3845
|
+
return str(e)
|
|
3846
|
+
except OSError as e:
|
|
3847
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
3848
|
+
|
|
3849
|
+
@tool
|
|
3850
|
+
def file_write(path: str, content: str) -> str:
|
|
3851
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
3852
|
+
try:
|
|
3853
|
+
full_path = _safe_resolve(path)
|
|
3854
|
+
parent = os.path.dirname(full_path)
|
|
3855
|
+
if parent:
|
|
3856
|
+
os.makedirs(parent, exist_ok=True)
|
|
3857
|
+
with open(full_path, "w") as f:
|
|
3858
|
+
f.write(content)
|
|
3859
|
+
return f"Written to {path}"
|
|
3860
|
+
except ValueError as e:
|
|
3861
|
+
return str(e)
|
|
3862
|
+
except OSError as e:
|
|
3863
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
3864
|
+
|
|
3865
|
+
@tool
|
|
3866
|
+
def list_files(directory: str = "") -> str:
|
|
3867
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
3868
|
+
try:
|
|
3869
|
+
target = _safe_resolve(directory)
|
|
3870
|
+
entries = os.listdir(target)
|
|
3871
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
3872
|
+
except ValueError as e:
|
|
3873
|
+
return str(e)
|
|
3874
|
+
except OSError as e:
|
|
3875
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
3876
|
+
|
|
3877
|
+
tools.extend([file_read, file_write, list_files])
|
|
3878
|
+
{{/if}}
|
|
3879
|
+
|
|
3880
|
+
SYSTEM_PROMPT = """
|
|
3881
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
3882
|
+
{{#if sessionStorageMountPath}}
|
|
3883
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
3884
|
+
{{/if}}
|
|
3885
|
+
"""
|
|
3886
|
+
|
|
2566
3887
|
|
|
2567
3888
|
@app.entrypoint
|
|
2568
3889
|
async def invoke(payload, context):
|
|
@@ -2581,7 +3902,7 @@ async def invoke(payload, context):
|
|
|
2581
3902
|
mcp_tools = await mcp_client.get_tools()
|
|
2582
3903
|
|
|
2583
3904
|
# Define the agent using create_react_agent
|
|
2584
|
-
graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools)
|
|
3905
|
+
graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools, prompt=SYSTEM_PROMPT)
|
|
2585
3906
|
|
|
2586
3907
|
# Process the user prompt
|
|
2587
3908
|
prompt = payload.get("prompt", "What can you help me with?")
|
|
@@ -2986,6 +4307,68 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
2986
4307
|
return a + b
|
|
2987
4308
|
|
|
2988
4309
|
|
|
4310
|
+
tools = [add_numbers]
|
|
4311
|
+
|
|
4312
|
+
{{#if sessionStorageMountPath}}
|
|
4313
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
4314
|
+
|
|
4315
|
+
def _safe_resolve(path: str) -> str:
|
|
4316
|
+
"""Resolve path safely within the storage boundary."""
|
|
4317
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
4318
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
4319
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
4320
|
+
return resolved
|
|
4321
|
+
|
|
4322
|
+
@function_tool
|
|
4323
|
+
def file_read(path: str) -> str:
|
|
4324
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
4325
|
+
try:
|
|
4326
|
+
full_path = _safe_resolve(path)
|
|
4327
|
+
with open(full_path) as f:
|
|
4328
|
+
return f.read()
|
|
4329
|
+
except ValueError as e:
|
|
4330
|
+
return str(e)
|
|
4331
|
+
except OSError as e:
|
|
4332
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
4333
|
+
|
|
4334
|
+
@function_tool
|
|
4335
|
+
def file_write(path: str, content: str) -> str:
|
|
4336
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
4337
|
+
try:
|
|
4338
|
+
full_path = _safe_resolve(path)
|
|
4339
|
+
parent = os.path.dirname(full_path)
|
|
4340
|
+
if parent:
|
|
4341
|
+
os.makedirs(parent, exist_ok=True)
|
|
4342
|
+
with open(full_path, "w") as f:
|
|
4343
|
+
f.write(content)
|
|
4344
|
+
return f"Written to {path}"
|
|
4345
|
+
except ValueError as e:
|
|
4346
|
+
return str(e)
|
|
4347
|
+
except OSError as e:
|
|
4348
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
4349
|
+
|
|
4350
|
+
@function_tool
|
|
4351
|
+
def list_files(directory: str = "") -> str:
|
|
4352
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
4353
|
+
try:
|
|
4354
|
+
target = _safe_resolve(directory)
|
|
4355
|
+
entries = os.listdir(target)
|
|
4356
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
4357
|
+
except ValueError as e:
|
|
4358
|
+
return str(e)
|
|
4359
|
+
except OSError as e:
|
|
4360
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
4361
|
+
|
|
4362
|
+
tools.extend([file_read, file_write, list_files])
|
|
4363
|
+
{{/if}}
|
|
4364
|
+
|
|
4365
|
+
INSTRUCTIONS = """
|
|
4366
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
4367
|
+
{{#if sessionStorageMountPath}}
|
|
4368
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
4369
|
+
{{/if}}
|
|
4370
|
+
"""
|
|
4371
|
+
|
|
2989
4372
|
# Define the agent execution
|
|
2990
4373
|
async def main(query):
|
|
2991
4374
|
ensure_credentials_loaded()
|
|
@@ -2995,8 +4378,9 @@ async def main(query):
|
|
|
2995
4378
|
agent = Agent(
|
|
2996
4379
|
name="{{ name }}",
|
|
2997
4380
|
model="gpt-4.1",
|
|
4381
|
+
instructions=INSTRUCTIONS,
|
|
2998
4382
|
mcp_servers=mcp_servers,
|
|
2999
|
-
tools=
|
|
4383
|
+
tools=tools
|
|
3000
4384
|
)
|
|
3001
4385
|
result = await Runner.run(agent, query)
|
|
3002
4386
|
return result
|
|
@@ -3004,8 +4388,9 @@ async def main(query):
|
|
|
3004
4388
|
agent = Agent(
|
|
3005
4389
|
name="{{ name }}",
|
|
3006
4390
|
model="gpt-4.1",
|
|
4391
|
+
instructions=INSTRUCTIONS,
|
|
3007
4392
|
mcp_servers=[],
|
|
3008
|
-
tools=
|
|
4393
|
+
tools=tools
|
|
3009
4394
|
)
|
|
3010
4395
|
result = await Runner.run(agent, query)
|
|
3011
4396
|
return result
|
|
@@ -3016,8 +4401,9 @@ async def main(query):
|
|
|
3016
4401
|
agent = Agent(
|
|
3017
4402
|
name="{{ name }}",
|
|
3018
4403
|
model="gpt-4.1",
|
|
4404
|
+
instructions=INSTRUCTIONS,
|
|
3019
4405
|
mcp_servers=active_servers,
|
|
3020
|
-
tools=
|
|
4406
|
+
tools=tools
|
|
3021
4407
|
)
|
|
3022
4408
|
result = await Runner.run(agent, query)
|
|
3023
4409
|
return result
|
|
@@ -3025,8 +4411,9 @@ async def main(query):
|
|
|
3025
4411
|
agent = Agent(
|
|
3026
4412
|
name="{{ name }}",
|
|
3027
4413
|
model="gpt-4.1",
|
|
4414
|
+
instructions=INSTRUCTIONS,
|
|
3028
4415
|
mcp_servers=[],
|
|
3029
|
-
tools=
|
|
4416
|
+
tools=tools
|
|
3030
4417
|
)
|
|
3031
4418
|
result = await Runner.run(agent, query)
|
|
3032
4419
|
return result
|
|
@@ -3308,6 +4695,9 @@ from mcp_client.client import get_streamable_http_mcp_client
|
|
|
3308
4695
|
{{#if hasMemory}}
|
|
3309
4696
|
from memory.session import get_memory_session_manager
|
|
3310
4697
|
{{/if}}
|
|
4698
|
+
{{#if sessionStorageMountPath}}
|
|
4699
|
+
import os
|
|
4700
|
+
{{/if}}
|
|
3311
4701
|
|
|
3312
4702
|
app = BedrockAgentCoreApp()
|
|
3313
4703
|
log = app.logger
|
|
@@ -3329,11 +4719,70 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
3329
4719
|
return a+b
|
|
3330
4720
|
tools.append(add_numbers)
|
|
3331
4721
|
|
|
4722
|
+
{{#if sessionStorageMountPath}}
|
|
4723
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
4724
|
+
|
|
4725
|
+
def _safe_resolve(path: str) -> str:
|
|
4726
|
+
"""Resolve path safely within the storage boundary."""
|
|
4727
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
4728
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
4729
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
4730
|
+
return resolved
|
|
4731
|
+
|
|
4732
|
+
@tool
|
|
4733
|
+
def file_read(path: str) -> str:
|
|
4734
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
4735
|
+
try:
|
|
4736
|
+
full_path = _safe_resolve(path)
|
|
4737
|
+
with open(full_path) as f:
|
|
4738
|
+
return f.read()
|
|
4739
|
+
except ValueError as e:
|
|
4740
|
+
return str(e)
|
|
4741
|
+
except OSError as e:
|
|
4742
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
4743
|
+
|
|
4744
|
+
@tool
|
|
4745
|
+
def file_write(path: str, content: str) -> str:
|
|
4746
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
4747
|
+
try:
|
|
4748
|
+
full_path = _safe_resolve(path)
|
|
4749
|
+
parent = os.path.dirname(full_path)
|
|
4750
|
+
if parent:
|
|
4751
|
+
os.makedirs(parent, exist_ok=True)
|
|
4752
|
+
with open(full_path, "w") as f:
|
|
4753
|
+
f.write(content)
|
|
4754
|
+
return f"Written to {path}"
|
|
4755
|
+
except ValueError as e:
|
|
4756
|
+
return str(e)
|
|
4757
|
+
except OSError as e:
|
|
4758
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
4759
|
+
|
|
4760
|
+
@tool
|
|
4761
|
+
def list_files(directory: str = "") -> str:
|
|
4762
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
4763
|
+
try:
|
|
4764
|
+
target = _safe_resolve(directory)
|
|
4765
|
+
entries = os.listdir(target)
|
|
4766
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
4767
|
+
except ValueError as e:
|
|
4768
|
+
return str(e)
|
|
4769
|
+
except OSError as e:
|
|
4770
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
4771
|
+
|
|
4772
|
+
tools.extend([file_read, file_write, list_files])
|
|
4773
|
+
{{/if}}
|
|
4774
|
+
|
|
3332
4775
|
# Add MCP client to tools if available
|
|
3333
4776
|
for mcp_client in mcp_clients:
|
|
3334
4777
|
if mcp_client:
|
|
3335
4778
|
tools.append(mcp_client)
|
|
3336
4779
|
|
|
4780
|
+
SYSTEM_PROMPT = """
|
|
4781
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
4782
|
+
{{#if sessionStorageMountPath}}
|
|
4783
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
4784
|
+
{{/if}}
|
|
4785
|
+
"""
|
|
3337
4786
|
|
|
3338
4787
|
{{#if hasMemory}}
|
|
3339
4788
|
def agent_factory():
|
|
@@ -3345,9 +4794,7 @@ def agent_factory():
|
|
|
3345
4794
|
cache[key] = Agent(
|
|
3346
4795
|
model=load_model(),
|
|
3347
4796
|
session_manager=get_memory_session_manager(session_id, user_id),
|
|
3348
|
-
system_prompt=
|
|
3349
|
-
You are a helpful assistant. Use tools when appropriate.
|
|
3350
|
-
""",
|
|
4797
|
+
system_prompt=SYSTEM_PROMPT,
|
|
3351
4798
|
tools=tools
|
|
3352
4799
|
)
|
|
3353
4800
|
return cache[key]
|
|
@@ -3361,9 +4808,7 @@ def get_or_create_agent():
|
|
|
3361
4808
|
if _agent is None:
|
|
3362
4809
|
_agent = Agent(
|
|
3363
4810
|
model=load_model(),
|
|
3364
|
-
system_prompt=
|
|
3365
|
-
You are a helpful assistant. Use tools when appropriate.
|
|
3366
|
-
""",
|
|
4811
|
+
system_prompt=SYSTEM_PROMPT,
|
|
3367
4812
|
tools=tools
|
|
3368
4813
|
)
|
|
3369
4814
|
return _agent
|
|
@@ -3826,27 +5271,47 @@ exports[`Assets Directory Snapshots > Root-level assets > AGENTS.md should match
|
|
|
3826
5271
|
|
|
3827
5272
|
This directory stores:
|
|
3828
5273
|
|
|
3829
|
-
- Template assets for agents written in different
|
|
5274
|
+
- Template assets for agents written in different languages, SDKs, and configurations
|
|
3830
5275
|
- Container templates (\`container/python/\`) with \`Dockerfile\` and \`.dockerignore\` for Container build agents
|
|
5276
|
+
- Vended documentation (\`README.md\`, \`agents/AGENTS.md\`) copied into user projects at create time
|
|
5277
|
+
- CDK project template (\`cdk/\`) using \`@aws/agentcore-cdk\` L3 constructs
|
|
5278
|
+
- Evaluator templates (\`evaluators/\`) for code-based evaluators
|
|
5279
|
+
- MCP tool templates (\`mcp/\`) for Lambda and AgentCoreRuntime compute
|
|
3831
5280
|
|
|
3832
5281
|
### Directory Layout
|
|
3833
5282
|
|
|
3834
5283
|
\`\`\`
|
|
3835
5284
|
assets/
|
|
3836
|
-
├──
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
│
|
|
3840
|
-
|
|
3841
|
-
│
|
|
5285
|
+
├── README.md # Vended to project root as project README
|
|
5286
|
+
├── AGENTS.md # This file — internal dev context
|
|
5287
|
+
├── agents/
|
|
5288
|
+
│ └── AGENTS.md # Vended to project root for AI coding assistants
|
|
5289
|
+
├── python/ # Framework templates (one per SDK per protocol)
|
|
5290
|
+
│ ├── http/ # HTTP protocol agents
|
|
5291
|
+
│ │ ├── strands/
|
|
5292
|
+
│ │ ├── langchain_langgraph/
|
|
5293
|
+
│ │ ├── googleadk/
|
|
5294
|
+
│ │ ├── openaiagents/
|
|
5295
|
+
│ │ └── autogen/
|
|
5296
|
+
│ ├── mcp/ # MCP protocol agents
|
|
5297
|
+
│ │ └── standalone/
|
|
5298
|
+
│ └── a2a/ # A2A protocol agents
|
|
5299
|
+
│ ├── strands/
|
|
5300
|
+
│ ├── langchain_langgraph/
|
|
5301
|
+
│ └── googleadk/
|
|
5302
|
+
├── typescript/ # TypeScript agent templates
|
|
3842
5303
|
├── container/ # Container build templates
|
|
3843
5304
|
│ └── python/
|
|
3844
5305
|
│ ├── Dockerfile
|
|
3845
5306
|
│ └── dockerignore.template
|
|
3846
|
-
|
|
5307
|
+
├── cdk/ # CDK project template (@aws/agentcore-cdk)
|
|
5308
|
+
├── evaluators/ # Code-based evaluator templates
|
|
5309
|
+
└── mcp/ # MCP tool templates (Lambda + AgentCoreRuntime)
|
|
5310
|
+
├── python/
|
|
5311
|
+
└── python-lambda/
|
|
3847
5312
|
\`\`\`
|
|
3848
5313
|
|
|
3849
|
-
The rendering logic is rooted in the \`AgentEnvSpec\` and must ALWAYS respect the configuration in the
|
|
5314
|
+
The rendering logic is rooted in the \`AgentEnvSpec\` and must ALWAYS respect the configuration in the spec.
|
|
3850
5315
|
|
|
3851
5316
|
For Container builds, \`BaseRenderer.render()\` automatically copies the \`container/<language>/\` templates (Dockerfile,
|
|
3852
5317
|
.dockerignore) into the agent directory when \`buildType === 'Container'\`.
|
|
@@ -3855,10 +5320,13 @@ For Container builds, \`BaseRenderer.render()\` automatically copies the \`conta
|
|
|
3855
5320
|
|
|
3856
5321
|
- Always make sure the templates are as close to working code as possible
|
|
3857
5322
|
- AVOID as much as possible using any conditionals within the templates
|
|
5323
|
+
- Test template rendering with \`agentcore add agent\` for each framework/protocol combination
|
|
3858
5324
|
|
|
3859
5325
|
## How to use the assets in this directory
|
|
3860
5326
|
|
|
3861
|
-
- These assets are rendered by the CLI's template renderer in \`src/cli/templates
|
|
5327
|
+
- These assets are rendered by the CLI's template renderer in \`src/cli/templates/\`
|
|
5328
|
+
- The \`README.md\` and \`agents/AGENTS.md\` are copied verbatim (no template rendering) during project creation
|
|
5329
|
+
- The \`.llm-context/\` files are sourced from \`src/schema/llm-compacted/\` and written during init
|
|
3862
5330
|
"
|
|
3863
5331
|
`;
|
|
3864
5332
|
|
|
@@ -3870,14 +5338,19 @@ This project was created with the [AgentCore CLI](https://github.com/aws/agentco
|
|
|
3870
5338
|
## Project Structure
|
|
3871
5339
|
|
|
3872
5340
|
\`\`\`
|
|
3873
|
-
.
|
|
3874
5341
|
my-project/
|
|
5342
|
+
├── AGENTS.md # AI coding assistant context
|
|
3875
5343
|
├── agentcore/
|
|
3876
|
-
│ ├── .
|
|
3877
|
-
│ ├──
|
|
3878
|
-
│ ├──
|
|
3879
|
-
│
|
|
3880
|
-
├──
|
|
5344
|
+
│ ├── agentcore.json # Project config (agents, memories, credentials, gateways, evaluators)
|
|
5345
|
+
│ ├── aws-targets.json # Deployment targets (account + region)
|
|
5346
|
+
│ ├── .env.local # Secrets — API keys (gitignored)
|
|
5347
|
+
│ ├── .llm-context/ # TypeScript type definitions for AI assistants
|
|
5348
|
+
│ │ ├── agentcore.ts # AgentCoreProjectSpec types
|
|
5349
|
+
│ │ ├── aws-targets.ts # Deployment target types
|
|
5350
|
+
│ │ └── mcp.ts # Gateway and MCP tool types
|
|
5351
|
+
│ └── cdk/ # CDK infrastructure (@aws/agentcore-cdk)
|
|
5352
|
+
├── app/ # Agent application code
|
|
5353
|
+
└── evaluators/ # Custom evaluator code (if any)
|
|
3881
5354
|
\`\`\`
|
|
3882
5355
|
|
|
3883
5356
|
## Getting Started
|
|
@@ -3885,7 +5358,9 @@ my-project/
|
|
|
3885
5358
|
### Prerequisites
|
|
3886
5359
|
|
|
3887
5360
|
- **Node.js** 20.x or later
|
|
3888
|
-
- **uv** for Python agents ([install](https://docs.astral.sh/uv/getting-started/installation/))
|
|
5361
|
+
- **Python 3.10+** and **uv** for Python agents ([install uv](https://docs.astral.sh/uv/getting-started/installation/))
|
|
5362
|
+
- **AWS credentials** configured (\`aws configure\` or environment variables)
|
|
5363
|
+
- **Docker** (only for Container build agents)
|
|
3889
5364
|
|
|
3890
5365
|
### Development
|
|
3891
5366
|
|
|
@@ -3903,44 +5378,62 @@ Deploy to AWS:
|
|
|
3903
5378
|
agentcore deploy
|
|
3904
5379
|
\`\`\`
|
|
3905
5380
|
|
|
3906
|
-
|
|
5381
|
+
## Commands
|
|
3907
5382
|
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
5383
|
+
| Command | Description |
|
|
5384
|
+
| --- | --- |
|
|
5385
|
+
| \`agentcore create\` | Create a new AgentCore project |
|
|
5386
|
+
| \`agentcore add\` | Add resources (agent, memory, credential, gateway, evaluator, policy) |
|
|
5387
|
+
| \`agentcore remove\` | Remove resources |
|
|
5388
|
+
| \`agentcore dev\` | Run agent locally with hot-reload |
|
|
5389
|
+
| \`agentcore deploy\` | Deploy to AWS via CDK |
|
|
5390
|
+
| \`agentcore status\` | Show deployment status |
|
|
5391
|
+
| \`agentcore invoke\` | Invoke agent (local or deployed) |
|
|
5392
|
+
| \`agentcore logs\` | View agent logs |
|
|
5393
|
+
| \`agentcore traces\` | View agent traces |
|
|
5394
|
+
| \`agentcore eval\` | Run evaluations |
|
|
5395
|
+
| \`agentcore package\` | Package agent artifacts |
|
|
5396
|
+
| \`agentcore validate\` | Validate configuration |
|
|
5397
|
+
| \`agentcore pause\` | Pause a deployed agent |
|
|
5398
|
+
| \`agentcore resume\` | Resume a paused agent |
|
|
5399
|
+
| \`agentcore fetch\` | Fetch remote resource definitions |
|
|
5400
|
+
| \`agentcore import\` | Import existing resources |
|
|
5401
|
+
| \`agentcore update\` | Check for CLI updates |
|
|
3912
5402
|
|
|
3913
5403
|
## Configuration
|
|
3914
5404
|
|
|
3915
|
-
Edit the JSON files in \`agentcore/\` to configure your
|
|
3916
|
-
type definitions and validation constraints.
|
|
5405
|
+
Edit the JSON files in \`agentcore/\` to configure your project. See \`agentcore/.llm-context/\` for type definitions and validation constraints.
|
|
3917
5406
|
|
|
3918
|
-
The project uses a **flat resource model**
|
|
3919
|
-
\`agentcore.json\`.
|
|
5407
|
+
The project uses a **flat resource model** — agents, memories, credentials, gateways, evaluators, and policies are top-level arrays in \`agentcore.json\`. Resources are independent; agents discover memories and credentials at runtime via environment variables or SDK calls.
|
|
3920
5408
|
|
|
3921
|
-
##
|
|
5409
|
+
## Resources
|
|
3922
5410
|
|
|
3923
|
-
|
|
|
3924
|
-
|
|
|
3925
|
-
|
|
|
3926
|
-
|
|
|
3927
|
-
|
|
|
3928
|
-
|
|
|
3929
|
-
|
|
|
3930
|
-
|
|
|
3931
|
-
|
|
|
3932
|
-
|
|
|
3933
|
-
| \`agentcore validate\` | Validate configuration |
|
|
3934
|
-
| \`agentcore update\` | Check for CLI updates |
|
|
5411
|
+
| Resource | Purpose |
|
|
5412
|
+
| --- | --- |
|
|
5413
|
+
| Agent (runtime) | HTTP, MCP, or A2A agent deployed to AgentCore Runtime |
|
|
5414
|
+
| Memory | Persistent context storage with configurable strategies |
|
|
5415
|
+
| Credential | API key or OAuth credential providers |
|
|
5416
|
+
| Gateway | MCP gateway that routes tool calls to targets |
|
|
5417
|
+
| Gateway Target | Tool implementation (Lambda, MCP server, OpenAPI, Smithy, API Gateway) |
|
|
5418
|
+
| Evaluator | Custom LLM-as-a-Judge or code-based evaluation |
|
|
5419
|
+
| Online Eval Config | Continuous evaluation pipeline for deployed agents |
|
|
5420
|
+
| Policy | Cedar authorization policies for gateway tools |
|
|
3935
5421
|
|
|
3936
5422
|
### Agent Types
|
|
3937
5423
|
|
|
3938
|
-
- **Template agents**: Created from framework templates (Strands,
|
|
5424
|
+
- **Template agents**: Created from framework templates (Strands, LangChain/LangGraph, GoogleADK, OpenAI Agents, Autogen)
|
|
3939
5425
|
- **BYO agents**: Bring your own code with \`agentcore add agent --type byo\`
|
|
5426
|
+
- **Import agents**: Import existing Bedrock agents with \`agentcore import\`
|
|
5427
|
+
|
|
5428
|
+
### Build Types
|
|
5429
|
+
|
|
5430
|
+
- **CodeZip**: Python source packaged as a zip and deployed directly to AgentCore Runtime
|
|
5431
|
+
- **Container**: Docker image built via CodeBuild (ARM64), pushed to ECR, and deployed to AgentCore Runtime
|
|
3940
5432
|
|
|
3941
5433
|
## Documentation
|
|
3942
5434
|
|
|
3943
|
-
- [AgentCore CLI
|
|
5435
|
+
- [AgentCore CLI](https://github.com/aws/agentcore-cli)
|
|
5436
|
+
- [AgentCore CDK Constructs](https://github.com/aws/agentcore-l3-cdk-constructs)
|
|
3944
5437
|
- [Amazon Bedrock AgentCore](https://aws.amazon.com/bedrock/agentcore/)
|
|
3945
5438
|
"
|
|
3946
5439
|
`;
|
|
@@ -3950,103 +5443,114 @@ exports[`Assets Directory Snapshots > Root-level assets > agents/AGENTS.md shoul
|
|
|
3950
5443
|
|
|
3951
5444
|
This project contains configuration and infrastructure for an Amazon Bedrock AgentCore application.
|
|
3952
5445
|
|
|
3953
|
-
The \`agentcore/\` directory
|
|
3954
|
-
|
|
3955
|
-
model** where agents, memories, and credentials are top-level arrays.
|
|
5446
|
+
The \`agentcore/\` directory is a declarative model of the project. The \`agentcore/cdk/\` subdirectory uses the
|
|
5447
|
+
\`@aws/agentcore-cdk\` L3 constructs to deploy the configuration to AWS.
|
|
3956
5448
|
|
|
3957
5449
|
## Mental Model
|
|
3958
5450
|
|
|
3959
|
-
The project uses a **flat resource model**. Agents, memories,
|
|
3960
|
-
\`agentcore.json\`. There is no binding
|
|
3961
|
-
independently.
|
|
3962
|
-
|
|
5451
|
+
The project uses a **flat resource model**. Agents, memories, credentials, gateways, evaluators, and policies are
|
|
5452
|
+
independent top-level arrays in \`agentcore.json\`. There is no binding between resources in the schema — each resource is
|
|
5453
|
+
provisioned independently. Agents discover memories and credentials at runtime via environment variables or SDK calls.
|
|
5454
|
+
Tags defined in \`agentcore.json\` flow through to deployed CloudFormation resources.
|
|
3963
5455
|
|
|
3964
5456
|
## Critical Invariants
|
|
3965
5457
|
|
|
3966
|
-
1. **Schema-First Authority:** The \`.json\` files are the
|
|
3967
|
-
|
|
3968
|
-
2. **Resource Identity:** The \`name\` field
|
|
3969
|
-
- **Renaming**
|
|
3970
|
-
- **Modifying** other fields
|
|
3971
|
-
3. **
|
|
3972
|
-
|
|
3973
|
-
4. **Resource Removal:**
|
|
3974
|
-
|
|
5458
|
+
1. **Schema-First Authority:** The \`.json\` files are the source of truth. Do not modify agent behavior by editing
|
|
5459
|
+
generated CDK code in \`cdk/\`.
|
|
5460
|
+
2. **Resource Identity:** The \`name\` field determines the CloudFormation Logical ID.
|
|
5461
|
+
- **Renaming** a resource will **destroy and recreate** it.
|
|
5462
|
+
- **Modifying** other fields will update the resource **in-place**.
|
|
5463
|
+
3. **Schema Validation:** If your JSON conforms to the types in \`.llm-context/\`, it will deploy successfully. Run
|
|
5464
|
+
\`agentcore validate\` to check.
|
|
5465
|
+
4. **Resource Removal:** Use \`agentcore remove\` to remove resources. Run \`agentcore deploy\` after removal to tear down
|
|
5466
|
+
deployed infrastructure.
|
|
3975
5467
|
|
|
3976
5468
|
## Directory Structure
|
|
3977
5469
|
|
|
3978
5470
|
\`\`\`
|
|
3979
|
-
|
|
3980
|
-
├── AGENTS.md # This file
|
|
3981
|
-
├── agentcore/
|
|
5471
|
+
myProject/
|
|
5472
|
+
├── AGENTS.md # This file — AI coding assistant context
|
|
5473
|
+
├── agentcore/
|
|
3982
5474
|
│ ├── agentcore.json # Main project config (AgentCoreProjectSpec)
|
|
3983
|
-
│ ├── aws-targets.json # Deployment targets
|
|
3984
|
-
│ ├── .
|
|
3985
|
-
│
|
|
5475
|
+
│ ├── aws-targets.json # Deployment targets (account + region)
|
|
5476
|
+
│ ├── .env.local # Secrets — API keys (gitignored)
|
|
5477
|
+
│ ├── .llm-context/ # TypeScript type definitions for AI assistants
|
|
5478
|
+
│ │ ├── README.md # Guide to using schema files
|
|
3986
5479
|
│ │ ├── agentcore.ts # AgentCoreProjectSpec types
|
|
3987
|
-
│ │
|
|
3988
|
-
│ └──
|
|
3989
|
-
└──
|
|
5480
|
+
│ │ ├── aws-targets.ts # AWS deployment target types
|
|
5481
|
+
│ │ └── mcp.ts # Gateway and MCP tool types
|
|
5482
|
+
│ └── cdk/ # AWS CDK project (@aws/agentcore-cdk L3 constructs)
|
|
5483
|
+
├── app/ # Agent application code
|
|
5484
|
+
└── evaluators/ # Custom evaluator code (if any)
|
|
3990
5485
|
\`\`\`
|
|
3991
5486
|
|
|
3992
5487
|
## Schema Reference
|
|
3993
5488
|
|
|
3994
5489
|
The \`agentcore/.llm-context/\` directory contains TypeScript type definitions optimized for AI coding assistants. Each
|
|
3995
|
-
file maps to a JSON config file and includes validation constraints as comments.
|
|
5490
|
+
file maps to a JSON config file and includes validation constraints as comments (\`@regex\`, \`@min\`, \`@max\`).
|
|
3996
5491
|
|
|
3997
|
-
| JSON Config
|
|
3998
|
-
|
|
|
3999
|
-
| \`agentcore/agentcore.json\`
|
|
4000
|
-
| \`agentcore/
|
|
5492
|
+
| JSON Config | Schema File | Root Type |
|
|
5493
|
+
| --- | --- | --- |
|
|
5494
|
+
| \`agentcore/agentcore.json\` | \`agentcore/.llm-context/agentcore.ts\` | \`AgentCoreProjectSpec\` |
|
|
5495
|
+
| \`agentcore/agentcore.json\` (gateways) | \`agentcore/.llm-context/mcp.ts\` | \`AgentCoreMcpSpec\` |
|
|
5496
|
+
| \`agentcore/aws-targets.json\` | \`agentcore/.llm-context/aws-targets.ts\` | \`AwsDeploymentTarget[]\` |
|
|
4001
5497
|
|
|
4002
5498
|
### Key Types
|
|
4003
5499
|
|
|
4004
|
-
- **AgentCoreProjectSpec**: Root
|
|
4005
|
-
- **AgentEnvSpec**: Agent configuration (
|
|
4006
|
-
- **Memory**: Memory resource with strategies and expiry
|
|
4007
|
-
- **Credential**: API key credential provider
|
|
5500
|
+
- **AgentCoreProjectSpec**: Root config with \`runtimes\`, \`memories\`, \`credentials\`, \`agentCoreGateways\`, \`evaluators\`, \`onlineEvalConfigs\`, \`policyEngines\` arrays
|
|
5501
|
+
- **AgentEnvSpec**: Agent configuration (build type, entrypoint, code location, runtime version, network mode)
|
|
5502
|
+
- **Memory**: Memory resource with strategies (SEMANTIC, SUMMARIZATION, USER_PREFERENCE, EPISODIC) and expiry
|
|
5503
|
+
- **Credential**: API key or OAuth credential provider
|
|
5504
|
+
- **AgentCoreGateway**: MCP gateway with targets (Lambda, MCP server, OpenAPI, Smithy, API Gateway)
|
|
5505
|
+
- **Evaluator**: LLM-as-a-Judge or code-based evaluator
|
|
5506
|
+
- **OnlineEvalConfig**: Continuous evaluation pipeline bound to an agent
|
|
4008
5507
|
|
|
4009
5508
|
### Common Enum Values
|
|
4010
5509
|
|
|
4011
5510
|
- **BuildType**: \`'CodeZip'\` | \`'Container'\`
|
|
4012
|
-
- **NetworkMode**: \`'PUBLIC'\`
|
|
4013
|
-
- **RuntimeVersion**: \`'PYTHON_3_10'\` | \`'PYTHON_3_11'\` | \`'PYTHON_3_12'\` | \`'PYTHON_3_13'\` | \`'PYTHON_3_14'\`
|
|
5511
|
+
- **NetworkMode**: \`'PUBLIC'\` | \`'VPC'\`
|
|
5512
|
+
- **RuntimeVersion**: \`'PYTHON_3_10'\` | \`'PYTHON_3_11'\` | \`'PYTHON_3_12'\` | \`'PYTHON_3_13'\` | \`'PYTHON_3_14'\` | \`'NODE_18'\` | \`'NODE_20'\` | \`'NODE_22'\`
|
|
4014
5513
|
- **MemoryStrategyType**: \`'SEMANTIC'\` | \`'SUMMARIZATION'\` | \`'USER_PREFERENCE'\` | \`'EPISODIC'\`
|
|
5514
|
+
- **GatewayTargetType**: \`'lambda'\` | \`'mcpServer'\` | \`'openApiSchema'\` | \`'smithyModel'\` | \`'apiGateway'\` | \`'lambdaFunctionArn'\`
|
|
5515
|
+
- **ModelProvider**: \`'Bedrock'\` | \`'Gemini'\` | \`'OpenAI'\` | \`'Anthropic'\`
|
|
4015
5516
|
|
|
4016
5517
|
### Build Types
|
|
4017
5518
|
|
|
4018
|
-
- **CodeZip**: Python source
|
|
4019
|
-
- **Container**:
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
container is built and run locally with volume-mounted hot-reload.
|
|
5519
|
+
- **CodeZip**: Python source packaged as a zip and deployed directly to AgentCore Runtime.
|
|
5520
|
+
- **Container**: Docker image built in CodeBuild (ARM64), pushed to a per-agent ECR repository. Requires a \`Dockerfile\`
|
|
5521
|
+
in the agent's \`codeLocation\` directory. For local development (\`agentcore dev\`), the container is built and run
|
|
5522
|
+
locally with volume-mounted hot-reload.
|
|
4023
5523
|
|
|
4024
5524
|
### Supported Frameworks (for template agents)
|
|
4025
5525
|
|
|
4026
|
-
- **Strands**
|
|
4027
|
-
- **
|
|
4028
|
-
- **GoogleADK**
|
|
4029
|
-
- **
|
|
5526
|
+
- **Strands** — Bedrock, Anthropic, OpenAI, Gemini
|
|
5527
|
+
- **LangChain/LangGraph** — Bedrock, Anthropic, OpenAI, Gemini
|
|
5528
|
+
- **GoogleADK** — Gemini
|
|
5529
|
+
- **OpenAI Agents** — OpenAI
|
|
5530
|
+
- **Autogen** — Bedrock, Anthropic, OpenAI, Gemini
|
|
4030
5531
|
|
|
5532
|
+
### Protocols
|
|
4031
5533
|
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
deployment options are available.
|
|
5534
|
+
- **HTTP** — Standard HTTP agent endpoint
|
|
5535
|
+
- **MCP** — Model Context Protocol server
|
|
5536
|
+
- **A2A** — Agent-to-Agent protocol (Google A2A)
|
|
4036
5537
|
|
|
4037
5538
|
## Deployment
|
|
4038
5539
|
|
|
4039
|
-
|
|
5540
|
+
Deployments are orchestrated through the CLI:
|
|
4040
5541
|
|
|
4041
|
-
|
|
5542
|
+
\`\`\`bash
|
|
5543
|
+
agentcore deploy # Synthesizes CDK and deploys to AWS
|
|
5544
|
+
agentcore status # Shows deployment status
|
|
5545
|
+
\`\`\`
|
|
4042
5546
|
|
|
4043
|
-
Alternatively,
|
|
5547
|
+
Alternatively, deploy directly via CDK:
|
|
4044
5548
|
|
|
4045
5549
|
\`\`\`bash
|
|
4046
5550
|
cd agentcore/cdk
|
|
4047
5551
|
npm install
|
|
4048
|
-
npx cdk synth
|
|
4049
|
-
npx cdk deploy
|
|
5552
|
+
npx cdk synth
|
|
5553
|
+
npx cdk deploy
|
|
4050
5554
|
\`\`\`
|
|
4051
5555
|
|
|
4052
5556
|
## Editing Schemas
|
|
@@ -4057,7 +5561,25 @@ When modifying JSON config files:
|
|
|
4057
5561
|
2. Check validation constraint comments (\`@regex\`, \`@min\`, \`@max\`)
|
|
4058
5562
|
3. Use exact enum values as string literals
|
|
4059
5563
|
4. Use CloudFormation-safe names (alphanumeric, start with letter)
|
|
4060
|
-
5. Run \`agentcore validate\`
|
|
5564
|
+
5. Run \`agentcore validate\` to verify changes
|
|
5565
|
+
|
|
5566
|
+
## CLI Commands
|
|
5567
|
+
|
|
5568
|
+
| Command | Description |
|
|
5569
|
+
| --- | --- |
|
|
5570
|
+
| \`agentcore create\` | Create a new project |
|
|
5571
|
+
| \`agentcore add <resource>\` | Add agent, memory, credential, gateway, evaluator, policy |
|
|
5572
|
+
| \`agentcore remove <resource>\` | Remove a resource |
|
|
5573
|
+
| \`agentcore dev\` | Run agent locally with hot-reload |
|
|
5574
|
+
| \`agentcore deploy\` | Deploy to AWS |
|
|
5575
|
+
| \`agentcore status\` | Show deployment status |
|
|
5576
|
+
| \`agentcore invoke\` | Invoke agent (local or deployed) |
|
|
5577
|
+
| \`agentcore logs\` | View agent logs |
|
|
5578
|
+
| \`agentcore traces\` | View agent traces |
|
|
5579
|
+
| \`agentcore eval\` | Run evaluations against an agent |
|
|
5580
|
+
| \`agentcore package\` | Package agent artifacts |
|
|
5581
|
+
| \`agentcore validate\` | Validate configuration |
|
|
5582
|
+
| \`agentcore pause\` / \`resume\` | Pause or resume a deployed agent |
|
|
4061
5583
|
"
|
|
4062
5584
|
`;
|
|
4063
5585
|
|