@aws/agentcore 1.0.0-preview.17 → 1.0.0-preview.18
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/__tests__/__snapshots__/assets.snapshot.test.ts.snap +271 -7
- package/dist/assets/python/http/strands/base/main.py +13 -3
- package/dist/assets/python/http/strands/base/mcp_client/client.py +4 -1
- package/dist/assets/python/http/strands/base/model/load.py +116 -0
- package/dist/assets/python/http/strands/base/model/mantle_compat.py +21 -0
- package/dist/assets/python/http/strands/base/pyproject.toml +3 -0
- package/dist/assets/typescript/http/strands/base/main.ts +48 -0
- package/dist/assets/typescript/http/strands/base/package.json +3 -2
- package/dist/assets/typescript/http/strands/capabilities/memory/memory.ts +52 -0
- package/dist/cli/index.mjs +523 -557
- package/dist/schema/constants.d.ts +3 -2
- package/dist/schema/constants.d.ts.map +1 -1
- package/dist/schema/constants.js +7 -4
- package/dist/schema/constants.js.map +1 -1
- package/dist/schema/schemas/agent-env.d.ts +43 -0
- package/dist/schema/schemas/agent-env.d.ts.map +1 -1
- package/dist/schema/schemas/agent-env.js +11 -0
- package/dist/schema/schemas/agent-env.js.map +1 -1
- package/dist/schema/schemas/agentcore-project.d.ts +43 -0
- package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
- package/dist/schema/schemas/connections.d.ts +185 -0
- package/dist/schema/schemas/connections.d.ts.map +1 -0
- package/dist/schema/schemas/connections.js +176 -0
- package/dist/schema/schemas/connections.js.map +1 -0
- package/dist/schema/schemas/index.d.ts +1 -0
- package/dist/schema/schemas/index.d.ts.map +1 -1
- package/dist/schema/schemas/index.js +1 -0
- package/dist/schema/schemas/index.js.map +1 -1
- package/dist/schema/schemas/primitives/harness.d.ts +42 -0
- package/dist/schema/schemas/primitives/harness.d.ts.map +1 -1
- package/dist/schema/schemas/primitives/harness.js +4 -0
- package/dist/schema/schemas/primitives/harness.js.map +1 -1
- package/package.json +1 -1
|
@@ -831,6 +831,7 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
|
|
|
831
831
|
"python/http/strands/base/mcp_client/client.py",
|
|
832
832
|
"python/http/strands/base/model/__init__.py",
|
|
833
833
|
"python/http/strands/base/model/load.py",
|
|
834
|
+
"python/http/strands/base/model/mantle_compat.py",
|
|
834
835
|
"python/http/strands/base/pyproject.toml",
|
|
835
836
|
"python/http/strands/base/skills/fetcher.py",
|
|
836
837
|
"python/http/strands/capabilities/execution-limits/hooks/execution_limits.py",
|
|
@@ -850,6 +851,7 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
|
|
|
850
851
|
"typescript/http/strands/base/model/load.ts",
|
|
851
852
|
"typescript/http/strands/base/package.json",
|
|
852
853
|
"typescript/http/strands/base/tsconfig.json",
|
|
854
|
+
"typescript/http/strands/capabilities/memory/memory.ts",
|
|
853
855
|
"typescript/http/vercelai/base/README.md",
|
|
854
856
|
"typescript/http/vercelai/base/gitignore.template",
|
|
855
857
|
"typescript/http/vercelai/base/main.ts",
|
|
@@ -5276,7 +5278,7 @@ from mcp_client.client import get_streamable_http_mcp_client
|
|
|
5276
5278
|
from memory.session import get_memory_session_manager
|
|
5277
5279
|
{{/if}}
|
|
5278
5280
|
{{#unless hasFileOperations}}
|
|
5279
|
-
{{#if (or needsOs (some gitSkills "credentialArn"))}}
|
|
5281
|
+
{{#if (or needsOs browserIdentifierEnvVar codeInterpreterIdentifierEnvVar (some gitSkills "credentialArn"))}}
|
|
5280
5282
|
import os
|
|
5281
5283
|
{{/if}}
|
|
5282
5284
|
{{/unless}}
|
|
@@ -5362,10 +5364,20 @@ tools.append(add_numbers)
|
|
|
5362
5364
|
{{/unless}}
|
|
5363
5365
|
{{/if}}
|
|
5364
5366
|
{{#if hasBrowser}}
|
|
5365
|
-
|
|
5367
|
+
{{#if browserIdentifierEnvVar}}
|
|
5368
|
+
_browser_id = os.getenv("{{browserIdentifierEnvVar}}")
|
|
5369
|
+
tools.append(AgentCoreBrowser(**({"identifier": _browser_id} if _browser_id else {})).browser)
|
|
5370
|
+
{{else}}
|
|
5371
|
+
tools.append(AgentCoreBrowser().browser)
|
|
5372
|
+
{{/if}}
|
|
5366
5373
|
{{/if}}
|
|
5367
5374
|
{{#if hasCodeInterpreter}}
|
|
5368
|
-
|
|
5375
|
+
{{#if codeInterpreterIdentifierEnvVar}}
|
|
5376
|
+
_code_interpreter_id = os.getenv("{{codeInterpreterIdentifierEnvVar}}")
|
|
5377
|
+
tools.append(AgentCoreCodeInterpreter(**({"identifier": _code_interpreter_id} if _code_interpreter_id else {})).code_interpreter)
|
|
5378
|
+
{{else}}
|
|
5379
|
+
tools.append(AgentCoreCodeInterpreter().code_interpreter)
|
|
5380
|
+
{{/if}}
|
|
5369
5381
|
{{/if}}
|
|
5370
5382
|
{{#if hasShell}}
|
|
5371
5383
|
@tool
|
|
@@ -5908,7 +5920,10 @@ from bedrock_agentcore.identity import requires_access_token
|
|
|
5908
5920
|
@requires_access_token(
|
|
5909
5921
|
provider_name="{{credentialProviderName}}",
|
|
5910
5922
|
scopes=[{{#if scopes}}"{{scopes}}"{{/if}}],
|
|
5911
|
-
auth_flow="M2M",
|
|
5923
|
+
auth_flow="{{#if authFlow}}{{authFlow}}{{else}}M2M{{/if}}",
|
|
5924
|
+
{{#if customParameters}}
|
|
5925
|
+
custom_parameters={{safeJson customParameters}},
|
|
5926
|
+
{{/if}}
|
|
5912
5927
|
)
|
|
5913
5928
|
def _get_bearer_token_{{snakeCase name}}(*, access_token: str):
|
|
5914
5929
|
"""Obtain OAuth access token via AgentCore Identity for {{name}}."""
|
|
@@ -6011,6 +6026,67 @@ exports[`Assets Directory Snapshots > Python framework assets > python/python/ht
|
|
|
6011
6026
|
|
|
6012
6027
|
exports[`Assets Directory Snapshots > Python framework assets > python/python/http/strands/base/model/load.py should match snapshot 1`] = `
|
|
6013
6028
|
"{{#if (eq modelProvider "Bedrock")}}
|
|
6029
|
+
{{#if bedrockMantle}}
|
|
6030
|
+
import os
|
|
6031
|
+
|
|
6032
|
+
from aws_bedrock_token_generator import provide_token
|
|
6033
|
+
{{#if (eq mantleApiFormat "chat_completions")}}
|
|
6034
|
+
from strands.models.openai import OpenAIModel
|
|
6035
|
+
{{else}}
|
|
6036
|
+
{{#if mantleProprietary}}
|
|
6037
|
+
from strands.models.openai_responses import OpenAIResponsesModel
|
|
6038
|
+
{{else}}
|
|
6039
|
+
from model.mantle_compat import MantleCompatResponsesModel
|
|
6040
|
+
{{/if}}
|
|
6041
|
+
{{/if}}
|
|
6042
|
+
|
|
6043
|
+
MODEL_ID = "{{modelId}}"
|
|
6044
|
+
|
|
6045
|
+
|
|
6046
|
+
def load_model():
|
|
6047
|
+
"""
|
|
6048
|
+
Get a Bedrock Mantle model client. These OpenAI-compatible models (e.g. openai.gpt-5.5,
|
|
6049
|
+
openai.gpt-oss-120b) are served via the Bedrock Mantle endpoint, NOT the Converse API — so they
|
|
6050
|
+
are invoked through an OpenAI-style client authenticated with a short-lived Bedrock bearer token.
|
|
6051
|
+
Region is read from AWS_REGION (set by the AgentCore runtime).
|
|
6052
|
+
"""
|
|
6053
|
+
region = os.environ.get("AWS_REGION", os.environ.get("AWS_DEFAULT_REGION", "us-east-1"))
|
|
6054
|
+
token = provide_token(region=region)
|
|
6055
|
+
{{#if mantleProprietary}}
|
|
6056
|
+
# Proprietary OpenAI models only work on the /openai/v1 Mantle path.
|
|
6057
|
+
base_url = f"https://bedrock-mantle.{region}.api.aws/openai/v1"
|
|
6058
|
+
{{else}}
|
|
6059
|
+
# Open-source OpenAI models (gpt-oss-*) only work on the /v1 Mantle path.
|
|
6060
|
+
base_url = f"https://bedrock-mantle.{region}.api.aws/v1"
|
|
6061
|
+
{{/if}}
|
|
6062
|
+
client_args = {"api_key": token, "base_url": base_url}
|
|
6063
|
+
|
|
6064
|
+
params = {}
|
|
6065
|
+
{{#if modelMaxTokens}}
|
|
6066
|
+
{{#if (eq mantleApiFormat "chat_completions")}}
|
|
6067
|
+
params["max_completion_tokens"] = {{modelMaxTokens}}
|
|
6068
|
+
{{else}}
|
|
6069
|
+
params["max_output_tokens"] = {{modelMaxTokens}}
|
|
6070
|
+
{{/if}}
|
|
6071
|
+
{{/if}}
|
|
6072
|
+
{{#if modelTemperature}}
|
|
6073
|
+
params["temperature"] = {{modelTemperature}}
|
|
6074
|
+
{{/if}}
|
|
6075
|
+
{{#if modelTopP}}
|
|
6076
|
+
params["top_p"] = {{modelTopP}}
|
|
6077
|
+
{{/if}}
|
|
6078
|
+
{{#if (eq mantleApiFormat "chat_completions")}}
|
|
6079
|
+
return OpenAIModel(client_args=client_args, model_id=MODEL_ID, params=params)
|
|
6080
|
+
{{else}}
|
|
6081
|
+
# Responses API: Mantle does not persist responses, so disable server-side storage.
|
|
6082
|
+
params["store"] = False
|
|
6083
|
+
{{#if mantleProprietary}}
|
|
6084
|
+
return OpenAIResponsesModel(client_args=client_args, model_id=MODEL_ID, params=params)
|
|
6085
|
+
{{else}}
|
|
6086
|
+
return MantleCompatResponsesModel(client_args=client_args, model_id=MODEL_ID, params=params)
|
|
6087
|
+
{{/if}}
|
|
6088
|
+
{{/if}}
|
|
6089
|
+
{{else}}
|
|
6014
6090
|
from strands.models.bedrock import BedrockModel
|
|
6015
6091
|
|
|
6016
6092
|
|
|
@@ -6018,6 +6094,7 @@ def load_model() -> BedrockModel:
|
|
|
6018
6094
|
"""Get Bedrock model client using IAM credentials."""
|
|
6019
6095
|
return BedrockModel(model_id="{{#if modelId}}{{modelId}}{{else}}global.anthropic.claude-sonnet-4-5-20250929-v1:0{{/if}}")
|
|
6020
6096
|
{{/if}}
|
|
6097
|
+
{{/if}}
|
|
6021
6098
|
{{#if (eq modelProvider "Anthropic")}}
|
|
6022
6099
|
import os
|
|
6023
6100
|
|
|
@@ -6133,6 +6210,85 @@ def load_model() -> GeminiModel:
|
|
|
6133
6210
|
model_id="{{#if modelId}}{{modelId}}{{else}}gemini-2.5-flash{{/if}}",
|
|
6134
6211
|
)
|
|
6135
6212
|
{{/if}}
|
|
6213
|
+
{{#if (eq modelProvider "LiteLLM")}}
|
|
6214
|
+
import os
|
|
6215
|
+
{{#if litellmAdditionalParams}}
|
|
6216
|
+
import json
|
|
6217
|
+
{{/if}}
|
|
6218
|
+
|
|
6219
|
+
from strands.models.litellm import LiteLLMModel
|
|
6220
|
+
{{#if identityProviders.[0].name}}
|
|
6221
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
6222
|
+
|
|
6223
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
6224
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
6225
|
+
|
|
6226
|
+
|
|
6227
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
6228
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
6229
|
+
"""Fetch API key from AgentCore Identity."""
|
|
6230
|
+
return api_key
|
|
6231
|
+
|
|
6232
|
+
|
|
6233
|
+
def _get_api_key() -> str:
|
|
6234
|
+
"""
|
|
6235
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
6236
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
6237
|
+
"""
|
|
6238
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
6239
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
6240
|
+
if not api_key:
|
|
6241
|
+
raise RuntimeError(
|
|
6242
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
6243
|
+
)
|
|
6244
|
+
return api_key
|
|
6245
|
+
return _agentcore_identity_api_key_provider()
|
|
6246
|
+
{{/if}}
|
|
6247
|
+
|
|
6248
|
+
|
|
6249
|
+
|
|
6250
|
+
|
|
6251
|
+
def load_model() -> LiteLLMModel:
|
|
6252
|
+
"""Get a LiteLLM model client (proxies to the provider encoded in model_id)."""
|
|
6253
|
+
client_args = {}
|
|
6254
|
+
{{#if identityProviders.[0].name}}
|
|
6255
|
+
client_args["api_key"] = _get_api_key()
|
|
6256
|
+
{{/if}}
|
|
6257
|
+
{{#if litellmApiBase}}
|
|
6258
|
+
client_args["api_base"] = {{safeJson litellmApiBase}}
|
|
6259
|
+
{{/if}}
|
|
6260
|
+
params = {{#if litellmAdditionalParams}}json.loads({{pyJsonStr litellmAdditionalParams}}){{else}}{}{{/if}}
|
|
6261
|
+
return LiteLLMModel(
|
|
6262
|
+
client_args=client_args,
|
|
6263
|
+
model_id="{{#if modelId}}{{modelId}}{{else}}bedrock/us.anthropic.claude-sonnet-4-5-20250514-v1:0{{/if}}",
|
|
6264
|
+
params=params,
|
|
6265
|
+
)
|
|
6266
|
+
{{/if}}
|
|
6267
|
+
"
|
|
6268
|
+
`;
|
|
6269
|
+
|
|
6270
|
+
exports[`Assets Directory Snapshots > Python framework assets > python/python/http/strands/base/model/mantle_compat.py should match snapshot 1`] = `
|
|
6271
|
+
"from strands.models.openai_responses import OpenAIResponsesModel
|
|
6272
|
+
|
|
6273
|
+
|
|
6274
|
+
class MantleCompatResponsesModel(OpenAIResponsesModel):
|
|
6275
|
+
"""Workaround for Bedrock Mantle rejecting output_text in EasyInputMessage content arrays.
|
|
6276
|
+
|
|
6277
|
+
Mantle's Pydantic validation only accepts content as a plain string for assistant messages, while
|
|
6278
|
+
real OpenAI accepts both formats. Flatten assistant content arrays to strings so multi-turn works.
|
|
6279
|
+
Used for open-source OpenAI models (gpt-oss-*) on the /v1 Mantle path; proprietary models use the
|
|
6280
|
+
plain OpenAIResponsesModel on /openai/v1.
|
|
6281
|
+
"""
|
|
6282
|
+
|
|
6283
|
+
@classmethod
|
|
6284
|
+
def _format_request_messages(cls, messages):
|
|
6285
|
+
formatted = super()._format_request_messages(messages)
|
|
6286
|
+
for msg in formatted:
|
|
6287
|
+
if msg.get("role") == "assistant" and isinstance(msg.get("content"), list):
|
|
6288
|
+
msg["content"] = "".join(
|
|
6289
|
+
part.get("text", "") for part in msg["content"] if part.get("type") == "output_text"
|
|
6290
|
+
)
|
|
6291
|
+
return formatted
|
|
6136
6292
|
"
|
|
6137
6293
|
`;
|
|
6138
6294
|
|
|
@@ -6155,6 +6311,9 @@ dependencies = [
|
|
|
6155
6311
|
{{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
|
|
6156
6312
|
{{/if}}"mcp >= 1.19.0",
|
|
6157
6313
|
{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
|
|
6314
|
+
{{/if}}{{#if (eq modelProvider "LiteLLM")}}"litellm >= 1.0.0",
|
|
6315
|
+
{{/if}}{{#if bedrockMantle}}"openai >= 1.0.0",
|
|
6316
|
+
"aws-bedrock-token-generator >= 1.0.0",
|
|
6158
6317
|
{{/if}}"strands-agents >= 1.15.0",
|
|
6159
6318
|
{{#if (or hasBrowser hasCodeInterpreter)}}"strands-agents-tools >= 0.1.0",
|
|
6160
6319
|
{{/if}}{{#if hasBrowser}}"nest-asyncio >= 1.5.0",
|
|
@@ -7379,6 +7538,9 @@ import { Agent, McpClient, tool, type ToolList } from '@strands-agents/sdk';
|
|
|
7379
7538
|
import { z } from 'zod';
|
|
7380
7539
|
import { loadModel } from './model/load.js';
|
|
7381
7540
|
import { getStreamableHttpMcpClient } from './mcp_client/client.js';
|
|
7541
|
+
{{#if hasMemory}}
|
|
7542
|
+
import { getActorId, getOrCreateMemoryManager } from './memory/memory.js';
|
|
7543
|
+
{{/if}}
|
|
7382
7544
|
|
|
7383
7545
|
// Define a collection of MCP clients (filter out anything that failed to initialize)
|
|
7384
7546
|
const mcpClients: McpClient[] = [getStreamableHttpMcpClient()].filter(
|
|
@@ -7407,6 +7569,25 @@ const SYSTEM_PROMPT = \`
|
|
|
7407
7569
|
You are a helpful assistant. Use tools when appropriate.
|
|
7408
7570
|
\`;
|
|
7409
7571
|
|
|
7572
|
+
{{#if hasMemory}}
|
|
7573
|
+
const agentCache = new Map<string, Agent>();
|
|
7574
|
+
|
|
7575
|
+
async function getOrCreateAgent(sessionId: string, actorId: string): Promise<Agent> {
|
|
7576
|
+
const key = \`\${actorId}:\${sessionId}\`;
|
|
7577
|
+
let agent = agentCache.get(key);
|
|
7578
|
+
if (agent) return agent;
|
|
7579
|
+
|
|
7580
|
+
const model = await loadModel();
|
|
7581
|
+
agent = new Agent({
|
|
7582
|
+
model,
|
|
7583
|
+
systemPrompt: SYSTEM_PROMPT,
|
|
7584
|
+
tools,
|
|
7585
|
+
memoryManager: getOrCreateMemoryManager(sessionId, actorId) ?? undefined,
|
|
7586
|
+
});
|
|
7587
|
+
agentCache.set(key, agent);
|
|
7588
|
+
return agent;
|
|
7589
|
+
}
|
|
7590
|
+
{{else}}
|
|
7410
7591
|
let cachedAgent: Agent | null = null;
|
|
7411
7592
|
|
|
7412
7593
|
async function getOrCreateAgent(): Promise<Agent> {
|
|
@@ -7420,12 +7601,37 @@ async function getOrCreateAgent(): Promise<Agent> {
|
|
|
7420
7601
|
}
|
|
7421
7602
|
return cachedAgent;
|
|
7422
7603
|
}
|
|
7604
|
+
{{/if}}
|
|
7423
7605
|
|
|
7424
7606
|
const app = new BedrockAgentCoreApp({
|
|
7425
7607
|
invocationHandler: {
|
|
7426
7608
|
async *process(payload: any, context: any) {
|
|
7609
|
+
{{#if hasMemory}}
|
|
7610
|
+
const sessionId = context?.sessionId ?? 'default-session';
|
|
7611
|
+
const actorId = getActorId(payload, context);
|
|
7612
|
+
const agent = await getOrCreateAgent(sessionId, actorId);
|
|
7613
|
+
{{else}}
|
|
7427
7614
|
const agent = await getOrCreateAgent();
|
|
7428
|
-
|
|
7615
|
+
{{/if}}
|
|
7616
|
+
|
|
7617
|
+
{{#if hasMemory}}
|
|
7618
|
+
try {
|
|
7619
|
+
for await (const event of agent.stream(payload.prompt ?? '')) {
|
|
7620
|
+
if (
|
|
7621
|
+
event.type === 'modelStreamUpdateEvent' &&
|
|
7622
|
+
event.event?.type === 'modelContentBlockDeltaEvent' &&
|
|
7623
|
+
event.event.delta?.type === 'textDelta'
|
|
7624
|
+
) {
|
|
7625
|
+
yield { data: event.event.delta.text };
|
|
7626
|
+
}
|
|
7627
|
+
}
|
|
7628
|
+
} finally {
|
|
7629
|
+
// Drain in-flight createEvent calls before the runtime can reclaim
|
|
7630
|
+
// the session microVM. flush() is the durability mechanism — without
|
|
7631
|
+
// it, an idle reclamation can lose the tail of the conversation.
|
|
7632
|
+
await agent.memoryManager?.flush();
|
|
7633
|
+
}
|
|
7634
|
+
{{else}}
|
|
7429
7635
|
for await (const event of agent.stream(payload.prompt ?? '')) {
|
|
7430
7636
|
if (
|
|
7431
7637
|
event.type === 'modelStreamUpdateEvent' &&
|
|
@@ -7435,6 +7641,7 @@ const app = new BedrockAgentCoreApp({
|
|
|
7435
7641
|
yield { data: event.event.delta.text };
|
|
7436
7642
|
}
|
|
7437
7643
|
}
|
|
7644
|
+
{{/if}}
|
|
7438
7645
|
},
|
|
7439
7646
|
},
|
|
7440
7647
|
});
|
|
@@ -7587,8 +7794,9 @@ exports[`Assets Directory Snapshots > TypeScript assets > typescript/typescript/
|
|
|
7587
7794
|
"@google/genai": "^1.40.0",
|
|
7588
7795
|
{{/if}}
|
|
7589
7796
|
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
7590
|
-
"@
|
|
7591
|
-
"
|
|
7797
|
+
"@opentelemetry/api": "^1.9.0",
|
|
7798
|
+
"@strands-agents/sdk": "^1.5.0",
|
|
7799
|
+
"bedrock-agentcore": "^0.3.0",
|
|
7592
7800
|
"tsx": "^4.19.0",
|
|
7593
7801
|
"zod": "^4.4.3"
|
|
7594
7802
|
},
|
|
@@ -7628,6 +7836,62 @@ exports[`Assets Directory Snapshots > TypeScript assets > typescript/typescript/
|
|
|
7628
7836
|
"
|
|
7629
7837
|
`;
|
|
7630
7838
|
|
|
7839
|
+
exports[`Assets Directory Snapshots > TypeScript assets > typescript/typescript/http/strands/capabilities/memory/memory.ts should match snapshot 1`] = `
|
|
7840
|
+
"import { randomUUID } from 'node:crypto';
|
|
7841
|
+
import { MemoryManager } from '@strands-agents/sdk';
|
|
7842
|
+
import { createAgentCoreMemoryStores } from 'bedrock-agentcore/experimental/memory/strands';
|
|
7843
|
+
|
|
7844
|
+
const MEMORY_ID = process.env.{{memoryProviders.[0].envVarName}};
|
|
7845
|
+
|
|
7846
|
+
const CUSTOM_ACTOR_ID_HEADER = 'x-amzn-bedrock-agentcore-runtime-custom-actor-id';
|
|
7847
|
+
|
|
7848
|
+
export function getActorId(payload: any, context: any): string {
|
|
7849
|
+
const raw =
|
|
7850
|
+
context?.headers?.[CUSTOM_ACTOR_ID_HEADER] ||
|
|
7851
|
+
payload?.userId ||
|
|
7852
|
+
context?.sessionId;
|
|
7853
|
+
return typeof raw === 'string' && raw.trim().length > 0 ? raw.trim() : randomUUID();
|
|
7854
|
+
}
|
|
7855
|
+
|
|
7856
|
+
const memoryManagerCache = new Map<string, MemoryManager>();
|
|
7857
|
+
|
|
7858
|
+
export function getOrCreateMemoryManager(sessionId: string, actorId: string): MemoryManager | null {
|
|
7859
|
+
if (!MEMORY_ID) return null;
|
|
7860
|
+
|
|
7861
|
+
const key = \`\${actorId}:\${sessionId}\`;
|
|
7862
|
+
let manager = memoryManagerCache.get(key);
|
|
7863
|
+
if (manager) return manager;
|
|
7864
|
+
|
|
7865
|
+
const stores = createAgentCoreMemoryStores({
|
|
7866
|
+
memoryId: MEMORY_ID,
|
|
7867
|
+
actorId,
|
|
7868
|
+
sessionId,
|
|
7869
|
+
namespaces: [
|
|
7870
|
+
{{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}
|
|
7871
|
+
{ namespace: '/users/{actorId}/facts' },
|
|
7872
|
+
{{/if}}
|
|
7873
|
+
{{#if (includes memoryProviders.[0].strategies "USER_PREFERENCE")}}
|
|
7874
|
+
{ namespace: '/users/{actorId}/preferences' },
|
|
7875
|
+
{{/if}}
|
|
7876
|
+
{{#if (includes memoryProviders.[0].strategies "EPISODIC")}}
|
|
7877
|
+
{ namespace: '/episodes/{actorId}/{sessionId}' },
|
|
7878
|
+
{{/if}}
|
|
7879
|
+
{{#if (includes memoryProviders.[0].strategies "SUMMARIZATION")}}
|
|
7880
|
+
{ namespace: '/summaries/{actorId}/{sessionId}' },
|
|
7881
|
+
{{/if}}
|
|
7882
|
+
],
|
|
7883
|
+
// readMode defaults to 'per-namespace' (one retrieve call per namespace).
|
|
7884
|
+
// Switch to 'subtree' to consolidate to a single hierarchical recall call.
|
|
7885
|
+
extraction: true,
|
|
7886
|
+
});
|
|
7887
|
+
|
|
7888
|
+
manager = new MemoryManager({ stores });
|
|
7889
|
+
memoryManagerCache.set(key, manager);
|
|
7890
|
+
return manager;
|
|
7891
|
+
}
|
|
7892
|
+
"
|
|
7893
|
+
`;
|
|
7894
|
+
|
|
7631
7895
|
exports[`Assets Directory Snapshots > TypeScript assets > typescript/typescript/http/vercelai/base/README.md should match snapshot 1`] = `
|
|
7632
7896
|
"This is a project generated by the AgentCore CLI!
|
|
7633
7897
|
|
|
@@ -66,7 +66,7 @@ from mcp_client.client import get_streamable_http_mcp_client
|
|
|
66
66
|
from memory.session import get_memory_session_manager
|
|
67
67
|
{{/if}}
|
|
68
68
|
{{#unless hasFileOperations}}
|
|
69
|
-
{{#if (or needsOs (some gitSkills "credentialArn"))}}
|
|
69
|
+
{{#if (or needsOs browserIdentifierEnvVar codeInterpreterIdentifierEnvVar (some gitSkills "credentialArn"))}}
|
|
70
70
|
import os
|
|
71
71
|
{{/if}}
|
|
72
72
|
{{/unless}}
|
|
@@ -152,10 +152,20 @@ tools.append(add_numbers)
|
|
|
152
152
|
{{/unless}}
|
|
153
153
|
{{/if}}
|
|
154
154
|
{{#if hasBrowser}}
|
|
155
|
-
|
|
155
|
+
{{#if browserIdentifierEnvVar}}
|
|
156
|
+
_browser_id = os.getenv("{{browserIdentifierEnvVar}}")
|
|
157
|
+
tools.append(AgentCoreBrowser(**({"identifier": _browser_id} if _browser_id else {})).browser)
|
|
158
|
+
{{else}}
|
|
159
|
+
tools.append(AgentCoreBrowser().browser)
|
|
160
|
+
{{/if}}
|
|
156
161
|
{{/if}}
|
|
157
162
|
{{#if hasCodeInterpreter}}
|
|
158
|
-
|
|
163
|
+
{{#if codeInterpreterIdentifierEnvVar}}
|
|
164
|
+
_code_interpreter_id = os.getenv("{{codeInterpreterIdentifierEnvVar}}")
|
|
165
|
+
tools.append(AgentCoreCodeInterpreter(**({"identifier": _code_interpreter_id} if _code_interpreter_id else {})).code_interpreter)
|
|
166
|
+
{{else}}
|
|
167
|
+
tools.append(AgentCoreCodeInterpreter().code_interpreter)
|
|
168
|
+
{{/if}}
|
|
159
169
|
{{/if}}
|
|
160
170
|
{{#if hasShell}}
|
|
161
171
|
@tool
|
|
@@ -18,7 +18,10 @@ from bedrock_agentcore.identity import requires_access_token
|
|
|
18
18
|
@requires_access_token(
|
|
19
19
|
provider_name="{{credentialProviderName}}",
|
|
20
20
|
scopes=[{{#if scopes}}"{{scopes}}"{{/if}}],
|
|
21
|
-
auth_flow="M2M",
|
|
21
|
+
auth_flow="{{#if authFlow}}{{authFlow}}{{else}}M2M{{/if}}",
|
|
22
|
+
{{#if customParameters}}
|
|
23
|
+
custom_parameters={{safeJson customParameters}},
|
|
24
|
+
{{/if}}
|
|
22
25
|
)
|
|
23
26
|
def _get_bearer_token_{{snakeCase name}}(*, access_token: str):
|
|
24
27
|
"""Obtain OAuth access token via AgentCore Identity for {{name}}."""
|
|
@@ -1,4 +1,65 @@
|
|
|
1
1
|
{{#if (eq modelProvider "Bedrock")}}
|
|
2
|
+
{{#if bedrockMantle}}
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from aws_bedrock_token_generator import provide_token
|
|
6
|
+
{{#if (eq mantleApiFormat "chat_completions")}}
|
|
7
|
+
from strands.models.openai import OpenAIModel
|
|
8
|
+
{{else}}
|
|
9
|
+
{{#if mantleProprietary}}
|
|
10
|
+
from strands.models.openai_responses import OpenAIResponsesModel
|
|
11
|
+
{{else}}
|
|
12
|
+
from model.mantle_compat import MantleCompatResponsesModel
|
|
13
|
+
{{/if}}
|
|
14
|
+
{{/if}}
|
|
15
|
+
|
|
16
|
+
MODEL_ID = "{{modelId}}"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def load_model():
|
|
20
|
+
"""
|
|
21
|
+
Get a Bedrock Mantle model client. These OpenAI-compatible models (e.g. openai.gpt-5.5,
|
|
22
|
+
openai.gpt-oss-120b) are served via the Bedrock Mantle endpoint, NOT the Converse API — so they
|
|
23
|
+
are invoked through an OpenAI-style client authenticated with a short-lived Bedrock bearer token.
|
|
24
|
+
Region is read from AWS_REGION (set by the AgentCore runtime).
|
|
25
|
+
"""
|
|
26
|
+
region = os.environ.get("AWS_REGION", os.environ.get("AWS_DEFAULT_REGION", "us-east-1"))
|
|
27
|
+
token = provide_token(region=region)
|
|
28
|
+
{{#if mantleProprietary}}
|
|
29
|
+
# Proprietary OpenAI models only work on the /openai/v1 Mantle path.
|
|
30
|
+
base_url = f"https://bedrock-mantle.{region}.api.aws/openai/v1"
|
|
31
|
+
{{else}}
|
|
32
|
+
# Open-source OpenAI models (gpt-oss-*) only work on the /v1 Mantle path.
|
|
33
|
+
base_url = f"https://bedrock-mantle.{region}.api.aws/v1"
|
|
34
|
+
{{/if}}
|
|
35
|
+
client_args = {"api_key": token, "base_url": base_url}
|
|
36
|
+
|
|
37
|
+
params = {}
|
|
38
|
+
{{#if modelMaxTokens}}
|
|
39
|
+
{{#if (eq mantleApiFormat "chat_completions")}}
|
|
40
|
+
params["max_completion_tokens"] = {{modelMaxTokens}}
|
|
41
|
+
{{else}}
|
|
42
|
+
params["max_output_tokens"] = {{modelMaxTokens}}
|
|
43
|
+
{{/if}}
|
|
44
|
+
{{/if}}
|
|
45
|
+
{{#if modelTemperature}}
|
|
46
|
+
params["temperature"] = {{modelTemperature}}
|
|
47
|
+
{{/if}}
|
|
48
|
+
{{#if modelTopP}}
|
|
49
|
+
params["top_p"] = {{modelTopP}}
|
|
50
|
+
{{/if}}
|
|
51
|
+
{{#if (eq mantleApiFormat "chat_completions")}}
|
|
52
|
+
return OpenAIModel(client_args=client_args, model_id=MODEL_ID, params=params)
|
|
53
|
+
{{else}}
|
|
54
|
+
# Responses API: Mantle does not persist responses, so disable server-side storage.
|
|
55
|
+
params["store"] = False
|
|
56
|
+
{{#if mantleProprietary}}
|
|
57
|
+
return OpenAIResponsesModel(client_args=client_args, model_id=MODEL_ID, params=params)
|
|
58
|
+
{{else}}
|
|
59
|
+
return MantleCompatResponsesModel(client_args=client_args, model_id=MODEL_ID, params=params)
|
|
60
|
+
{{/if}}
|
|
61
|
+
{{/if}}
|
|
62
|
+
{{else}}
|
|
2
63
|
from strands.models.bedrock import BedrockModel
|
|
3
64
|
|
|
4
65
|
|
|
@@ -6,6 +67,7 @@ def load_model() -> BedrockModel:
|
|
|
6
67
|
"""Get Bedrock model client using IAM credentials."""
|
|
7
68
|
return BedrockModel(model_id="{{#if modelId}}{{modelId}}{{else}}global.anthropic.claude-sonnet-4-5-20250929-v1:0{{/if}}")
|
|
8
69
|
{{/if}}
|
|
70
|
+
{{/if}}
|
|
9
71
|
{{#if (eq modelProvider "Anthropic")}}
|
|
10
72
|
import os
|
|
11
73
|
|
|
@@ -121,3 +183,57 @@ def load_model() -> GeminiModel:
|
|
|
121
183
|
model_id="{{#if modelId}}{{modelId}}{{else}}gemini-2.5-flash{{/if}}",
|
|
122
184
|
)
|
|
123
185
|
{{/if}}
|
|
186
|
+
{{#if (eq modelProvider "LiteLLM")}}
|
|
187
|
+
import os
|
|
188
|
+
{{#if litellmAdditionalParams}}
|
|
189
|
+
import json
|
|
190
|
+
{{/if}}
|
|
191
|
+
|
|
192
|
+
from strands.models.litellm import LiteLLMModel
|
|
193
|
+
{{#if identityProviders.[0].name}}
|
|
194
|
+
from bedrock_agentcore.identity.auth import requires_api_key
|
|
195
|
+
|
|
196
|
+
IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
|
|
197
|
+
IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
|
|
201
|
+
def _agentcore_identity_api_key_provider(api_key: str) -> str:
|
|
202
|
+
"""Fetch API key from AgentCore Identity."""
|
|
203
|
+
return api_key
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _get_api_key() -> str:
|
|
207
|
+
"""
|
|
208
|
+
Uses AgentCore Identity for API key management in deployed environments.
|
|
209
|
+
For local development, run via 'agentcore dev' which loads agentcore/.env.
|
|
210
|
+
"""
|
|
211
|
+
if os.getenv("LOCAL_DEV") == "1":
|
|
212
|
+
api_key = os.getenv(IDENTITY_ENV_VAR)
|
|
213
|
+
if not api_key:
|
|
214
|
+
raise RuntimeError(
|
|
215
|
+
f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
|
|
216
|
+
)
|
|
217
|
+
return api_key
|
|
218
|
+
return _agentcore_identity_api_key_provider()
|
|
219
|
+
{{/if}}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def load_model() -> LiteLLMModel:
|
|
225
|
+
"""Get a LiteLLM model client (proxies to the provider encoded in model_id)."""
|
|
226
|
+
client_args = {}
|
|
227
|
+
{{#if identityProviders.[0].name}}
|
|
228
|
+
client_args["api_key"] = _get_api_key()
|
|
229
|
+
{{/if}}
|
|
230
|
+
{{#if litellmApiBase}}
|
|
231
|
+
client_args["api_base"] = {{safeJson litellmApiBase}}
|
|
232
|
+
{{/if}}
|
|
233
|
+
params = {{#if litellmAdditionalParams}}json.loads({{pyJsonStr litellmAdditionalParams}}){{else}}{}{{/if}}
|
|
234
|
+
return LiteLLMModel(
|
|
235
|
+
client_args=client_args,
|
|
236
|
+
model_id="{{#if modelId}}{{modelId}}{{else}}bedrock/us.anthropic.claude-sonnet-4-5-20250514-v1:0{{/if}}",
|
|
237
|
+
params=params,
|
|
238
|
+
)
|
|
239
|
+
{{/if}}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from strands.models.openai_responses import OpenAIResponsesModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class MantleCompatResponsesModel(OpenAIResponsesModel):
|
|
5
|
+
"""Workaround for Bedrock Mantle rejecting output_text in EasyInputMessage content arrays.
|
|
6
|
+
|
|
7
|
+
Mantle's Pydantic validation only accepts content as a plain string for assistant messages, while
|
|
8
|
+
real OpenAI accepts both formats. Flatten assistant content arrays to strings so multi-turn works.
|
|
9
|
+
Used for open-source OpenAI models (gpt-oss-*) on the /v1 Mantle path; proprietary models use the
|
|
10
|
+
plain OpenAIResponsesModel on /openai/v1.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def _format_request_messages(cls, messages):
|
|
15
|
+
formatted = super()._format_request_messages(messages)
|
|
16
|
+
for msg in formatted:
|
|
17
|
+
if msg.get("role") == "assistant" and isinstance(msg.get("content"), list):
|
|
18
|
+
msg["content"] = "".join(
|
|
19
|
+
part.get("text", "") for part in msg["content"] if part.get("type") == "output_text"
|
|
20
|
+
)
|
|
21
|
+
return formatted
|
|
@@ -16,6 +16,9 @@ dependencies = [
|
|
|
16
16
|
{{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
|
|
17
17
|
{{/if}}"mcp >= 1.19.0",
|
|
18
18
|
{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
|
|
19
|
+
{{/if}}{{#if (eq modelProvider "LiteLLM")}}"litellm >= 1.0.0",
|
|
20
|
+
{{/if}}{{#if bedrockMantle}}"openai >= 1.0.0",
|
|
21
|
+
"aws-bedrock-token-generator >= 1.0.0",
|
|
19
22
|
{{/if}}"strands-agents >= 1.15.0",
|
|
20
23
|
{{#if (or hasBrowser hasCodeInterpreter)}}"strands-agents-tools >= 0.1.0",
|
|
21
24
|
{{/if}}{{#if hasBrowser}}"nest-asyncio >= 1.5.0",
|
|
@@ -3,6 +3,9 @@ import { Agent, McpClient, tool, type ToolList } from '@strands-agents/sdk';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { loadModel } from './model/load.js';
|
|
5
5
|
import { getStreamableHttpMcpClient } from './mcp_client/client.js';
|
|
6
|
+
{{#if hasMemory}}
|
|
7
|
+
import { getActorId, getOrCreateMemoryManager } from './memory/memory.js';
|
|
8
|
+
{{/if}}
|
|
6
9
|
|
|
7
10
|
// Define a collection of MCP clients (filter out anything that failed to initialize)
|
|
8
11
|
const mcpClients: McpClient[] = [getStreamableHttpMcpClient()].filter(
|
|
@@ -31,6 +34,25 @@ const SYSTEM_PROMPT = `
|
|
|
31
34
|
You are a helpful assistant. Use tools when appropriate.
|
|
32
35
|
`;
|
|
33
36
|
|
|
37
|
+
{{#if hasMemory}}
|
|
38
|
+
const agentCache = new Map<string, Agent>();
|
|
39
|
+
|
|
40
|
+
async function getOrCreateAgent(sessionId: string, actorId: string): Promise<Agent> {
|
|
41
|
+
const key = `${actorId}:${sessionId}`;
|
|
42
|
+
let agent = agentCache.get(key);
|
|
43
|
+
if (agent) return agent;
|
|
44
|
+
|
|
45
|
+
const model = await loadModel();
|
|
46
|
+
agent = new Agent({
|
|
47
|
+
model,
|
|
48
|
+
systemPrompt: SYSTEM_PROMPT,
|
|
49
|
+
tools,
|
|
50
|
+
memoryManager: getOrCreateMemoryManager(sessionId, actorId) ?? undefined,
|
|
51
|
+
});
|
|
52
|
+
agentCache.set(key, agent);
|
|
53
|
+
return agent;
|
|
54
|
+
}
|
|
55
|
+
{{else}}
|
|
34
56
|
let cachedAgent: Agent | null = null;
|
|
35
57
|
|
|
36
58
|
async function getOrCreateAgent(): Promise<Agent> {
|
|
@@ -44,12 +66,37 @@ async function getOrCreateAgent(): Promise<Agent> {
|
|
|
44
66
|
}
|
|
45
67
|
return cachedAgent;
|
|
46
68
|
}
|
|
69
|
+
{{/if}}
|
|
47
70
|
|
|
48
71
|
const app = new BedrockAgentCoreApp({
|
|
49
72
|
invocationHandler: {
|
|
50
73
|
async *process(payload: any, context: any) {
|
|
74
|
+
{{#if hasMemory}}
|
|
75
|
+
const sessionId = context?.sessionId ?? 'default-session';
|
|
76
|
+
const actorId = getActorId(payload, context);
|
|
77
|
+
const agent = await getOrCreateAgent(sessionId, actorId);
|
|
78
|
+
{{else}}
|
|
51
79
|
const agent = await getOrCreateAgent();
|
|
80
|
+
{{/if}}
|
|
52
81
|
|
|
82
|
+
{{#if hasMemory}}
|
|
83
|
+
try {
|
|
84
|
+
for await (const event of agent.stream(payload.prompt ?? '')) {
|
|
85
|
+
if (
|
|
86
|
+
event.type === 'modelStreamUpdateEvent' &&
|
|
87
|
+
event.event?.type === 'modelContentBlockDeltaEvent' &&
|
|
88
|
+
event.event.delta?.type === 'textDelta'
|
|
89
|
+
) {
|
|
90
|
+
yield { data: event.event.delta.text };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} finally {
|
|
94
|
+
// Drain in-flight createEvent calls before the runtime can reclaim
|
|
95
|
+
// the session microVM. flush() is the durability mechanism — without
|
|
96
|
+
// it, an idle reclamation can lose the tail of the conversation.
|
|
97
|
+
await agent.memoryManager?.flush();
|
|
98
|
+
}
|
|
99
|
+
{{else}}
|
|
53
100
|
for await (const event of agent.stream(payload.prompt ?? '')) {
|
|
54
101
|
if (
|
|
55
102
|
event.type === 'modelStreamUpdateEvent' &&
|
|
@@ -59,6 +106,7 @@ const app = new BedrockAgentCoreApp({
|
|
|
59
106
|
yield { data: event.event.delta.text };
|
|
60
107
|
}
|
|
61
108
|
}
|
|
109
|
+
{{/if}}
|
|
62
110
|
},
|
|
63
111
|
},
|
|
64
112
|
});
|