@aws/agentcore 0.8.2 → 1.0.0-preview.1
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/favicon.svg +60 -0
- package/dist/agent-inspector/index.css +1 -0
- package/dist/agent-inspector/index.html +14 -0
- package/dist/agent-inspector/index.js +275 -0
- package/dist/agent-inspector/index.js.map +1 -0
- package/dist/assets/README.md +56 -31
- package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +1732 -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 +35 -0
- package/dist/assets/cdk/lib/cdk-stack.ts +15 -2
- package/dist/assets/cdk/package.json +1 -1
- package/dist/assets/container/python/Dockerfile +4 -0
- 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 +517 -466
- 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/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/evaluator.d.ts.map +1 -1
- package/dist/schema/schemas/primitives/evaluator.js +0 -1
- package/dist/schema/schemas/primitives/evaluator.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 +4 -1
- package/scripts/bump-version.ts +7 -80
- package/scripts/bundle.mjs +57 -3
- package/scripts/copy-assets.mjs +14 -0
|
@@ -99,6 +99,40 @@ 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
|
+
tools?: { type: string; name: string }[];
|
|
114
|
+
apiKeyArn?: string;
|
|
115
|
+
}[] = [];
|
|
116
|
+
for (const entry of specAny.harnesses ?? []) {
|
|
117
|
+
const harnessPath = path.resolve(projectRoot, entry.path, 'harness.json');
|
|
118
|
+
try {
|
|
119
|
+
const harnessSpec = JSON.parse(fs.readFileSync(harnessPath, 'utf-8'));
|
|
120
|
+
harnessConfigs.push({
|
|
121
|
+
name: entry.name,
|
|
122
|
+
executionRoleArn: harnessSpec.executionRoleArn,
|
|
123
|
+
memoryName: harnessSpec.memory?.name,
|
|
124
|
+
containerUri: harnessSpec.containerUri,
|
|
125
|
+
hasDockerfile: !!harnessSpec.dockerfile,
|
|
126
|
+
tools: harnessSpec.tools,
|
|
127
|
+
apiKeyArn: harnessSpec.model?.apiKeyArn,
|
|
128
|
+
});
|
|
129
|
+
} catch (err) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
\`Could not read harness.json for "\${entry.name}" at \${harnessPath}: \${err instanceof Error ? err.message : err}\`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
102
136
|
const app = new App();
|
|
103
137
|
|
|
104
138
|
for (const target of targets) {
|
|
@@ -118,6 +152,7 @@ async function main() {
|
|
|
118
152
|
spec,
|
|
119
153
|
mcpSpec,
|
|
120
154
|
credentials,
|
|
155
|
+
harnesses: harnessConfigs.length > 0 ? harnessConfigs : undefined,
|
|
121
156
|
env,
|
|
122
157
|
description: \`AgentCore stack for \${spec.name} deployed to \${target.name} (\${target.region})\`,
|
|
123
158
|
tags: {
|
|
@@ -278,6 +313,18 @@ export interface AgentCoreStackProps extends StackProps {
|
|
|
278
313
|
* Credential provider ARNs from deployed state, keyed by credential name.
|
|
279
314
|
*/
|
|
280
315
|
credentials?: Record<string, { credentialProviderArn: string; clientSecretArn?: string }>;
|
|
316
|
+
/**
|
|
317
|
+
* Harness role configurations. Each entry creates an IAM execution role for a harness.
|
|
318
|
+
*/
|
|
319
|
+
harnesses?: {
|
|
320
|
+
name: string;
|
|
321
|
+
executionRoleArn?: string;
|
|
322
|
+
memoryName?: string;
|
|
323
|
+
containerUri?: string;
|
|
324
|
+
hasDockerfile?: boolean;
|
|
325
|
+
tools?: { type: string; name: string }[];
|
|
326
|
+
apiKeyArn?: string;
|
|
327
|
+
}[];
|
|
281
328
|
}
|
|
282
329
|
|
|
283
330
|
/**
|
|
@@ -293,11 +340,12 @@ export class AgentCoreStack extends Stack {
|
|
|
293
340
|
constructor(scope: Construct, id: string, props: AgentCoreStackProps) {
|
|
294
341
|
super(scope, id, props);
|
|
295
342
|
|
|
296
|
-
const { spec, mcpSpec, credentials } = props;
|
|
343
|
+
const { spec, mcpSpec, credentials, harnesses } = props;
|
|
297
344
|
|
|
298
|
-
// Create AgentCoreApplication with all agents
|
|
345
|
+
// Create AgentCoreApplication with all agents and harness roles
|
|
299
346
|
this.application = new AgentCoreApplication(this, 'Application', {
|
|
300
347
|
spec,
|
|
348
|
+
harnesses,
|
|
301
349
|
});
|
|
302
350
|
|
|
303
351
|
// Create AgentCoreMcp if there are gateways configured
|
|
@@ -357,7 +405,7 @@ exports[`Assets Directory Snapshots > CDK assets > cdk/cdk/package.json should m
|
|
|
357
405
|
"typescript": "~5.9.3"
|
|
358
406
|
},
|
|
359
407
|
"dependencies": {
|
|
360
|
-
"@aws/agentcore-cdk": "0.1.0-alpha.19",
|
|
408
|
+
"@aws/agentcore-cdk": "^0.1.0-alpha.19",
|
|
361
409
|
"aws-cdk-lib": "^2.248.0",
|
|
362
410
|
"constructs": "^10.0.0"
|
|
363
411
|
}
|
|
@@ -449,6 +497,7 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
|
|
|
449
497
|
"evaluators/python-lambda/execution-role-policy.json",
|
|
450
498
|
"evaluators/python-lambda/lambda_function.py",
|
|
451
499
|
"evaluators/python-lambda/pyproject.toml",
|
|
500
|
+
"harness/invoke.py.template",
|
|
452
501
|
"mcp/python-lambda/README.md",
|
|
453
502
|
"mcp/python-lambda/handler.py",
|
|
454
503
|
"mcp/python-lambda/pyproject.toml",
|
|
@@ -475,6 +524,26 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
|
|
|
475
524
|
"python/a2a/strands/base/pyproject.toml",
|
|
476
525
|
"python/a2a/strands/capabilities/memory/__init__.py",
|
|
477
526
|
"python/a2a/strands/capabilities/memory/session.py",
|
|
527
|
+
"python/agui/googleadk/base/README.md",
|
|
528
|
+
"python/agui/googleadk/base/gitignore.template",
|
|
529
|
+
"python/agui/googleadk/base/main.py",
|
|
530
|
+
"python/agui/googleadk/base/model/__init__.py",
|
|
531
|
+
"python/agui/googleadk/base/model/load.py",
|
|
532
|
+
"python/agui/googleadk/base/pyproject.toml",
|
|
533
|
+
"python/agui/langchain_langgraph/base/README.md",
|
|
534
|
+
"python/agui/langchain_langgraph/base/gitignore.template",
|
|
535
|
+
"python/agui/langchain_langgraph/base/main.py",
|
|
536
|
+
"python/agui/langchain_langgraph/base/model/__init__.py",
|
|
537
|
+
"python/agui/langchain_langgraph/base/model/load.py",
|
|
538
|
+
"python/agui/langchain_langgraph/base/pyproject.toml",
|
|
539
|
+
"python/agui/strands/base/README.md",
|
|
540
|
+
"python/agui/strands/base/gitignore.template",
|
|
541
|
+
"python/agui/strands/base/main.py",
|
|
542
|
+
"python/agui/strands/base/model/__init__.py",
|
|
543
|
+
"python/agui/strands/base/model/load.py",
|
|
544
|
+
"python/agui/strands/base/pyproject.toml",
|
|
545
|
+
"python/agui/strands/capabilities/memory/__init__.py",
|
|
546
|
+
"python/agui/strands/capabilities/memory/session.py",
|
|
478
547
|
"python/http/autogen/base/README.md",
|
|
479
548
|
"python/http/autogen/base/gitignore.template",
|
|
480
549
|
"python/http/autogen/base/main.py",
|
|
@@ -907,12 +976,1033 @@ This agent implements the A2A protocol using Google's Agent Development Kit, ena
|
|
|
907
976
|
|
|
908
977
|
## Local Development
|
|
909
978
|
|
|
910
|
-
\`\`\`bash
|
|
911
|
-
uv sync
|
|
912
|
-
uv run python main.py
|
|
979
|
+
\`\`\`bash
|
|
980
|
+
uv sync
|
|
981
|
+
uv run python main.py
|
|
982
|
+
\`\`\`
|
|
983
|
+
|
|
984
|
+
The agent starts on port 9000.
|
|
985
|
+
|
|
986
|
+
## Deploy
|
|
987
|
+
|
|
988
|
+
\`\`\`bash
|
|
989
|
+
agentcore deploy
|
|
990
|
+
\`\`\`
|
|
991
|
+
"
|
|
992
|
+
`;
|
|
993
|
+
|
|
994
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/gitignore.template should match snapshot 1`] = `
|
|
995
|
+
"# Environment variables
|
|
996
|
+
.env
|
|
997
|
+
|
|
998
|
+
# Python
|
|
999
|
+
__pycache__/
|
|
1000
|
+
*.py[cod]
|
|
1001
|
+
*$py.class
|
|
1002
|
+
*.so
|
|
1003
|
+
.Python
|
|
1004
|
+
build/
|
|
1005
|
+
develop-eggs/
|
|
1006
|
+
dist/
|
|
1007
|
+
downloads/
|
|
1008
|
+
eggs/
|
|
1009
|
+
.eggs/
|
|
1010
|
+
lib/
|
|
1011
|
+
lib64/
|
|
1012
|
+
parts/
|
|
1013
|
+
sdist/
|
|
1014
|
+
var/
|
|
1015
|
+
wheels/
|
|
1016
|
+
*.egg-info/
|
|
1017
|
+
.installed.cfg
|
|
1018
|
+
*.egg
|
|
1019
|
+
|
|
1020
|
+
# Virtual environments
|
|
1021
|
+
.venv/
|
|
1022
|
+
venv/
|
|
1023
|
+
ENV/
|
|
1024
|
+
env/
|
|
1025
|
+
|
|
1026
|
+
# IDE
|
|
1027
|
+
.vscode/
|
|
1028
|
+
.idea/
|
|
1029
|
+
*.swp
|
|
1030
|
+
*.swo
|
|
1031
|
+
*~
|
|
1032
|
+
|
|
1033
|
+
# OS
|
|
1034
|
+
.DS_Store
|
|
1035
|
+
Thumbs.db
|
|
1036
|
+
"
|
|
1037
|
+
`;
|
|
1038
|
+
|
|
1039
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/main.py should match snapshot 1`] = `
|
|
1040
|
+
"import os
|
|
1041
|
+
from google.adk.agents import Agent
|
|
1042
|
+
from google.adk.a2a.executor.a2a_agent_executor import A2aAgentExecutor
|
|
1043
|
+
from google.adk.runners import Runner
|
|
1044
|
+
from google.adk.sessions import InMemorySessionService
|
|
1045
|
+
from a2a.types import AgentCapabilities, AgentCard, AgentSkill
|
|
1046
|
+
from bedrock_agentcore.runtime import serve_a2a
|
|
1047
|
+
from model.load import load_model
|
|
1048
|
+
|
|
1049
|
+
load_model() # Sets GOOGLE_API_KEY env var (returns None)
|
|
1050
|
+
|
|
1051
|
+
|
|
1052
|
+
def add_numbers(a: int, b: int) -> int:
|
|
1053
|
+
"""Return the sum of two numbers."""
|
|
1054
|
+
return a + b
|
|
1055
|
+
|
|
1056
|
+
|
|
1057
|
+
tools = [add_numbers]
|
|
1058
|
+
|
|
1059
|
+
{{#if sessionStorageMountPath}}
|
|
1060
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
1061
|
+
|
|
1062
|
+
def _safe_resolve(path: str) -> str:
|
|
1063
|
+
"""Resolve path safely within the storage boundary."""
|
|
1064
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
1065
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
1066
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
1067
|
+
return resolved
|
|
1068
|
+
|
|
1069
|
+
def file_read(path: str) -> str:
|
|
1070
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
1071
|
+
try:
|
|
1072
|
+
full_path = _safe_resolve(path)
|
|
1073
|
+
with open(full_path) as f:
|
|
1074
|
+
return f.read()
|
|
1075
|
+
except ValueError as e:
|
|
1076
|
+
return str(e)
|
|
1077
|
+
except OSError as e:
|
|
1078
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
1079
|
+
|
|
1080
|
+
def file_write(path: str, content: str) -> str:
|
|
1081
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
1082
|
+
try:
|
|
1083
|
+
full_path = _safe_resolve(path)
|
|
1084
|
+
parent = os.path.dirname(full_path)
|
|
1085
|
+
if parent:
|
|
1086
|
+
os.makedirs(parent, exist_ok=True)
|
|
1087
|
+
with open(full_path, "w") as f:
|
|
1088
|
+
f.write(content)
|
|
1089
|
+
return f"Written to {path}"
|
|
1090
|
+
except ValueError as e:
|
|
1091
|
+
return str(e)
|
|
1092
|
+
except OSError as e:
|
|
1093
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
1094
|
+
|
|
1095
|
+
def list_files(directory: str = "") -> str:
|
|
1096
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
1097
|
+
try:
|
|
1098
|
+
target = _safe_resolve(directory)
|
|
1099
|
+
entries = os.listdir(target)
|
|
1100
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
1101
|
+
except ValueError as e:
|
|
1102
|
+
return str(e)
|
|
1103
|
+
except OSError as e:
|
|
1104
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
1105
|
+
|
|
1106
|
+
tools.extend([file_read, file_write, list_files])
|
|
1107
|
+
{{/if}}
|
|
1108
|
+
|
|
1109
|
+
AGENT_INSTRUCTION = """
|
|
1110
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
1111
|
+
{{#if sessionStorageMountPath}}
|
|
1112
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
1113
|
+
{{/if}}
|
|
1114
|
+
"""
|
|
1115
|
+
|
|
1116
|
+
agent = Agent(
|
|
1117
|
+
model="gemini-2.5-flash",
|
|
1118
|
+
name="{{ name }}",
|
|
1119
|
+
description="A helpful assistant that can use tools.",
|
|
1120
|
+
instruction=AGENT_INSTRUCTION,
|
|
1121
|
+
tools=tools,
|
|
1122
|
+
)
|
|
1123
|
+
|
|
1124
|
+
runner = Runner(
|
|
1125
|
+
app_name=agent.name,
|
|
1126
|
+
agent=agent,
|
|
1127
|
+
session_service=InMemorySessionService(),
|
|
1128
|
+
)
|
|
1129
|
+
|
|
1130
|
+
card = AgentCard(
|
|
1131
|
+
name=agent.name,
|
|
1132
|
+
description=agent.description,
|
|
1133
|
+
url="http://localhost:9000/",
|
|
1134
|
+
version="0.1.0",
|
|
1135
|
+
capabilities=AgentCapabilities(streaming=True),
|
|
1136
|
+
skills=[
|
|
1137
|
+
AgentSkill(
|
|
1138
|
+
id="tools",
|
|
1139
|
+
name="tools",
|
|
1140
|
+
description="Use tools to help answer questions",
|
|
1141
|
+
tags=["tools"],
|
|
1142
|
+
)
|
|
1143
|
+
],
|
|
1144
|
+
default_input_modes=["text"],
|
|
1145
|
+
default_output_modes=["text"],
|
|
1146
|
+
)
|
|
1147
|
+
|
|
1148
|
+
if __name__ == "__main__":
|
|
1149
|
+
serve_a2a(A2aAgentExecutor(runner=runner), card)
|
|
1150
|
+
"
|
|
1151
|
+
`;
|
|
1152
|
+
|
|
1153
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/model/__init__.py should match snapshot 1`] = `
|
|
1154
|
+
"# Package marker
|
|
1155
|
+
"
|
|
1156
|
+
`;
|
|
1157
|
+
|
|
1158
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/model/load.py should match snapshot 1`] = `
|
|
1159
|
+
"import os
|
|
1160
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1161
|
+
|
|
1162
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1163
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1164
|
+
|
|
1165
|
+
|
|
1166
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1167
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1168
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1169
|
+
return api_key
|
|
1170
|
+
|
|
1171
|
+
|
|
1172
|
+
def _get_api_key() -> str:
|
|
1173
|
+
"""
|
|
1174
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1175
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1176
|
+
"""
|
|
1177
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1178
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1179
|
+
if not api_key:
|
|
1180
|
+
raise RuntimeError(
|
|
1181
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1182
|
+
)
|
|
1183
|
+
return api_key
|
|
1184
|
+
return _agentcore_identity_api_key_provider()
|
|
1185
|
+
|
|
1186
|
+
|
|
1187
|
+
def load_model() -> None:
|
|
1188
|
+
"""
|
|
1189
|
+
Set up Gemini API key authentication.
|
|
1190
|
+
Uses AgentCore Identity for API key management in deployed environments,
|
|
1191
|
+
and falls back to .env file for local development.
|
|
1192
|
+
Sets the GOOGLE_API_KEY environment variable for the Google ADK.
|
|
1193
|
+
"""
|
|
1194
|
+
api_key = _get_api_key()
|
|
1195
|
+
# Use Google AI Studios API Key Authentication.
|
|
1196
|
+
# https://google.github.io/adk-docs/agents/models/#google-ai-studio
|
|
1197
|
+
os.environ["GOOGLE_API_KEY"] = api_key
|
|
1198
|
+
# Set to TRUE is using Google Vertex AI, Set to FALSE for Google AI Studio
|
|
1199
|
+
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "FALSE"
|
|
1200
|
+
"
|
|
1201
|
+
`;
|
|
1202
|
+
|
|
1203
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/pyproject.toml should match snapshot 1`] = `
|
|
1204
|
+
"[build-system]
|
|
1205
|
+
requires = ["hatchling"]
|
|
1206
|
+
build-backend = "hatchling.build"
|
|
1207
|
+
|
|
1208
|
+
[project]
|
|
1209
|
+
name = "{{ name }}"
|
|
1210
|
+
version = "0.1.0"
|
|
1211
|
+
description = "AgentCore A2A Agent using Google ADK"
|
|
1212
|
+
readme = "README.md"
|
|
1213
|
+
requires-python = ">=3.10"
|
|
1214
|
+
dependencies = [
|
|
1215
|
+
"a2a-sdk >= 0.2.0",
|
|
1216
|
+
"aws-opentelemetry-distro",
|
|
1217
|
+
"bedrock-agentcore[a2a] >= 1.0.3",
|
|
1218
|
+
"google-adk >= 1.0.0",
|
|
1219
|
+
"google-genai >= 1.0.0",
|
|
1220
|
+
]
|
|
1221
|
+
|
|
1222
|
+
[tool.hatch.build.targets.wheel]
|
|
1223
|
+
packages = ["."]
|
|
1224
|
+
"
|
|
1225
|
+
`;
|
|
1226
|
+
|
|
1227
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/README.md should match snapshot 1`] = `
|
|
1228
|
+
"# {{ name }}
|
|
1229
|
+
|
|
1230
|
+
An A2A (Agent-to-Agent) agent deployed on Amazon Bedrock AgentCore using LangChain + LangGraph.
|
|
1231
|
+
|
|
1232
|
+
## Overview
|
|
1233
|
+
|
|
1234
|
+
This agent implements the A2A protocol using LangGraph, enabling agent-to-agent communication.
|
|
1235
|
+
|
|
1236
|
+
## Local Development
|
|
1237
|
+
|
|
1238
|
+
\`\`\`bash
|
|
1239
|
+
uv sync
|
|
1240
|
+
uv run python main.py
|
|
1241
|
+
\`\`\`
|
|
1242
|
+
|
|
1243
|
+
The agent starts on port 9000.
|
|
1244
|
+
|
|
1245
|
+
## Deploy
|
|
1246
|
+
|
|
1247
|
+
\`\`\`bash
|
|
1248
|
+
agentcore deploy
|
|
1249
|
+
\`\`\`
|
|
1250
|
+
"
|
|
1251
|
+
`;
|
|
1252
|
+
|
|
1253
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/gitignore.template should match snapshot 1`] = `
|
|
1254
|
+
"# Environment variables
|
|
1255
|
+
.env
|
|
1256
|
+
|
|
1257
|
+
# Python
|
|
1258
|
+
__pycache__/
|
|
1259
|
+
*.py[cod]
|
|
1260
|
+
*$py.class
|
|
1261
|
+
*.so
|
|
1262
|
+
.Python
|
|
1263
|
+
build/
|
|
1264
|
+
develop-eggs/
|
|
1265
|
+
dist/
|
|
1266
|
+
downloads/
|
|
1267
|
+
eggs/
|
|
1268
|
+
.eggs/
|
|
1269
|
+
lib/
|
|
1270
|
+
lib64/
|
|
1271
|
+
parts/
|
|
1272
|
+
sdist/
|
|
1273
|
+
var/
|
|
1274
|
+
wheels/
|
|
1275
|
+
*.egg-info/
|
|
1276
|
+
.installed.cfg
|
|
1277
|
+
*.egg
|
|
1278
|
+
|
|
1279
|
+
# Virtual environments
|
|
1280
|
+
.venv/
|
|
1281
|
+
venv/
|
|
1282
|
+
ENV/
|
|
1283
|
+
env/
|
|
1284
|
+
|
|
1285
|
+
# IDE
|
|
1286
|
+
.vscode/
|
|
1287
|
+
.idea/
|
|
1288
|
+
*.swp
|
|
1289
|
+
*.swo
|
|
1290
|
+
*~
|
|
1291
|
+
|
|
1292
|
+
# OS
|
|
1293
|
+
.DS_Store
|
|
1294
|
+
Thumbs.db
|
|
1295
|
+
"
|
|
1296
|
+
`;
|
|
1297
|
+
|
|
1298
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/main.py should match snapshot 1`] = `
|
|
1299
|
+
"import os
|
|
1300
|
+
from langchain_core.tools import tool
|
|
1301
|
+
from langgraph.prebuilt import create_react_agent
|
|
1302
|
+
from opentelemetry.instrumentation.langchain import LangchainInstrumentor
|
|
1303
|
+
from a2a.server.agent_execution import AgentExecutor, RequestContext
|
|
1304
|
+
from a2a.server.events import EventQueue
|
|
1305
|
+
from a2a.server.tasks import TaskUpdater
|
|
1306
|
+
from a2a.types import AgentCapabilities, AgentCard, AgentSkill, Part, TextPart
|
|
1307
|
+
from a2a.utils import new_task
|
|
1308
|
+
from bedrock_agentcore.runtime import serve_a2a
|
|
1309
|
+
from model.load import load_model
|
|
1310
|
+
|
|
1311
|
+
LangchainInstrumentor().instrument()
|
|
1312
|
+
|
|
1313
|
+
|
|
1314
|
+
@tool
|
|
1315
|
+
def add_numbers(a: int, b: int) -> int:
|
|
1316
|
+
"""Return the sum of two numbers."""
|
|
1317
|
+
return a + b
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
tools = [add_numbers]
|
|
1321
|
+
|
|
1322
|
+
{{#if sessionStorageMountPath}}
|
|
1323
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
1324
|
+
|
|
1325
|
+
def _safe_resolve(path: str) -> str:
|
|
1326
|
+
"""Resolve path safely within the storage boundary."""
|
|
1327
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
1328
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
1329
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
1330
|
+
return resolved
|
|
1331
|
+
|
|
1332
|
+
@tool
|
|
1333
|
+
def file_read(path: str) -> str:
|
|
1334
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
1335
|
+
try:
|
|
1336
|
+
full_path = _safe_resolve(path)
|
|
1337
|
+
with open(full_path) as f:
|
|
1338
|
+
return f.read()
|
|
1339
|
+
except ValueError as e:
|
|
1340
|
+
return str(e)
|
|
1341
|
+
except OSError as e:
|
|
1342
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
1343
|
+
|
|
1344
|
+
@tool
|
|
1345
|
+
def file_write(path: str, content: str) -> str:
|
|
1346
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
1347
|
+
try:
|
|
1348
|
+
full_path = _safe_resolve(path)
|
|
1349
|
+
parent = os.path.dirname(full_path)
|
|
1350
|
+
if parent:
|
|
1351
|
+
os.makedirs(parent, exist_ok=True)
|
|
1352
|
+
with open(full_path, "w") as f:
|
|
1353
|
+
f.write(content)
|
|
1354
|
+
return f"Written to {path}"
|
|
1355
|
+
except ValueError as e:
|
|
1356
|
+
return str(e)
|
|
1357
|
+
except OSError as e:
|
|
1358
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
1359
|
+
|
|
1360
|
+
@tool
|
|
1361
|
+
def list_files(directory: str = "") -> str:
|
|
1362
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
1363
|
+
try:
|
|
1364
|
+
target = _safe_resolve(directory)
|
|
1365
|
+
entries = os.listdir(target)
|
|
1366
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
1367
|
+
except ValueError as e:
|
|
1368
|
+
return str(e)
|
|
1369
|
+
except OSError as e:
|
|
1370
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
1371
|
+
|
|
1372
|
+
tools.extend([file_read, file_write, list_files])
|
|
1373
|
+
{{/if}}
|
|
1374
|
+
|
|
1375
|
+
SYSTEM_PROMPT = """
|
|
1376
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
1377
|
+
{{#if sessionStorageMountPath}}
|
|
1378
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
1379
|
+
{{/if}}
|
|
1380
|
+
"""
|
|
1381
|
+
|
|
1382
|
+
model = load_model()
|
|
1383
|
+
graph = create_react_agent(model, tools=tools, prompt=SYSTEM_PROMPT)
|
|
1384
|
+
|
|
1385
|
+
|
|
1386
|
+
class LangGraphA2AExecutor(AgentExecutor):
|
|
1387
|
+
"""Wraps a LangGraph CompiledGraph as an a2a-sdk AgentExecutor."""
|
|
1388
|
+
|
|
1389
|
+
def __init__(self, graph):
|
|
1390
|
+
self.graph = graph
|
|
1391
|
+
|
|
1392
|
+
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
1393
|
+
task = context.current_task or new_task(context.message)
|
|
1394
|
+
if not context.current_task:
|
|
1395
|
+
await event_queue.enqueue_event(task)
|
|
1396
|
+
updater = TaskUpdater(event_queue, task.id, task.context_id)
|
|
1397
|
+
|
|
1398
|
+
user_text = context.get_user_input()
|
|
1399
|
+
result = await self.graph.ainvoke({"messages": [("user", user_text)]})
|
|
1400
|
+
response = result["messages"][-1].content
|
|
1401
|
+
|
|
1402
|
+
await updater.add_artifact([Part(root=TextPart(text=response))])
|
|
1403
|
+
await updater.complete()
|
|
1404
|
+
|
|
1405
|
+
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
1406
|
+
pass
|
|
1407
|
+
|
|
1408
|
+
|
|
1409
|
+
card = AgentCard(
|
|
1410
|
+
name="{{ name }}",
|
|
1411
|
+
description="A LangGraph agent on Bedrock AgentCore",
|
|
1412
|
+
url="http://localhost:9000/",
|
|
1413
|
+
version="0.1.0",
|
|
1414
|
+
capabilities=AgentCapabilities(streaming=True),
|
|
1415
|
+
skills=[
|
|
1416
|
+
AgentSkill(
|
|
1417
|
+
id="tools",
|
|
1418
|
+
name="tools",
|
|
1419
|
+
description="Use tools to help answer questions",
|
|
1420
|
+
tags=["tools"],
|
|
1421
|
+
)
|
|
1422
|
+
],
|
|
1423
|
+
default_input_modes=["text"],
|
|
1424
|
+
default_output_modes=["text"],
|
|
1425
|
+
)
|
|
1426
|
+
|
|
1427
|
+
if __name__ == "__main__":
|
|
1428
|
+
serve_a2a(LangGraphA2AExecutor(graph), card)
|
|
1429
|
+
"
|
|
1430
|
+
`;
|
|
1431
|
+
|
|
1432
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/model/__init__.py should match snapshot 1`] = `
|
|
1433
|
+
"# Package marker
|
|
1434
|
+
"
|
|
1435
|
+
`;
|
|
1436
|
+
|
|
1437
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/model/load.py should match snapshot 1`] = `
|
|
1438
|
+
"{{#if (eq modelProvider "Bedrock")}}
|
|
1439
|
+
from langchain_aws import ChatBedrock
|
|
1440
|
+
|
|
1441
|
+
# Uses global inference profile for Claude Sonnet 4.5
|
|
1442
|
+
# https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html
|
|
1443
|
+
MODEL_ID = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
|
1444
|
+
|
|
1445
|
+
|
|
1446
|
+
def load_model() -> ChatBedrock:
|
|
1447
|
+
"""Get Bedrock model client using IAM credentials."""
|
|
1448
|
+
return ChatBedrock(model_id=MODEL_ID)
|
|
1449
|
+
{{/if}}
|
|
1450
|
+
{{#if (eq modelProvider "Anthropic")}}
|
|
1451
|
+
import os
|
|
1452
|
+
from langchain_anthropic import ChatAnthropic
|
|
1453
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1454
|
+
|
|
1455
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1456
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1457
|
+
|
|
1458
|
+
|
|
1459
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1460
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1461
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1462
|
+
return api_key
|
|
1463
|
+
|
|
1464
|
+
|
|
1465
|
+
def _get_api_key() -> str:
|
|
1466
|
+
"""
|
|
1467
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1468
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1469
|
+
"""
|
|
1470
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1471
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1472
|
+
if not api_key:
|
|
1473
|
+
raise RuntimeError(
|
|
1474
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1475
|
+
)
|
|
1476
|
+
return api_key
|
|
1477
|
+
return _agentcore_identity_api_key_provider()
|
|
1478
|
+
|
|
1479
|
+
|
|
1480
|
+
def load_model() -> ChatAnthropic:
|
|
1481
|
+
"""Get authenticated Anthropic model client."""
|
|
1482
|
+
return ChatAnthropic(
|
|
1483
|
+
model="claude-sonnet-4-5-20250929",
|
|
1484
|
+
api_key=_get_api_key()
|
|
1485
|
+
)
|
|
1486
|
+
{{/if}}
|
|
1487
|
+
{{#if (eq modelProvider "OpenAI")}}
|
|
1488
|
+
import os
|
|
1489
|
+
from langchain_openai import ChatOpenAI
|
|
1490
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1491
|
+
|
|
1492
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1493
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1494
|
+
|
|
1495
|
+
|
|
1496
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1497
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1498
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1499
|
+
return api_key
|
|
1500
|
+
|
|
1501
|
+
|
|
1502
|
+
def _get_api_key() -> str:
|
|
1503
|
+
"""
|
|
1504
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1505
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1506
|
+
"""
|
|
1507
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1508
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1509
|
+
if not api_key:
|
|
1510
|
+
raise RuntimeError(
|
|
1511
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1512
|
+
)
|
|
1513
|
+
return api_key
|
|
1514
|
+
return _agentcore_identity_api_key_provider()
|
|
1515
|
+
|
|
1516
|
+
|
|
1517
|
+
def load_model() -> ChatOpenAI:
|
|
1518
|
+
"""Get authenticated OpenAI model client."""
|
|
1519
|
+
return ChatOpenAI(
|
|
1520
|
+
model="gpt-4.1",
|
|
1521
|
+
api_key=_get_api_key()
|
|
1522
|
+
)
|
|
1523
|
+
{{/if}}
|
|
1524
|
+
{{#if (eq modelProvider "Gemini")}}
|
|
1525
|
+
import os
|
|
1526
|
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
|
1527
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1528
|
+
|
|
1529
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1530
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1531
|
+
|
|
1532
|
+
|
|
1533
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1534
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1535
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1536
|
+
return api_key
|
|
1537
|
+
|
|
1538
|
+
|
|
1539
|
+
def _get_api_key() -> str:
|
|
1540
|
+
"""
|
|
1541
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1542
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1543
|
+
"""
|
|
1544
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1545
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1546
|
+
if not api_key:
|
|
1547
|
+
raise RuntimeError(
|
|
1548
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1549
|
+
)
|
|
1550
|
+
return api_key
|
|
1551
|
+
return _agentcore_identity_api_key_provider()
|
|
1552
|
+
|
|
1553
|
+
|
|
1554
|
+
def load_model() -> ChatGoogleGenerativeAI:
|
|
1555
|
+
"""Get authenticated Gemini model client."""
|
|
1556
|
+
return ChatGoogleGenerativeAI(
|
|
1557
|
+
model="gemini-2.5-flash",
|
|
1558
|
+
api_key=_get_api_key()
|
|
1559
|
+
)
|
|
1560
|
+
{{/if}}
|
|
1561
|
+
"
|
|
1562
|
+
`;
|
|
1563
|
+
|
|
1564
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/pyproject.toml should match snapshot 1`] = `
|
|
1565
|
+
"[build-system]
|
|
1566
|
+
requires = ["hatchling"]
|
|
1567
|
+
build-backend = "hatchling.build"
|
|
1568
|
+
|
|
1569
|
+
[project]
|
|
1570
|
+
name = "{{ name }}"
|
|
1571
|
+
version = "0.1.0"
|
|
1572
|
+
description = "AgentCore A2A Agent using LangChain + LangGraph"
|
|
1573
|
+
readme = "README.md"
|
|
1574
|
+
requires-python = ">=3.10"
|
|
1575
|
+
dependencies = [
|
|
1576
|
+
"a2a-sdk >= 0.2.0",
|
|
1577
|
+
{{#if (eq modelProvider "Anthropic")}}"langchain-anthropic >= 0.3.0",
|
|
1578
|
+
{{/if}}{{#if (eq modelProvider "Bedrock")}}"langchain-aws >= 0.2.0",
|
|
1579
|
+
{{/if}}{{#if (eq modelProvider "Gemini")}}"langchain-google-genai >= 2.0.0",
|
|
1580
|
+
{{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
|
|
1581
|
+
{{/if}}"aws-opentelemetry-distro",
|
|
1582
|
+
"opentelemetry-instrumentation-langchain >= 0.59.0",
|
|
1583
|
+
"bedrock-agentcore[a2a] >= 1.0.3",
|
|
1584
|
+
"botocore[crt] >= 1.35.0",
|
|
1585
|
+
"langgraph >= 0.2.0",
|
|
1586
|
+
]
|
|
1587
|
+
|
|
1588
|
+
[tool.hatch.build.targets.wheel]
|
|
1589
|
+
packages = ["."]
|
|
1590
|
+
"
|
|
1591
|
+
`;
|
|
1592
|
+
|
|
1593
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/README.md should match snapshot 1`] = `
|
|
1594
|
+
"# {{ name }}
|
|
1595
|
+
|
|
1596
|
+
An A2A (Agent-to-Agent) agent deployed on Amazon Bedrock AgentCore using Strands SDK.
|
|
1597
|
+
|
|
1598
|
+
## Overview
|
|
1599
|
+
|
|
1600
|
+
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.
|
|
1601
|
+
|
|
1602
|
+
## Local Development
|
|
1603
|
+
|
|
1604
|
+
\`\`\`bash
|
|
1605
|
+
uv sync
|
|
1606
|
+
uv run python main.py
|
|
1607
|
+
\`\`\`
|
|
1608
|
+
|
|
1609
|
+
The agent starts on port 9000.
|
|
1610
|
+
|
|
1611
|
+
## Deploy
|
|
1612
|
+
|
|
1613
|
+
\`\`\`bash
|
|
1614
|
+
agentcore deploy
|
|
1615
|
+
\`\`\`
|
|
1616
|
+
"
|
|
1617
|
+
`;
|
|
1618
|
+
|
|
1619
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/gitignore.template should match snapshot 1`] = `
|
|
1620
|
+
"# Environment variables
|
|
1621
|
+
.env
|
|
1622
|
+
|
|
1623
|
+
# Python
|
|
1624
|
+
__pycache__/
|
|
1625
|
+
*.py[cod]
|
|
1626
|
+
*$py.class
|
|
1627
|
+
*.so
|
|
1628
|
+
.Python
|
|
1629
|
+
build/
|
|
1630
|
+
develop-eggs/
|
|
1631
|
+
dist/
|
|
1632
|
+
downloads/
|
|
1633
|
+
eggs/
|
|
1634
|
+
.eggs/
|
|
1635
|
+
lib/
|
|
1636
|
+
lib64/
|
|
1637
|
+
parts/
|
|
1638
|
+
sdist/
|
|
1639
|
+
var/
|
|
1640
|
+
wheels/
|
|
1641
|
+
*.egg-info/
|
|
1642
|
+
.installed.cfg
|
|
1643
|
+
*.egg
|
|
1644
|
+
|
|
1645
|
+
# Virtual environments
|
|
1646
|
+
.venv/
|
|
1647
|
+
venv/
|
|
1648
|
+
ENV/
|
|
1649
|
+
env/
|
|
1650
|
+
|
|
1651
|
+
# IDE
|
|
1652
|
+
.vscode/
|
|
1653
|
+
.idea/
|
|
1654
|
+
*.swp
|
|
1655
|
+
*.swo
|
|
1656
|
+
*~
|
|
1657
|
+
|
|
1658
|
+
# OS
|
|
1659
|
+
.DS_Store
|
|
1660
|
+
Thumbs.db
|
|
1661
|
+
"
|
|
1662
|
+
`;
|
|
1663
|
+
|
|
1664
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/main.py should match snapshot 1`] = `
|
|
1665
|
+
"from strands import Agent, tool
|
|
1666
|
+
from strands.multiagent.a2a.executor import StrandsA2AExecutor
|
|
1667
|
+
from bedrock_agentcore.runtime import serve_a2a
|
|
1668
|
+
from model.load import load_model
|
|
1669
|
+
{{#if hasMemory}}
|
|
1670
|
+
from memory.session import get_memory_session_manager
|
|
1671
|
+
{{/if}}
|
|
1672
|
+
{{#if sessionStorageMountPath}}
|
|
1673
|
+
import os
|
|
1674
|
+
{{/if}}
|
|
1675
|
+
|
|
1676
|
+
|
|
1677
|
+
@tool
|
|
1678
|
+
def add_numbers(a: int, b: int) -> int:
|
|
1679
|
+
"""Return the sum of two numbers."""
|
|
1680
|
+
return a + b
|
|
1681
|
+
|
|
1682
|
+
|
|
1683
|
+
tools = [add_numbers]
|
|
1684
|
+
|
|
1685
|
+
{{#if sessionStorageMountPath}}
|
|
1686
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
1687
|
+
|
|
1688
|
+
def _safe_resolve(path: str) -> str:
|
|
1689
|
+
"""Resolve path safely within the storage boundary."""
|
|
1690
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
1691
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
1692
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
1693
|
+
return resolved
|
|
1694
|
+
|
|
1695
|
+
@tool
|
|
1696
|
+
def file_read(path: str) -> str:
|
|
1697
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
1698
|
+
try:
|
|
1699
|
+
full_path = _safe_resolve(path)
|
|
1700
|
+
with open(full_path) as f:
|
|
1701
|
+
return f.read()
|
|
1702
|
+
except ValueError as e:
|
|
1703
|
+
return str(e)
|
|
1704
|
+
except OSError as e:
|
|
1705
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
1706
|
+
|
|
1707
|
+
@tool
|
|
1708
|
+
def file_write(path: str, content: str) -> str:
|
|
1709
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
1710
|
+
try:
|
|
1711
|
+
full_path = _safe_resolve(path)
|
|
1712
|
+
parent = os.path.dirname(full_path)
|
|
1713
|
+
if parent:
|
|
1714
|
+
os.makedirs(parent, exist_ok=True)
|
|
1715
|
+
with open(full_path, "w") as f:
|
|
1716
|
+
f.write(content)
|
|
1717
|
+
return f"Written to {path}"
|
|
1718
|
+
except ValueError as e:
|
|
1719
|
+
return str(e)
|
|
1720
|
+
except OSError as e:
|
|
1721
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
1722
|
+
|
|
1723
|
+
@tool
|
|
1724
|
+
def list_files(directory: str = "") -> str:
|
|
1725
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
1726
|
+
try:
|
|
1727
|
+
target = _safe_resolve(directory)
|
|
1728
|
+
entries = os.listdir(target)
|
|
1729
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
1730
|
+
except ValueError as e:
|
|
1731
|
+
return str(e)
|
|
1732
|
+
except OSError as e:
|
|
1733
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
1734
|
+
|
|
1735
|
+
tools.extend([file_read, file_write, list_files])
|
|
1736
|
+
{{/if}}
|
|
1737
|
+
|
|
1738
|
+
SYSTEM_PROMPT = """
|
|
1739
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
1740
|
+
{{#if sessionStorageMountPath}}
|
|
1741
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
1742
|
+
{{/if}}
|
|
1743
|
+
"""
|
|
1744
|
+
|
|
1745
|
+
{{#if hasMemory}}
|
|
1746
|
+
def agent_factory():
|
|
1747
|
+
cache = {}
|
|
1748
|
+
def get_or_create_agent(session_id, user_id):
|
|
1749
|
+
key = f"{session_id}/{user_id}"
|
|
1750
|
+
if key not in cache:
|
|
1751
|
+
cache[key] = Agent(
|
|
1752
|
+
model=load_model(),
|
|
1753
|
+
session_manager=get_memory_session_manager(session_id, user_id),
|
|
1754
|
+
system_prompt=SYSTEM_PROMPT,
|
|
1755
|
+
tools=tools,
|
|
1756
|
+
)
|
|
1757
|
+
return cache[key]
|
|
1758
|
+
return get_or_create_agent
|
|
1759
|
+
|
|
1760
|
+
get_or_create_agent = agent_factory()
|
|
1761
|
+
agent = get_or_create_agent("default-session", "default-user")
|
|
1762
|
+
{{else}}
|
|
1763
|
+
agent = Agent(
|
|
1764
|
+
model=load_model(),
|
|
1765
|
+
system_prompt=SYSTEM_PROMPT,
|
|
1766
|
+
tools=tools,
|
|
1767
|
+
)
|
|
1768
|
+
{{/if}}
|
|
1769
|
+
|
|
1770
|
+
if __name__ == "__main__":
|
|
1771
|
+
serve_a2a(StrandsA2AExecutor(agent))
|
|
1772
|
+
"
|
|
1773
|
+
`;
|
|
1774
|
+
|
|
1775
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/model/__init__.py should match snapshot 1`] = `
|
|
1776
|
+
"# Package marker
|
|
1777
|
+
"
|
|
1778
|
+
`;
|
|
1779
|
+
|
|
1780
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/model/load.py should match snapshot 1`] = `
|
|
1781
|
+
"{{#if (eq modelProvider "Bedrock")}}
|
|
1782
|
+
from strands.models.bedrock import BedrockModel
|
|
1783
|
+
|
|
1784
|
+
|
|
1785
|
+
def load_model() -> BedrockModel:
|
|
1786
|
+
"""Get Bedrock model client using IAM credentials."""
|
|
1787
|
+
return BedrockModel(model_id="global.anthropic.claude-sonnet-4-5-20250929-v1:0")
|
|
1788
|
+
{{/if}}
|
|
1789
|
+
{{#if (eq modelProvider "Anthropic")}}
|
|
1790
|
+
import os
|
|
1791
|
+
|
|
1792
|
+
from strands.models.anthropic import AnthropicModel
|
|
1793
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1794
|
+
|
|
1795
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1796
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1797
|
+
|
|
1798
|
+
|
|
1799
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1800
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1801
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1802
|
+
return api_key
|
|
1803
|
+
|
|
1804
|
+
|
|
1805
|
+
def _get_api_key() -> str:
|
|
1806
|
+
"""
|
|
1807
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1808
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1809
|
+
"""
|
|
1810
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1811
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1812
|
+
if not api_key:
|
|
1813
|
+
raise RuntimeError(
|
|
1814
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1815
|
+
)
|
|
1816
|
+
return api_key
|
|
1817
|
+
return _agentcore_identity_api_key_provider()
|
|
1818
|
+
|
|
1819
|
+
|
|
1820
|
+
def load_model() -> AnthropicModel:
|
|
1821
|
+
"""Get authenticated Anthropic model client."""
|
|
1822
|
+
return AnthropicModel(
|
|
1823
|
+
client_args={"api_key": _get_api_key()},
|
|
1824
|
+
model_id="claude-sonnet-4-5-20250929",
|
|
1825
|
+
max_tokens=5000,
|
|
1826
|
+
)
|
|
1827
|
+
{{/if}}
|
|
1828
|
+
{{#if (eq modelProvider "OpenAI")}}
|
|
1829
|
+
import os
|
|
1830
|
+
|
|
1831
|
+
from strands.models.openai import OpenAIModel
|
|
1832
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1833
|
+
|
|
1834
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1835
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1836
|
+
|
|
1837
|
+
|
|
1838
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1839
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1840
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1841
|
+
return api_key
|
|
1842
|
+
|
|
1843
|
+
|
|
1844
|
+
def _get_api_key() -> str:
|
|
1845
|
+
"""
|
|
1846
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1847
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1848
|
+
"""
|
|
1849
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1850
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1851
|
+
if not api_key:
|
|
1852
|
+
raise RuntimeError(
|
|
1853
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1854
|
+
)
|
|
1855
|
+
return api_key
|
|
1856
|
+
return _agentcore_identity_api_key_provider()
|
|
1857
|
+
|
|
1858
|
+
|
|
1859
|
+
def load_model() -> OpenAIModel:
|
|
1860
|
+
"""Get authenticated OpenAI model client."""
|
|
1861
|
+
return OpenAIModel(
|
|
1862
|
+
client_args={"api_key": _get_api_key()},
|
|
1863
|
+
model_id="gpt-4.1",
|
|
1864
|
+
)
|
|
1865
|
+
{{/if}}
|
|
1866
|
+
{{#if (eq modelProvider "Gemini")}}
|
|
1867
|
+
import os
|
|
1868
|
+
|
|
1869
|
+
from strands.models.gemini import GeminiModel
|
|
1870
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1871
|
+
|
|
1872
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
1873
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
1874
|
+
|
|
1875
|
+
|
|
1876
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
1877
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
1878
|
+
"""Fetch API key from AgentCore Identity."""
|
|
1879
|
+
return api_key
|
|
1880
|
+
|
|
1881
|
+
|
|
1882
|
+
def _get_api_key() -> str:
|
|
1883
|
+
"""
|
|
1884
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
1885
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
1886
|
+
"""
|
|
1887
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
1888
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
1889
|
+
if not api_key:
|
|
1890
|
+
raise RuntimeError(
|
|
1891
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
1892
|
+
)
|
|
1893
|
+
return api_key
|
|
1894
|
+
return _agentcore_identity_api_key_provider()
|
|
1895
|
+
|
|
1896
|
+
|
|
1897
|
+
def load_model() -> GeminiModel:
|
|
1898
|
+
"""Get authenticated Gemini model client."""
|
|
1899
|
+
return GeminiModel(
|
|
1900
|
+
client_args={"api_key": _get_api_key()},
|
|
1901
|
+
model_id="gemini-2.5-flash",
|
|
1902
|
+
)
|
|
1903
|
+
{{/if}}
|
|
1904
|
+
"
|
|
1905
|
+
`;
|
|
1906
|
+
|
|
1907
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/pyproject.toml should match snapshot 1`] = `
|
|
1908
|
+
"[build-system]
|
|
1909
|
+
requires = ["hatchling"]
|
|
1910
|
+
build-backend = "hatchling.build"
|
|
1911
|
+
|
|
1912
|
+
[project]
|
|
1913
|
+
name = "{{ name }}"
|
|
1914
|
+
version = "0.1.0"
|
|
1915
|
+
description = "AgentCore A2A Agent using Strands SDK"
|
|
1916
|
+
readme = "README.md"
|
|
1917
|
+
requires-python = ">=3.10"
|
|
1918
|
+
dependencies = [
|
|
1919
|
+
{{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
|
|
1920
|
+
{{/if}}"a2a-sdk[all] >= 0.2.0",
|
|
1921
|
+
"aws-opentelemetry-distro",
|
|
1922
|
+
"bedrock-agentcore[a2a] >= 1.0.3",
|
|
1923
|
+
"botocore[crt] >= 1.35.0",
|
|
1924
|
+
{{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
|
|
1925
|
+
{{/if}}{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
|
|
1926
|
+
{{/if}}"strands-agents >= 1.13.0",
|
|
1927
|
+
]
|
|
1928
|
+
|
|
1929
|
+
[tool.hatch.build.targets.wheel]
|
|
1930
|
+
packages = ["."]
|
|
1931
|
+
"
|
|
1932
|
+
`;
|
|
1933
|
+
|
|
1934
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/capabilities/memory/__init__.py should match snapshot 1`] = `
|
|
1935
|
+
"# Package marker
|
|
1936
|
+
"
|
|
1937
|
+
`;
|
|
1938
|
+
|
|
1939
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/capabilities/memory/session.py should match snapshot 1`] = `
|
|
1940
|
+
"import os
|
|
1941
|
+
from typing import Optional
|
|
1942
|
+
|
|
1943
|
+
from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig{{#if memoryProviders.[0].strategies.length}}, RetrievalConfig{{/if}}
|
|
1944
|
+
from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager
|
|
1945
|
+
|
|
1946
|
+
MEMORY_ID = os.getenv("{{memoryProviders.[0].envVarName}}")
|
|
1947
|
+
REGION = os.getenv("AWS_REGION")
|
|
1948
|
+
|
|
1949
|
+
def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
|
|
1950
|
+
if not MEMORY_ID:
|
|
1951
|
+
return None
|
|
1952
|
+
|
|
1953
|
+
{{#if memoryProviders.[0].strategies.length}}
|
|
1954
|
+
retrieval_config = {
|
|
1955
|
+
{{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}
|
|
1956
|
+
f"/users/{actor_id}/facts": RetrievalConfig(top_k=3, relevance_score=0.5),
|
|
1957
|
+
{{/if}}
|
|
1958
|
+
{{#if (includes memoryProviders.[0].strategies "USER_PREFERENCE")}}
|
|
1959
|
+
f"/users/{actor_id}/preferences": RetrievalConfig(top_k=3, relevance_score=0.5),
|
|
1960
|
+
{{/if}}
|
|
1961
|
+
{{#if (includes memoryProviders.[0].strategies "SUMMARIZATION")}}
|
|
1962
|
+
f"/summaries/{actor_id}/{session_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
|
|
1963
|
+
{{/if}}
|
|
1964
|
+
}
|
|
1965
|
+
{{/if}}
|
|
1966
|
+
|
|
1967
|
+
return AgentCoreMemorySessionManager(
|
|
1968
|
+
AgentCoreMemoryConfig(
|
|
1969
|
+
memory_id=MEMORY_ID,
|
|
1970
|
+
session_id=session_id,
|
|
1971
|
+
actor_id=actor_id,
|
|
1972
|
+
{{#if memoryProviders.[0].strategies.length}}
|
|
1973
|
+
retrieval_config=retrieval_config,
|
|
1974
|
+
{{/if}}
|
|
1975
|
+
),
|
|
1976
|
+
REGION
|
|
1977
|
+
)
|
|
1978
|
+
"
|
|
1979
|
+
`;
|
|
1980
|
+
|
|
1981
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/README.md should match snapshot 1`] = `
|
|
1982
|
+
"# {{ name }}
|
|
1983
|
+
|
|
1984
|
+
An AG-UI agent deployed on Amazon Bedrock AgentCore using Google ADK.
|
|
1985
|
+
|
|
1986
|
+
## Overview
|
|
1987
|
+
|
|
1988
|
+
This agent implements the AG-UI protocol using Google's Agent Development Kit, enabling rich agent-user interaction via the AG-UI event stream.
|
|
1989
|
+
|
|
1990
|
+
## Local Development
|
|
1991
|
+
|
|
1992
|
+
\`\`\`bash
|
|
1993
|
+
uv sync
|
|
1994
|
+
uv run python main.py
|
|
1995
|
+
\`\`\`
|
|
1996
|
+
|
|
1997
|
+
The agent starts on port 8080 and serves requests at \`/invocations\`.
|
|
1998
|
+
|
|
1999
|
+
## Health Check
|
|
2000
|
+
|
|
2001
|
+
\`\`\`
|
|
2002
|
+
GET /ping
|
|
913
2003
|
\`\`\`
|
|
914
2004
|
|
|
915
|
-
|
|
2005
|
+
Returns \`{"status": "healthy"}\`.
|
|
916
2006
|
|
|
917
2007
|
## Deploy
|
|
918
2008
|
|
|
@@ -922,7 +2012,7 @@ agentcore deploy
|
|
|
922
2012
|
"
|
|
923
2013
|
`;
|
|
924
2014
|
|
|
925
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2015
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/gitignore.template should match snapshot 1`] = `
|
|
926
2016
|
"# Environment variables
|
|
927
2017
|
.env
|
|
928
2018
|
|
|
@@ -967,64 +2057,47 @@ Thumbs.db
|
|
|
967
2057
|
"
|
|
968
2058
|
`;
|
|
969
2059
|
|
|
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
|
|
2060
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/main.py should match snapshot 1`] = `
|
|
2061
|
+
"import os
|
|
2062
|
+
import uvicorn
|
|
2063
|
+
from google.adk.agents import LlmAgent
|
|
2064
|
+
from ag_ui_adk import ADKAgent, AGUIToolset, create_adk_app
|
|
977
2065
|
from model.load import load_model
|
|
978
2066
|
|
|
2067
|
+
load_model()
|
|
979
2068
|
|
|
980
|
-
|
|
981
|
-
"""Return the sum of two numbers."""
|
|
982
|
-
return a + b
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
agent = Agent(
|
|
986
|
-
model=load_model(),
|
|
2069
|
+
agent = LlmAgent(
|
|
987
2070
|
name="{{ name }}",
|
|
988
|
-
|
|
989
|
-
instruction="You are a helpful assistant.
|
|
990
|
-
tools=[
|
|
2071
|
+
model="gemini-2.5-flash",
|
|
2072
|
+
instruction="You are a helpful assistant.",
|
|
2073
|
+
tools=[AGUIToolset()],
|
|
991
2074
|
)
|
|
992
2075
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
2076
|
+
adk_agent = ADKAgent(
|
|
2077
|
+
adk_agent=agent,
|
|
2078
|
+
app_name="{{ name }}",
|
|
2079
|
+
use_in_memory_services=True,
|
|
997
2080
|
)
|
|
998
2081
|
|
|
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
|
-
)
|
|
2082
|
+
app = create_adk_app(adk_agent, path="/invocations")
|
|
2083
|
+
|
|
2084
|
+
|
|
2085
|
+
@app.get("/ping")
|
|
2086
|
+
async def ping():
|
|
2087
|
+
return {"status": "healthy"}
|
|
2088
|
+
|
|
1016
2089
|
|
|
1017
2090
|
if __name__ == "__main__":
|
|
1018
|
-
|
|
2091
|
+
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))
|
|
1019
2092
|
"
|
|
1020
2093
|
`;
|
|
1021
2094
|
|
|
1022
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2095
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/model/__init__.py should match snapshot 1`] = `
|
|
1023
2096
|
"# Package marker
|
|
1024
2097
|
"
|
|
1025
2098
|
`;
|
|
1026
2099
|
|
|
1027
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2100
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/model/load.py should match snapshot 1`] = `
|
|
1028
2101
|
"import os
|
|
1029
2102
|
from bedrock_agentcore.identity.auth import requires_api_key
|
|
1030
2103
|
|
|
@@ -1069,7 +2142,7 @@ def load_model() -> None:
|
|
|
1069
2142
|
"
|
|
1070
2143
|
`;
|
|
1071
2144
|
|
|
1072
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2145
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/pyproject.toml should match snapshot 1`] = `
|
|
1073
2146
|
"[build-system]
|
|
1074
2147
|
requires = ["hatchling"]
|
|
1075
2148
|
build-backend = "hatchling.build"
|
|
@@ -1077,15 +2150,19 @@ build-backend = "hatchling.build"
|
|
|
1077
2150
|
[project]
|
|
1078
2151
|
name = "{{ name }}"
|
|
1079
2152
|
version = "0.1.0"
|
|
1080
|
-
description = "AgentCore
|
|
2153
|
+
description = "AgentCore AG-UI Agent using Google ADK"
|
|
1081
2154
|
readme = "README.md"
|
|
1082
2155
|
requires-python = ">=3.10"
|
|
1083
2156
|
dependencies = [
|
|
1084
|
-
"
|
|
1085
|
-
"
|
|
1086
|
-
"bedrock-agentcore
|
|
1087
|
-
"
|
|
2157
|
+
"ag-ui-adk >= 0.6.0",
|
|
2158
|
+
"ag-ui-protocol >= 0.1.10",
|
|
2159
|
+
"bedrock-agentcore >= 1.0.3",
|
|
2160
|
+
"fastapi >= 0.115.12",
|
|
2161
|
+
"google-adk >= 1.16.0",
|
|
1088
2162
|
"google-genai >= 1.0.0",
|
|
2163
|
+
"opentelemetry-distro",
|
|
2164
|
+
"opentelemetry-exporter-otlp",
|
|
2165
|
+
"uvicorn >= 0.34.3",
|
|
1089
2166
|
]
|
|
1090
2167
|
|
|
1091
2168
|
[tool.hatch.build.targets.wheel]
|
|
@@ -1093,14 +2170,14 @@ packages = ["."]
|
|
|
1093
2170
|
"
|
|
1094
2171
|
`;
|
|
1095
2172
|
|
|
1096
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2173
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/README.md should match snapshot 1`] = `
|
|
1097
2174
|
"# {{ name }}
|
|
1098
2175
|
|
|
1099
|
-
An
|
|
2176
|
+
An AG-UI agent deployed on Amazon Bedrock AgentCore using LangChain + LangGraph.
|
|
1100
2177
|
|
|
1101
2178
|
## Overview
|
|
1102
2179
|
|
|
1103
|
-
This agent implements the
|
|
2180
|
+
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
2181
|
|
|
1105
2182
|
## Local Development
|
|
1106
2183
|
|
|
@@ -1109,7 +2186,7 @@ uv sync
|
|
|
1109
2186
|
uv run python main.py
|
|
1110
2187
|
\`\`\`
|
|
1111
2188
|
|
|
1112
|
-
The agent starts on port
|
|
2189
|
+
The agent starts on port 8080.
|
|
1113
2190
|
|
|
1114
2191
|
## Deploy
|
|
1115
2192
|
|
|
@@ -1119,7 +2196,7 @@ agentcore deploy
|
|
|
1119
2196
|
"
|
|
1120
2197
|
`;
|
|
1121
2198
|
|
|
1122
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2199
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/gitignore.template should match snapshot 1`] = `
|
|
1123
2200
|
"# Environment variables
|
|
1124
2201
|
.env
|
|
1125
2202
|
|
|
@@ -1164,16 +2241,22 @@ Thumbs.db
|
|
|
1164
2241
|
"
|
|
1165
2242
|
`;
|
|
1166
2243
|
|
|
1167
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
1168
|
-
"
|
|
1169
|
-
|
|
2244
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/main.py should match snapshot 1`] = `
|
|
2245
|
+
"import os
|
|
2246
|
+
|
|
2247
|
+
os.environ["LANGGRAPH_FAST_API"] = "true"
|
|
2248
|
+
|
|
2249
|
+
import uvicorn
|
|
2250
|
+
from typing import Any, List
|
|
2251
|
+
from fastapi import FastAPI
|
|
2252
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
2253
|
+
from langgraph.graph import StateGraph, START
|
|
2254
|
+
from langgraph.graph.message import MessagesState
|
|
2255
|
+
from langgraph.checkpoint.memory import MemorySaver
|
|
2256
|
+
from langgraph.prebuilt import ToolNode, tools_condition
|
|
2257
|
+
from langchain_core.tools import tool
|
|
1170
2258
|
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
|
|
2259
|
+
from ag_ui_langgraph import LangGraphAgent, add_langgraph_fastapi_endpoint
|
|
1177
2260
|
from model.load import load_model
|
|
1178
2261
|
|
|
1179
2262
|
LangchainInstrumentor().instrument()
|
|
@@ -1185,62 +2268,63 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
1185
2268
|
return a + b
|
|
1186
2269
|
|
|
1187
2270
|
|
|
2271
|
+
backend_tools = [add_numbers]
|
|
1188
2272
|
model = load_model()
|
|
1189
|
-
graph = create_react_agent(model, tools=[add_numbers])
|
|
1190
2273
|
|
|
1191
2274
|
|
|
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)
|
|
2275
|
+
class AgentState(MessagesState):
|
|
2276
|
+
tools: List[Any]
|
|
1203
2277
|
|
|
1204
|
-
user_text = context.get_user_input()
|
|
1205
|
-
result = await self.graph.ainvoke({"messages": [("user", user_text)]})
|
|
1206
|
-
response = result["messages"][-1].content
|
|
1207
2278
|
|
|
1208
|
-
|
|
1209
|
-
|
|
2279
|
+
def chat_node(state: AgentState):
|
|
2280
|
+
bound_model = model.bind_tools(
|
|
2281
|
+
[*state.get("tools", []), *backend_tools],
|
|
2282
|
+
)
|
|
2283
|
+
response = bound_model.invoke(state["messages"])
|
|
2284
|
+
return {"messages": [response]}
|
|
1210
2285
|
|
|
1211
|
-
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
1212
|
-
pass
|
|
1213
2286
|
|
|
2287
|
+
builder = StateGraph(AgentState)
|
|
2288
|
+
builder.add_node("chat", chat_node)
|
|
2289
|
+
builder.add_node("tools", ToolNode(tools=backend_tools))
|
|
2290
|
+
builder.add_edge(START, "chat")
|
|
2291
|
+
builder.add_conditional_edges("chat", tools_condition)
|
|
2292
|
+
builder.add_edge("tools", "chat")
|
|
2293
|
+
graph = builder.compile(checkpointer=MemorySaver())
|
|
1214
2294
|
|
|
1215
|
-
|
|
2295
|
+
agent = LangGraphAgent(
|
|
1216
2296
|
name="{{ name }}",
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
)
|
|
1228
|
-
],
|
|
1229
|
-
default_input_modes=["text"],
|
|
1230
|
-
default_output_modes=["text"],
|
|
2297
|
+
graph=graph,
|
|
2298
|
+
description="A helpful assistant",
|
|
2299
|
+
)
|
|
2300
|
+
|
|
2301
|
+
app = FastAPI()
|
|
2302
|
+
app.add_middleware(
|
|
2303
|
+
CORSMiddleware,
|
|
2304
|
+
allow_origins=["*"],
|
|
2305
|
+
allow_methods=["*"],
|
|
2306
|
+
allow_headers=["*"],
|
|
1231
2307
|
)
|
|
1232
2308
|
|
|
2309
|
+
add_langgraph_fastapi_endpoint(app=app, agent=agent, path="/invocations")
|
|
2310
|
+
|
|
2311
|
+
|
|
2312
|
+
@app.get("/ping")
|
|
2313
|
+
async def ping():
|
|
2314
|
+
return {"status": "healthy"}
|
|
2315
|
+
|
|
2316
|
+
|
|
1233
2317
|
if __name__ == "__main__":
|
|
1234
|
-
|
|
2318
|
+
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))
|
|
1235
2319
|
"
|
|
1236
2320
|
`;
|
|
1237
2321
|
|
|
1238
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2322
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/model/__init__.py should match snapshot 1`] = `
|
|
1239
2323
|
"# Package marker
|
|
1240
2324
|
"
|
|
1241
2325
|
`;
|
|
1242
2326
|
|
|
1243
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2327
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/model/load.py should match snapshot 1`] = `
|
|
1244
2328
|
"{{#if (eq modelProvider "Bedrock")}}
|
|
1245
2329
|
from langchain_aws import ChatBedrock
|
|
1246
2330
|
|
|
@@ -1367,7 +2451,7 @@ def load_model() -> ChatGoogleGenerativeAI:
|
|
|
1367
2451
|
"
|
|
1368
2452
|
`;
|
|
1369
2453
|
|
|
1370
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2454
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/pyproject.toml should match snapshot 1`] = `
|
|
1371
2455
|
"[build-system]
|
|
1372
2456
|
requires = ["hatchling"]
|
|
1373
2457
|
build-backend = "hatchling.build"
|
|
@@ -1375,20 +2459,25 @@ build-backend = "hatchling.build"
|
|
|
1375
2459
|
[project]
|
|
1376
2460
|
name = "{{ name }}"
|
|
1377
2461
|
version = "0.1.0"
|
|
1378
|
-
description = "AgentCore
|
|
2462
|
+
description = "AgentCore AG-UI Agent using LangChain + LangGraph"
|
|
1379
2463
|
readme = "README.md"
|
|
1380
2464
|
requires-python = ">=3.10"
|
|
1381
2465
|
dependencies = [
|
|
1382
|
-
"
|
|
2466
|
+
"ag-ui-langgraph >= 0.0.31",
|
|
2467
|
+
"ag-ui-protocol >= 0.1.10",
|
|
1383
2468
|
{{#if (eq modelProvider "Anthropic")}}"langchain-anthropic >= 0.3.0",
|
|
1384
2469
|
{{/if}}{{#if (eq modelProvider "Bedrock")}}"langchain-aws >= 0.2.0",
|
|
1385
2470
|
{{/if}}{{#if (eq modelProvider "Gemini")}}"langchain-google-genai >= 2.0.0",
|
|
1386
2471
|
{{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
|
|
1387
2472
|
{{/if}}"aws-opentelemetry-distro",
|
|
1388
2473
|
"opentelemetry-instrumentation-langchain >= 0.59.0",
|
|
1389
|
-
"bedrock-agentcore
|
|
2474
|
+
"bedrock-agentcore >= 1.0.3",
|
|
1390
2475
|
"botocore[crt] >= 1.35.0",
|
|
1391
|
-
"langgraph >= 0.
|
|
2476
|
+
"langgraph >= 0.3.25",
|
|
2477
|
+
"langchain >= 0.3.0",
|
|
2478
|
+
"langchain-core >= 0.3.0",
|
|
2479
|
+
"fastapi >= 0.115.12",
|
|
2480
|
+
"uvicorn >= 0.34.3",
|
|
1392
2481
|
]
|
|
1393
2482
|
|
|
1394
2483
|
[tool.hatch.build.targets.wheel]
|
|
@@ -1396,14 +2485,14 @@ packages = ["."]
|
|
|
1396
2485
|
"
|
|
1397
2486
|
`;
|
|
1398
2487
|
|
|
1399
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2488
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/README.md should match snapshot 1`] = `
|
|
1400
2489
|
"# {{ name }}
|
|
1401
2490
|
|
|
1402
|
-
An
|
|
2491
|
+
An AG-UI agent deployed on Amazon Bedrock AgentCore using Strands SDK.
|
|
1403
2492
|
|
|
1404
2493
|
## Overview
|
|
1405
2494
|
|
|
1406
|
-
This agent implements the
|
|
2495
|
+
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
2496
|
|
|
1408
2497
|
## Local Development
|
|
1409
2498
|
|
|
@@ -1412,7 +2501,7 @@ uv sync
|
|
|
1412
2501
|
uv run python main.py
|
|
1413
2502
|
\`\`\`
|
|
1414
2503
|
|
|
1415
|
-
The agent starts on port
|
|
2504
|
+
The agent starts on port 8080.
|
|
1416
2505
|
|
|
1417
2506
|
## Deploy
|
|
1418
2507
|
|
|
@@ -1422,7 +2511,7 @@ agentcore deploy
|
|
|
1422
2511
|
"
|
|
1423
2512
|
`;
|
|
1424
2513
|
|
|
1425
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2514
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/gitignore.template should match snapshot 1`] = `
|
|
1426
2515
|
"# Environment variables
|
|
1427
2516
|
.env
|
|
1428
2517
|
|
|
@@ -1467,10 +2556,16 @@ Thumbs.db
|
|
|
1467
2556
|
"
|
|
1468
2557
|
`;
|
|
1469
2558
|
|
|
1470
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
1471
|
-
"
|
|
1472
|
-
|
|
1473
|
-
|
|
2559
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/main.py should match snapshot 1`] = `
|
|
2560
|
+
"import os
|
|
2561
|
+
|
|
2562
|
+
# Suppress OpenTelemetry warnings during local development; remove for production
|
|
2563
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
2564
|
+
os.environ["OTEL_SDK_DISABLED"] = "true"
|
|
2565
|
+
|
|
2566
|
+
import uvicorn
|
|
2567
|
+
from strands import Agent, tool
|
|
2568
|
+
from ag_ui_strands import StrandsAgent, StrandsAgentConfig, create_strands_app
|
|
1474
2569
|
from model.load import load_model
|
|
1475
2570
|
{{#if hasMemory}}
|
|
1476
2571
|
from memory.session import get_memory_session_manager
|
|
@@ -1485,42 +2580,35 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
1485
2580
|
|
|
1486
2581
|
tools = [add_numbers]
|
|
1487
2582
|
|
|
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
2583
|
agent = Agent(
|
|
1507
2584
|
model=load_model(),
|
|
1508
2585
|
system_prompt="You are a helpful assistant. Use tools when appropriate.",
|
|
1509
2586
|
tools=tools,
|
|
1510
2587
|
)
|
|
2588
|
+
|
|
2589
|
+
{{#if hasMemory}}
|
|
2590
|
+
def session_manager_provider(input_data):
|
|
2591
|
+
return get_memory_session_manager(input_data.thread_id, "default-user")
|
|
2592
|
+
|
|
2593
|
+
config = StrandsAgentConfig(session_manager_provider=session_manager_provider)
|
|
2594
|
+
{{else}}
|
|
2595
|
+
config = StrandsAgentConfig()
|
|
1511
2596
|
{{/if}}
|
|
1512
2597
|
|
|
2598
|
+
agui_agent = StrandsAgent(agent=agent, name="{{ name }}", description="A helpful assistant", config=config)
|
|
2599
|
+
app = create_strands_app(agui_agent, path="/invocations", ping_path="/ping")
|
|
2600
|
+
|
|
1513
2601
|
if __name__ == "__main__":
|
|
1514
|
-
|
|
2602
|
+
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))
|
|
1515
2603
|
"
|
|
1516
2604
|
`;
|
|
1517
2605
|
|
|
1518
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2606
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/model/__init__.py should match snapshot 1`] = `
|
|
1519
2607
|
"# Package marker
|
|
1520
2608
|
"
|
|
1521
2609
|
`;
|
|
1522
2610
|
|
|
1523
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2611
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/model/load.py should match snapshot 1`] = `
|
|
1524
2612
|
"{{#if (eq modelProvider "Bedrock")}}
|
|
1525
2613
|
from strands.models.bedrock import BedrockModel
|
|
1526
2614
|
|
|
@@ -1647,7 +2735,7 @@ def load_model() -> GeminiModel:
|
|
|
1647
2735
|
"
|
|
1648
2736
|
`;
|
|
1649
2737
|
|
|
1650
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2738
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/pyproject.toml should match snapshot 1`] = `
|
|
1651
2739
|
"[build-system]
|
|
1652
2740
|
requires = ["hatchling"]
|
|
1653
2741
|
build-backend = "hatchling.build"
|
|
@@ -1655,18 +2743,22 @@ build-backend = "hatchling.build"
|
|
|
1655
2743
|
[project]
|
|
1656
2744
|
name = "{{ name }}"
|
|
1657
2745
|
version = "0.1.0"
|
|
1658
|
-
description = "AgentCore
|
|
2746
|
+
description = "AgentCore AG-UI Agent using Strands SDK"
|
|
1659
2747
|
readme = "README.md"
|
|
1660
|
-
requires-python = ">=3.
|
|
2748
|
+
requires-python = ">=3.12"
|
|
1661
2749
|
dependencies = [
|
|
1662
2750
|
{{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
|
|
1663
|
-
{{/if}}"
|
|
2751
|
+
{{/if}}"ag-ui-strands >= 0.1.7",
|
|
2752
|
+
"ag-ui-protocol >= 0.1.10",
|
|
1664
2753
|
"aws-opentelemetry-distro",
|
|
1665
|
-
"bedrock-agentcore
|
|
2754
|
+
"bedrock-agentcore >= 1.0.3",
|
|
1666
2755
|
"botocore[crt] >= 1.35.0",
|
|
2756
|
+
"fastapi >= 0.115.12",
|
|
1667
2757
|
{{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
|
|
1668
2758
|
{{/if}}{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
|
|
1669
|
-
{{/if}}"strands-agents >= 1.
|
|
2759
|
+
{{/if}}"strands-agents >= 1.15.0",
|
|
2760
|
+
"strands-agents-tools >= 0.2.14",
|
|
2761
|
+
"uvicorn >= 0.34.3",
|
|
1670
2762
|
]
|
|
1671
2763
|
|
|
1672
2764
|
[tool.hatch.build.targets.wheel]
|
|
@@ -1674,12 +2766,12 @@ packages = ["."]
|
|
|
1674
2766
|
"
|
|
1675
2767
|
`;
|
|
1676
2768
|
|
|
1677
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2769
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/capabilities/memory/__init__.py should match snapshot 1`] = `
|
|
1678
2770
|
"# Package marker
|
|
1679
2771
|
"
|
|
1680
2772
|
`;
|
|
1681
2773
|
|
|
1682
|
-
exports[`Assets Directory Snapshots > Python framework assets > python/python/
|
|
2774
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/capabilities/memory/session.py should match snapshot 1`] = `
|
|
1683
2775
|
"import os
|
|
1684
2776
|
from typing import Optional
|
|
1685
2777
|
|
|
@@ -1834,6 +2926,66 @@ add_numbers_tool = FunctionTool(
|
|
|
1834
2926
|
# Define a collection of tools used by the model
|
|
1835
2927
|
tools = [add_numbers_tool]
|
|
1836
2928
|
|
|
2929
|
+
{{#if sessionStorageMountPath}}
|
|
2930
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
2931
|
+
|
|
2932
|
+
def _safe_resolve(path: str) -> str:
|
|
2933
|
+
"""Resolve path safely within the storage boundary."""
|
|
2934
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
2935
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
2936
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
2937
|
+
return resolved
|
|
2938
|
+
|
|
2939
|
+
def file_read(path: str) -> str:
|
|
2940
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
2941
|
+
try:
|
|
2942
|
+
full_path = _safe_resolve(path)
|
|
2943
|
+
with open(full_path) as f:
|
|
2944
|
+
return f.read()
|
|
2945
|
+
except ValueError as e:
|
|
2946
|
+
return str(e)
|
|
2947
|
+
except OSError as e:
|
|
2948
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
2949
|
+
|
|
2950
|
+
def file_write(path: str, content: str) -> str:
|
|
2951
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
2952
|
+
try:
|
|
2953
|
+
full_path = _safe_resolve(path)
|
|
2954
|
+
parent = os.path.dirname(full_path)
|
|
2955
|
+
if parent:
|
|
2956
|
+
os.makedirs(parent, exist_ok=True)
|
|
2957
|
+
with open(full_path, "w") as f:
|
|
2958
|
+
f.write(content)
|
|
2959
|
+
return f"Written to {path}"
|
|
2960
|
+
except ValueError as e:
|
|
2961
|
+
return str(e)
|
|
2962
|
+
except OSError as e:
|
|
2963
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
2964
|
+
|
|
2965
|
+
def list_files(directory: str = "") -> str:
|
|
2966
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
2967
|
+
try:
|
|
2968
|
+
target = _safe_resolve(directory)
|
|
2969
|
+
entries = os.listdir(target)
|
|
2970
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
2971
|
+
except ValueError as e:
|
|
2972
|
+
return str(e)
|
|
2973
|
+
except OSError as e:
|
|
2974
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
2975
|
+
|
|
2976
|
+
tools.extend([
|
|
2977
|
+
FunctionTool(file_read, description="Read a file from persistent storage. The path is relative to the storage root."),
|
|
2978
|
+
FunctionTool(file_write, description="Write content to a file in persistent storage. The path is relative to the storage root."),
|
|
2979
|
+
FunctionTool(list_files, description="List files in persistent storage. The directory is relative to the storage root."),
|
|
2980
|
+
])
|
|
2981
|
+
{{/if}}
|
|
2982
|
+
|
|
2983
|
+
SYSTEM_MESSAGE = """
|
|
2984
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
2985
|
+
{{#if sessionStorageMountPath}}
|
|
2986
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
2987
|
+
{{/if}}
|
|
2988
|
+
"""
|
|
1837
2989
|
|
|
1838
2990
|
@app.entrypoint
|
|
1839
2991
|
async def invoke(payload, context):
|
|
@@ -1847,7 +2999,7 @@ async def invoke(payload, context):
|
|
|
1847
2999
|
name="{{ name }}",
|
|
1848
3000
|
model_client=load_model(),
|
|
1849
3001
|
tools=tools + mcp_tools,
|
|
1850
|
-
system_message=
|
|
3002
|
+
system_message=SYSTEM_MESSAGE,
|
|
1851
3003
|
)
|
|
1852
3004
|
|
|
1853
3005
|
# Process the user prompt
|
|
@@ -2202,6 +3354,66 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
2202
3354
|
return a + b
|
|
2203
3355
|
|
|
2204
3356
|
|
|
3357
|
+
# Define a collection of tools used by the model
|
|
3358
|
+
tools = [add_numbers]
|
|
3359
|
+
|
|
3360
|
+
{{#if sessionStorageMountPath}}
|
|
3361
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
3362
|
+
|
|
3363
|
+
def _safe_resolve(path: str) -> str:
|
|
3364
|
+
"""Resolve path safely within the storage boundary."""
|
|
3365
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
3366
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
3367
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
3368
|
+
return resolved
|
|
3369
|
+
|
|
3370
|
+
def file_read(path: str) -> str:
|
|
3371
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
3372
|
+
try:
|
|
3373
|
+
full_path = _safe_resolve(path)
|
|
3374
|
+
with open(full_path) as f:
|
|
3375
|
+
return f.read()
|
|
3376
|
+
except ValueError as e:
|
|
3377
|
+
return str(e)
|
|
3378
|
+
except OSError as e:
|
|
3379
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
3380
|
+
|
|
3381
|
+
def file_write(path: str, content: str) -> str:
|
|
3382
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
3383
|
+
try:
|
|
3384
|
+
full_path = _safe_resolve(path)
|
|
3385
|
+
parent = os.path.dirname(full_path)
|
|
3386
|
+
if parent:
|
|
3387
|
+
os.makedirs(parent, exist_ok=True)
|
|
3388
|
+
with open(full_path, "w") as f:
|
|
3389
|
+
f.write(content)
|
|
3390
|
+
return f"Written to {path}"
|
|
3391
|
+
except ValueError as e:
|
|
3392
|
+
return str(e)
|
|
3393
|
+
except OSError as e:
|
|
3394
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
3395
|
+
|
|
3396
|
+
def list_files(directory: str = "") -> str:
|
|
3397
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
3398
|
+
try:
|
|
3399
|
+
target = _safe_resolve(directory)
|
|
3400
|
+
entries = os.listdir(target)
|
|
3401
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
3402
|
+
except ValueError as e:
|
|
3403
|
+
return str(e)
|
|
3404
|
+
except OSError as e:
|
|
3405
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
3406
|
+
|
|
3407
|
+
tools.extend([file_read, file_write, list_files])
|
|
3408
|
+
{{/if}}
|
|
3409
|
+
|
|
3410
|
+
AGENT_INSTRUCTION = """
|
|
3411
|
+
I can answer your questions using the knowledge I have!
|
|
3412
|
+
{{#if sessionStorageMountPath}}
|
|
3413
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
3414
|
+
{{/if}}
|
|
3415
|
+
"""
|
|
3416
|
+
|
|
2205
3417
|
# Get MCP Toolset
|
|
2206
3418
|
{{#if hasGateway}}
|
|
2207
3419
|
mcp_toolset = get_all_gateway_mcp_toolsets()
|
|
@@ -2224,8 +3436,8 @@ agent = Agent(
|
|
|
2224
3436
|
model=MODEL_ID,
|
|
2225
3437
|
name="{{ name }}",
|
|
2226
3438
|
description="Agent to answer questions",
|
|
2227
|
-
instruction=
|
|
2228
|
-
tools=mcp_toolset +
|
|
3439
|
+
instruction=AGENT_INSTRUCTION,
|
|
3440
|
+
tools=mcp_toolset + tools,
|
|
2229
3441
|
)
|
|
2230
3442
|
|
|
2231
3443
|
|
|
@@ -2563,6 +3775,66 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
2563
3775
|
# Define a collection of tools used by the model
|
|
2564
3776
|
tools = [add_numbers]
|
|
2565
3777
|
|
|
3778
|
+
{{#if sessionStorageMountPath}}
|
|
3779
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
3780
|
+
|
|
3781
|
+
def _safe_resolve(path: str) -> str:
|
|
3782
|
+
"""Resolve path safely within the storage boundary."""
|
|
3783
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
3784
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
3785
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
3786
|
+
return resolved
|
|
3787
|
+
|
|
3788
|
+
@tool
|
|
3789
|
+
def file_read(path: str) -> str:
|
|
3790
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
3791
|
+
try:
|
|
3792
|
+
full_path = _safe_resolve(path)
|
|
3793
|
+
with open(full_path) as f:
|
|
3794
|
+
return f.read()
|
|
3795
|
+
except ValueError as e:
|
|
3796
|
+
return str(e)
|
|
3797
|
+
except OSError as e:
|
|
3798
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
3799
|
+
|
|
3800
|
+
@tool
|
|
3801
|
+
def file_write(path: str, content: str) -> str:
|
|
3802
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
3803
|
+
try:
|
|
3804
|
+
full_path = _safe_resolve(path)
|
|
3805
|
+
parent = os.path.dirname(full_path)
|
|
3806
|
+
if parent:
|
|
3807
|
+
os.makedirs(parent, exist_ok=True)
|
|
3808
|
+
with open(full_path, "w") as f:
|
|
3809
|
+
f.write(content)
|
|
3810
|
+
return f"Written to {path}"
|
|
3811
|
+
except ValueError as e:
|
|
3812
|
+
return str(e)
|
|
3813
|
+
except OSError as e:
|
|
3814
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
3815
|
+
|
|
3816
|
+
@tool
|
|
3817
|
+
def list_files(directory: str = "") -> str:
|
|
3818
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
3819
|
+
try:
|
|
3820
|
+
target = _safe_resolve(directory)
|
|
3821
|
+
entries = os.listdir(target)
|
|
3822
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
3823
|
+
except ValueError as e:
|
|
3824
|
+
return str(e)
|
|
3825
|
+
except OSError as e:
|
|
3826
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
3827
|
+
|
|
3828
|
+
tools.extend([file_read, file_write, list_files])
|
|
3829
|
+
{{/if}}
|
|
3830
|
+
|
|
3831
|
+
SYSTEM_PROMPT = """
|
|
3832
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
3833
|
+
{{#if sessionStorageMountPath}}
|
|
3834
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
3835
|
+
{{/if}}
|
|
3836
|
+
"""
|
|
3837
|
+
|
|
2566
3838
|
|
|
2567
3839
|
@app.entrypoint
|
|
2568
3840
|
async def invoke(payload, context):
|
|
@@ -2581,7 +3853,7 @@ async def invoke(payload, context):
|
|
|
2581
3853
|
mcp_tools = await mcp_client.get_tools()
|
|
2582
3854
|
|
|
2583
3855
|
# Define the agent using create_react_agent
|
|
2584
|
-
graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools)
|
|
3856
|
+
graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools, prompt=SYSTEM_PROMPT)
|
|
2585
3857
|
|
|
2586
3858
|
# Process the user prompt
|
|
2587
3859
|
prompt = payload.get("prompt", "What can you help me with?")
|
|
@@ -2986,6 +4258,68 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
2986
4258
|
return a + b
|
|
2987
4259
|
|
|
2988
4260
|
|
|
4261
|
+
tools = [add_numbers]
|
|
4262
|
+
|
|
4263
|
+
{{#if sessionStorageMountPath}}
|
|
4264
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
4265
|
+
|
|
4266
|
+
def _safe_resolve(path: str) -> str:
|
|
4267
|
+
"""Resolve path safely within the storage boundary."""
|
|
4268
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
4269
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
4270
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
4271
|
+
return resolved
|
|
4272
|
+
|
|
4273
|
+
@function_tool
|
|
4274
|
+
def file_read(path: str) -> str:
|
|
4275
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
4276
|
+
try:
|
|
4277
|
+
full_path = _safe_resolve(path)
|
|
4278
|
+
with open(full_path) as f:
|
|
4279
|
+
return f.read()
|
|
4280
|
+
except ValueError as e:
|
|
4281
|
+
return str(e)
|
|
4282
|
+
except OSError as e:
|
|
4283
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
4284
|
+
|
|
4285
|
+
@function_tool
|
|
4286
|
+
def file_write(path: str, content: str) -> str:
|
|
4287
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
4288
|
+
try:
|
|
4289
|
+
full_path = _safe_resolve(path)
|
|
4290
|
+
parent = os.path.dirname(full_path)
|
|
4291
|
+
if parent:
|
|
4292
|
+
os.makedirs(parent, exist_ok=True)
|
|
4293
|
+
with open(full_path, "w") as f:
|
|
4294
|
+
f.write(content)
|
|
4295
|
+
return f"Written to {path}"
|
|
4296
|
+
except ValueError as e:
|
|
4297
|
+
return str(e)
|
|
4298
|
+
except OSError as e:
|
|
4299
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
4300
|
+
|
|
4301
|
+
@function_tool
|
|
4302
|
+
def list_files(directory: str = "") -> str:
|
|
4303
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
4304
|
+
try:
|
|
4305
|
+
target = _safe_resolve(directory)
|
|
4306
|
+
entries = os.listdir(target)
|
|
4307
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
4308
|
+
except ValueError as e:
|
|
4309
|
+
return str(e)
|
|
4310
|
+
except OSError as e:
|
|
4311
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
4312
|
+
|
|
4313
|
+
tools.extend([file_read, file_write, list_files])
|
|
4314
|
+
{{/if}}
|
|
4315
|
+
|
|
4316
|
+
INSTRUCTIONS = """
|
|
4317
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
4318
|
+
{{#if sessionStorageMountPath}}
|
|
4319
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
4320
|
+
{{/if}}
|
|
4321
|
+
"""
|
|
4322
|
+
|
|
2989
4323
|
# Define the agent execution
|
|
2990
4324
|
async def main(query):
|
|
2991
4325
|
ensure_credentials_loaded()
|
|
@@ -2995,8 +4329,9 @@ async def main(query):
|
|
|
2995
4329
|
agent = Agent(
|
|
2996
4330
|
name="{{ name }}",
|
|
2997
4331
|
model="gpt-4.1",
|
|
4332
|
+
instructions=INSTRUCTIONS,
|
|
2998
4333
|
mcp_servers=mcp_servers,
|
|
2999
|
-
tools=
|
|
4334
|
+
tools=tools
|
|
3000
4335
|
)
|
|
3001
4336
|
result = await Runner.run(agent, query)
|
|
3002
4337
|
return result
|
|
@@ -3004,8 +4339,9 @@ async def main(query):
|
|
|
3004
4339
|
agent = Agent(
|
|
3005
4340
|
name="{{ name }}",
|
|
3006
4341
|
model="gpt-4.1",
|
|
4342
|
+
instructions=INSTRUCTIONS,
|
|
3007
4343
|
mcp_servers=[],
|
|
3008
|
-
tools=
|
|
4344
|
+
tools=tools
|
|
3009
4345
|
)
|
|
3010
4346
|
result = await Runner.run(agent, query)
|
|
3011
4347
|
return result
|
|
@@ -3016,8 +4352,9 @@ async def main(query):
|
|
|
3016
4352
|
agent = Agent(
|
|
3017
4353
|
name="{{ name }}",
|
|
3018
4354
|
model="gpt-4.1",
|
|
4355
|
+
instructions=INSTRUCTIONS,
|
|
3019
4356
|
mcp_servers=active_servers,
|
|
3020
|
-
tools=
|
|
4357
|
+
tools=tools
|
|
3021
4358
|
)
|
|
3022
4359
|
result = await Runner.run(agent, query)
|
|
3023
4360
|
return result
|
|
@@ -3025,8 +4362,9 @@ async def main(query):
|
|
|
3025
4362
|
agent = Agent(
|
|
3026
4363
|
name="{{ name }}",
|
|
3027
4364
|
model="gpt-4.1",
|
|
4365
|
+
instructions=INSTRUCTIONS,
|
|
3028
4366
|
mcp_servers=[],
|
|
3029
|
-
tools=
|
|
4367
|
+
tools=tools
|
|
3030
4368
|
)
|
|
3031
4369
|
result = await Runner.run(agent, query)
|
|
3032
4370
|
return result
|
|
@@ -3308,6 +4646,9 @@ from mcp_client.client import get_streamable_http_mcp_client
|
|
|
3308
4646
|
{{#if hasMemory}}
|
|
3309
4647
|
from memory.session import get_memory_session_manager
|
|
3310
4648
|
{{/if}}
|
|
4649
|
+
{{#if sessionStorageMountPath}}
|
|
4650
|
+
import os
|
|
4651
|
+
{{/if}}
|
|
3311
4652
|
|
|
3312
4653
|
app = BedrockAgentCoreApp()
|
|
3313
4654
|
log = app.logger
|
|
@@ -3329,11 +4670,70 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
3329
4670
|
return a+b
|
|
3330
4671
|
tools.append(add_numbers)
|
|
3331
4672
|
|
|
4673
|
+
{{#if sessionStorageMountPath}}
|
|
4674
|
+
SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
|
|
4675
|
+
|
|
4676
|
+
def _safe_resolve(path: str) -> str:
|
|
4677
|
+
"""Resolve path safely within the storage boundary."""
|
|
4678
|
+
resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
|
|
4679
|
+
if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
|
|
4680
|
+
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
4681
|
+
return resolved
|
|
4682
|
+
|
|
4683
|
+
@tool
|
|
4684
|
+
def file_read(path: str) -> str:
|
|
4685
|
+
"""Read a file from persistent storage. The path is relative to the storage root."""
|
|
4686
|
+
try:
|
|
4687
|
+
full_path = _safe_resolve(path)
|
|
4688
|
+
with open(full_path) as f:
|
|
4689
|
+
return f.read()
|
|
4690
|
+
except ValueError as e:
|
|
4691
|
+
return str(e)
|
|
4692
|
+
except OSError as e:
|
|
4693
|
+
return f"Error reading '{path}': {e.strerror}"
|
|
4694
|
+
|
|
4695
|
+
@tool
|
|
4696
|
+
def file_write(path: str, content: str) -> str:
|
|
4697
|
+
"""Write content to a file in persistent storage. The path is relative to the storage root."""
|
|
4698
|
+
try:
|
|
4699
|
+
full_path = _safe_resolve(path)
|
|
4700
|
+
parent = os.path.dirname(full_path)
|
|
4701
|
+
if parent:
|
|
4702
|
+
os.makedirs(parent, exist_ok=True)
|
|
4703
|
+
with open(full_path, "w") as f:
|
|
4704
|
+
f.write(content)
|
|
4705
|
+
return f"Written to {path}"
|
|
4706
|
+
except ValueError as e:
|
|
4707
|
+
return str(e)
|
|
4708
|
+
except OSError as e:
|
|
4709
|
+
return f"Error writing '{path}': {e.strerror}"
|
|
4710
|
+
|
|
4711
|
+
@tool
|
|
4712
|
+
def list_files(directory: str = "") -> str:
|
|
4713
|
+
"""List files in persistent storage. The directory is relative to the storage root."""
|
|
4714
|
+
try:
|
|
4715
|
+
target = _safe_resolve(directory)
|
|
4716
|
+
entries = os.listdir(target)
|
|
4717
|
+
return "\\n".join(entries) if entries else "(empty directory)"
|
|
4718
|
+
except ValueError as e:
|
|
4719
|
+
return str(e)
|
|
4720
|
+
except OSError as e:
|
|
4721
|
+
return f"Error listing '{directory}': {e.strerror}"
|
|
4722
|
+
|
|
4723
|
+
tools.extend([file_read, file_write, list_files])
|
|
4724
|
+
{{/if}}
|
|
4725
|
+
|
|
3332
4726
|
# Add MCP client to tools if available
|
|
3333
4727
|
for mcp_client in mcp_clients:
|
|
3334
4728
|
if mcp_client:
|
|
3335
4729
|
tools.append(mcp_client)
|
|
3336
4730
|
|
|
4731
|
+
SYSTEM_PROMPT = """
|
|
4732
|
+
You are a helpful assistant. Use tools when appropriate.
|
|
4733
|
+
{{#if sessionStorageMountPath}}
|
|
4734
|
+
You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
|
|
4735
|
+
{{/if}}
|
|
4736
|
+
"""
|
|
3337
4737
|
|
|
3338
4738
|
{{#if hasMemory}}
|
|
3339
4739
|
def agent_factory():
|
|
@@ -3345,9 +4745,7 @@ def agent_factory():
|
|
|
3345
4745
|
cache[key] = Agent(
|
|
3346
4746
|
model=load_model(),
|
|
3347
4747
|
session_manager=get_memory_session_manager(session_id, user_id),
|
|
3348
|
-
system_prompt=
|
|
3349
|
-
You are a helpful assistant. Use tools when appropriate.
|
|
3350
|
-
""",
|
|
4748
|
+
system_prompt=SYSTEM_PROMPT,
|
|
3351
4749
|
tools=tools
|
|
3352
4750
|
)
|
|
3353
4751
|
return cache[key]
|
|
@@ -3361,9 +4759,7 @@ def get_or_create_agent():
|
|
|
3361
4759
|
if _agent is None:
|
|
3362
4760
|
_agent = Agent(
|
|
3363
4761
|
model=load_model(),
|
|
3364
|
-
system_prompt=
|
|
3365
|
-
You are a helpful assistant. Use tools when appropriate.
|
|
3366
|
-
""",
|
|
4762
|
+
system_prompt=SYSTEM_PROMPT,
|
|
3367
4763
|
tools=tools
|
|
3368
4764
|
)
|
|
3369
4765
|
return _agent
|
|
@@ -3826,27 +5222,47 @@ exports[`Assets Directory Snapshots > Root-level assets > AGENTS.md should match
|
|
|
3826
5222
|
|
|
3827
5223
|
This directory stores:
|
|
3828
5224
|
|
|
3829
|
-
- Template assets for agents written in different
|
|
5225
|
+
- Template assets for agents written in different languages, SDKs, and configurations
|
|
3830
5226
|
- Container templates (\`container/python/\`) with \`Dockerfile\` and \`.dockerignore\` for Container build agents
|
|
5227
|
+
- Vended documentation (\`README.md\`, \`agents/AGENTS.md\`) copied into user projects at create time
|
|
5228
|
+
- CDK project template (\`cdk/\`) using \`@aws/agentcore-cdk\` L3 constructs
|
|
5229
|
+
- Evaluator templates (\`evaluators/\`) for code-based evaluators
|
|
5230
|
+
- MCP tool templates (\`mcp/\`) for Lambda and AgentCoreRuntime compute
|
|
3831
5231
|
|
|
3832
5232
|
### Directory Layout
|
|
3833
5233
|
|
|
3834
5234
|
\`\`\`
|
|
3835
5235
|
assets/
|
|
3836
|
-
├──
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
│
|
|
3840
|
-
|
|
3841
|
-
│
|
|
5236
|
+
├── README.md # Vended to project root as project README
|
|
5237
|
+
├── AGENTS.md # This file — internal dev context
|
|
5238
|
+
├── agents/
|
|
5239
|
+
│ └── AGENTS.md # Vended to project root for AI coding assistants
|
|
5240
|
+
├── python/ # Framework templates (one per SDK per protocol)
|
|
5241
|
+
│ ├── http/ # HTTP protocol agents
|
|
5242
|
+
│ │ ├── strands/
|
|
5243
|
+
│ │ ├── langchain_langgraph/
|
|
5244
|
+
│ │ ├── googleadk/
|
|
5245
|
+
│ │ ├── openaiagents/
|
|
5246
|
+
│ │ └── autogen/
|
|
5247
|
+
│ ├── mcp/ # MCP protocol agents
|
|
5248
|
+
│ │ └── standalone/
|
|
5249
|
+
│ └── a2a/ # A2A protocol agents
|
|
5250
|
+
│ ├── strands/
|
|
5251
|
+
│ ├── langchain_langgraph/
|
|
5252
|
+
│ └── googleadk/
|
|
5253
|
+
├── typescript/ # TypeScript agent templates
|
|
3842
5254
|
├── container/ # Container build templates
|
|
3843
5255
|
│ └── python/
|
|
3844
5256
|
│ ├── Dockerfile
|
|
3845
5257
|
│ └── dockerignore.template
|
|
3846
|
-
|
|
5258
|
+
├── cdk/ # CDK project template (@aws/agentcore-cdk)
|
|
5259
|
+
├── evaluators/ # Code-based evaluator templates
|
|
5260
|
+
└── mcp/ # MCP tool templates (Lambda + AgentCoreRuntime)
|
|
5261
|
+
├── python/
|
|
5262
|
+
└── python-lambda/
|
|
3847
5263
|
\`\`\`
|
|
3848
5264
|
|
|
3849
|
-
The rendering logic is rooted in the \`AgentEnvSpec\` and must ALWAYS respect the configuration in the
|
|
5265
|
+
The rendering logic is rooted in the \`AgentEnvSpec\` and must ALWAYS respect the configuration in the spec.
|
|
3850
5266
|
|
|
3851
5267
|
For Container builds, \`BaseRenderer.render()\` automatically copies the \`container/<language>/\` templates (Dockerfile,
|
|
3852
5268
|
.dockerignore) into the agent directory when \`buildType === 'Container'\`.
|
|
@@ -3855,10 +5271,13 @@ For Container builds, \`BaseRenderer.render()\` automatically copies the \`conta
|
|
|
3855
5271
|
|
|
3856
5272
|
- Always make sure the templates are as close to working code as possible
|
|
3857
5273
|
- AVOID as much as possible using any conditionals within the templates
|
|
5274
|
+
- Test template rendering with \`agentcore add agent\` for each framework/protocol combination
|
|
3858
5275
|
|
|
3859
5276
|
## How to use the assets in this directory
|
|
3860
5277
|
|
|
3861
|
-
- These assets are rendered by the CLI's template renderer in \`src/cli/templates
|
|
5278
|
+
- These assets are rendered by the CLI's template renderer in \`src/cli/templates/\`
|
|
5279
|
+
- The \`README.md\` and \`agents/AGENTS.md\` are copied verbatim (no template rendering) during project creation
|
|
5280
|
+
- The \`.llm-context/\` files are sourced from \`src/schema/llm-compacted/\` and written during init
|
|
3862
5281
|
"
|
|
3863
5282
|
`;
|
|
3864
5283
|
|
|
@@ -3870,14 +5289,19 @@ This project was created with the [AgentCore CLI](https://github.com/aws/agentco
|
|
|
3870
5289
|
## Project Structure
|
|
3871
5290
|
|
|
3872
5291
|
\`\`\`
|
|
3873
|
-
.
|
|
3874
5292
|
my-project/
|
|
5293
|
+
├── AGENTS.md # AI coding assistant context
|
|
3875
5294
|
├── agentcore/
|
|
3876
|
-
│ ├── .
|
|
3877
|
-
│ ├──
|
|
3878
|
-
│ ├──
|
|
3879
|
-
│
|
|
3880
|
-
├──
|
|
5295
|
+
│ ├── agentcore.json # Project config (agents, memories, credentials, gateways, evaluators)
|
|
5296
|
+
│ ├── aws-targets.json # Deployment targets (account + region)
|
|
5297
|
+
│ ├── .env.local # Secrets — API keys (gitignored)
|
|
5298
|
+
│ ├── .llm-context/ # TypeScript type definitions for AI assistants
|
|
5299
|
+
│ │ ├── agentcore.ts # AgentCoreProjectSpec types
|
|
5300
|
+
│ │ ├── aws-targets.ts # Deployment target types
|
|
5301
|
+
│ │ └── mcp.ts # Gateway and MCP tool types
|
|
5302
|
+
│ └── cdk/ # CDK infrastructure (@aws/agentcore-cdk)
|
|
5303
|
+
├── app/ # Agent application code
|
|
5304
|
+
└── evaluators/ # Custom evaluator code (if any)
|
|
3881
5305
|
\`\`\`
|
|
3882
5306
|
|
|
3883
5307
|
## Getting Started
|
|
@@ -3885,7 +5309,9 @@ my-project/
|
|
|
3885
5309
|
### Prerequisites
|
|
3886
5310
|
|
|
3887
5311
|
- **Node.js** 20.x or later
|
|
3888
|
-
- **uv** for Python agents ([install](https://docs.astral.sh/uv/getting-started/installation/))
|
|
5312
|
+
- **Python 3.10+** and **uv** for Python agents ([install uv](https://docs.astral.sh/uv/getting-started/installation/))
|
|
5313
|
+
- **AWS credentials** configured (\`aws configure\` or environment variables)
|
|
5314
|
+
- **Docker** (only for Container build agents)
|
|
3889
5315
|
|
|
3890
5316
|
### Development
|
|
3891
5317
|
|
|
@@ -3903,44 +5329,62 @@ Deploy to AWS:
|
|
|
3903
5329
|
agentcore deploy
|
|
3904
5330
|
\`\`\`
|
|
3905
5331
|
|
|
3906
|
-
|
|
5332
|
+
## Commands
|
|
3907
5333
|
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
5334
|
+
| Command | Description |
|
|
5335
|
+
| --- | --- |
|
|
5336
|
+
| \`agentcore create\` | Create a new AgentCore project |
|
|
5337
|
+
| \`agentcore add\` | Add resources (agent, memory, credential, gateway, evaluator, policy) |
|
|
5338
|
+
| \`agentcore remove\` | Remove resources |
|
|
5339
|
+
| \`agentcore dev\` | Run agent locally with hot-reload |
|
|
5340
|
+
| \`agentcore deploy\` | Deploy to AWS via CDK |
|
|
5341
|
+
| \`agentcore status\` | Show deployment status |
|
|
5342
|
+
| \`agentcore invoke\` | Invoke agent (local or deployed) |
|
|
5343
|
+
| \`agentcore logs\` | View agent logs |
|
|
5344
|
+
| \`agentcore traces\` | View agent traces |
|
|
5345
|
+
| \`agentcore eval\` | Run evaluations |
|
|
5346
|
+
| \`agentcore package\` | Package agent artifacts |
|
|
5347
|
+
| \`agentcore validate\` | Validate configuration |
|
|
5348
|
+
| \`agentcore pause\` | Pause a deployed agent |
|
|
5349
|
+
| \`agentcore resume\` | Resume a paused agent |
|
|
5350
|
+
| \`agentcore fetch\` | Fetch remote resource definitions |
|
|
5351
|
+
| \`agentcore import\` | Import existing resources |
|
|
5352
|
+
| \`agentcore update\` | Check for CLI updates |
|
|
3912
5353
|
|
|
3913
5354
|
## Configuration
|
|
3914
5355
|
|
|
3915
|
-
Edit the JSON files in \`agentcore/\` to configure your
|
|
3916
|
-
type definitions and validation constraints.
|
|
5356
|
+
Edit the JSON files in \`agentcore/\` to configure your project. See \`agentcore/.llm-context/\` for type definitions and validation constraints.
|
|
3917
5357
|
|
|
3918
|
-
The project uses a **flat resource model**
|
|
3919
|
-
\`agentcore.json\`.
|
|
5358
|
+
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
5359
|
|
|
3921
|
-
##
|
|
5360
|
+
## Resources
|
|
3922
5361
|
|
|
3923
|
-
|
|
|
3924
|
-
|
|
|
3925
|
-
|
|
|
3926
|
-
|
|
|
3927
|
-
|
|
|
3928
|
-
|
|
|
3929
|
-
|
|
|
3930
|
-
|
|
|
3931
|
-
|
|
|
3932
|
-
|
|
|
3933
|
-
| \`agentcore validate\` | Validate configuration |
|
|
3934
|
-
| \`agentcore update\` | Check for CLI updates |
|
|
5362
|
+
| Resource | Purpose |
|
|
5363
|
+
| --- | --- |
|
|
5364
|
+
| Agent (runtime) | HTTP, MCP, or A2A agent deployed to AgentCore Runtime |
|
|
5365
|
+
| Memory | Persistent context storage with configurable strategies |
|
|
5366
|
+
| Credential | API key or OAuth credential providers |
|
|
5367
|
+
| Gateway | MCP gateway that routes tool calls to targets |
|
|
5368
|
+
| Gateway Target | Tool implementation (Lambda, MCP server, OpenAPI, Smithy, API Gateway) |
|
|
5369
|
+
| Evaluator | Custom LLM-as-a-Judge or code-based evaluation |
|
|
5370
|
+
| Online Eval Config | Continuous evaluation pipeline for deployed agents |
|
|
5371
|
+
| Policy | Cedar authorization policies for gateway tools |
|
|
3935
5372
|
|
|
3936
5373
|
### Agent Types
|
|
3937
5374
|
|
|
3938
|
-
- **Template agents**: Created from framework templates (Strands,
|
|
5375
|
+
- **Template agents**: Created from framework templates (Strands, LangChain/LangGraph, GoogleADK, OpenAI Agents, Autogen)
|
|
3939
5376
|
- **BYO agents**: Bring your own code with \`agentcore add agent --type byo\`
|
|
5377
|
+
- **Import agents**: Import existing Bedrock agents with \`agentcore import\`
|
|
5378
|
+
|
|
5379
|
+
### Build Types
|
|
5380
|
+
|
|
5381
|
+
- **CodeZip**: Python source packaged as a zip and deployed directly to AgentCore Runtime
|
|
5382
|
+
- **Container**: Docker image built via CodeBuild (ARM64), pushed to ECR, and deployed to AgentCore Runtime
|
|
3940
5383
|
|
|
3941
5384
|
## Documentation
|
|
3942
5385
|
|
|
3943
|
-
- [AgentCore CLI
|
|
5386
|
+
- [AgentCore CLI](https://github.com/aws/agentcore-cli)
|
|
5387
|
+
- [AgentCore CDK Constructs](https://github.com/aws/agentcore-l3-cdk-constructs)
|
|
3944
5388
|
- [Amazon Bedrock AgentCore](https://aws.amazon.com/bedrock/agentcore/)
|
|
3945
5389
|
"
|
|
3946
5390
|
`;
|
|
@@ -3950,103 +5394,114 @@ exports[`Assets Directory Snapshots > Root-level assets > agents/AGENTS.md shoul
|
|
|
3950
5394
|
|
|
3951
5395
|
This project contains configuration and infrastructure for an Amazon Bedrock AgentCore application.
|
|
3952
5396
|
|
|
3953
|
-
The \`agentcore/\` directory
|
|
3954
|
-
|
|
3955
|
-
model** where agents, memories, and credentials are top-level arrays.
|
|
5397
|
+
The \`agentcore/\` directory is a declarative model of the project. The \`agentcore/cdk/\` subdirectory uses the
|
|
5398
|
+
\`@aws/agentcore-cdk\` L3 constructs to deploy the configuration to AWS.
|
|
3956
5399
|
|
|
3957
5400
|
## Mental Model
|
|
3958
5401
|
|
|
3959
|
-
The project uses a **flat resource model**. Agents, memories,
|
|
3960
|
-
\`agentcore.json\`. There is no binding
|
|
3961
|
-
independently.
|
|
3962
|
-
|
|
5402
|
+
The project uses a **flat resource model**. Agents, memories, credentials, gateways, evaluators, and policies are
|
|
5403
|
+
independent top-level arrays in \`agentcore.json\`. There is no binding between resources in the schema — each resource is
|
|
5404
|
+
provisioned independently. Agents discover memories and credentials at runtime via environment variables or SDK calls.
|
|
5405
|
+
Tags defined in \`agentcore.json\` flow through to deployed CloudFormation resources.
|
|
3963
5406
|
|
|
3964
5407
|
## Critical Invariants
|
|
3965
5408
|
|
|
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
|
-
|
|
5409
|
+
1. **Schema-First Authority:** The \`.json\` files are the source of truth. Do not modify agent behavior by editing
|
|
5410
|
+
generated CDK code in \`cdk/\`.
|
|
5411
|
+
2. **Resource Identity:** The \`name\` field determines the CloudFormation Logical ID.
|
|
5412
|
+
- **Renaming** a resource will **destroy and recreate** it.
|
|
5413
|
+
- **Modifying** other fields will update the resource **in-place**.
|
|
5414
|
+
3. **Schema Validation:** If your JSON conforms to the types in \`.llm-context/\`, it will deploy successfully. Run
|
|
5415
|
+
\`agentcore validate\` to check.
|
|
5416
|
+
4. **Resource Removal:** Use \`agentcore remove\` to remove resources. Run \`agentcore deploy\` after removal to tear down
|
|
5417
|
+
deployed infrastructure.
|
|
3975
5418
|
|
|
3976
5419
|
## Directory Structure
|
|
3977
5420
|
|
|
3978
5421
|
\`\`\`
|
|
3979
|
-
|
|
3980
|
-
├── AGENTS.md # This file
|
|
3981
|
-
├── agentcore/
|
|
5422
|
+
myProject/
|
|
5423
|
+
├── AGENTS.md # This file — AI coding assistant context
|
|
5424
|
+
├── agentcore/
|
|
3982
5425
|
│ ├── agentcore.json # Main project config (AgentCoreProjectSpec)
|
|
3983
|
-
│ ├── aws-targets.json # Deployment targets
|
|
3984
|
-
│ ├── .
|
|
3985
|
-
│
|
|
5426
|
+
│ ├── aws-targets.json # Deployment targets (account + region)
|
|
5427
|
+
│ ├── .env.local # Secrets — API keys (gitignored)
|
|
5428
|
+
│ ├── .llm-context/ # TypeScript type definitions for AI assistants
|
|
5429
|
+
│ │ ├── README.md # Guide to using schema files
|
|
3986
5430
|
│ │ ├── agentcore.ts # AgentCoreProjectSpec types
|
|
3987
|
-
│ │
|
|
3988
|
-
│ └──
|
|
3989
|
-
└──
|
|
5431
|
+
│ │ ├── aws-targets.ts # AWS deployment target types
|
|
5432
|
+
│ │ └── mcp.ts # Gateway and MCP tool types
|
|
5433
|
+
│ └── cdk/ # AWS CDK project (@aws/agentcore-cdk L3 constructs)
|
|
5434
|
+
├── app/ # Agent application code
|
|
5435
|
+
└── evaluators/ # Custom evaluator code (if any)
|
|
3990
5436
|
\`\`\`
|
|
3991
5437
|
|
|
3992
5438
|
## Schema Reference
|
|
3993
5439
|
|
|
3994
5440
|
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.
|
|
5441
|
+
file maps to a JSON config file and includes validation constraints as comments (\`@regex\`, \`@min\`, \`@max\`).
|
|
3996
5442
|
|
|
3997
|
-
| JSON Config
|
|
3998
|
-
|
|
|
3999
|
-
| \`agentcore/agentcore.json\`
|
|
4000
|
-
| \`agentcore/
|
|
5443
|
+
| JSON Config | Schema File | Root Type |
|
|
5444
|
+
| --- | --- | --- |
|
|
5445
|
+
| \`agentcore/agentcore.json\` | \`agentcore/.llm-context/agentcore.ts\` | \`AgentCoreProjectSpec\` |
|
|
5446
|
+
| \`agentcore/agentcore.json\` (gateways) | \`agentcore/.llm-context/mcp.ts\` | \`AgentCoreMcpSpec\` |
|
|
5447
|
+
| \`agentcore/aws-targets.json\` | \`agentcore/.llm-context/aws-targets.ts\` | \`AwsDeploymentTarget[]\` |
|
|
4001
5448
|
|
|
4002
5449
|
### Key Types
|
|
4003
5450
|
|
|
4004
|
-
- **AgentCoreProjectSpec**: Root
|
|
4005
|
-
- **AgentEnvSpec**: Agent configuration (
|
|
4006
|
-
- **Memory**: Memory resource with strategies and expiry
|
|
4007
|
-
- **Credential**: API key credential provider
|
|
5451
|
+
- **AgentCoreProjectSpec**: Root config with \`runtimes\`, \`memories\`, \`credentials\`, \`agentCoreGateways\`, \`evaluators\`, \`onlineEvalConfigs\`, \`policyEngines\` arrays
|
|
5452
|
+
- **AgentEnvSpec**: Agent configuration (build type, entrypoint, code location, runtime version, network mode)
|
|
5453
|
+
- **Memory**: Memory resource with strategies (SEMANTIC, SUMMARIZATION, USER_PREFERENCE, EPISODIC) and expiry
|
|
5454
|
+
- **Credential**: API key or OAuth credential provider
|
|
5455
|
+
- **AgentCoreGateway**: MCP gateway with targets (Lambda, MCP server, OpenAPI, Smithy, API Gateway)
|
|
5456
|
+
- **Evaluator**: LLM-as-a-Judge or code-based evaluator
|
|
5457
|
+
- **OnlineEvalConfig**: Continuous evaluation pipeline bound to an agent
|
|
4008
5458
|
|
|
4009
5459
|
### Common Enum Values
|
|
4010
5460
|
|
|
4011
5461
|
- **BuildType**: \`'CodeZip'\` | \`'Container'\`
|
|
4012
|
-
- **NetworkMode**: \`'PUBLIC'\`
|
|
4013
|
-
- **RuntimeVersion**: \`'PYTHON_3_10'\` | \`'PYTHON_3_11'\` | \`'PYTHON_3_12'\` | \`'PYTHON_3_13'\` | \`'PYTHON_3_14'\`
|
|
5462
|
+
- **NetworkMode**: \`'PUBLIC'\` | \`'VPC'\`
|
|
5463
|
+
- **RuntimeVersion**: \`'PYTHON_3_10'\` | \`'PYTHON_3_11'\` | \`'PYTHON_3_12'\` | \`'PYTHON_3_13'\` | \`'PYTHON_3_14'\` | \`'NODE_18'\` | \`'NODE_20'\` | \`'NODE_22'\`
|
|
4014
5464
|
- **MemoryStrategyType**: \`'SEMANTIC'\` | \`'SUMMARIZATION'\` | \`'USER_PREFERENCE'\` | \`'EPISODIC'\`
|
|
5465
|
+
- **GatewayTargetType**: \`'lambda'\` | \`'mcpServer'\` | \`'openApiSchema'\` | \`'smithyModel'\` | \`'apiGateway'\` | \`'lambdaFunctionArn'\`
|
|
5466
|
+
- **ModelProvider**: \`'Bedrock'\` | \`'Gemini'\` | \`'OpenAI'\` | \`'Anthropic'\`
|
|
4015
5467
|
|
|
4016
5468
|
### Build Types
|
|
4017
5469
|
|
|
4018
|
-
- **CodeZip**: Python source
|
|
4019
|
-
- **Container**:
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
container is built and run locally with volume-mounted hot-reload.
|
|
5470
|
+
- **CodeZip**: Python source packaged as a zip and deployed directly to AgentCore Runtime.
|
|
5471
|
+
- **Container**: Docker image built in CodeBuild (ARM64), pushed to a per-agent ECR repository. Requires a \`Dockerfile\`
|
|
5472
|
+
in the agent's \`codeLocation\` directory. For local development (\`agentcore dev\`), the container is built and run
|
|
5473
|
+
locally with volume-mounted hot-reload.
|
|
4023
5474
|
|
|
4024
5475
|
### Supported Frameworks (for template agents)
|
|
4025
5476
|
|
|
4026
|
-
- **Strands**
|
|
4027
|
-
- **
|
|
4028
|
-
- **GoogleADK**
|
|
4029
|
-
- **
|
|
5477
|
+
- **Strands** — Bedrock, Anthropic, OpenAI, Gemini
|
|
5478
|
+
- **LangChain/LangGraph** — Bedrock, Anthropic, OpenAI, Gemini
|
|
5479
|
+
- **GoogleADK** — Gemini
|
|
5480
|
+
- **OpenAI Agents** — OpenAI
|
|
5481
|
+
- **Autogen** — Bedrock, Anthropic, OpenAI, Gemini
|
|
4030
5482
|
|
|
5483
|
+
### Protocols
|
|
4031
5484
|
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
deployment options are available.
|
|
5485
|
+
- **HTTP** — Standard HTTP agent endpoint
|
|
5486
|
+
- **MCP** — Model Context Protocol server
|
|
5487
|
+
- **A2A** — Agent-to-Agent protocol (Google A2A)
|
|
4036
5488
|
|
|
4037
5489
|
## Deployment
|
|
4038
5490
|
|
|
4039
|
-
|
|
5491
|
+
Deployments are orchestrated through the CLI:
|
|
4040
5492
|
|
|
4041
|
-
|
|
5493
|
+
\`\`\`bash
|
|
5494
|
+
agentcore deploy # Synthesizes CDK and deploys to AWS
|
|
5495
|
+
agentcore status # Shows deployment status
|
|
5496
|
+
\`\`\`
|
|
4042
5497
|
|
|
4043
|
-
Alternatively,
|
|
5498
|
+
Alternatively, deploy directly via CDK:
|
|
4044
5499
|
|
|
4045
5500
|
\`\`\`bash
|
|
4046
5501
|
cd agentcore/cdk
|
|
4047
5502
|
npm install
|
|
4048
|
-
npx cdk synth
|
|
4049
|
-
npx cdk deploy
|
|
5503
|
+
npx cdk synth
|
|
5504
|
+
npx cdk deploy
|
|
4050
5505
|
\`\`\`
|
|
4051
5506
|
|
|
4052
5507
|
## Editing Schemas
|
|
@@ -4057,7 +5512,25 @@ When modifying JSON config files:
|
|
|
4057
5512
|
2. Check validation constraint comments (\`@regex\`, \`@min\`, \`@max\`)
|
|
4058
5513
|
3. Use exact enum values as string literals
|
|
4059
5514
|
4. Use CloudFormation-safe names (alphanumeric, start with letter)
|
|
4060
|
-
5. Run \`agentcore validate\`
|
|
5515
|
+
5. Run \`agentcore validate\` to verify changes
|
|
5516
|
+
|
|
5517
|
+
## CLI Commands
|
|
5518
|
+
|
|
5519
|
+
| Command | Description |
|
|
5520
|
+
| --- | --- |
|
|
5521
|
+
| \`agentcore create\` | Create a new project |
|
|
5522
|
+
| \`agentcore add <resource>\` | Add agent, memory, credential, gateway, evaluator, policy |
|
|
5523
|
+
| \`agentcore remove <resource>\` | Remove a resource |
|
|
5524
|
+
| \`agentcore dev\` | Run agent locally with hot-reload |
|
|
5525
|
+
| \`agentcore deploy\` | Deploy to AWS |
|
|
5526
|
+
| \`agentcore status\` | Show deployment status |
|
|
5527
|
+
| \`agentcore invoke\` | Invoke agent (local or deployed) |
|
|
5528
|
+
| \`agentcore logs\` | View agent logs |
|
|
5529
|
+
| \`agentcore traces\` | View agent traces |
|
|
5530
|
+
| \`agentcore eval\` | Run evaluations against an agent |
|
|
5531
|
+
| \`agentcore package\` | Package agent artifacts |
|
|
5532
|
+
| \`agentcore validate\` | Validate configuration |
|
|
5533
|
+
| \`agentcore pause\` / \`resume\` | Pause or resume a deployed agent |
|
|
4061
5534
|
"
|
|
4062
5535
|
`;
|
|
4063
5536
|
|