@aws/agentcore 1.0.0-preview.23 → 1.0.0-preview.25
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/assets/README.md +5 -0
- package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +146 -28
- package/dist/assets/__tests__/input-validation.test.ts +44 -0
- package/dist/assets/agents/AGENTS.md +2 -0
- package/dist/assets/evaluators/autoevals-lambda/execution-role-policy.json +15 -0
- package/dist/assets/evaluators/autoevals-lambda/lambda_function.py +37 -0
- package/dist/assets/evaluators/autoevals-lambda/pyproject.toml +22 -0
- package/dist/assets/evaluators/deepeval-lambda/execution-role-policy.json +15 -0
- package/dist/assets/evaluators/deepeval-lambda/lambda_function.py +29 -0
- package/dist/assets/evaluators/deepeval-lambda/pyproject.toml +19 -0
- package/dist/assets/mcp/python/pyproject.toml +1 -1
- package/dist/assets/python/a2a/googleadk/base/main.py +8 -2
- package/dist/assets/python/a2a/googleadk/base/pyproject.toml +4 -4
- package/dist/assets/python/a2a/langchain_langgraph/base/main.py +11 -5
- package/dist/assets/python/a2a/langchain_langgraph/base/pyproject.toml +2 -2
- package/dist/assets/python/a2a/strands/base/pyproject.toml +1 -1
- package/dist/assets/python/http/autogen/base/README.md +5 -0
- package/dist/assets/python/http/autogen/base/main.py +2 -0
- package/dist/assets/python/http/googleadk/base/README.md +5 -0
- package/dist/assets/python/http/googleadk/base/main.py +2 -0
- package/dist/assets/python/http/langchain_langgraph/base/README.md +5 -0
- package/dist/assets/python/http/langchain_langgraph/base/main.py +4 -0
- package/dist/assets/python/http/langchain_langgraph/base/pyproject.toml +2 -2
- package/dist/assets/python/http/openaiagents/base/README.md +5 -0
- package/dist/assets/python/http/openaiagents/base/main.py +2 -0
- package/dist/assets/python/http/strands/base/README.md +6 -0
- package/dist/assets/python/http/strands/base/main.py +40 -4
- package/dist/assets/python/http/strands/base/pyproject.toml +1 -1
- package/dist/assets/python/mcp/standalone/base/pyproject.toml +1 -1
- package/dist/assets/typescript/http/strands/base/README.md +5 -0
- package/dist/assets/typescript/http/strands/base/main.ts +8 -3
- package/dist/assets/typescript/http/vercelai/base/README.md +5 -0
- package/dist/assets/typescript/http/vercelai/base/main.ts +8 -2
- package/dist/cli/index.mjs +507 -502
- package/npm-shrinkwrap.json +234 -376
- package/package.json +4 -1
- package/scripts/extract-cli-model.mjs +47 -6
- package/scripts/extract-cli-model.test.mjs +43 -0
- package/scripts/render_adoc.py +81 -5
- package/scripts/safe-junit-reporter.ts +7 -0
- package/scripts/sanitize-test-artifacts.ts +65 -0
- package/scripts/test_render_adoc.py +38 -0
package/dist/assets/README.md
CHANGED
|
@@ -37,6 +37,11 @@ Run your agent locally:
|
|
|
37
37
|
agentcore dev
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
### Validate Invocation Input
|
|
41
|
+
|
|
42
|
+
Validate runtime invocation payloads before forwarding them to an agent framework. Keep user prompts typed as strings
|
|
43
|
+
and pass only prompt text to the agent.
|
|
44
|
+
|
|
40
45
|
### Deployment
|
|
41
46
|
|
|
42
47
|
Deploy to AWS:
|
|
@@ -742,6 +742,12 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
|
|
|
742
742
|
"container/typescript/dockerignore.template",
|
|
743
743
|
"datasets/predefined-v1.jsonl",
|
|
744
744
|
"datasets/simulated-v1.jsonl",
|
|
745
|
+
"evaluators/autoevals-lambda/execution-role-policy.json",
|
|
746
|
+
"evaluators/autoevals-lambda/lambda_function.py",
|
|
747
|
+
"evaluators/autoevals-lambda/pyproject.toml",
|
|
748
|
+
"evaluators/deepeval-lambda/execution-role-policy.json",
|
|
749
|
+
"evaluators/deepeval-lambda/lambda_function.py",
|
|
750
|
+
"evaluators/deepeval-lambda/pyproject.toml",
|
|
745
751
|
"evaluators/python-lambda/execution-role-policy.json",
|
|
746
752
|
"evaluators/python-lambda/lambda_function.py",
|
|
747
753
|
"evaluators/python-lambda/pyproject.toml",
|
|
@@ -904,7 +910,7 @@ description = "MCP Server demonstrating HTTP tool patterns"
|
|
|
904
910
|
readme = "README.md"
|
|
905
911
|
requires-python = ">=3.10"
|
|
906
912
|
dependencies = [
|
|
907
|
-
"mcp[cli]
|
|
913
|
+
"mcp[cli] ~= 1.24.0",
|
|
908
914
|
"httpx >= 0.27.0",
|
|
909
915
|
"opentelemetry-distro",
|
|
910
916
|
"opentelemetry-exporter-otlp",
|
|
@@ -1311,7 +1317,7 @@ from google.adk.agents import Agent
|
|
|
1311
1317
|
from google.adk.a2a.executor.a2a_agent_executor import A2aAgentExecutor
|
|
1312
1318
|
from google.adk.runners import Runner
|
|
1313
1319
|
from google.adk.sessions import InMemorySessionService
|
|
1314
|
-
from a2a.types import AgentCapabilities, AgentCard, AgentSkill
|
|
1320
|
+
from a2a.types import AgentCapabilities, AgentCard, AgentInterface, AgentSkill
|
|
1315
1321
|
from bedrock_agentcore.runtime import serve_a2a
|
|
1316
1322
|
from model.load import load_model
|
|
1317
1323
|
|
|
@@ -1405,7 +1411,6 @@ runner = Runner(
|
|
|
1405
1411
|
card = AgentCard(
|
|
1406
1412
|
name=agent.name,
|
|
1407
1413
|
description=agent.description,
|
|
1408
|
-
url="http://localhost:9000/",
|
|
1409
1414
|
version="0.1.0",
|
|
1410
1415
|
capabilities=AgentCapabilities(streaming=True),
|
|
1411
1416
|
skills=[
|
|
@@ -1418,6 +1423,13 @@ card = AgentCard(
|
|
|
1418
1423
|
],
|
|
1419
1424
|
default_input_modes=["text"],
|
|
1420
1425
|
default_output_modes=["text"],
|
|
1426
|
+
supported_interfaces=[
|
|
1427
|
+
AgentInterface(
|
|
1428
|
+
protocol_binding="JSONRPC",
|
|
1429
|
+
protocol_version="1.0",
|
|
1430
|
+
url="http://localhost:9000/",
|
|
1431
|
+
)
|
|
1432
|
+
],
|
|
1421
1433
|
)
|
|
1422
1434
|
|
|
1423
1435
|
if __name__ == "__main__":
|
|
@@ -1487,11 +1499,11 @@ description = "AgentCore A2A Agent using Google ADK"
|
|
|
1487
1499
|
readme = "README.md"
|
|
1488
1500
|
requires-python = ">=3.10"
|
|
1489
1501
|
dependencies = [
|
|
1490
|
-
"a2a-sdk >= 0.
|
|
1502
|
+
"a2a-sdk[http-server] >= 1.0.1, < 2.0.0",
|
|
1491
1503
|
"aws-opentelemetry-distro",
|
|
1492
|
-
"bedrock-agentcore[a2a] >= 1.0
|
|
1493
|
-
"google-adk >=
|
|
1494
|
-
"google-genai >=
|
|
1504
|
+
"bedrock-agentcore[a2a-v1] >= 1.19.0",
|
|
1505
|
+
"google-adk[a2a] >= 2.5.0, < 3.0.0",
|
|
1506
|
+
"google-genai >= 2.9.0, < 3.0.0",
|
|
1495
1507
|
# 1.13.0 was yanked for broken imports.
|
|
1496
1508
|
"opentelemetry-resourcedetector-gcp >= 1.9.0a0, < 2.0.0, != 1.13.0",
|
|
1497
1509
|
]
|
|
@@ -1579,11 +1591,11 @@ import os
|
|
|
1579
1591
|
from langchain_core.tools import tool
|
|
1580
1592
|
from langgraph.prebuilt import create_react_agent
|
|
1581
1593
|
from opentelemetry.instrumentation.langchain import LangchainInstrumentor
|
|
1594
|
+
from a2a.helpers import new_task_from_user_message
|
|
1582
1595
|
from a2a.server.agent_execution import AgentExecutor, RequestContext
|
|
1583
1596
|
from a2a.server.events import EventQueue
|
|
1584
1597
|
from a2a.server.tasks import TaskUpdater
|
|
1585
|
-
from a2a.types import AgentCapabilities, AgentCard, AgentSkill, Part
|
|
1586
|
-
from a2a.utils import new_task
|
|
1598
|
+
from a2a.types import AgentCapabilities, AgentCard, AgentInterface, AgentSkill, Part
|
|
1587
1599
|
from bedrock_agentcore.runtime import serve_a2a
|
|
1588
1600
|
from model.load import load_model
|
|
1589
1601
|
|
|
@@ -1675,7 +1687,7 @@ class LangGraphA2AExecutor(AgentExecutor):
|
|
|
1675
1687
|
self.graph = graph
|
|
1676
1688
|
|
|
1677
1689
|
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
1678
|
-
task = context.current_task or
|
|
1690
|
+
task = context.current_task or new_task_from_user_message(context.message)
|
|
1679
1691
|
if not context.current_task:
|
|
1680
1692
|
await event_queue.enqueue_event(task)
|
|
1681
1693
|
updater = TaskUpdater(event_queue, task.id, task.context_id)
|
|
@@ -1684,7 +1696,7 @@ class LangGraphA2AExecutor(AgentExecutor):
|
|
|
1684
1696
|
result = await self.graph.ainvoke({"messages": [("user", user_text)]})
|
|
1685
1697
|
response = result["messages"][-1].content
|
|
1686
1698
|
|
|
1687
|
-
await updater.add_artifact([Part(
|
|
1699
|
+
await updater.add_artifact([Part(text=response)])
|
|
1688
1700
|
await updater.complete()
|
|
1689
1701
|
|
|
1690
1702
|
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
@@ -1694,7 +1706,6 @@ class LangGraphA2AExecutor(AgentExecutor):
|
|
|
1694
1706
|
card = AgentCard(
|
|
1695
1707
|
name="{{ name }}",
|
|
1696
1708
|
description="A LangGraph agent on Bedrock AgentCore",
|
|
1697
|
-
url="http://localhost:9000/",
|
|
1698
1709
|
version="0.1.0",
|
|
1699
1710
|
capabilities=AgentCapabilities(streaming=True),
|
|
1700
1711
|
skills=[
|
|
@@ -1707,6 +1718,13 @@ card = AgentCard(
|
|
|
1707
1718
|
],
|
|
1708
1719
|
default_input_modes=["text"],
|
|
1709
1720
|
default_output_modes=["text"],
|
|
1721
|
+
supported_interfaces=[
|
|
1722
|
+
AgentInterface(
|
|
1723
|
+
protocol_binding="JSONRPC",
|
|
1724
|
+
protocol_version="1.0",
|
|
1725
|
+
url="http://localhost:9000/",
|
|
1726
|
+
)
|
|
1727
|
+
],
|
|
1710
1728
|
)
|
|
1711
1729
|
|
|
1712
1730
|
if __name__ == "__main__":
|
|
@@ -1858,7 +1876,7 @@ description = "AgentCore A2A Agent using LangChain + LangGraph"
|
|
|
1858
1876
|
readme = "README.md"
|
|
1859
1877
|
requires-python = ">=3.10"
|
|
1860
1878
|
dependencies = [
|
|
1861
|
-
"a2a-sdk >= 0.
|
|
1879
|
+
"a2a-sdk[http-server] >= 1.0.1, < 2.0.0",
|
|
1862
1880
|
{{#if (eq modelProvider "Anthropic")}}"langchain-anthropic >= 0.3.0",
|
|
1863
1881
|
{{/if}}{{#if (eq modelProvider "Bedrock")}}"langchain-aws >= 0.2.0",
|
|
1864
1882
|
{{/if}}{{#if (eq modelProvider "Gemini")}}"langchain-google-genai >= 2.0.0",
|
|
@@ -1866,7 +1884,7 @@ dependencies = [
|
|
|
1866
1884
|
{{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
|
|
1867
1885
|
{{/if}}"aws-opentelemetry-distro",
|
|
1868
1886
|
"opentelemetry-instrumentation-langchain >= 0.59.0",
|
|
1869
|
-
"bedrock-agentcore[a2a] >= 1.
|
|
1887
|
+
"bedrock-agentcore[a2a-v1] >= 1.19.0",
|
|
1870
1888
|
"botocore[crt] >= 1.35.0",
|
|
1871
1889
|
"langgraph >= 0.2.0",
|
|
1872
1890
|
]
|
|
@@ -2209,7 +2227,7 @@ readme = "README.md"
|
|
|
2209
2227
|
requires-python = ">=3.10"
|
|
2210
2228
|
dependencies = [
|
|
2211
2229
|
{{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
|
|
2212
|
-
{{/if}}"a2a-sdk[all] >= 0.
|
|
2230
|
+
{{/if}}"a2a-sdk[all] >= 0.3.0, < 0.4.0",
|
|
2213
2231
|
"aws-opentelemetry-distro",
|
|
2214
2232
|
"bedrock-agentcore[a2a] >= 1.9.1",
|
|
2215
2233
|
"botocore[crt] >= 1.35.0",
|
|
@@ -3317,6 +3335,11 @@ file defines a Starlette ASGI app with the AutoGen framework running within.
|
|
|
3317
3335
|
|
|
3318
3336
|
\`model/load.py\` instantiates your chosen model provider.
|
|
3319
3337
|
|
|
3338
|
+
## Input Validation
|
|
3339
|
+
|
|
3340
|
+
Validate invocation input before forwarding it to the agent framework. Keep plain prompts typed as strings and pass
|
|
3341
|
+
only prompt text to the agent.
|
|
3342
|
+
|
|
3320
3343
|
## Environment Variables
|
|
3321
3344
|
|
|
3322
3345
|
| Variable | Required | Description |
|
|
@@ -3520,6 +3543,8 @@ async def invoke(payload, context):
|
|
|
3520
3543
|
|
|
3521
3544
|
# Process the user prompt
|
|
3522
3545
|
prompt = payload.get("prompt", "What can you help me with?")
|
|
3546
|
+
if not isinstance(prompt, str):
|
|
3547
|
+
raise ValueError("prompt must be a string")
|
|
3523
3548
|
session_id = getattr(context, "session_id", "default-session")
|
|
3524
3549
|
|
|
3525
3550
|
# Reuse the per-session agent (preserves conversation history)
|
|
@@ -3773,6 +3798,11 @@ file defines a Starlette ASGI app with the Google ADK framework running within.
|
|
|
3773
3798
|
|
|
3774
3799
|
\`model/load.py\` instantiates your chosen model provider (Gemini).
|
|
3775
3800
|
|
|
3801
|
+
## Input Validation
|
|
3802
|
+
|
|
3803
|
+
Validate invocation input before forwarding it to the agent framework. Keep plain prompts typed as strings and pass
|
|
3804
|
+
only prompt text to the agent.
|
|
3805
|
+
|
|
3776
3806
|
## Environment Variables
|
|
3777
3807
|
|
|
3778
3808
|
| Variable | Required | Description |
|
|
@@ -4040,6 +4070,8 @@ async def invoke(payload, context):
|
|
|
4040
4070
|
|
|
4041
4071
|
# Process the user prompt
|
|
4042
4072
|
prompt = payload.get("prompt", "What can you help me with?")
|
|
4073
|
+
if not isinstance(prompt, str):
|
|
4074
|
+
raise ValueError("prompt must be a string")
|
|
4043
4075
|
session_id = getattr(context, "session_id", "default_session")
|
|
4044
4076
|
user_id = payload.get("user_id", "default_user")
|
|
4045
4077
|
|
|
@@ -4233,6 +4265,11 @@ file defines a Starlette ASGI app with the LangChain/LangGraph framework running
|
|
|
4233
4265
|
|
|
4234
4266
|
\`model/load.py\` instantiates your chosen model provider.
|
|
4235
4267
|
|
|
4268
|
+
## Input Validation
|
|
4269
|
+
|
|
4270
|
+
Validate invocation input before forwarding it to the agent framework. Keep plain prompts typed as strings and pass
|
|
4271
|
+
only prompt text to the agent.
|
|
4272
|
+
|
|
4236
4273
|
## Environment Variables
|
|
4237
4274
|
|
|
4238
4275
|
| Variable | Required | Description |
|
|
@@ -4492,6 +4529,8 @@ async def invoke(payload, context):
|
|
|
4492
4529
|
|
|
4493
4530
|
# Process the user prompt
|
|
4494
4531
|
prompt = payload.get("prompt", "What can you help me with?")
|
|
4532
|
+
if not isinstance(prompt, str):
|
|
4533
|
+
raise ValueError("prompt must be a string")
|
|
4495
4534
|
session_id = getattr(context, "session_id", "default-session")
|
|
4496
4535
|
touch_thread(session_id)
|
|
4497
4536
|
log.info(f"Agent input: {prompt}")
|
|
@@ -4511,6 +4550,8 @@ async def invoke(payload, context):
|
|
|
4511
4550
|
|
|
4512
4551
|
# Process the user prompt
|
|
4513
4552
|
prompt = payload.get("prompt", "What can you help me with?")
|
|
4553
|
+
if not isinstance(prompt, str):
|
|
4554
|
+
raise ValueError("prompt must be a string")
|
|
4514
4555
|
session_id = getattr(context, "session_id", "default-session")
|
|
4515
4556
|
touch_thread(session_id)
|
|
4516
4557
|
log.info(f"Agent input: {prompt}")
|
|
@@ -4768,8 +4809,8 @@ dependencies = [
|
|
|
4768
4809
|
"aws-opentelemetry-distro",
|
|
4769
4810
|
"opentelemetry-instrumentation-langchain >= 0.59.0",
|
|
4770
4811
|
"langgraph >= 1.0.2",
|
|
4771
|
-
"mcp
|
|
4772
|
-
"langchain-mcp-adapters >= 0.
|
|
4812
|
+
"mcp ~= 1.24.0",
|
|
4813
|
+
"langchain-mcp-adapters >= 0.3.1",
|
|
4773
4814
|
"langchain >= 1.0.3",
|
|
4774
4815
|
"bedrock-agentcore >= 1.8.0",
|
|
4775
4816
|
"botocore[crt] >= 1.35.0",
|
|
@@ -4811,6 +4852,11 @@ file defines a Starlette ASGI app with the OpenAI Agents SDK framework running w
|
|
|
4811
4852
|
|
|
4812
4853
|
\`model/load.py\` instantiates your chosen model provider (OpenAI).
|
|
4813
4854
|
|
|
4855
|
+
## Input Validation
|
|
4856
|
+
|
|
4857
|
+
Validate invocation input before forwarding it to the agent framework. Keep plain prompts typed as strings and pass
|
|
4858
|
+
only prompt text to the agent.
|
|
4859
|
+
|
|
4814
4860
|
## Environment Variables
|
|
4815
4861
|
|
|
4816
4862
|
| Variable | Required | Description |
|
|
@@ -5071,6 +5117,8 @@ async def invoke(payload, context):
|
|
|
5071
5117
|
|
|
5072
5118
|
# Process the user prompt
|
|
5073
5119
|
prompt = payload.get("prompt", "What can you help me with?")
|
|
5120
|
+
if not isinstance(prompt, str):
|
|
5121
|
+
raise ValueError("prompt must be a string")
|
|
5074
5122
|
session_id = getattr(context, "session_id", "default-session")
|
|
5075
5123
|
session = get_session(session_id)
|
|
5076
5124
|
|
|
@@ -5256,6 +5304,12 @@ file defines a Starlette ASGI app with the chosen Agent framework SDK running wi
|
|
|
5256
5304
|
|
|
5257
5305
|
\`model/load.py\` instantiates your chosen model provider.
|
|
5258
5306
|
|
|
5307
|
+
## Input Validation
|
|
5308
|
+
|
|
5309
|
+
Validate invocation input before forwarding it to Strands. Keep plain prompts typed as strings. If the app accepts a
|
|
5310
|
+
caller-supplied message history, retain \`strip_trailing_tool_use()\`, which normalizes the history tail before
|
|
5311
|
+
invoking the agent.
|
|
5312
|
+
|
|
5259
5313
|
## Environment Variables
|
|
5260
5314
|
|
|
5261
5315
|
| Variable | Required | Description |
|
|
@@ -5812,17 +5866,53 @@ get_or_create_agent = agent_factory()
|
|
|
5812
5866
|
{{/if}}
|
|
5813
5867
|
|
|
5814
5868
|
|
|
5869
|
+
def strip_trailing_tool_use(messages: Any) -> list[dict]:
|
|
5870
|
+
"""Strip toolUse blocks from the tail until the last message has none."""
|
|
5871
|
+
if not isinstance(messages, list):
|
|
5872
|
+
raise ValueError("messages must be a list")
|
|
5873
|
+
|
|
5874
|
+
messages = list(messages)
|
|
5875
|
+
while messages:
|
|
5876
|
+
last = messages[-1]
|
|
5877
|
+
if not isinstance(last, dict):
|
|
5878
|
+
raise ValueError("each message must be an object")
|
|
5879
|
+
original_content = last.get("content", [])
|
|
5880
|
+
if not isinstance(original_content, list) or not all(isinstance(block, dict) for block in original_content):
|
|
5881
|
+
raise ValueError("each message content value must be a list of content blocks")
|
|
5882
|
+
|
|
5883
|
+
content = [block for block in original_content if "toolUse" not in block]
|
|
5884
|
+
if len(content) == len(original_content):
|
|
5885
|
+
break
|
|
5886
|
+
if content:
|
|
5887
|
+
messages[-1] = {**last, "content": content}
|
|
5888
|
+
break
|
|
5889
|
+
messages.pop()
|
|
5890
|
+
|
|
5891
|
+
return messages
|
|
5892
|
+
|
|
5893
|
+
|
|
5815
5894
|
def _extract_prompt(payload: dict):
|
|
5816
|
-
"""Accept harness
|
|
5895
|
+
"""Accept validated harness messages, tool results, or a plain prompt string."""
|
|
5896
|
+
if not isinstance(payload, dict):
|
|
5897
|
+
raise ValueError("payload must be a JSON object")
|
|
5817
5898
|
if "messages" in payload:
|
|
5818
|
-
return payload["messages"]
|
|
5899
|
+
return strip_trailing_tool_use(payload["messages"])
|
|
5819
5900
|
if "tool_results" in payload:
|
|
5901
|
+
tool_results = payload["tool_results"]
|
|
5902
|
+
if not isinstance(tool_results, list) or not all(
|
|
5903
|
+
isinstance(tool_result, dict) and isinstance(tool_result.get("toolUseId"), str)
|
|
5904
|
+
for tool_result in tool_results
|
|
5905
|
+
):
|
|
5906
|
+
raise ValueError("tool_results must contain objects with a toolUseId string")
|
|
5820
5907
|
return [{"role": "user", "content": [{"toolResult": {
|
|
5821
5908
|
"toolUseId": tr["toolUseId"],
|
|
5822
5909
|
"status": tr.get("status", "success"),
|
|
5823
5910
|
"content": tr.get("content", []),
|
|
5824
|
-
}} for tr in
|
|
5825
|
-
|
|
5911
|
+
}} for tr in tool_results]}]
|
|
5912
|
+
prompt = payload.get("prompt", "")
|
|
5913
|
+
if not isinstance(prompt, str):
|
|
5914
|
+
raise ValueError("prompt must be a string")
|
|
5915
|
+
return prompt
|
|
5826
5916
|
|
|
5827
5917
|
|
|
5828
5918
|
def _has_inline_function_call(messages) -> bool:
|
|
@@ -6425,7 +6515,7 @@ dependencies = [
|
|
|
6425
6515
|
"bedrock-agentcore >= 1.9.1",
|
|
6426
6516
|
"botocore[crt] >= 1.35.0",
|
|
6427
6517
|
{{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
|
|
6428
|
-
{{/if}}"mcp
|
|
6518
|
+
{{/if}}"mcp ~= 1.24.0",
|
|
6429
6519
|
{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
|
|
6430
6520
|
{{/if}}{{#if (eq modelProvider "LiteLLM")}}"litellm >= 1.0.0",
|
|
6431
6521
|
{{/if}}{{#if bedrockMantle}}"openai >= 1.0.0",
|
|
@@ -7178,7 +7268,7 @@ description = "AgentCore MCP Server"
|
|
|
7178
7268
|
readme = "README.md"
|
|
7179
7269
|
requires-python = ">=3.10"
|
|
7180
7270
|
dependencies = [
|
|
7181
|
-
"mcp
|
|
7271
|
+
"mcp ~= 1.24.0",
|
|
7182
7272
|
]
|
|
7183
7273
|
|
|
7184
7274
|
[tool.hatch.build.targets.wheel]
|
|
@@ -7290,6 +7380,11 @@ Run your agent locally:
|
|
|
7290
7380
|
agentcore dev
|
|
7291
7381
|
\`\`\`
|
|
7292
7382
|
|
|
7383
|
+
### Validate Invocation Input
|
|
7384
|
+
|
|
7385
|
+
Validate runtime invocation payloads before forwarding them to an agent framework. Keep user prompts typed as strings
|
|
7386
|
+
and pass only prompt text to the agent.
|
|
7387
|
+
|
|
7293
7388
|
### Deployment
|
|
7294
7389
|
|
|
7295
7390
|
Deploy to AWS:
|
|
@@ -7384,6 +7479,8 @@ Tags defined in \`agentcore.json\` flow through to deployed CloudFormation resou
|
|
|
7384
7479
|
\`agentcore validate\` to check.
|
|
7385
7480
|
4. **Resource Removal:** Use \`agentcore remove\` to remove resources. Run \`agentcore deploy\` after removal to tear down
|
|
7386
7481
|
deployed infrastructure.
|
|
7482
|
+
5. **Invocation Input:** Validate runtime payloads and require text prompts to be strings. If a Strands app accepts a
|
|
7483
|
+
caller-supplied message history, normalize the history tail with \`strip_trailing_tool_use()\` before invocation.
|
|
7387
7484
|
|
|
7388
7485
|
## Directory Structure
|
|
7389
7486
|
|
|
@@ -7597,6 +7694,11 @@ defines an HTTP server that streams tokens from your chosen Agent framework SDK.
|
|
|
7597
7694
|
|
|
7598
7695
|
\`model/load.ts\` instantiates your chosen model provider.
|
|
7599
7696
|
|
|
7697
|
+
## Input Validation
|
|
7698
|
+
|
|
7699
|
+
The generated Zod request schema keeps plain prompts typed as strings before forwarding them to Strands. Retain this
|
|
7700
|
+
validation when extending the request shape, and pass only prompt text to the agent.
|
|
7701
|
+
|
|
7600
7702
|
## Environment Variables
|
|
7601
7703
|
|
|
7602
7704
|
| Variable | Required | Description |
|
|
@@ -7685,6 +7787,10 @@ const SYSTEM_PROMPT = \`
|
|
|
7685
7787
|
You are a helpful assistant. Use tools when appropriate.
|
|
7686
7788
|
\`;
|
|
7687
7789
|
|
|
7790
|
+
const requestSchema = z.object({
|
|
7791
|
+
prompt: z.string().default(''),
|
|
7792
|
+
});
|
|
7793
|
+
|
|
7688
7794
|
{{#if hasMemory}}
|
|
7689
7795
|
const agentCache = new Map<string, Agent>();
|
|
7690
7796
|
|
|
@@ -7738,7 +7844,8 @@ async function getOrCreateAgent(sessionId: string): Promise<Agent> {
|
|
|
7738
7844
|
|
|
7739
7845
|
const app = new BedrockAgentCoreApp({
|
|
7740
7846
|
invocationHandler: {
|
|
7741
|
-
|
|
7847
|
+
requestSchema,
|
|
7848
|
+
async *process(payload, context) {
|
|
7742
7849
|
{{#if hasMemory}}
|
|
7743
7850
|
const sessionId = context?.sessionId ?? 'default-session';
|
|
7744
7851
|
const actorId = getActorId(payload, context);
|
|
@@ -7750,7 +7857,7 @@ const app = new BedrockAgentCoreApp({
|
|
|
7750
7857
|
|
|
7751
7858
|
{{#if hasMemory}}
|
|
7752
7859
|
try {
|
|
7753
|
-
for await (const event of agent.stream(payload.prompt
|
|
7860
|
+
for await (const event of agent.stream(payload.prompt)) {
|
|
7754
7861
|
if (
|
|
7755
7862
|
event.type === 'modelStreamUpdateEvent' &&
|
|
7756
7863
|
event.event?.type === 'modelContentBlockDeltaEvent' &&
|
|
@@ -7774,7 +7881,7 @@ const app = new BedrockAgentCoreApp({
|
|
|
7774
7881
|
// e.g. Anthropic). Restoring on error keeps the session reusable.
|
|
7775
7882
|
const snapshot = agent.takeSnapshot({ include: ['messages'] });
|
|
7776
7883
|
try {
|
|
7777
|
-
for await (const event of agent.stream(payload.prompt
|
|
7884
|
+
for await (const event of agent.stream(payload.prompt)) {
|
|
7778
7885
|
if (
|
|
7779
7886
|
event.type === 'modelStreamUpdateEvent' &&
|
|
7780
7887
|
event.event?.type === 'modelContentBlockDeltaEvent' &&
|
|
@@ -8054,6 +8161,11 @@ defines an HTTP app that streams tokens using the Vercel AI SDK's \`streamText\`
|
|
|
8054
8161
|
|
|
8055
8162
|
\`model/load.ts\` instantiates your chosen model provider.
|
|
8056
8163
|
|
|
8164
|
+
## Input Validation
|
|
8165
|
+
|
|
8166
|
+
The generated Zod request schema keeps plain prompts typed as strings before forwarding them to the agent framework.
|
|
8167
|
+
Retain this validation when extending the request shape, and pass only prompt text to the agent.
|
|
8168
|
+
|
|
8057
8169
|
## Environment Variables
|
|
8058
8170
|
|
|
8059
8171
|
| Variable | Required | Description |
|
|
@@ -8108,10 +8220,15 @@ Thumbs.db
|
|
|
8108
8220
|
exports[`Assets Directory Snapshots > TypeScript assets > typescript/typescript/http/vercelai/base/main.ts should match snapshot 1`] = `
|
|
8109
8221
|
"import { BedrockAgentCoreApp } from 'bedrock-agentcore/runtime';
|
|
8110
8222
|
import { streamText, type ModelMessage } from 'ai';
|
|
8223
|
+
import { z } from 'zod';
|
|
8111
8224
|
import { loadModel } from './model/load.js';
|
|
8112
8225
|
|
|
8113
8226
|
const SYSTEM_PROMPT = \`You are a helpful assistant.\`;
|
|
8114
8227
|
|
|
8228
|
+
const requestSchema = z.object({
|
|
8229
|
+
prompt: z.string().default(''),
|
|
8230
|
+
});
|
|
8231
|
+
|
|
8115
8232
|
const HISTORY_LIMIT = 128;
|
|
8116
8233
|
|
|
8117
8234
|
// Keeps one message history per sessionId so each session remembers its own
|
|
@@ -8140,10 +8257,11 @@ function getHistory(sessionId: string): ModelMessage[] {
|
|
|
8140
8257
|
|
|
8141
8258
|
const app = new BedrockAgentCoreApp({
|
|
8142
8259
|
invocationHandler: {
|
|
8143
|
-
|
|
8260
|
+
requestSchema,
|
|
8261
|
+
async *process(payload, context) {
|
|
8144
8262
|
const sessionId = context?.sessionId ?? 'default-session';
|
|
8145
8263
|
const history = getHistory(sessionId);
|
|
8146
|
-
const userMessage: ModelMessage = { role: 'user', content: payload.prompt
|
|
8264
|
+
const userMessage: ModelMessage = { role: 'user', content: payload.prompt };
|
|
8147
8265
|
|
|
8148
8266
|
const model = await loadModel();
|
|
8149
8267
|
const result = streamText({
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
|
|
5
|
+
const ASSETS_DIR = resolve(__dirname, '..');
|
|
6
|
+
|
|
7
|
+
const PYTHON_HTTP_ENTRYPOINTS = [
|
|
8
|
+
'python/http/autogen/base/main.py',
|
|
9
|
+
'python/http/googleadk/base/main.py',
|
|
10
|
+
'python/http/langchain_langgraph/base/main.py',
|
|
11
|
+
'python/http/openaiagents/base/main.py',
|
|
12
|
+
'python/http/strands/base/main.py',
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const TYPESCRIPT_HTTP_ENTRYPOINTS = [
|
|
16
|
+
'typescript/http/strands/base/main.ts',
|
|
17
|
+
'typescript/http/vercelai/base/main.ts',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
describe('HTTP agent template input validation', () => {
|
|
21
|
+
it.each(PYTHON_HTTP_ENTRYPOINTS)('%s rejects non-string prompts', templatePath => {
|
|
22
|
+
const template = readFileSync(resolve(ASSETS_DIR, templatePath), 'utf8');
|
|
23
|
+
|
|
24
|
+
expect(template).toContain('if not isinstance(prompt, str):');
|
|
25
|
+
expect(template).toContain('raise ValueError("prompt must be a string")');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it.each(TYPESCRIPT_HTTP_ENTRYPOINTS)('%s validates prompts with Zod', templatePath => {
|
|
29
|
+
const template = readFileSync(resolve(ASSETS_DIR, templatePath), 'utf8');
|
|
30
|
+
|
|
31
|
+
expect(template).toContain("prompt: z.string().default('')");
|
|
32
|
+
expect(template).toContain('requestSchema,');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('strips toolUse blocks from the Python Strands message-history tail', () => {
|
|
36
|
+
const template = readFileSync(resolve(ASSETS_DIR, 'python/http/strands/base/main.py'), 'utf8');
|
|
37
|
+
|
|
38
|
+
expect(template).toContain('def strip_trailing_tool_use(messages: Any) -> list[dict]:');
|
|
39
|
+
expect(template).toContain('while messages:');
|
|
40
|
+
expect(template).toContain('content = [block for block in original_content if "toolUse" not in block]');
|
|
41
|
+
expect(template).toContain('messages.pop()');
|
|
42
|
+
expect(template).toContain('return strip_trailing_tool_use(payload["messages"])');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -23,6 +23,8 @@ Tags defined in `agentcore.json` flow through to deployed CloudFormation resourc
|
|
|
23
23
|
`agentcore validate` to check.
|
|
24
24
|
4. **Resource Removal:** Use `agentcore remove` to remove resources. Run `agentcore deploy` after removal to tear down
|
|
25
25
|
deployed infrastructure.
|
|
26
|
+
5. **Invocation Input:** Validate runtime payloads and require text prompts to be strings. If a Strands app accepts a
|
|
27
|
+
caller-supplied message history, normalize the history tail with `strip_trailing_tool_use()` before invocation.
|
|
26
28
|
|
|
27
29
|
## Directory Structure
|
|
28
30
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Version": "2012-10-17",
|
|
3
|
+
"Statement": [
|
|
4
|
+
{
|
|
5
|
+
"Effect": "Allow",
|
|
6
|
+
"Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],
|
|
7
|
+
"Resource": "arn:*:logs:*:*:log-group:/aws/lambda/*"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"Effect": "Allow",
|
|
11
|
+
"Action": ["bedrock:InvokeModel"],
|
|
12
|
+
"Resource": "*"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{{#if ModelProviderBedrock}}
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
# litellm's Bedrock provider reads AWS_REGION_NAME; Lambda only sets AWS_REGION/AWS_DEFAULT_REGION.
|
|
5
|
+
os.environ.setdefault("AWS_REGION_NAME", os.environ.get("AWS_REGION", "us-west-2"))
|
|
6
|
+
|
|
7
|
+
from autoevals import {{ EvaluatorClass }}, init
|
|
8
|
+
from autoevals.litellm import LiteLLMClient
|
|
9
|
+
|
|
10
|
+
from bedrock_agentcore.evaluation.custom_code_based_evaluators import (
|
|
11
|
+
EvaluatorInput,
|
|
12
|
+
EvaluatorOutput,
|
|
13
|
+
custom_code_based_evaluator,
|
|
14
|
+
)
|
|
15
|
+
from bedrock_agentcore.evaluation.custom_code_based_evaluators.third_party.autoevals import AutoEvalsAdapter
|
|
16
|
+
|
|
17
|
+
client = LiteLLMClient()
|
|
18
|
+
init(client=client, default_model="{{ Model }}")
|
|
19
|
+
|
|
20
|
+
adapter = AutoEvalsAdapter(metric={{ EvaluatorClass }}(client=client, model="{{ Model }}"){{#if EvaluatorParams}}, {{{ EvaluatorParams }}}{{/if}})
|
|
21
|
+
{{else}}
|
|
22
|
+
from autoevals import {{ EvaluatorClass }}
|
|
23
|
+
|
|
24
|
+
from bedrock_agentcore.evaluation.custom_code_based_evaluators import (
|
|
25
|
+
EvaluatorInput,
|
|
26
|
+
EvaluatorOutput,
|
|
27
|
+
custom_code_based_evaluator,
|
|
28
|
+
)
|
|
29
|
+
from bedrock_agentcore.evaluation.custom_code_based_evaluators.third_party.autoevals import AutoEvalsAdapter
|
|
30
|
+
|
|
31
|
+
adapter = AutoEvalsAdapter(metric={{ EvaluatorClass }}({{#if Model}}model="{{ Model }}"{{/if}}){{#if EvaluatorParams}}, {{{ EvaluatorParams }}}{{/if}})
|
|
32
|
+
{{/if}}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@custom_code_based_evaluator()
|
|
36
|
+
def handler(evaluator_input: EvaluatorInput, context) -> EvaluatorOutput:
|
|
37
|
+
return adapter(evaluator_input, context)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "{{ Name }}"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "AgentCore Code-Based Evaluator (Autoevals)"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"bedrock-agentcore[autoevals]",
|
|
12
|
+
"autoevals>=0.0.80,<1.0.0",
|
|
13
|
+
{{#if ModelProviderBedrock}}
|
|
14
|
+
# autoevals grades via LiteLLMClient -> Bedrock (Converse); litellm replaces the openai judge
|
|
15
|
+
"litellm>=1.60,<1.85",
|
|
16
|
+
{{else}}
|
|
17
|
+
"openai>=1.0.0",
|
|
18
|
+
{{/if}}
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[tool.hatch.build.targets.wheel]
|
|
22
|
+
packages = ["."]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Version": "2012-10-17",
|
|
3
|
+
"Statement": [
|
|
4
|
+
{
|
|
5
|
+
"Effect": "Allow",
|
|
6
|
+
"Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],
|
|
7
|
+
"Resource": "arn:*:logs:*:*:log-group:/aws/lambda/*"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"Effect": "Allow",
|
|
11
|
+
"Action": ["bedrock:InvokeModel"],
|
|
12
|
+
"Resource": "*"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
os.environ.setdefault("DEEPEVAL_RESULTS_FOLDER", "/tmp/.deepeval")
|
|
4
|
+
os.environ.setdefault("DEEPEVAL_TELEMETRY_OPT_OUT", "YES")
|
|
5
|
+
os.chdir("/tmp")
|
|
6
|
+
|
|
7
|
+
{{#if ModelProviderBedrock}}
|
|
8
|
+
from deepeval.models import AmazonBedrockModel
|
|
9
|
+
{{/if}}
|
|
10
|
+
from deepeval.metrics import {{ EvaluatorClass }}
|
|
11
|
+
|
|
12
|
+
from bedrock_agentcore.evaluation.custom_code_based_evaluators import (
|
|
13
|
+
EvaluatorInput,
|
|
14
|
+
EvaluatorOutput,
|
|
15
|
+
custom_code_based_evaluator,
|
|
16
|
+
)
|
|
17
|
+
from bedrock_agentcore.evaluation.custom_code_based_evaluators.third_party.deepeval import DeepEvalAdapter
|
|
18
|
+
|
|
19
|
+
{{#if ModelProviderBedrock}}
|
|
20
|
+
model = AmazonBedrockModel(model="{{ Model }}", region=os.environ.get("AWS_REGION", "us-west-2"))
|
|
21
|
+
adapter = DeepEvalAdapter(metric={{ EvaluatorClass }}(model=model{{#if EvaluatorParams}}, {{{ EvaluatorParams }}}{{/if}}))
|
|
22
|
+
{{else}}
|
|
23
|
+
adapter = DeepEvalAdapter(metric={{ EvaluatorClass }}({{{ EvaluatorParams }}}))
|
|
24
|
+
{{/if}}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@custom_code_based_evaluator()
|
|
28
|
+
def handler(evaluator_input: EvaluatorInput, context) -> EvaluatorOutput:
|
|
29
|
+
return adapter(evaluator_input, context)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "{{ Name }}"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "AgentCore Code-Based Evaluator (DeepEval)"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"bedrock-agentcore[deepeval]",
|
|
12
|
+
"deepeval>=2.0.0,<3.0.0",
|
|
13
|
+
{{#if ModelProviderBedrock}}
|
|
14
|
+
"aiobotocore>=2.13.0",
|
|
15
|
+
{{/if}}
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[tool.hatch.build.targets.wheel]
|
|
19
|
+
packages = ["."]
|
|
@@ -9,7 +9,7 @@ description = "MCP Server demonstrating HTTP tool patterns"
|
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
dependencies = [
|
|
12
|
-
"mcp[cli]
|
|
12
|
+
"mcp[cli] ~= 1.24.0",
|
|
13
13
|
"httpx >= 0.27.0",
|
|
14
14
|
"opentelemetry-distro",
|
|
15
15
|
"opentelemetry-exporter-otlp",
|