@aws/nx-plugin 1.0.0-rc.21 → 1.0.0-rc.22
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/license/generator.js +22 -8
- package/src/license/generator.js.map +1 -1
- package/src/license/known-exceptions.d.ts +1 -0
- package/src/license/known-exceptions.js +13 -1
- package/src/license/known-exceptions.js.map +1 -1
- package/src/py/agent/__snapshots__/generator.spec.ts.snap +154 -0
- package/src/py/agent/a2a-connection/files/langchain/agent-connection/app/__targetAgentSnakeCase___client_langchain.py.template +28 -0
- package/src/py/agent/a2a-connection/generator.js +58 -29
- package/src/py/agent/a2a-connection/generator.js.map +1 -1
- package/src/py/agent/files/a2a-langchain/main.py.template +89 -0
- package/src/py/agent/files/ag-ui-langchain/main.py.template +73 -0
- package/src/py/agent/files/common-langchain/agent.py.template +30 -0
- package/src/py/agent/files/http-langchain/main.py.template +84 -0
- package/src/py/agent/gateway-connection/files/langchain/agent-connection/app/__gatewaySnakeCase___client_langchain.py.template +34 -0
- package/src/py/agent/gateway-connection/generator.js +14 -7
- package/src/py/agent/gateway-connection/generator.js.map +1 -1
- package/src/py/agent/generator.js +68 -12
- package/src/py/agent/generator.js.map +1 -1
- package/src/py/agent/mcp-connection/files/langchain/agent-connection/app/__mcpServerSnakeCase___client_langchain.py.template +30 -0
- package/src/py/agent/mcp-connection/generator.js +14 -9
- package/src/py/agent/mcp-connection/generator.js.map +1 -1
- package/src/py/agent/react-connection/serve-local.js +9 -1
- package/src/py/agent/react-connection/serve-local.js.map +1 -1
- package/src/py/agent/schema.d.ts +1 -1
- package/src/py/agent/schema.json +1 -1
- package/src/utils/agent-connection/agent-connection.d.ts +50 -7
- package/src/utils/agent-connection/agent-connection.js +138 -8
- package/src/utils/agent-connection/agent-connection.js.map +1 -1
- package/src/utils/agent-connection/files/py-core-langchain/a2a/agentcore_a2a_client_langchain.py.template +99 -0
- package/src/utils/agent-connection/files/py-core-langchain/gateway/agentcore_gateway_mcp_client_langchain.py.template +57 -0
- package/src/utils/agent-connection/files/py-core-langchain/mcp/agentcore_mcp_client_langchain.py.template +64 -0
- package/src/utils/versions.d.ts +6 -0
- package/src/utils/versions.js +6 -0
- package/src/utils/versions.js.map +1 -1
package/package.json
CHANGED
package/src/license/generator.js
CHANGED
|
@@ -7,15 +7,16 @@ exports.licenseGenerator = licenseGenerator;
|
|
|
7
7
|
* SPDX-License-Identifier: Apache-2.0
|
|
8
8
|
*/
|
|
9
9
|
const devkit_1 = require("@nx/devkit");
|
|
10
|
-
const generator_1 = require("../py/
|
|
11
|
-
const generator_2 = require("../
|
|
10
|
+
const generator_1 = require("../py/agent/generator");
|
|
11
|
+
const generator_2 = require("../py/mcp-server/generator");
|
|
12
|
+
const generator_3 = require("../ts/mcp-server/generator");
|
|
12
13
|
const utils_1 = require("../utils/config/utils");
|
|
13
14
|
const format_1 = require("../utils/format");
|
|
14
15
|
const metrics_1 = require("../utils/metrics");
|
|
15
16
|
const nx_1 = require("../utils/nx");
|
|
16
17
|
const config_1 = require("./config");
|
|
17
18
|
const known_exceptions_1 = require("./known-exceptions");
|
|
18
|
-
const
|
|
19
|
+
const generator_4 = require("./sync/generator");
|
|
19
20
|
exports.LICENSE_GENERATOR_INFO = (0, nx_1.getGeneratorInfo)(__filename);
|
|
20
21
|
async function licenseGenerator(tree, options) {
|
|
21
22
|
const { license, copyrightHolder } = options;
|
|
@@ -44,8 +45,8 @@ async function licenseGenerator(tree, options) {
|
|
|
44
45
|
const lintTargetDefault = {
|
|
45
46
|
...nxJson.targetDefaults?.lint,
|
|
46
47
|
syncGenerators: [
|
|
47
|
-
...(nxJson.targetDefaults?.lint?.syncGenerators ?? []).filter((g) => g !==
|
|
48
|
-
|
|
48
|
+
...(nxJson.targetDefaults?.lint?.syncGenerators ?? []).filter((g) => g !== generator_4.SYNC_GENERATOR_NAME),
|
|
49
|
+
generator_4.SYNC_GENERATOR_NAME,
|
|
49
50
|
],
|
|
50
51
|
};
|
|
51
52
|
const updatedNxJson = {
|
|
@@ -95,9 +96,15 @@ const writeLicenseCheckTarget = async (tree) => {
|
|
|
95
96
|
const addExceptionsForExistingProjects = async (tree) => {
|
|
96
97
|
const projects = (0, devkit_1.getProjects)(tree);
|
|
97
98
|
let needsMcp = false;
|
|
99
|
+
// A LangChain AG-UI agent pulls jsonpatch/jsonpointer (BSD-3-Clause but with
|
|
100
|
+
// only free-text metadata) via langchain-core. The py#agent generator adds
|
|
101
|
+
// these exceptions itself, but if the license generator runs afterwards (or
|
|
102
|
+
// the agent predates the dependency-check block) they may be missing, so
|
|
103
|
+
// re-assert them here when such an agent exists.
|
|
104
|
+
let needsLangchain = false;
|
|
98
105
|
const mcpIds = [
|
|
99
|
-
|
|
100
|
-
|
|
106
|
+
generator_3.TS_MCP_SERVER_GENERATOR_INFO.id,
|
|
107
|
+
generator_2.PY_MCP_SERVER_GENERATOR_INFO.id,
|
|
101
108
|
];
|
|
102
109
|
for (const [, config] of projects) {
|
|
103
110
|
const components = config.metadata?.components ??
|
|
@@ -108,9 +115,16 @@ const addExceptionsForExistingProjects = async (tree) => {
|
|
|
108
115
|
if (typeof gen === 'string' && mcpIds.includes(gen)) {
|
|
109
116
|
needsMcp = true;
|
|
110
117
|
}
|
|
118
|
+
if (gen === generator_1.PY_AGENT_GENERATOR_INFO.id &&
|
|
119
|
+
comp.framework === 'langchain') {
|
|
120
|
+
needsLangchain = true;
|
|
121
|
+
}
|
|
111
122
|
}
|
|
112
123
|
}
|
|
113
|
-
const exceptions = [
|
|
124
|
+
const exceptions = [
|
|
125
|
+
...(needsMcp ? known_exceptions_1.MCP_INSPECTOR_EXCEPTIONS : []),
|
|
126
|
+
...(needsLangchain ? known_exceptions_1.AG_UI_LANGGRAPH_EXCEPTIONS : []),
|
|
127
|
+
];
|
|
114
128
|
if (exceptions.length > 0) {
|
|
115
129
|
await (0, config_1.ensureLicenseExceptions)(tree, exceptions);
|
|
116
130
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../packages/nx-plugin/src/license/generator.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../packages/nx-plugin/src/license/generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MCP_INSPECTOR_EXCEPTIONS = void 0;
|
|
3
|
+
exports.AG_UI_LANGGRAPH_EXCEPTIONS = exports.MCP_INSPECTOR_EXCEPTIONS = void 0;
|
|
4
4
|
exports.MCP_INSPECTOR_EXCEPTIONS = [
|
|
5
5
|
{
|
|
6
6
|
package: '@modelcontextprotocol/inspector',
|
|
@@ -23,4 +23,16 @@ exports.MCP_INSPECTOR_EXCEPTIONS = [
|
|
|
23
23
|
spdx: 'Apache-2.0',
|
|
24
24
|
},
|
|
25
25
|
];
|
|
26
|
+
exports.AG_UI_LANGGRAPH_EXCEPTIONS = [
|
|
27
|
+
{
|
|
28
|
+
package: 'jsonpatch',
|
|
29
|
+
reason: 'Unconditional dependency of langchain-core. Genuinely BSD-3-Clause (LICENSE matches the SPDX BSD-3-Clause template), but the wheel only carries the free-text "Modified BSD License" metadata, not an SPDX expression.',
|
|
30
|
+
spdx: 'BSD-3-Clause',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
package: 'jsonpointer',
|
|
34
|
+
reason: 'Transitive dependency of jsonpatch (via langchain-core). Genuinely BSD-3-Clause (LICENSE matches the SPDX BSD-3-Clause template), but the wheel only carries the free-text "Modified BSD License" metadata, not an SPDX expression.',
|
|
35
|
+
spdx: 'BSD-3-Clause',
|
|
36
|
+
},
|
|
37
|
+
];
|
|
26
38
|
//# sourceMappingURL=known-exceptions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"known-exceptions.js","sourceRoot":"","sources":["../../../../../packages/nx-plugin/src/license/known-exceptions.ts"],"names":[],"mappings":";;;AAMa,QAAA,wBAAwB,GAA+B;IAClE;QACE,OAAO,EAAE,iCAAiC;QAC1C,MAAM,EACJ,2EAA2E;QAC7E,IAAI,EAAE,YAAY;KACnB;IACD;QACE,OAAO,EAAE,qCAAqC;QAC9C,MAAM,EACJ,2EAA2E;QAC7E,IAAI,EAAE,YAAY;KACnB;IACD;QACE,OAAO,EAAE,wCAAwC;QACjD,MAAM,EACJ,2EAA2E;QAC7E,IAAI,EAAE,YAAY;KACnB;IACD;QACE,OAAO,EAAE,wCAAwC;QACjD,MAAM,EACJ,2EAA2E;QAC7E,IAAI,EAAE,YAAY;KACnB;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"known-exceptions.js","sourceRoot":"","sources":["../../../../../packages/nx-plugin/src/license/known-exceptions.ts"],"names":[],"mappings":";;;AAMa,QAAA,wBAAwB,GAA+B;IAClE;QACE,OAAO,EAAE,iCAAiC;QAC1C,MAAM,EACJ,2EAA2E;QAC7E,IAAI,EAAE,YAAY;KACnB;IACD;QACE,OAAO,EAAE,qCAAqC;QAC9C,MAAM,EACJ,2EAA2E;QAC7E,IAAI,EAAE,YAAY;KACnB;IACD;QACE,OAAO,EAAE,wCAAwC;QACjD,MAAM,EACJ,2EAA2E;QAC7E,IAAI,EAAE,YAAY;KACnB;IACD;QACE,OAAO,EAAE,wCAAwC;QACjD,MAAM,EACJ,2EAA2E;QAC7E,IAAI,EAAE,YAAY;KACnB;CACF,CAAC;AAEW,QAAA,0BAA0B,GAA+B;IACpE;QACE,OAAO,EAAE,WAAW;QACpB,MAAM,EACJ,wNAAwN;QAC1N,IAAI,EAAE,cAAc;KACrB;IACD;QACE,OAAO,EAAE,aAAa;QACtB,MAAM,EACJ,qOAAqO;QACvO,IAAI,EAAE,cAAc;KACrB;CACF,CAAC"}
|
|
@@ -569,6 +569,160 @@ await chatLoop(new TestProjectAgentAdapter(), process.env.URL ?? '');
|
|
|
569
569
|
"
|
|
570
570
|
`;
|
|
571
571
|
|
|
572
|
+
exports[`py#agent generator > langchain framework > should match snapshot for langchain generated files > langchain-__init__.py 1`] = `""`;
|
|
573
|
+
|
|
574
|
+
exports[`py#agent generator > langchain framework > should match snapshot for langchain generated files > langchain-agent.py 1`] = `
|
|
575
|
+
"import os
|
|
576
|
+
|
|
577
|
+
from langchain.agents import create_agent
|
|
578
|
+
from langchain_aws import ChatBedrockConverse
|
|
579
|
+
from langchain_core.tools import tool
|
|
580
|
+
from langgraph.checkpoint.memory import InMemorySaver
|
|
581
|
+
|
|
582
|
+
REGION = os.environ.get("AWS_REGION", "us-east-1")
|
|
583
|
+
MODEL_ID = os.environ.get("MODEL_ID", "global.anthropic.claude-haiku-4-5-20251001-v1:0")
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
@tool
|
|
587
|
+
def subtract(a: int, b: int) -> int:
|
|
588
|
+
"""Subtract b from a."""
|
|
589
|
+
return a - b
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def get_agent():
|
|
593
|
+
model = ChatBedrockConverse(model=MODEL_ID, region_name=REGION)
|
|
594
|
+
# Swap InMemorySaver for a durable checkpointer in a multi-replica deployment.
|
|
595
|
+
return create_agent(
|
|
596
|
+
model=model,
|
|
597
|
+
tools=[subtract],
|
|
598
|
+
system_prompt="""
|
|
599
|
+
You are a mathematical wizard.
|
|
600
|
+
Use your tools for mathematical tasks.
|
|
601
|
+
Refer to tools as your 'spellbook'.
|
|
602
|
+
""",
|
|
603
|
+
checkpointer=InMemorySaver(),
|
|
604
|
+
)
|
|
605
|
+
"
|
|
606
|
+
`;
|
|
607
|
+
|
|
608
|
+
exports[`py#agent generator > langchain framework > should match snapshot for langchain generated files > langchain-main.py 1`] = `
|
|
609
|
+
"import logging
|
|
610
|
+
import time
|
|
611
|
+
import uuid
|
|
612
|
+
|
|
613
|
+
from ag_ui.core import EventType, RunAgentInput, RunErrorEvent
|
|
614
|
+
from ag_ui.encoder import EventEncoder
|
|
615
|
+
from ag_ui_langgraph import LangGraphAgent
|
|
616
|
+
from fastapi import FastAPI, Request
|
|
617
|
+
from fastapi.responses import JSONResponse, StreamingResponse
|
|
618
|
+
from proj_agent_connection import get_current_session_id, session_id_context
|
|
619
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
620
|
+
|
|
621
|
+
from .agent import get_agent
|
|
622
|
+
|
|
623
|
+
logging.basicConfig(level=logging.INFO)
|
|
624
|
+
|
|
625
|
+
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
|
|
626
|
+
|
|
627
|
+
_graph = get_agent()
|
|
628
|
+
|
|
629
|
+
agui_agent = LangGraphAgent(
|
|
630
|
+
name="SnapshotAgent",
|
|
631
|
+
graph=_graph,
|
|
632
|
+
description="A LangChain/LangGraph Agent exposed via the AG-UI protocol.",
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
class _SessionIdMiddleware(BaseHTTPMiddleware):
|
|
637
|
+
"""Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls."""
|
|
638
|
+
|
|
639
|
+
async def dispatch(self, request: Request, call_next):
|
|
640
|
+
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
|
|
641
|
+
with session_id_context(session_id):
|
|
642
|
+
return await call_next(request)
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
app = FastAPI(title="SnapshotAgent")
|
|
646
|
+
app.add_middleware(_SessionIdMiddleware)
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
@app.post("/invocations")
|
|
650
|
+
async def invocations(request: Request):
|
|
651
|
+
# Validate the body manually since AgentCore may omit Content-Type.
|
|
652
|
+
accept = request.headers.get("accept") or ""
|
|
653
|
+
encoder = EventEncoder(accept=accept)
|
|
654
|
+
raw = await request.body()
|
|
655
|
+
try:
|
|
656
|
+
input_data = RunAgentInput.model_validate_json(raw)
|
|
657
|
+
except Exception as exc:
|
|
658
|
+
message = f"Invalid RunAgentInput: {str(exc)[:200]}"
|
|
659
|
+
|
|
660
|
+
async def _bad():
|
|
661
|
+
yield encoder.encode(
|
|
662
|
+
RunErrorEvent(
|
|
663
|
+
type=EventType.RUN_ERROR, message=message, code="BAD_REQUEST"
|
|
664
|
+
)
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
return StreamingResponse(_bad(), media_type=encoder.get_content_type())
|
|
668
|
+
|
|
669
|
+
session_id = request.headers.get(SESSION_ID_HEADER) or get_current_session_id()
|
|
670
|
+
|
|
671
|
+
async def event_generator():
|
|
672
|
+
# Re-bind the session: the streaming body runs outside the middleware.
|
|
673
|
+
with session_id_context(session_id or str(uuid.uuid4())):
|
|
674
|
+
try:
|
|
675
|
+
async for event in agui_agent.run(input_data):
|
|
676
|
+
yield encoder.encode(event)
|
|
677
|
+
except Exception as e:
|
|
678
|
+
yield encoder.encode(
|
|
679
|
+
RunErrorEvent(
|
|
680
|
+
type=EventType.RUN_ERROR,
|
|
681
|
+
message=str(e)[:300],
|
|
682
|
+
code="AGENT_ERROR",
|
|
683
|
+
)
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
return StreamingResponse(event_generator(), media_type=encoder.get_content_type())
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
@app.get("/ping")
|
|
690
|
+
async def ping():
|
|
691
|
+
return JSONResponse({"status": "Healthy", "time_of_last_update": int(time.time())})
|
|
692
|
+
"
|
|
693
|
+
`;
|
|
694
|
+
|
|
695
|
+
exports[`py#agent generator > langchain framework > should match snapshot for langchain generated files > langchain-pyproject.toml 1`] = `
|
|
696
|
+
"[project]
|
|
697
|
+
name = "proj.test_project"
|
|
698
|
+
version = "0.1.0"
|
|
699
|
+
dependencies = [
|
|
700
|
+
"proj.agent_connection",
|
|
701
|
+
"aws-lambda-powertools==3.29.0",
|
|
702
|
+
"aws-opentelemetry-distro==0.17.1",
|
|
703
|
+
"bedrock-agentcore==1.14.1",
|
|
704
|
+
"boto3==1.43.29",
|
|
705
|
+
"fastapi==0.137.1",
|
|
706
|
+
"mcp==1.27.2",
|
|
707
|
+
"langchain==1.3.9",
|
|
708
|
+
"langchain-aws==1.5.1",
|
|
709
|
+
"langgraph==1.2.5",
|
|
710
|
+
"ag-ui-protocol==0.1.19",
|
|
711
|
+
"ag-ui-langgraph==0.0.42",
|
|
712
|
+
"uvicorn==0.49.0"
|
|
713
|
+
]
|
|
714
|
+
|
|
715
|
+
[dependency-groups]
|
|
716
|
+
dev = [ "fastapi[standard]==0.137.1" ]
|
|
717
|
+
|
|
718
|
+
[tool.uv]
|
|
719
|
+
dev-dependencies = [ ]
|
|
720
|
+
|
|
721
|
+
[tool.uv.sources."proj.agent_connection"]
|
|
722
|
+
workspace = true
|
|
723
|
+
"
|
|
724
|
+
`;
|
|
725
|
+
|
|
572
726
|
exports[`py#agent generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > agent-Dockerfile 1`] = `
|
|
573
727
|
"FROM public.ecr.aws/docker/library/python:3.14-slim
|
|
574
728
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from <%- agentConnectionModuleName %>.core.agentcore_a2a_client_langchain import (
|
|
4
|
+
AgentCoreA2aClientLangChain,
|
|
5
|
+
)
|
|
6
|
+
from <%- agentConnectionModuleName %>.core.runtime_config import (
|
|
7
|
+
get_agentcore_runtime_config,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class <%- targetAgentClassName %>ClientLangChain:
|
|
12
|
+
"""LangChain client for the <%- targetAgentClassName %> A2A agent."""
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def create():
|
|
16
|
+
if os.environ.get("SERVE_LOCAL") == "true":
|
|
17
|
+
return AgentCoreA2aClientLangChain.without_auth(
|
|
18
|
+
"http://localhost:<%- targetAgentPort %>/"
|
|
19
|
+
)
|
|
20
|
+
config = get_agentcore_runtime_config()
|
|
21
|
+
agent_runtime_arn = config.get("agentRuntimes", {}).get(
|
|
22
|
+
"<%- targetAgentClassName %>"
|
|
23
|
+
)
|
|
24
|
+
if not agent_runtime_arn:
|
|
25
|
+
raise RuntimeError(
|
|
26
|
+
"No connected agent runtime named '<%- targetAgentClassName %>' found in runtime configuration."
|
|
27
|
+
)
|
|
28
|
+
return AgentCoreA2aClientLangChain.with_iam_auth(agent_runtime_arn)
|
|
@@ -15,6 +15,25 @@ const nx_1 = require("../../../utils/nx");
|
|
|
15
15
|
const py_1 = require("../../../utils/py");
|
|
16
16
|
/** Prefix a GritQL pattern with `language python` */
|
|
17
17
|
const py = (pattern) => `language python\n${pattern}`;
|
|
18
|
+
/** The module each framework imports the `tool` decorator from. */
|
|
19
|
+
const TOOL_IMPORT_MODULE = {
|
|
20
|
+
strands: 'strands',
|
|
21
|
+
langchain: 'langchain_core.tools',
|
|
22
|
+
};
|
|
23
|
+
/** The GritQL pattern matching each framework's agent constructor. */
|
|
24
|
+
const AGENT_CONSTRUCTOR = {
|
|
25
|
+
strands: 'yield Agent($_)',
|
|
26
|
+
langchain: 'create_agent($_)',
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* The A2A transport dependency each framework's Layer-2 client needs. Strands
|
|
30
|
+
* wraps strands' `A2AAgent` (strands-agents[a2a]); LangChain drives the a2a SDK
|
|
31
|
+
* directly (a2a-sdk) and must not pull Strands in.
|
|
32
|
+
*/
|
|
33
|
+
const A2A_TRANSPORT_DEP = {
|
|
34
|
+
strands: 'strands-agents[a2a]',
|
|
35
|
+
langchain: 'a2a-sdk',
|
|
36
|
+
};
|
|
18
37
|
exports.PY_AGENT_A2A_CONNECTION_GENERATOR_INFO = (0, nx_1.getGeneratorInfo)(__filename);
|
|
19
38
|
const pyAgentA2aConnectionGenerator = async (tree, options) => {
|
|
20
39
|
const sourceProject = (0, nx_1.readProjectConfigurationUnqualified)(tree, options.sourceProject);
|
|
@@ -35,24 +54,32 @@ const pyAgentA2aConnectionGenerator = async (tree, options) => {
|
|
|
35
54
|
const targetAgentClassName = targetAgentComponent.rc;
|
|
36
55
|
const targetAgentSnakeCase = (0, names_1.snakeCase)(targetAgentClassName);
|
|
37
56
|
const targetAgentPort = targetAgentComponent.port ?? 9000;
|
|
57
|
+
// The source agent's framework drives everything that varies: the client
|
|
58
|
+
// naming + app template (shared with the mcp/gateway generators via
|
|
59
|
+
// PY_CLIENT_NAMING), the A2A transport dep (see A2A_TRANSPORT_DEP), and the
|
|
60
|
+
// agent.py transform below. Each framework wraps its own A2A Layer-2 client
|
|
61
|
+
// (Strands' A2AAgent vs an a2a-sdk-only client) over the shared,
|
|
62
|
+
// framework-agnostic A2A client config. Keyed on the framework, not a
|
|
63
|
+
// boolean, so a third framework stays additive.
|
|
64
|
+
const framework = (0, agent_connection_1.resolveAgentFramework)(agentComponent.framework);
|
|
65
|
+
const naming = agent_connection_1.PY_CLIENT_NAMING[framework];
|
|
38
66
|
// 1. Ensure the shared Python agent-connection project exists + has the
|
|
39
|
-
// A2A core client and its shared SigV4 auth helper.
|
|
67
|
+
// A2A core client (framework Layer-2) and its shared SigV4 auth helper.
|
|
40
68
|
await (0, agent_connection_1.ensurePythonAgentConnectionProject)(tree);
|
|
41
|
-
await (0, agent_connection_1.addPythonCoreClient)(tree, 'a2a');
|
|
69
|
+
await (0, agent_connection_1.addPythonCoreClient)(tree, 'a2a', framework);
|
|
42
70
|
const agentConnectionProjectDir = (0, agent_connection_1.getPythonAgentConnectionProjectDir)(tree);
|
|
43
71
|
const agentConnectionModuleName = (0, agent_connection_1.getPythonAgentConnectionModuleName)(tree);
|
|
44
72
|
const agentConnectionPackageName = (0, agent_connection_1.getPythonAgentConnectionPackageName)(tree);
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// dependency is added by addPythonCoreClient).
|
|
73
|
+
// A2A client config + shared auth helper deps, plus the framework's A2A
|
|
74
|
+
// transport dep (see A2A_TRANSPORT_DEP).
|
|
48
75
|
(0, py_1.addDependenciesToPyProjectToml)(tree, agentConnectionProjectDir, [
|
|
49
76
|
'boto3',
|
|
50
77
|
'httpx',
|
|
51
|
-
|
|
78
|
+
A2A_TRANSPORT_DEP[framework],
|
|
52
79
|
]);
|
|
53
80
|
// 2. Generate the per-connection client into the shared agent-connection project
|
|
54
81
|
const appDir = (0, devkit_1.joinPathFragments)(agentConnectionProjectDir, agentConnectionModuleName, 'app');
|
|
55
|
-
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files',
|
|
82
|
+
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files', naming.appTemplateSubdir), appDir, {
|
|
56
83
|
targetAgentSnakeCase,
|
|
57
84
|
targetAgentClassName,
|
|
58
85
|
targetAgentPort,
|
|
@@ -65,17 +92,17 @@ const pyAgentA2aConnectionGenerator = async (tree, options) => {
|
|
|
65
92
|
}
|
|
66
93
|
// Add re-export to module __init__.py
|
|
67
94
|
const moduleInitPath = (0, devkit_1.joinPathFragments)(agentConnectionProjectDir, agentConnectionModuleName, '__init__.py');
|
|
68
|
-
await (0, agent_connection_1.addPythonReExport)(tree, moduleInitPath, `.app.${targetAgentSnakeCase}
|
|
95
|
+
await (0, agent_connection_1.addPythonReExport)(tree, moduleInitPath, `.app.${targetAgentSnakeCase}_client_${naming.clientModuleSuffix}`, `${targetAgentClassName}Client${naming.clientClassSuffix}`);
|
|
69
96
|
// 3. Transform agent.py to add the A2A client import + wrap it as a tool
|
|
70
97
|
const agentSourceDir = (0, devkit_1.joinPathFragments)(sourceProject.root, agentComponent.path ?? 'src');
|
|
71
98
|
const agentFilePath = (0, devkit_1.joinPathFragments)(agentSourceDir, 'agent.py');
|
|
72
99
|
if (tree.exists(agentFilePath)) {
|
|
73
|
-
const clientClassName = `${targetAgentClassName}
|
|
100
|
+
const clientClassName = `${targetAgentClassName}Client${naming.clientClassSuffix}`;
|
|
74
101
|
const clientVarName = targetAgentSnakeCase;
|
|
75
102
|
const toolName = `ask_${targetAgentSnakeCase}`;
|
|
76
|
-
await addImportToAgentFile(tree, agentFilePath, agentConnectionModuleName, clientClassName);
|
|
77
|
-
await addToolToAgent(tree, agentFilePath, toolName);
|
|
78
|
-
await addClientToolToGetAgent(tree, agentFilePath, clientClassName, clientVarName, toolName, targetAgentClassName);
|
|
103
|
+
await addImportToAgentFile(tree, agentFilePath, agentConnectionModuleName, clientClassName, framework);
|
|
104
|
+
await addToolToAgent(tree, agentFilePath, toolName, framework);
|
|
105
|
+
await addClientToolToGetAgent(tree, agentFilePath, clientClassName, clientVarName, toolName, targetAgentClassName, framework);
|
|
79
106
|
}
|
|
80
107
|
// 4. Add workspace dependency from agent project to agent-connection project
|
|
81
108
|
(0, py_1.addWorkspaceDependencyToPyProject)(tree, sourceProject.root, agentConnectionPackageName);
|
|
@@ -101,22 +128,23 @@ const pyAgentA2aConnectionGenerator = async (tree, options) => {
|
|
|
101
128
|
exports.pyAgentA2aConnectionGenerator = pyAgentA2aConnectionGenerator;
|
|
102
129
|
/**
|
|
103
130
|
* Add imports to agent.py:
|
|
104
|
-
* - `tool` from `strands`
|
|
131
|
+
* - `tool` (from `strands`, or `langchain_core.tools` for LangChain agents)
|
|
105
132
|
* - the per-connection client class from the shared agent-connection module
|
|
106
133
|
*/
|
|
107
|
-
const addImportToAgentFile = async (tree, filePath, agentConnectionModuleName, clientClassName) => {
|
|
108
|
-
await (0, ast_1.addPythonDestructuredImport)(tree, filePath, ['tool'],
|
|
134
|
+
const addImportToAgentFile = async (tree, filePath, agentConnectionModuleName, clientClassName, framework) => {
|
|
135
|
+
await (0, ast_1.addPythonDestructuredImport)(tree, filePath, ['tool'], TOOL_IMPORT_MODULE[framework]);
|
|
109
136
|
await (0, ast_1.addPythonDestructuredImport)(tree, filePath, [clientClassName], agentConnectionModuleName);
|
|
110
137
|
};
|
|
111
138
|
/**
|
|
112
139
|
* Add the A2A tool to the Agent's tools array using GritQL.
|
|
113
140
|
*
|
|
114
|
-
* Scoped
|
|
115
|
-
*
|
|
141
|
+
* Scoped to the framework's agent constructor so unrelated `tools=` keyword
|
|
142
|
+
* arguments elsewhere in the file are not touched.
|
|
116
143
|
*/
|
|
117
|
-
const addToolToAgent = async (tree, filePath, toolName) => {
|
|
144
|
+
const addToolToAgent = async (tree, filePath, toolName, framework) => {
|
|
145
|
+
const scope = AGENT_CONSTRUCTOR[framework];
|
|
118
146
|
await (0, ast_1.applyGritQL)(tree, filePath, py(`\`tools=$old\` where {
|
|
119
|
-
$old <: within
|
|
147
|
+
$old <: within \`${scope}\`,
|
|
120
148
|
$old <: not contains \`${toolName}\`,
|
|
121
149
|
if ($old <: \`[]\`) {
|
|
122
150
|
$old => \`[${toolName}]\`
|
|
@@ -127,19 +155,19 @@ const addToolToAgent = async (tree, filePath, toolName) => {
|
|
|
127
155
|
};
|
|
128
156
|
/**
|
|
129
157
|
* Wrap the get_agent body so the remote A2A client is created and exposed
|
|
130
|
-
* as a `@tool`-decorated closure before
|
|
158
|
+
* as a `@tool`-decorated closure before constructing the Agent.
|
|
131
159
|
*
|
|
132
160
|
* Unlike MCP clients, A2A clients aren't context managers — we don't need
|
|
133
161
|
* a `with` block around them. Creation + tool definition happen at the
|
|
134
|
-
* top of the function body.
|
|
162
|
+
* top of the function body, anchored on the framework's agent constructor.
|
|
135
163
|
*/
|
|
136
|
-
const addClientToolToGetAgent = async (tree, filePath, clientClassName, clientVarName, toolName, targetAgentClassName) => {
|
|
164
|
+
const addClientToolToGetAgent = async (tree, filePath, clientClassName, clientVarName, toolName, targetAgentClassName, framework) => {
|
|
137
165
|
if (await (0, ast_1.matchGritQL)(tree, filePath, py(`\`${clientClassName}.create\``))) {
|
|
138
166
|
return;
|
|
139
167
|
}
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
168
|
+
// Both frameworks' tools are @tool-decorated callables (the decorator's
|
|
169
|
+
// import differs, handled in addImportToAgentFile), defined inline in
|
|
170
|
+
// get_agent. The client is directly callable, returning the remote's reply.
|
|
143
171
|
const toolBlock = `${clientVarName} = ${clientClassName}.create()
|
|
144
172
|
|
|
145
173
|
@tool
|
|
@@ -147,12 +175,13 @@ const addClientToolToGetAgent = async (tree, filePath, clientClassName, clientVa
|
|
|
147
175
|
"""Delegate a question to the remote ${targetAgentClassName} A2A agent and return its reply."""
|
|
148
176
|
return str(${clientVarName}(prompt))
|
|
149
177
|
`;
|
|
150
|
-
// Prepend client creation + tool definition to the function body
|
|
151
|
-
// This works whether or not a prior MCP
|
|
152
|
-
// insert before any existing statements.
|
|
178
|
+
// Prepend client creation + tool definition to the function body, anchored on
|
|
179
|
+
// the framework's agent constructor. This works whether or not a prior MCP
|
|
180
|
+
// block is present since we insert before any existing statements.
|
|
181
|
+
const anchor = AGENT_CONSTRUCTOR[framework];
|
|
153
182
|
await (0, ast_1.applyGritQL)(tree, filePath, py(`\`def get_agent($params):
|
|
154
183
|
$body\` where {
|
|
155
|
-
$body <: contains
|
|
184
|
+
$body <: contains \`${anchor}\`,
|
|
156
185
|
$body <: not contains \`${clientClassName}.create\`
|
|
157
186
|
} => \`def get_agent($params):
|
|
158
187
|
${toolBlock}
|
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../../packages/nx-plugin/src/py/agent/a2a-connection/generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
from a2a.server.agent_execution import AgentExecutor, RequestContext
|
|
6
|
+
from a2a.server.apps import A2AStarletteApplication
|
|
7
|
+
from a2a.server.events import EventQueue
|
|
8
|
+
from a2a.server.request_handlers import DefaultRequestHandler
|
|
9
|
+
from a2a.server.tasks import InMemoryTaskStore, TaskUpdater
|
|
10
|
+
from a2a.types import AgentCapabilities, AgentCard, Part, TextPart
|
|
11
|
+
from a2a.utils import new_task
|
|
12
|
+
from fastapi import FastAPI, Request
|
|
13
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
14
|
+
|
|
15
|
+
from <%- agentConnectionModuleName %> import get_current_session_id, session_id_context
|
|
16
|
+
|
|
17
|
+
from .agent import get_agent
|
|
18
|
+
|
|
19
|
+
logging.basicConfig(level=logging.INFO)
|
|
20
|
+
|
|
21
|
+
PORT = int(os.environ.get("PORT", "9000"))
|
|
22
|
+
RUNTIME_URL = os.environ.get("AGENTCORE_RUNTIME_URL", f"http://localhost:{PORT}/")
|
|
23
|
+
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
|
|
24
|
+
DEFAULT_MODES = ["text/plain"]
|
|
25
|
+
|
|
26
|
+
_graph = get_agent()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class _GraphAgentExecutor(AgentExecutor):
|
|
30
|
+
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
31
|
+
if context.message is None:
|
|
32
|
+
raise ValueError("A2A request is missing a message")
|
|
33
|
+
task = context.current_task or new_task(context.message)
|
|
34
|
+
await event_queue.enqueue_event(task)
|
|
35
|
+
updater = TaskUpdater(event_queue, task.id, task.context_id)
|
|
36
|
+
await updater.start_work()
|
|
37
|
+
# The session bound by the middleware drives the LangGraph thread, keeping
|
|
38
|
+
# checkpointed conversation state separate per caller session.
|
|
39
|
+
session_id = get_current_session_id() or task.context_id
|
|
40
|
+
result = await _graph.ainvoke(
|
|
41
|
+
{"messages": [{"role": "user", "content": context.get_user_input()}]},
|
|
42
|
+
{"configurable": {"thread_id": session_id}},
|
|
43
|
+
)
|
|
44
|
+
reply = result["messages"][-1].content
|
|
45
|
+
await updater.add_artifact([Part(TextPart(kind="text", text=reply))])
|
|
46
|
+
await updater.complete()
|
|
47
|
+
|
|
48
|
+
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
49
|
+
raise NotImplementedError("Cancellation is not supported.")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
_handler = DefaultRequestHandler(
|
|
53
|
+
agent_executor=_GraphAgentExecutor(),
|
|
54
|
+
task_store=InMemoryTaskStore(),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
_agent_card = AgentCard(
|
|
58
|
+
name="<%= agentNameClassName %>",
|
|
59
|
+
description="A LangChain/LangGraph Agent exposed via the Agent-to-Agent (A2A) protocol.",
|
|
60
|
+
url=RUNTIME_URL,
|
|
61
|
+
version="1.0.0",
|
|
62
|
+
default_input_modes=DEFAULT_MODES,
|
|
63
|
+
default_output_modes=DEFAULT_MODES,
|
|
64
|
+
capabilities=AgentCapabilities(streaming=True),
|
|
65
|
+
skills=[],
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
a2a_app = A2AStarletteApplication(agent_card=_agent_card, http_handler=_handler).build()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class _SessionIdMiddleware(BaseHTTPMiddleware):
|
|
72
|
+
"""Bind the inbound session (or a fresh UUID) to async context."""
|
|
73
|
+
|
|
74
|
+
async def dispatch(self, request: Request, call_next):
|
|
75
|
+
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
|
|
76
|
+
with session_id_context(session_id):
|
|
77
|
+
return await call_next(request)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
app = FastAPI(title="<%= agentNameClassName %>")
|
|
81
|
+
app.add_middleware(_SessionIdMiddleware)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@app.get("/ping")
|
|
85
|
+
def ping() -> dict[str, str]:
|
|
86
|
+
return {"status": "Healthy"}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
app.mount("/", a2a_app)
|