@aws/nx-plugin 1.0.0-rc.18 → 1.0.0-rc.19
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/package.json +1 -1
- package/src/py/agent/__snapshots__/generator.spec.ts.snap +15 -6
- package/src/py/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +201 -0
- package/src/py/agent/a2a-connection/files/agent-connection/app/{__targetAgentSnakeCase___client.py.template → __targetAgentSnakeCase___client_strands.py.template} +7 -5
- package/src/py/agent/a2a-connection/generator.js +6 -5
- package/src/py/agent/a2a-connection/generator.js.map +1 -1
- package/src/py/agent/files/http/main.py.template +2 -2
- package/src/py/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +197 -0
- package/src/py/agent/gateway-connection/files/agent-connection/app/{__gatewaySnakeCase___client.py.template → __gatewaySnakeCase___client_strands.py.template} +6 -6
- package/src/py/agent/gateway-connection/generator.js +5 -5
- package/src/py/agent/gateway-connection/generator.js.map +1 -1
- package/src/py/agent/generator.js +3 -0
- package/src/py/agent/generator.js.map +1 -1
- package/src/py/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +157 -79
- package/src/py/agent/mcp-connection/files/agent-connection/app/{__mcpServerSnakeCase___client.py.template → __mcpServerSnakeCase___client_strands.py.template} +7 -5
- package/src/py/agent/mcp-connection/generator.js +5 -5
- package/src/py/agent/mcp-connection/generator.js.map +1 -1
- package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +37 -3
- package/src/py/fast-api/files/app/__name__/main.py.template +4 -1
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +13 -4
- package/src/smithy/project/__snapshots__/generator.spec.ts.snap +1 -0
- package/src/smithy/project/files/src/operations/echo.smithy.template +1 -0
- package/src/terraform/project/files/application/src/main.tf.template +6 -1
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +35 -4
- package/src/trpc/backend/files/src/procedures/echo.ts.template +1 -1
- package/src/trpc/backend/files/src/schema/echo.ts.template +2 -2
- package/src/ts/agent/__snapshots__/generator.spec.ts.snap +14 -5
- package/src/ts/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +256 -0
- package/src/ts/agent/a2a-connection/files/agent-connection/app/{__targetAgentKebabCase__-client.ts.template → __targetAgentKebabCase__-client-strands.ts.template} +5 -5
- package/src/ts/agent/a2a-connection/generator.js +3 -3
- package/src/ts/agent/a2a-connection/generator.js.map +1 -1
- package/src/ts/agent/files/http/router.ts.template +1 -1
- package/src/ts/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +220 -0
- package/src/ts/agent/gateway-connection/files/agent-connection/app/{__gatewayKebabCase__-client.ts.template → __gatewayKebabCase__-client-strands.ts.template} +5 -5
- package/src/ts/agent/gateway-connection/generator.js +3 -3
- package/src/ts/agent/gateway-connection/generator.js.map +1 -1
- package/src/ts/agent/generator.js +3 -0
- package/src/ts/agent/generator.js.map +1 -1
- package/src/ts/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +183 -110
- package/src/ts/agent/mcp-connection/files/agent-connection/app/{__mcpServerKebabCase__-client.ts.template → __mcpServerKebabCase__-client-strands.ts.template} +5 -5
- package/src/ts/agent/mcp-connection/generator.js +3 -3
- package/src/ts/agent/mcp-connection/generator.js.map +1 -1
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +13 -4
- package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +64 -24
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +13 -4
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +9 -3
- package/src/utils/agent-connection/agent-connection.d.ts +50 -24
- package/src/utils/agent-connection/agent-connection.js +137 -45
- package/src/utils/agent-connection/agent-connection.js.map +1 -1
- package/src/utils/agent-connection/files/core-a2a/agentcore-a2a-client-config.ts.template +93 -0
- package/src/utils/agent-connection/files/core-auth/agentcore-endpoints.ts.template +37 -0
- package/src/utils/agent-connection/files/core-auth/agentcore-fetch.ts.template +54 -0
- package/src/utils/agent-connection/files/core-gateway/agentcore-gateway-mcp-transport.ts.template +41 -0
- package/src/utils/agent-connection/files/core-mcp/agentcore-mcp-transport.ts.template +70 -0
- package/src/utils/agent-connection/files/core-shared/agentcore-transport.ts.template +39 -0
- package/src/utils/agent-connection/files/core-strands/a2a/agentcore-a2a-client-strands.ts.template +61 -0
- package/src/utils/agent-connection/files/core-strands/gateway/agentcore-gateway-mcp-client-strands.ts.template +26 -0
- package/src/utils/agent-connection/files/core-strands/mcp/agentcore-mcp-client-strands.ts.template +27 -0
- package/src/utils/agent-connection/files/py-core-a2a/agentcore_a2a_client_config.py.template +43 -0
- package/src/utils/agent-connection/files/py-core-auth/agentcore_endpoints.py.template +47 -0
- package/src/utils/agent-connection/files/py-core-auth/auth/session.py.template +51 -0
- package/src/utils/agent-connection/files/py-core-gateway/agentcore_gateway_mcp_transport.py.template +28 -0
- package/src/utils/agent-connection/files/py-core-mcp/agentcore_mcp_transport.py.template +34 -0
- package/src/utils/agent-connection/files/py-core-shared/agentcore_transport.py.template +40 -0
- package/src/utils/agent-connection/files/py-core-strands/a2a/agentcore_a2a_client_strands.py.template +66 -0
- package/src/utils/agent-connection/files/py-core-strands/gateway/agentcore_gateway_mcp_client_strands.py.template +22 -0
- package/src/utils/agent-connection/files/py-core-strands/mcp/agentcore_mcp_client_strands.py.template +30 -0
- package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.tf.template +7 -2
- package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +13 -4
- package/src/utils/api-constructs/files/cdk/core/api/http/http-api.ts.template +9 -0
- package/src/utils/api-constructs/files/cdk/core/api/rest/rest-api.ts.template +23 -1
- package/src/utils/files/terraform/src/core/runtime-config/appconfig-deployment/appconfig-deployment.tf.template +9 -3
- package/src/utils/files/terraform/src/core/runtime-config/read/read.tf.template +7 -2
- package/src/utils/identity-constructs/files/terraform/core/user-identity/add-callback-url/add-callback-url.tf.template +9 -3
- package/src/utils/rdb-constructs/files/terraform/app/dbs/__nameKebabCase__/__nameKebabCase__.tf.template +22 -8
- package/src/utils/rdb-constructs/files/terraform/core/rdb/aurora/aurora.tf.template +10 -4
- package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +13 -4
- package/src/utils/agent-connection/files/core-a2a/agentcore-a2a-client.ts.template +0 -123
- package/src/utils/agent-connection/files/core-gateway/agentcore-gateway-mcp-client.ts.template +0 -78
- package/src/utils/agent-connection/files/core-mcp/agentcore-mcp-client.ts.template +0 -128
- package/src/utils/agent-connection/files/py-core-a2a/agentcore_a2a_client.py.template +0 -120
- package/src/utils/agent-connection/files/py-core-gateway/agentcore_gateway_mcp_client.py.template +0 -78
- package/src/utils/agent-connection/files/py-core-mcp/agentcore_mcp_client.py.template +0 -89
- /package/src/utils/agent-connection/files/{core-runtime-config/model-errors.ts.template → core-strands/base/model-errors-strands.ts.template} +0 -0
- /package/src/utils/agent-connection/files/{core-runtime-config/with-session-id.ts.template → core-strands/base/with-session-id-strands.ts.template} +0 -0
- /package/src/utils/agent-connection/files/{py-core-runtime-config/model_errors.py.template → py-core-strands/base/model_errors_strands.py.template} +0 -0
- /package/src/utils/agent-connection/files/{py-core-runtime-config/with_session_id.py.template → py-core-strands/base/with_session_id_strands.py.template} +0 -0
package/package.json
CHANGED
|
@@ -1108,14 +1108,19 @@ resource "null_resource" "docker_publish" {
|
|
|
1108
1108
|
provisioner "local-exec" {
|
|
1109
1109
|
command = <<-EOT
|
|
1110
1110
|
# Get ECR login token
|
|
1111
|
-
aws ecr get-login-password --region
|
|
1111
|
+
aws ecr get-login-password --region "$AWS_REGION" | docker login --username AWS --password-stdin "$REPOSITORY_URL"
|
|
1112
1112
|
|
|
1113
1113
|
# Tag the image
|
|
1114
|
-
docker tag
|
|
1114
|
+
docker tag "$DOCKER_IMAGE_TAG" "$REPOSITORY_URL:latest"
|
|
1115
1115
|
|
|
1116
1116
|
# Push the image
|
|
1117
|
-
docker push
|
|
1117
|
+
docker push "$REPOSITORY_URL:latest"
|
|
1118
1118
|
EOT
|
|
1119
|
+
environment = {
|
|
1120
|
+
AWS_REGION = local.aws_region
|
|
1121
|
+
REPOSITORY_URL = self.triggers.repository_url
|
|
1122
|
+
DOCKER_IMAGE_TAG = self.triggers.docker_image_tag
|
|
1123
|
+
}
|
|
1119
1124
|
}
|
|
1120
1125
|
|
|
1121
1126
|
depends_on = [aws_ecr_repository_policy.agent_core_ecr_policy]
|
|
@@ -1177,10 +1182,11 @@ resource "null_resource" "runtime_ready" {
|
|
|
1177
1182
|
uv run --with boto3 python -c "
|
|
1178
1183
|
import boto3
|
|
1179
1184
|
import json
|
|
1185
|
+
import os
|
|
1180
1186
|
import time
|
|
1181
1187
|
import uuid
|
|
1182
1188
|
|
|
1183
|
-
runtime_arn =
|
|
1189
|
+
runtime_arn = os.environ['RUNTIME_ARN']
|
|
1184
1190
|
client = boto3.client('bedrock-agentcore')
|
|
1185
1191
|
initialize_request = json.dumps({
|
|
1186
1192
|
'jsonrpc': '2.0',
|
|
@@ -1212,6 +1218,9 @@ while True:
|
|
|
1212
1218
|
print(f'Runtime {runtime_arn} is serving MCP')
|
|
1213
1219
|
"
|
|
1214
1220
|
EOT
|
|
1221
|
+
environment = {
|
|
1222
|
+
RUNTIME_ARN = self.triggers.runtime_arn
|
|
1223
|
+
}
|
|
1215
1224
|
}
|
|
1216
1225
|
}
|
|
1217
1226
|
|
|
@@ -1285,7 +1294,7 @@ exports[`py#agent generator > should match snapshot for generated files > agent-
|
|
|
1285
1294
|
import uvicorn
|
|
1286
1295
|
from bedrock_agentcore.runtime.models import PingStatus
|
|
1287
1296
|
from fastapi import Request
|
|
1288
|
-
from pydantic import BaseModel
|
|
1297
|
+
from pydantic import BaseModel, Field
|
|
1289
1298
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
1290
1299
|
from proj_agent_connection import session_id_context, with_session_id
|
|
1291
1300
|
|
|
@@ -1303,7 +1312,7 @@ _agent = _agent_ctx.__enter__()
|
|
|
1303
1312
|
|
|
1304
1313
|
|
|
1305
1314
|
class InvokeInput(BaseModel):
|
|
1306
|
-
message: str
|
|
1315
|
+
message: str = Field(max_length=100000)
|
|
1307
1316
|
|
|
1308
1317
|
|
|
1309
1318
|
class StreamChunk(BaseModel):
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`py#agent#a2a-connection generator > should match snapshot for agent-connection core files > agentcore_a2a_client_config.py 1`] = `
|
|
4
|
+
"from collections.abc import Callable
|
|
5
|
+
|
|
6
|
+
import boto3
|
|
7
|
+
import httpx
|
|
8
|
+
from a2a.client import ClientConfig
|
|
9
|
+
|
|
10
|
+
from .agentcore_endpoints import a2a_url_from_arn, region_from_arn
|
|
11
|
+
from .auth.session import jwt_auth, plain_auth, sigv4_auth
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _config(url: str, auth: httpx.Auth) -> tuple[str, ClientConfig]:
|
|
15
|
+
httpx_client = httpx.AsyncClient(auth=auth, timeout=120)
|
|
16
|
+
return url, ClientConfig(httpx_client=httpx_client, streaming=False)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AgentCoreA2aClientConfig:
|
|
20
|
+
"""Factory for A2A client configs that connect to a Bedrock AgentCore
|
|
21
|
+
runtime. Each forwards the current async-context session via the AgentCore
|
|
22
|
+
session header. Framework-agnostic — wrap the result in a framework's
|
|
23
|
+
A2A client."""
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def with_iam_auth(agent_runtime_arn: str) -> tuple[str, ClientConfig]:
|
|
27
|
+
"""SigV4-authenticated A2A client config for a Bedrock AgentCore runtime."""
|
|
28
|
+
region = region_from_arn(agent_runtime_arn)
|
|
29
|
+
credentials = boto3.Session(region_name=region).get_credentials()
|
|
30
|
+
return _config(
|
|
31
|
+
a2a_url_from_arn(agent_runtime_arn), sigv4_auth(credentials, region)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def with_jwt_auth(
|
|
36
|
+
agent_runtime_arn: str, access_token_provider: Callable[[], str]
|
|
37
|
+
) -> tuple[str, ClientConfig]:
|
|
38
|
+
"""Bearer-authenticated A2A client config for a Bedrock AgentCore runtime."""
|
|
39
|
+
return _config(
|
|
40
|
+
a2a_url_from_arn(agent_runtime_arn), jwt_auth(access_token_provider)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def without_auth(url: str) -> tuple[str, ClientConfig]:
|
|
45
|
+
"""Plain-HTTP A2A client config — for local dev."""
|
|
46
|
+
return _config(url, plain_auth())
|
|
47
|
+
"
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
exports[`py#agent#a2a-connection generator > should match snapshot for agent-connection core files > agentcore_a2a_client_strands.py 1`] = `
|
|
51
|
+
"from collections.abc import Callable
|
|
52
|
+
|
|
53
|
+
from strands.agent.a2a_agent import A2AAgent
|
|
54
|
+
|
|
55
|
+
from .agentcore_a2a_client_config import AgentCoreA2aClientConfig
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _build(config: tuple, *, name: str | None, description: str | None) -> A2AAgent:
|
|
59
|
+
url, client_config = config
|
|
60
|
+
kwargs: dict = {"endpoint": url, "client_config": client_config}
|
|
61
|
+
if name:
|
|
62
|
+
kwargs["name"] = name
|
|
63
|
+
if description:
|
|
64
|
+
kwargs["description"] = description
|
|
65
|
+
return A2AAgent(**kwargs)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class AgentCoreA2aClientStrands:
|
|
69
|
+
"""Factory for Strands A2A clients that connect to an AgentCore runtime."""
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def with_iam_auth(
|
|
73
|
+
agent_runtime_arn: str,
|
|
74
|
+
*,
|
|
75
|
+
name: str | None = None,
|
|
76
|
+
description: str | None = None,
|
|
77
|
+
) -> A2AAgent:
|
|
78
|
+
"""SigV4-authenticated client for a Bedrock AgentCore runtime."""
|
|
79
|
+
return _build(
|
|
80
|
+
AgentCoreA2aClientConfig.with_iam_auth(agent_runtime_arn),
|
|
81
|
+
name=name,
|
|
82
|
+
description=description,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def with_jwt_auth(
|
|
87
|
+
agent_runtime_arn: str,
|
|
88
|
+
access_token_provider: Callable[[], str],
|
|
89
|
+
*,
|
|
90
|
+
name: str | None = None,
|
|
91
|
+
description: str | None = None,
|
|
92
|
+
) -> A2AAgent:
|
|
93
|
+
"""Bearer-authenticated client for a Bedrock AgentCore runtime."""
|
|
94
|
+
return _build(
|
|
95
|
+
AgentCoreA2aClientConfig.with_jwt_auth(
|
|
96
|
+
agent_runtime_arn, access_token_provider
|
|
97
|
+
),
|
|
98
|
+
name=name,
|
|
99
|
+
description=description,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def without_auth(
|
|
104
|
+
url: str,
|
|
105
|
+
*,
|
|
106
|
+
name: str | None = None,
|
|
107
|
+
description: str | None = None,
|
|
108
|
+
) -> A2AAgent:
|
|
109
|
+
"""Plain-HTTP client — for local dev."""
|
|
110
|
+
return _build(
|
|
111
|
+
AgentCoreA2aClientConfig.without_auth(url),
|
|
112
|
+
name=name,
|
|
113
|
+
description=description,
|
|
114
|
+
)
|
|
115
|
+
"
|
|
116
|
+
`;
|
|
117
|
+
|
|
118
|
+
exports[`py#agent#a2a-connection generator > should match snapshot for agent-connection core files > remote_client_strands.py 1`] = `
|
|
119
|
+
"import os
|
|
120
|
+
|
|
121
|
+
from strands.agent.a2a_agent import A2AAgent
|
|
122
|
+
|
|
123
|
+
from test_agent_connection.core.agentcore_a2a_client_strands import (
|
|
124
|
+
AgentCoreA2aClientStrands,
|
|
125
|
+
)
|
|
126
|
+
from test_agent_connection.core.runtime_config import (
|
|
127
|
+
get_agentcore_runtime_config,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class RemoteClientStrands:
|
|
132
|
+
"""Strands client for the Remote A2A agent."""
|
|
133
|
+
|
|
134
|
+
@staticmethod
|
|
135
|
+
def create() -> A2AAgent:
|
|
136
|
+
if os.environ.get("SERVE_LOCAL") == "true":
|
|
137
|
+
return AgentCoreA2aClientStrands.without_auth("http://localhost:9001/")
|
|
138
|
+
config = get_agentcore_runtime_config()
|
|
139
|
+
agent_runtime_arn = config.get("agentRuntimes", {}).get("Remote")
|
|
140
|
+
if not agent_runtime_arn:
|
|
141
|
+
raise RuntimeError(
|
|
142
|
+
"No connected agent runtime named 'Remote' found in runtime configuration."
|
|
143
|
+
)
|
|
144
|
+
return AgentCoreA2aClientStrands.with_iam_auth(agent_runtime_arn)
|
|
145
|
+
"
|
|
146
|
+
`;
|
|
147
|
+
|
|
148
|
+
exports[`py#agent#a2a-connection generator > should match snapshot for agent-connection core files > session.py 1`] = `
|
|
149
|
+
"from collections.abc import Callable
|
|
150
|
+
|
|
151
|
+
import httpx
|
|
152
|
+
|
|
153
|
+
from ..session_context import get_current_session_id
|
|
154
|
+
from .sigv4 import SigV4HTTPXAuth
|
|
155
|
+
|
|
156
|
+
SESSION_HEADER = "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id"
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class SessionHeaderAuth(httpx.Auth):
|
|
160
|
+
"""Stamps the AgentCore session header from the current async context,
|
|
161
|
+
then delegates to an optional inner auth (e.g. SigV4)."""
|
|
162
|
+
|
|
163
|
+
requires_request_body = True
|
|
164
|
+
requires_response_body = True
|
|
165
|
+
|
|
166
|
+
def __init__(self, inner: httpx.Auth | None = None):
|
|
167
|
+
self._inner = inner
|
|
168
|
+
|
|
169
|
+
def auth_flow(self, request: httpx.Request): # type: ignore[override]
|
|
170
|
+
sid = get_current_session_id()
|
|
171
|
+
if sid:
|
|
172
|
+
request.headers[SESSION_HEADER] = sid
|
|
173
|
+
if self._inner is None:
|
|
174
|
+
yield request
|
|
175
|
+
return
|
|
176
|
+
yield from self._inner.auth_flow(request)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def sigv4_auth(
|
|
180
|
+
credentials, region: str, service: str = "bedrock-agentcore"
|
|
181
|
+
) -> httpx.Auth:
|
|
182
|
+
"""Session-forwarding SigV4 auth (per-request, body-aware)."""
|
|
183
|
+
return SessionHeaderAuth(SigV4HTTPXAuth(credentials, service, region))
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def jwt_auth(access_token_provider: Callable[[], str]) -> httpx.Auth:
|
|
187
|
+
"""Session-forwarding bearer-token auth."""
|
|
188
|
+
|
|
189
|
+
class _Bearer(httpx.Auth):
|
|
190
|
+
def auth_flow(self, request: httpx.Request):
|
|
191
|
+
request.headers["Authorization"] = f"Bearer {access_token_provider()}"
|
|
192
|
+
yield request
|
|
193
|
+
|
|
194
|
+
return SessionHeaderAuth(_Bearer())
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def plain_auth() -> httpx.Auth:
|
|
198
|
+
"""Session-forwarding plain auth — for local dev."""
|
|
199
|
+
return SessionHeaderAuth()
|
|
200
|
+
"
|
|
201
|
+
`;
|
|
@@ -2,19 +2,21 @@ import os
|
|
|
2
2
|
|
|
3
3
|
from strands.agent.a2a_agent import A2AAgent
|
|
4
4
|
|
|
5
|
-
from <%- agentConnectionModuleName %>.core.
|
|
5
|
+
from <%- agentConnectionModuleName %>.core.agentcore_a2a_client_strands import (
|
|
6
|
+
AgentCoreA2aClientStrands,
|
|
7
|
+
)
|
|
6
8
|
from <%- agentConnectionModuleName %>.core.runtime_config import (
|
|
7
9
|
get_agentcore_runtime_config,
|
|
8
10
|
)
|
|
9
11
|
|
|
10
12
|
|
|
11
|
-
class <%- targetAgentClassName %>
|
|
12
|
-
"""
|
|
13
|
+
class <%- targetAgentClassName %>ClientStrands:
|
|
14
|
+
"""Strands client for the <%- targetAgentClassName %> A2A agent."""
|
|
13
15
|
|
|
14
16
|
@staticmethod
|
|
15
17
|
def create() -> A2AAgent:
|
|
16
18
|
if os.environ.get("SERVE_LOCAL") == "true":
|
|
17
|
-
return
|
|
19
|
+
return AgentCoreA2aClientStrands.without_auth(
|
|
18
20
|
"http://localhost:<%- targetAgentPort %>/"
|
|
19
21
|
)
|
|
20
22
|
config = get_agentcore_runtime_config()
|
|
@@ -25,4 +27,4 @@ class <%- targetAgentClassName %>Client:
|
|
|
25
27
|
raise RuntimeError(
|
|
26
28
|
"No connected agent runtime named '<%- targetAgentClassName %>' found in runtime configuration."
|
|
27
29
|
)
|
|
28
|
-
return
|
|
30
|
+
return AgentCoreA2aClientStrands.with_iam_auth(agent_runtime_arn)
|
|
@@ -38,12 +38,13 @@ const pyAgentA2aConnectionGenerator = async (tree, options) => {
|
|
|
38
38
|
// 1. Ensure the shared Python agent-connection project exists + has the
|
|
39
39
|
// A2A core client and its shared SigV4 auth helper.
|
|
40
40
|
await (0, agent_connection_1.ensurePythonAgentConnectionProject)(tree);
|
|
41
|
-
(0, agent_connection_1.addPythonCoreClient)(tree, '
|
|
42
|
-
(0, agent_connection_1.addPythonCoreClient)(tree, 'a2a');
|
|
41
|
+
await (0, agent_connection_1.addPythonCoreClient)(tree, 'a2a');
|
|
43
42
|
const agentConnectionProjectDir = (0, agent_connection_1.getPythonAgentConnectionProjectDir)(tree);
|
|
44
43
|
const agentConnectionModuleName = (0, agent_connection_1.getPythonAgentConnectionModuleName)(tree);
|
|
45
44
|
const agentConnectionPackageName = (0, agent_connection_1.getPythonAgentConnectionPackageName)(tree);
|
|
46
|
-
//
|
|
45
|
+
// Layer 0/1 deps for the A2A client config + shared auth helper, plus the
|
|
46
|
+
// Strands A2A extra the framework's A2A client needs (the base strands-agents
|
|
47
|
+
// dependency is added by addPythonCoreClient).
|
|
47
48
|
(0, py_1.addDependenciesToPyProjectToml)(tree, agentConnectionProjectDir, [
|
|
48
49
|
'boto3',
|
|
49
50
|
'httpx',
|
|
@@ -64,12 +65,12 @@ const pyAgentA2aConnectionGenerator = async (tree, options) => {
|
|
|
64
65
|
}
|
|
65
66
|
// Add re-export to module __init__.py
|
|
66
67
|
const moduleInitPath = (0, devkit_1.joinPathFragments)(agentConnectionProjectDir, agentConnectionModuleName, '__init__.py');
|
|
67
|
-
await (0, agent_connection_1.addPythonReExport)(tree, moduleInitPath, `.app.${targetAgentSnakeCase}
|
|
68
|
+
await (0, agent_connection_1.addPythonReExport)(tree, moduleInitPath, `.app.${targetAgentSnakeCase}_client_strands`, `${targetAgentClassName}ClientStrands`);
|
|
68
69
|
// 3. Transform agent.py to add the A2A client import + wrap it as a tool
|
|
69
70
|
const agentSourceDir = (0, devkit_1.joinPathFragments)(sourceProject.root, agentComponent.path ?? 'src');
|
|
70
71
|
const agentFilePath = (0, devkit_1.joinPathFragments)(agentSourceDir, 'agent.py');
|
|
71
72
|
if (tree.exists(agentFilePath)) {
|
|
72
|
-
const clientClassName = `${targetAgentClassName}
|
|
73
|
+
const clientClassName = `${targetAgentClassName}ClientStrands`;
|
|
73
74
|
const clientVarName = targetAgentSnakeCase;
|
|
74
75
|
const toolName = `ask_${targetAgentSnakeCase}`;
|
|
75
76
|
await addImportToAgentFile(tree, agentFilePath, agentConnectionModuleName, clientClassName);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../../packages/nx-plugin/src/py/agent/a2a-connection/generator.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,uCAQoB;AACpB,uFAO0D;AAC1D,4CAI4B;AAC5B,kDAA6D;AAC7D,oDAAyE;AACzE,gDAAiD;AACjD,0CAK2B;AAC3B,0CAG2B;AAG3B,qDAAqD;AACrD,MAAM,EAAE,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,oBAAoB,OAAO,EAAE,CAAC;AAEjD,QAAA,sCAAsC,GACjD,IAAA,qBAAgB,EAAC,UAAU,CAAC,CAAC;AAExB,MAAM,6BAA6B,GAAG,KAAK,EAChD,IAAU,EACV,OAA4C,EAChB,EAAE;IAC9B,MAAM,aAAa,GAAG,IAAA,wCAAmC,EACvD,IAAI,EACJ,OAAO,CAAC,aAAa,CACtB,CAAC;IACF,MAAM,aAAa,GAAG,IAAA,wCAAmC,EACvD,IAAI,EACJ,OAAO,CAAC,aAAa,CACtB,CAAC;IAEF,MAAM,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAC/C,MAAM,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAErD,IAAI,CAAC,cAAc,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CACb,2FAA2F,CAC5F,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,oBAAoB,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CACb,iBAAiB,oBAAoB,CAAC,IAAI,cAAc,oBAAoB,CAAC,QAAQ,IAAI,MAAM,wDAAwD,CACxJ,CAAC;IACJ,CAAC;IAED,IACE,oBAAoB,CAAC,IAAI;QACzB,oBAAoB,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,KAAK,EACjD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,yEAAyE,oBAAoB,CAAC,IAAI,WAAW,oBAAoB,CAAC,IAAI,mBAAmB,CAC1J,CAAC;IACJ,CAAC;IAED,MAAM,wBAAwB,GAAG,oBAAoB,CAAC,IAAI,IAAI,OAAO,CAAC;IACtE,MAAM,oBAAoB,GAAG,oBAAoB,CAAC,EAAY,CAAC;IAC/D,MAAM,oBAAoB,GAAG,IAAA,iBAAS,EAAC,oBAAoB,CAAC,CAAC;IAC7D,MAAM,eAAe,GAAG,oBAAoB,CAAC,IAAI,IAAI,IAAI,CAAC;IAE1D,wEAAwE;IACxE,uDAAuD;IACvD,MAAM,IAAA,qDAAkC,EAAC,IAAI,CAAC,CAAC;IAC/C,
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../../packages/nx-plugin/src/py/agent/a2a-connection/generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import uuid
|
|
|
3
3
|
import uvicorn
|
|
4
4
|
from bedrock_agentcore.runtime.models import PingStatus
|
|
5
5
|
from fastapi import Request
|
|
6
|
-
from pydantic import BaseModel
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
7
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
8
8
|
from <%- agentConnectionModuleName %> import session_id_context, with_session_id
|
|
9
9
|
|
|
@@ -21,7 +21,7 @@ _agent = _agent_ctx.__enter__()
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class InvokeInput(BaseModel):
|
|
24
|
-
message: str
|
|
24
|
+
message: str = Field(max_length=100000)
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
class StreamChunk(BaseModel):
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`py#agent#gateway-connection generator > should match snapshot for agent-connection core files > agentcore_gateway_mcp_client_strands.py 1`] = `
|
|
4
|
+
"from strands.tools.mcp.mcp_client import MCPClient
|
|
5
|
+
|
|
6
|
+
from .agentcore_gateway_mcp_transport import AgentCoreGatewayMCPTransport
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AgentCoreGatewayMCPClientStrands:
|
|
10
|
+
"""Factory for Strands MCP clients that connect to an AgentCore Gateway."""
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def with_iam_auth(
|
|
14
|
+
gateway_url: str,
|
|
15
|
+
region: str | None = None,
|
|
16
|
+
) -> MCPClient:
|
|
17
|
+
"""Create a gateway MCP client authenticated with IAM SigV4."""
|
|
18
|
+
return MCPClient(
|
|
19
|
+
AgentCoreGatewayMCPTransport.with_iam_auth(gateway_url, region)
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def without_auth(gateway_url: str) -> MCPClient:
|
|
24
|
+
"""Plain-HTTP gateway client, for the local gateway started by serve-local."""
|
|
25
|
+
return MCPClient(AgentCoreGatewayMCPTransport.without_auth(gateway_url))
|
|
26
|
+
"
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
exports[`py#agent#gateway-connection generator > should match snapshot for agent-connection core files > agentcore_gateway_mcp_transport.py 1`] = `
|
|
30
|
+
"from .agentcore_endpoints import region_from_gateway_url
|
|
31
|
+
from .agentcore_transport import (
|
|
32
|
+
TransportFactory,
|
|
33
|
+
no_auth_transport,
|
|
34
|
+
sigv4_transport,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class AgentCoreGatewayMCPTransport:
|
|
39
|
+
"""Factory for MCP transport factories that connect to an AgentCore Gateway.
|
|
40
|
+
Forwards the current async-context session via the AgentCore session header,
|
|
41
|
+
which the gateway propagates to downstream MCP server targets.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def with_iam_auth(
|
|
46
|
+
gateway_url: str,
|
|
47
|
+
region: str | None = None,
|
|
48
|
+
) -> TransportFactory:
|
|
49
|
+
"""Create a gateway MCP transport authenticated with IAM SigV4."""
|
|
50
|
+
return sigv4_transport(
|
|
51
|
+
gateway_url, region or region_from_gateway_url(gateway_url)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def without_auth(gateway_url: str) -> TransportFactory:
|
|
56
|
+
"""Plain-HTTP gateway transport, for the local gateway started by serve-local."""
|
|
57
|
+
return no_auth_transport(gateway_url)
|
|
58
|
+
"
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
exports[`py#agent#gateway-connection generator > should match snapshot for agent-connection core files > agentcore_transport.py 1`] = `
|
|
62
|
+
"from collections.abc import Callable
|
|
63
|
+
from typing import Any
|
|
64
|
+
|
|
65
|
+
import boto3
|
|
66
|
+
|
|
67
|
+
from mcp.client.streamable_http import streamablehttp_client
|
|
68
|
+
|
|
69
|
+
from .auth.session import jwt_auth, plain_auth, sigv4_auth
|
|
70
|
+
|
|
71
|
+
# Agent frameworks wrap a transport *factory* — a no-arg callable returning a
|
|
72
|
+
# streamable-HTTP transport context manager. Typed as \`\`Any\`\` so this layer
|
|
73
|
+
# stays free of any framework-specific transport type.
|
|
74
|
+
TransportFactory = Callable[[], Any]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _factory(url: str, auth) -> TransportFactory:
|
|
78
|
+
return lambda: streamablehttp_client(
|
|
79
|
+
url,
|
|
80
|
+
auth=auth,
|
|
81
|
+
timeout=120,
|
|
82
|
+
terminate_on_close=False,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def sigv4_transport(url: str, region: str) -> TransportFactory:
|
|
87
|
+
"""SigV4-signed transport factory for a resolved AgentCore endpoint."""
|
|
88
|
+
credentials = boto3.Session(region_name=region).get_credentials()
|
|
89
|
+
return _factory(url, sigv4_auth(credentials, region))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def jwt_transport(
|
|
93
|
+
url: str, access_token_provider: Callable[[], str]
|
|
94
|
+
) -> TransportFactory:
|
|
95
|
+
"""Bearer-token transport factory for a resolved AgentCore endpoint."""
|
|
96
|
+
return _factory(url, jwt_auth(access_token_provider))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def no_auth_transport(url: str) -> TransportFactory:
|
|
100
|
+
"""Plain-HTTP transport factory — for local dev."""
|
|
101
|
+
return _factory(url, plain_auth())
|
|
102
|
+
"
|
|
103
|
+
`;
|
|
104
|
+
|
|
105
|
+
exports[`py#agent#gateway-connection generator > should match snapshot for agent-connection core files > my_gateway_client_strands.py 1`] = `
|
|
106
|
+
"import os
|
|
107
|
+
|
|
108
|
+
from strands.tools.mcp.mcp_client import MCPClient
|
|
109
|
+
|
|
110
|
+
from proj_agent_connection.core.agentcore_gateway_mcp_client_strands import (
|
|
111
|
+
AgentCoreGatewayMCPClientStrands,
|
|
112
|
+
)
|
|
113
|
+
from proj_agent_connection.core.runtime_config import (
|
|
114
|
+
get_agentcore_runtime_config,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class MyGatewayClientStrands:
|
|
119
|
+
"""Strands client for the MyGateway AgentCore Gateway.
|
|
120
|
+
|
|
121
|
+
Deployed: a SigV4-signed MCP client pointing at the gateway URL.
|
|
122
|
+
Local (\`\`SERVE_LOCAL=true\`\`): a plain-HTTP client pointing at the local
|
|
123
|
+
gateway started by the gateway project's serve-local target. The local
|
|
124
|
+
gateway aggregates every attached MCP server but does not evaluate
|
|
125
|
+
Cedar policies.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def create() -> MCPClient:
|
|
130
|
+
if os.environ.get("SERVE_LOCAL") == "true":
|
|
131
|
+
return AgentCoreGatewayMCPClientStrands.without_auth(
|
|
132
|
+
gateway_url="http://localhost:8100/mcp"
|
|
133
|
+
)
|
|
134
|
+
config = get_agentcore_runtime_config()
|
|
135
|
+
gateway_url = config.get("gateways", {}).get("MyGateway")
|
|
136
|
+
if not gateway_url:
|
|
137
|
+
raise RuntimeError(
|
|
138
|
+
"No connected gateway named 'MyGateway' found in runtime configuration."
|
|
139
|
+
)
|
|
140
|
+
return AgentCoreGatewayMCPClientStrands.with_iam_auth(gateway_url=gateway_url)
|
|
141
|
+
"
|
|
142
|
+
`;
|
|
143
|
+
|
|
144
|
+
exports[`py#agent#gateway-connection generator > should match snapshot for agent-connection core files > session.py 1`] = `
|
|
145
|
+
"from collections.abc import Callable
|
|
146
|
+
|
|
147
|
+
import httpx
|
|
148
|
+
|
|
149
|
+
from ..session_context import get_current_session_id
|
|
150
|
+
from .sigv4 import SigV4HTTPXAuth
|
|
151
|
+
|
|
152
|
+
SESSION_HEADER = "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class SessionHeaderAuth(httpx.Auth):
|
|
156
|
+
"""Stamps the AgentCore session header from the current async context,
|
|
157
|
+
then delegates to an optional inner auth (e.g. SigV4)."""
|
|
158
|
+
|
|
159
|
+
requires_request_body = True
|
|
160
|
+
requires_response_body = True
|
|
161
|
+
|
|
162
|
+
def __init__(self, inner: httpx.Auth | None = None):
|
|
163
|
+
self._inner = inner
|
|
164
|
+
|
|
165
|
+
def auth_flow(self, request: httpx.Request): # type: ignore[override]
|
|
166
|
+
sid = get_current_session_id()
|
|
167
|
+
if sid:
|
|
168
|
+
request.headers[SESSION_HEADER] = sid
|
|
169
|
+
if self._inner is None:
|
|
170
|
+
yield request
|
|
171
|
+
return
|
|
172
|
+
yield from self._inner.auth_flow(request)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def sigv4_auth(
|
|
176
|
+
credentials, region: str, service: str = "bedrock-agentcore"
|
|
177
|
+
) -> httpx.Auth:
|
|
178
|
+
"""Session-forwarding SigV4 auth (per-request, body-aware)."""
|
|
179
|
+
return SessionHeaderAuth(SigV4HTTPXAuth(credentials, service, region))
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def jwt_auth(access_token_provider: Callable[[], str]) -> httpx.Auth:
|
|
183
|
+
"""Session-forwarding bearer-token auth."""
|
|
184
|
+
|
|
185
|
+
class _Bearer(httpx.Auth):
|
|
186
|
+
def auth_flow(self, request: httpx.Request):
|
|
187
|
+
request.headers["Authorization"] = f"Bearer {access_token_provider()}"
|
|
188
|
+
yield request
|
|
189
|
+
|
|
190
|
+
return SessionHeaderAuth(_Bearer())
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def plain_auth() -> httpx.Auth:
|
|
194
|
+
"""Session-forwarding plain auth — for local dev."""
|
|
195
|
+
return SessionHeaderAuth()
|
|
196
|
+
"
|
|
197
|
+
`;
|
|
@@ -2,16 +2,16 @@ import os
|
|
|
2
2
|
|
|
3
3
|
from strands.tools.mcp.mcp_client import MCPClient
|
|
4
4
|
|
|
5
|
-
from <%- agentConnectionModuleName %>.core.
|
|
6
|
-
|
|
5
|
+
from <%- agentConnectionModuleName %>.core.agentcore_gateway_mcp_client_strands import (
|
|
6
|
+
AgentCoreGatewayMCPClientStrands,
|
|
7
7
|
)
|
|
8
8
|
from <%- agentConnectionModuleName %>.core.runtime_config import (
|
|
9
9
|
get_agentcore_runtime_config,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class <%- gatewayClassName %>
|
|
14
|
-
"""
|
|
13
|
+
class <%- gatewayClassName %>ClientStrands:
|
|
14
|
+
"""Strands client for the <%- gatewayClassName %> AgentCore Gateway.
|
|
15
15
|
|
|
16
16
|
Deployed: a SigV4-signed MCP client pointing at the gateway URL.
|
|
17
17
|
Local (``SERVE_LOCAL=true``): a plain-HTTP client pointing at the local
|
|
@@ -23,7 +23,7 @@ class <%- gatewayClassName %>Client:
|
|
|
23
23
|
@staticmethod
|
|
24
24
|
def create() -> MCPClient:
|
|
25
25
|
if os.environ.get("SERVE_LOCAL") == "true":
|
|
26
|
-
return
|
|
26
|
+
return AgentCoreGatewayMCPClientStrands.without_auth(
|
|
27
27
|
gateway_url="http://localhost:<%- gatewayPort %>/mcp"
|
|
28
28
|
)
|
|
29
29
|
config = get_agentcore_runtime_config()
|
|
@@ -32,4 +32,4 @@ class <%- gatewayClassName %>Client:
|
|
|
32
32
|
raise RuntimeError(
|
|
33
33
|
"No connected gateway named '<%- gatewayClassName %>' found in runtime configuration."
|
|
34
34
|
)
|
|
35
|
-
return
|
|
35
|
+
return AgentCoreGatewayMCPClientStrands.with_iam_auth(gateway_url=gateway_url)
|
|
@@ -38,16 +38,16 @@ const pyAgentGatewayConnectionGenerator = async (tree, options) => {
|
|
|
38
38
|
const gatewayServeTargetName = `${gatewayKebabCase}-serve`;
|
|
39
39
|
const gatewayServeLocalTargetName = `${gatewayKebabCase}-serve-local`;
|
|
40
40
|
await (0, agent_connection_1.ensurePythonAgentConnectionProject)(tree);
|
|
41
|
-
(0, agent_connection_1.addPythonCoreClient)(tree, '
|
|
42
|
-
(0, agent_connection_1.addPythonCoreClient)(tree, 'gateway');
|
|
41
|
+
await (0, agent_connection_1.addPythonCoreClient)(tree, 'gateway');
|
|
43
42
|
const agentConnectionProjectDir = (0, agent_connection_1.getPythonAgentConnectionProjectDir)(tree);
|
|
44
43
|
const agentConnectionModuleName = (0, agent_connection_1.getPythonAgentConnectionModuleName)(tree);
|
|
45
44
|
const agentConnectionPackageName = (0, agent_connection_1.getPythonAgentConnectionPackageName)(tree);
|
|
45
|
+
// Layer 0/1 deps for the MCP transport + signed httpx auth. The framework's
|
|
46
|
+
// own dependency (strands-agents) is added by addPythonCoreClient.
|
|
46
47
|
(0, py_1.addDependenciesToPyProjectToml)(tree, agentConnectionProjectDir, [
|
|
47
48
|
'boto3',
|
|
48
49
|
'httpx',
|
|
49
50
|
'mcp',
|
|
50
|
-
'strands-agents',
|
|
51
51
|
]);
|
|
52
52
|
const appDir = (0, devkit_1.joinPathFragments)(agentConnectionProjectDir, agentConnectionModuleName, 'app');
|
|
53
53
|
// Local mode points at the gateway project's local gateway port.
|
|
@@ -62,11 +62,11 @@ const pyAgentGatewayConnectionGenerator = async (tree, options) => {
|
|
|
62
62
|
tree.write(appInitPath, '');
|
|
63
63
|
}
|
|
64
64
|
const moduleInitPath = (0, devkit_1.joinPathFragments)(agentConnectionProjectDir, agentConnectionModuleName, '__init__.py');
|
|
65
|
-
await (0, agent_connection_1.addPythonReExport)(tree, moduleInitPath, `.app.${gatewaySnakeCase}
|
|
65
|
+
await (0, agent_connection_1.addPythonReExport)(tree, moduleInitPath, `.app.${gatewaySnakeCase}_client_strands`, `${gatewayClassName}ClientStrands`);
|
|
66
66
|
const agentSourceDir = (0, devkit_1.joinPathFragments)(sourceProject.root, agentComponent.path ?? 'src');
|
|
67
67
|
const agentFilePath = (0, devkit_1.joinPathFragments)(agentSourceDir, 'agent.py');
|
|
68
68
|
if (tree.exists(agentFilePath)) {
|
|
69
|
-
const clientClassName = `${gatewayClassName}
|
|
69
|
+
const clientClassName = `${gatewayClassName}ClientStrands`;
|
|
70
70
|
const clientVarName = gatewaySnakeCase;
|
|
71
71
|
await (0, ast_1.addPythonDestructuredImport)(tree, agentFilePath, [clientClassName], agentConnectionModuleName);
|
|
72
72
|
await (0, agent_connection_1.addPythonClientToAgent)(tree, agentFilePath, clientClassName, clientVarName);
|