@aws/nx-plugin 1.0.0-rc.71 → 1.0.0-rc.73
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/LICENSE-THIRD-PARTY +210 -730
- package/migrations.json +34 -19
- package/package.json +4 -4
- package/src/agentcore-gateway/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/agentcore-harness/generator.d.ts +1 -1
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +3 -3
- package/src/infra/app/generator.d.ts +1 -1
- package/src/init/generator.d.ts +1 -1
- package/src/license/config.d.ts +9 -0
- package/src/license/config.js +28 -0
- package/src/license/config.js.map +1 -1
- package/src/license/dependency-check/collectors/collector.js.map +1 -1
- package/src/license/dependency-check/evaluator.js +55 -10
- package/src/license/dependency-check/evaluator.js.map +1 -1
- package/src/license/generator.js +3 -17
- package/src/license/generator.js.map +1 -1
- package/src/license/known-exceptions.d.ts +0 -1
- package/src/license/known-exceptions.js +1 -23
- package/src/license/known-exceptions.js.map +1 -1
- package/src/migrations/latest/apt-security-updates-in-container-images/__snapshots__/migration.spec.ts.snap +90 -0
- package/src/migrations/latest/apt-security-updates-in-container-images/metadata.json +3 -0
- package/src/migrations/latest/apt-security-updates-in-container-images/migration.js +90 -0
- package/src/migrations/latest/apt-security-updates-in-container-images/migration.js.map +1 -0
- package/src/migrations/latest/remove-mcp-inspector-license-exceptions/metadata.json +3 -0
- package/src/migrations/latest/remove-mcp-inspector-license-exceptions/migration.d.ts +6 -0
- package/src/migrations/latest/remove-mcp-inspector-license-exceptions/migration.js +38 -0
- package/src/migrations/latest/remove-mcp-inspector-license-exceptions/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.67/0001-smithy-server-package-rename/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/migration.js.map +1 -0
- package/src/migrations/{latest/py-agent-a2a-lifespan-construction → v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction}/migration.js +14 -2
- package/src/migrations/v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call/migration.d.ts +6 -0
- package/src/migrations/v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/metadata.json +3 -0
- package/src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/migration.d.ts +2 -0
- package/src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/migration.js +308 -0
- package/src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/migration.js.map +1 -0
- package/src/preset/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/preset/generator.d.ts +1 -1
- package/src/py/agent/__snapshots__/generator.constructs.spec.ts.snap +626 -10
- package/src/py/agent/__snapshots__/generator.core.spec.ts.snap +186 -0
- package/src/py/agent/__snapshots__/generator.frameworks.spec.ts.snap +55 -11
- package/src/py/agent/__snapshots__/generator.protocols.spec.ts.snap +669 -4
- package/src/py/agent/a2a-connection/generator.js +5 -11
- package/src/py/agent/a2a-connection/generator.js.map +1 -1
- package/src/py/agent/files/deploy/Dockerfile.template +7 -0
- package/src/py/agent/files/langchain/common/__init__.py.template +0 -0
- package/src/py/agent/files/langchain/common/agent.py.template +3 -3
- package/src/py/agent/files/langchain/common/session.py.template +61 -0
- package/src/py/agent/files/strands/ag-ui/main.py.template +5 -1
- package/src/py/agent/files/strands/common/agent.py.template +9 -2
- package/src/py/agent/files/strands/common/session.py.template +36 -0
- package/src/py/agent/generator.d.ts +11 -1
- package/src/py/agent/generator.js +78 -28
- package/src/py/agent/generator.js.map +1 -1
- package/src/py/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +2 -0
- package/src/py/agent/react-connection/generator.d.ts +1 -1
- package/src/py/agent/schema.d.js.map +1 -1
- package/src/py/agent/schema.d.ts +1 -1
- package/src/py/agent/schema.json +3 -3
- package/src/py/fast-api/react/generator.d.ts +1 -1
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +5 -5
- package/src/py/mcp-server/files/deploy/Dockerfile.template +7 -0
- package/src/py/mcp-server/generator.js +8 -3
- package/src/py/mcp-server/generator.js.map +1 -1
- package/src/py/rdb/__snapshots__/generator.spec.ts.snap +7 -5
- package/src/py/rdb/generator.js +36 -2
- package/src/py/rdb/generator.js.map +1 -1
- package/src/smithy/project/__snapshots__/generator.spec.ts.snap +14 -14
- package/src/smithy/project/generator.d.ts +1 -1
- package/src/smithy/react-connection/generator.d.ts +1 -1
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/terraform/project/generator.d.ts +1 -1
- package/src/trpc/react/generator.d.ts +1 -1
- package/src/ts/agent/__snapshots__/generator.spec.ts.snap +36 -8
- package/src/ts/agent/a2a-connection/generator.d.ts +1 -1
- package/src/ts/agent/a2a-connection/generator.js +4 -2
- package/src/ts/agent/a2a-connection/generator.js.map +1 -1
- package/src/ts/agent/files/deploy/Dockerfile.template +7 -0
- package/src/ts/agent/gateway-connection/generator.d.ts +1 -1
- package/src/ts/agent/mcp-connection/generator.d.ts +1 -1
- package/src/ts/agent/react-connection/generator.d.ts +1 -1
- package/src/ts/astro-docs/generator.d.ts +1 -1
- package/src/ts/dcr-proxy/generator.d.ts +1 -1
- package/src/ts/dynamodb/generator.d.ts +1 -1
- package/src/ts/lambda-function/generator.d.ts +1 -1
- package/src/ts/lib/generator.d.ts +1 -1
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/ts/mcp-server/files/Dockerfile.template +7 -0
- package/src/ts/mcp-server/generator.js +8 -3
- package/src/ts/mcp-server/generator.js.map +1 -1
- package/src/ts/nx-generator/generator.d.ts +1 -1
- package/src/ts/nx-migration/generator.d.ts +1 -1
- package/src/ts/nx-plugin/generator.d.ts +1 -1
- package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +8 -6
- package/src/ts/rdb/generator.d.ts +1 -1
- package/src/ts/rdb/generator.js +29 -2
- package/src/ts/rdb/generator.js.map +1 -1
- package/src/ts/react-website/agui/generator.d.ts +1 -1
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +15 -15
- package/src/ts/react-website/app/generator.js +4 -2
- package/src/ts/react-website/app/generator.js.map +1 -1
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +1 -1
- package/src/utils/agent-connection/agent-connection.d.ts +10 -0
- package/src/utils/agent-connection/agent-connection.js +20 -9
- package/src/utils/agent-connection/agent-connection.js.map +1 -1
- package/src/utils/agent-connection/files/core-runtime-config/runtime-config.ts.template +5 -3
- package/src/utils/agent-connection/files/py-core-langchain/s3/s3_checkpoint_saver_langchain.py.template +360 -0
- package/src/utils/agent-connection/files/py-core-runtime-config/runtime_config.py.template +2 -4
- package/src/utils/agent-connection/files/py-core-strands/base/tool_errors_strands.py.template +24 -0
- package/src/utils/agent-core-constructs/agent-core-constructs.d.ts +1 -1
- package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
- package/src/utils/agent-core-constructs/files/cdk/app/agent-core/__nameKebabCase__/__nameKebabCase__.ts.template +113 -13
- package/src/utils/agent-core-constructs/files/terraform/app/agent-core/__nameKebabCase__/__nameKebabCase__.tf.template +76 -7
- package/src/utils/ast.d.ts +29 -0
- package/src/utils/ast.js +31 -0
- package/src/utils/ast.js.map +1 -1
- package/src/utils/metrics.js +2 -2
- package/src/utils/metrics.js.map +1 -1
- package/src/utils/versions.d.ts +75 -72
- package/src/utils/versions.js +72 -69
- package/src/utils/versions.js.map +1 -1
- package/src/migrations/latest/py-agent-a2a-httpx-client-per-call/migration.js.map +0 -1
- package/src/migrations/latest/py-agent-a2a-lifespan-construction/migration.js.map +0 -1
- package/src/migrations/latest/smithy-server-package-rename/migration.js.map +0 -1
- package/src/migrations/latest/translate-script-whole-file-writes/migration.js.map +0 -1
- package/src/py/rdb/files/__name__/__init__.py.template +0 -3
- package/src/py/rdb/files/config.json.template +0 -14
- package/src/ts/rdb/files/config.json.template +0 -14
- package/src/ts/rdb/files/src/index.ts.template +0 -1
- /package/src/migrations/latest/{py-agent-a2a-httpx-client-per-call → apt-security-updates-in-container-images}/migration.d.ts +0 -0
- /package/src/migrations/{latest/smithy-server-package-rename → v1.0.0-rc.67/0001-smithy-server-package-rename}/metadata.json +0 -0
- /package/src/migrations/{latest/py-agent-a2a-lifespan-construction → v1.0.0-rc.67/0001-smithy-server-package-rename}/migration.d.ts +0 -0
- /package/src/migrations/{latest/smithy-server-package-rename → v1.0.0-rc.67/0001-smithy-server-package-rename}/migration.js +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/build-system-prompt.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/build-user-prompt.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/config-path.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/file-to-translate.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/get-files-to-translate.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/header.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/log.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/main-call.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/main.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/project-root.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/run-with-concurrency.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/scripts-dir.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/translate-file-for-language.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/already-migrated.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/build-system-prompt.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/build-user-prompt.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/config-path.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/file-to-translate.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/get-files-to-translate.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/log.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/main-call.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/main.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/project-root.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/run-with-concurrency.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/translate-file-for-language.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/metadata.json +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/migration.d.ts +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/migration.js +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/test-fixtures/released-script-first.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/test-fixtures/released-script.ts.fixture +0 -0
- /package/src/migrations/{latest/py-agent-a2a-lifespan-construction → v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction}/metadata.json +0 -0
- /package/src/migrations/{latest/smithy-server-package-rename → v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction}/migration.d.ts +0 -0
- /package/src/migrations/{latest/py-agent-a2a-httpx-client-per-call → v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call}/metadata.json +0 -0
- /package/src/migrations/{latest/py-agent-a2a-httpx-client-per-call → v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call}/migration.js +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/ import { generateFiles, joinPathFragments, OverwriteStrategy, updateProjectConfiguration } from "@nx/devkit";
|
|
5
5
|
import { addPyDependencies } from "../../../utils/add-dependencies.js";
|
|
6
6
|
import { AGENT_CONNECTION_PY_DEPENDENCIES, addPythonCoreClient, addPythonReExport, ensurePythonAgentConnectionProject, getPythonAgentConnectionModuleName, getPythonAgentConnectionProject, getPythonAgentConnectionProjectDir, PY_CLIENT_NAMING, resolveAgentFramework } from "../../../utils/agent-connection/agent-connection.js";
|
|
7
|
-
import { addPythonDestructuredImport, applyGritQL, matchGritQL } from "../../../utils/ast.js";
|
|
7
|
+
import { addPythonDestructuredImport, appendToArrayViaGritQL, applyGritQL, matchGritQL } from "../../../utils/ast.js";
|
|
8
8
|
import { declareDependencies, ownedElsewhere } from "../../../utils/declared-dependencies.js";
|
|
9
9
|
import { formatFilesInSubtree } from "../../../utils/format.js";
|
|
10
10
|
import { installDependencies } from "../../../utils/install.js";
|
|
@@ -169,16 +169,10 @@ export const pyAgentA2aConnectionGenerator = async (tree, options)=>{
|
|
|
169
169
|
* Scoped to the framework's agent constructor so unrelated `tools=` keyword
|
|
170
170
|
* arguments elsewhere in the file are not touched.
|
|
171
171
|
*/ const addToolToAgent = async (tree, filePath, toolName, framework)=>{
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if ($old <: \`[]\`) {
|
|
177
|
-
$old => \`[${toolName}]\`
|
|
178
|
-
} else {
|
|
179
|
-
$old <: \`[$items]\` where { $items += \`, ${toolName}\` }
|
|
180
|
-
}
|
|
181
|
-
}`));
|
|
172
|
+
await appendToArrayViaGritQL(tree, filePath, 'tools=', toolName, {
|
|
173
|
+
scope: AGENT_CONSTRUCTOR[framework],
|
|
174
|
+
language: 'py'
|
|
175
|
+
});
|
|
182
176
|
};
|
|
183
177
|
/**
|
|
184
178
|
* Wrap the get_agent body so the remote A2A client is created and exposed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/py/agent/a2a-connection/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n type GeneratorCallback,\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type Tree,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { addPyDependencies } from '../../../utils/add-dependencies';\nimport {\n AGENT_CONNECTION_PY_DEPENDENCIES,\n type AgentFramework,\n addPythonCoreClient,\n addPythonReExport,\n ensurePythonAgentConnectionProject,\n getPythonAgentConnectionModuleName,\n getPythonAgentConnectionProject,\n getPythonAgentConnectionProjectDir,\n PY_CLIENT_NAMING,\n resolveAgentFramework,\n} from '../../../utils/agent-connection/agent-connection';\nimport {\n addPythonDestructuredImport,\n applyGritQL,\n matchGritQL,\n} from '../../../utils/ast';\nimport {\n declareDependencies,\n ownedElsewhere,\n} from '../../../utils/declared-dependencies';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport { installDependencies } from '../../../utils/install';\nimport { addGeneratorMetricsIfApplicable } from '../../../utils/metrics';\nimport { snakeCase } from '../../../utils/names';\nimport {\n addComponentGeneratorMetadata,\n addDependencyToTargetIfNotPresent,\n getGeneratorInfo,\n type NxGeneratorInfo,\n readProjectConfigurationUnqualified,\n} from '../../../utils/nx';\nimport { toProjectRelativePath } from '../../../utils/paths';\nimport { addWorkspaceDependencyToPyProject } from '../../../utils/py';\nimport type { PyAgentA2aConnectionGeneratorSchema } from './schema';\n\n/** Prefix a GritQL pattern with `language python` */\nconst py = (pattern: string) => `language python\\n${pattern}`;\n\n/** The module each framework imports the `tool` decorator from. */\nconst TOOL_IMPORT_MODULE: Record<AgentFramework, string> = {\n strands: 'strands',\n langchain: 'langchain_core.tools',\n};\n\n/** The GritQL pattern matching each framework's agent constructor. */\nconst AGENT_CONSTRUCTOR: Record<AgentFramework, string> = {\n strands: 'yield Agent($_)',\n langchain: 'create_agent($_)',\n};\n\n/** The metadata this generator records, which its predicates read. */\nexport interface PyAgentA2aConnectionMetadata {\n readonly framework: AgentFramework;\n}\n\n// Each framework's Layer-2 client needs its own A2A transport: Strands wraps\n// strands' `A2AAgent` (strands-agents[a2a]); LangChain drives the a2a SDK\n// directly (a2a-sdk) and must not pull Strands in.\nexport const DEPENDENCIES = declareDependencies<PyAgentA2aConnectionMetadata>()(\n {\n py: [\n { name: 'boto3' },\n { name: 'httpx' },\n { name: 'strands-agents[a2a]', when: (m) => m.framework === 'strands' },\n { name: 'a2a-sdk', when: (m) => m.framework === 'langchain' },\n ...ownedElsewhere(AGENT_CONNECTION_PY_DEPENDENCIES),\n ],\n },\n);\n\nexport const PY_AGENT_A2A_CONNECTION_GENERATOR_INFO: NxGeneratorInfo =\n getGeneratorInfo(import.meta.filename);\n\nexport const pyAgentA2aConnectionGenerator = async (\n tree: Tree,\n options: PyAgentA2aConnectionGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const sourceProject = readProjectConfigurationUnqualified(\n tree,\n options.sourceProject,\n );\n const targetProject = readProjectConfigurationUnqualified(\n tree,\n options.targetProject,\n );\n\n const agentComponent = options.sourceComponent;\n const targetAgentComponent = options.targetComponent;\n\n if (!agentComponent || !targetAgentComponent) {\n throw new Error(\n 'Both sourceComponent and targetComponent must be provided for py#agent -> a2a connections',\n );\n }\n\n if ((targetAgentComponent.protocol ?? '').toLowerCase() !== 'a2a') {\n throw new Error(\n `Target agent '${targetAgentComponent.name}' uses the ${targetAgentComponent.protocol ?? 'http'} protocol — only A2A agents can be connected as tools.`,\n );\n }\n\n if (\n targetAgentComponent.auth &&\n targetAgentComponent.auth.toLowerCase() !== 'iam'\n ) {\n throw new Error(\n `A2A agent connection currently only supports IAM authentication, but '${targetAgentComponent.name}' uses '${targetAgentComponent.auth}' authentication.`,\n );\n }\n\n const targetAgentComponentName = targetAgentComponent.name ?? 'agent';\n const targetAgentClassName = targetAgentComponent.rc as string;\n const targetAgentSnakeCase = snakeCase(targetAgentClassName);\n const targetAgentPort = targetAgentComponent.port ?? 9000;\n\n // The source agent's framework drives everything that varies: the client\n // naming + app template (shared with the mcp/gateway generators via\n // PY_CLIENT_NAMING), the A2A transport dep (see DEPENDENCIES), and the\n // agent.py transform below. Each framework wraps its own A2A Layer-2 client\n // (Strands' A2AAgent vs an a2a-sdk-only client) over the shared,\n // framework-agnostic A2A client config. Keyed on the framework, not a\n // boolean, so a third framework stays additive.\n const framework = resolveAgentFramework(agentComponent.framework);\n const naming = PY_CLIENT_NAMING[framework];\n\n // Recorded below and read by the declaration's predicates, so the packages\n // added here are exactly the ones the version sync will own.\n const metadata: PyAgentA2aConnectionMetadata = { framework };\n\n // 1. Ensure the shared Python agent-connection project exists + has the\n // A2A core client (framework Layer-2) and its shared SigV4 auth helper.\n await ensurePythonAgentConnectionProject(tree, DEPENDENCIES);\n await addPythonCoreClient(tree, 'a2a', DEPENDENCIES, framework);\n\n const agentConnectionProjectDir = getPythonAgentConnectionProjectDir(tree);\n const agentConnectionModuleName = getPythonAgentConnectionModuleName(tree);\n\n // A2A client config + shared auth helper deps, plus the framework's A2A\n // transport dep. These belong to the shared agent-connection project, which\n // holds the client.\n addPyDependencies(tree, DEPENDENCIES, {\n metadata,\n projectRoot: agentConnectionProjectDir,\n });\n\n // 2. Generate the per-connection client into the shared agent-connection project\n const appDir = joinPathFragments(\n agentConnectionProjectDir,\n agentConnectionModuleName,\n 'app',\n );\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', naming.appTemplateSubdir),\n appDir,\n {\n targetAgentSnakeCase,\n targetAgentClassName,\n targetAgentPort,\n agentConnectionModuleName,\n },\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n\n // Ensure app/__init__.py exists\n const appInitPath = joinPathFragments(appDir, '__init__.py');\n if (!tree.exists(appInitPath)) {\n tree.write(appInitPath, '');\n }\n\n // Add re-export to module __init__.py\n const moduleInitPath = joinPathFragments(\n agentConnectionProjectDir,\n agentConnectionModuleName,\n '__init__.py',\n );\n await addPythonReExport(\n tree,\n moduleInitPath,\n `.app.${targetAgentSnakeCase}_client_${naming.clientModuleSuffix}`,\n `${targetAgentClassName}Client${naming.clientClassSuffix}`,\n );\n\n // 3. Transform agent.py to add the A2A client import + wrap it as a tool\n const agentSourceDir = joinPathFragments(\n sourceProject.root,\n agentComponent.path ?? 'src',\n );\n const agentFilePath = joinPathFragments(agentSourceDir, 'agent.py');\n\n if (tree.exists(agentFilePath)) {\n const clientClassName = `${targetAgentClassName}Client${naming.clientClassSuffix}`;\n const clientVarName = targetAgentSnakeCase;\n const toolName = `ask_${targetAgentSnakeCase}`;\n\n await addImportToAgentFile(\n tree,\n agentFilePath,\n agentConnectionModuleName,\n clientClassName,\n framework,\n );\n await addToolToAgent(tree, agentFilePath, toolName, framework);\n await addClientToolToGetAgent(\n tree,\n agentFilePath,\n clientClassName,\n clientVarName,\n toolName,\n targetAgentClassName,\n framework,\n );\n }\n\n // 4. Add workspace dependency from agent project to agent-connection project\n addWorkspaceDependencyToPyProject(\n tree,\n sourceProject,\n getPythonAgentConnectionProject(tree),\n );\n\n // 5. Set up dev target dependencies — chain onto the target agent\n const agentName = agentComponent.name ?? 'agent';\n const devTargetName = `${agentName}-dev`;\n const targetDevTargetName = `${targetAgentComponentName}-dev`;\n\n if (sourceProject.targets?.[devTargetName]) {\n addDependencyToTargetIfNotPresent(sourceProject, devTargetName, {\n projects: [targetProject.name],\n target: targetDevTargetName,\n });\n updateProjectConfiguration(tree, sourceProject.name, sourceProject);\n }\n\n // Recorded so the version sync knows this connection's dependencies are ours.\n addComponentGeneratorMetadata(\n tree,\n sourceProject.name,\n PY_AGENT_A2A_CONNECTION_GENERATOR_INFO,\n toProjectRelativePath(sourceProject, agentFilePath),\n targetAgentClassName,\n // `sourcePath` names the source component this connection is made from, so\n // the pair is identifiable rather than just the two projects.\n { ...metadata, sourcePath: options.sourceComponent?.path },\n );\n\n await addGeneratorMetricsIfApplicable(tree, [\n PY_AGENT_A2A_CONNECTION_GENERATOR_INFO,\n ]);\n\n await formatFilesInSubtree(tree);\n return () =>\n installDependencies(tree, options.preferInstallDependencies, {\n languages: ['typescript'],\n });\n};\n\n/**\n * Add imports to agent.py:\n * - `tool` (from `strands`, or `langchain_core.tools` for LangChain agents)\n * - the per-connection client class from the shared agent-connection module\n */\nconst addImportToAgentFile = async (\n tree: Tree,\n filePath: string,\n agentConnectionModuleName: string,\n clientClassName: string,\n framework: AgentFramework,\n): Promise<void> => {\n await addPythonDestructuredImport(\n tree,\n filePath,\n ['tool'],\n TOOL_IMPORT_MODULE[framework],\n );\n await addPythonDestructuredImport(\n tree,\n filePath,\n [clientClassName],\n agentConnectionModuleName,\n );\n};\n\n/**\n * Add the A2A tool to the Agent's tools array using GritQL.\n *\n * Scoped to the framework's agent constructor so unrelated `tools=` keyword\n * arguments elsewhere in the file are not touched.\n */\nconst addToolToAgent = async (\n tree: Tree,\n filePath: string,\n toolName: string,\n framework: AgentFramework,\n): Promise<void> => {\n const scope = AGENT_CONSTRUCTOR[framework];\n await applyGritQL(\n tree,\n filePath,\n py(`\\`tools=$old\\` where {\n $old <: within \\`${scope}\\`,\n $old <: not contains \\`${toolName}\\`,\n if ($old <: \\`[]\\`) {\n $old => \\`[${toolName}]\\`\n } else {\n $old <: \\`[$items]\\` where { $items += \\`, ${toolName}\\` }\n }\n}`),\n );\n};\n\n/**\n * Wrap the get_agent body so the remote A2A client is created and exposed\n * as a `@tool`-decorated closure before constructing the Agent.\n *\n * Unlike MCP clients, A2A clients aren't context managers — we don't need\n * a `with` block around them. Creation + tool definition happen at the\n * top of the function body, anchored on the framework's agent constructor.\n */\nconst addClientToolToGetAgent = async (\n tree: Tree,\n filePath: string,\n clientClassName: string,\n clientVarName: string,\n toolName: string,\n targetAgentClassName: string,\n framework: AgentFramework,\n): Promise<void> => {\n if (await matchGritQL(tree, filePath, py(`\\`${clientClassName}.create\\``))) {\n return;\n }\n\n // Both frameworks' tools are @tool-decorated callables (the decorator's\n // import differs, handled in addImportToAgentFile), defined inline in\n // get_agent. The client is directly callable, returning the remote's reply.\n const toolBlock = `${clientVarName} = ${clientClassName}.create()\n\n @tool\n def ${toolName}(prompt: str) -> str:\n \"\"\"Delegate a question to the remote ${targetAgentClassName} A2A agent and return its reply.\"\"\"\n return str(${clientVarName}(prompt))\n`;\n\n // Prepend client creation + tool definition to the function body, anchored on\n // the framework's agent constructor. This works whether or not a prior MCP\n // block is present since we insert before any existing statements.\n const anchor = AGENT_CONSTRUCTOR[framework];\n await applyGritQL(\n tree,\n filePath,\n py(`\\`def get_agent($params):\n $body\\` where {\n $body <: contains \\`${anchor}\\`,\n $body <: not contains \\`${clientClassName}.create\\`\n} => \\`def get_agent($params):\n ${toolBlock}\n $body\\``),\n );\n};\n\nexport default pyAgentA2aConnectionGenerator;\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","updateProjectConfiguration","addPyDependencies","AGENT_CONNECTION_PY_DEPENDENCIES","addPythonCoreClient","addPythonReExport","ensurePythonAgentConnectionProject","getPythonAgentConnectionModuleName","getPythonAgentConnectionProject","getPythonAgentConnectionProjectDir","PY_CLIENT_NAMING","resolveAgentFramework","addPythonDestructuredImport","applyGritQL","matchGritQL","declareDependencies","ownedElsewhere","formatFilesInSubtree","installDependencies","addGeneratorMetricsIfApplicable","snakeCase","addComponentGeneratorMetadata","addDependencyToTargetIfNotPresent","getGeneratorInfo","readProjectConfigurationUnqualified","toProjectRelativePath","addWorkspaceDependencyToPyProject","py","pattern","TOOL_IMPORT_MODULE","strands","langchain","AGENT_CONSTRUCTOR","DEPENDENCIES","name","when","m","framework","PY_AGENT_A2A_CONNECTION_GENERATOR_INFO","filename","pyAgentA2aConnectionGenerator","tree","options","sourceProject","targetProject","agentComponent","sourceComponent","targetAgentComponent","targetComponent","Error","protocol","toLowerCase","auth","targetAgentComponentName","targetAgentClassName","rc","targetAgentSnakeCase","targetAgentPort","port","naming","metadata","agentConnectionProjectDir","agentConnectionModuleName","projectRoot","appDir","dirname","appTemplateSubdir","overwriteStrategy","KeepExisting","appInitPath","exists","write","moduleInitPath","clientModuleSuffix","clientClassSuffix","agentSourceDir","root","path","agentFilePath","clientClassName","clientVarName","toolName","addImportToAgentFile","addToolToAgent","addClientToolToGetAgent","agentName","devTargetName","targetDevTargetName","targets","projects","target","sourcePath","preferInstallDependencies","languages","filePath","scope","toolBlock","anchor"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/py/agent/a2a-connection/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n type GeneratorCallback,\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type Tree,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { addPyDependencies } from '../../../utils/add-dependencies';\nimport {\n AGENT_CONNECTION_PY_DEPENDENCIES,\n type AgentFramework,\n addPythonCoreClient,\n addPythonReExport,\n ensurePythonAgentConnectionProject,\n getPythonAgentConnectionModuleName,\n getPythonAgentConnectionProject,\n getPythonAgentConnectionProjectDir,\n PY_CLIENT_NAMING,\n resolveAgentFramework,\n} from '../../../utils/agent-connection/agent-connection';\nimport {\n addPythonDestructuredImport,\n appendToArrayViaGritQL,\n applyGritQL,\n matchGritQL,\n} from '../../../utils/ast';\nimport {\n declareDependencies,\n ownedElsewhere,\n} from '../../../utils/declared-dependencies';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport { installDependencies } from '../../../utils/install';\nimport { addGeneratorMetricsIfApplicable } from '../../../utils/metrics';\nimport { snakeCase } from '../../../utils/names';\nimport {\n addComponentGeneratorMetadata,\n addDependencyToTargetIfNotPresent,\n getGeneratorInfo,\n type NxGeneratorInfo,\n readProjectConfigurationUnqualified,\n} from '../../../utils/nx';\nimport { toProjectRelativePath } from '../../../utils/paths';\nimport { addWorkspaceDependencyToPyProject } from '../../../utils/py';\nimport type { PyAgentA2aConnectionGeneratorSchema } from './schema';\n\n/** Prefix a GritQL pattern with `language python` */\nconst py = (pattern: string) => `language python\\n${pattern}`;\n\n/** The module each framework imports the `tool` decorator from. */\nconst TOOL_IMPORT_MODULE: Record<AgentFramework, string> = {\n strands: 'strands',\n langchain: 'langchain_core.tools',\n};\n\n/** The GritQL pattern matching each framework's agent constructor. */\nconst AGENT_CONSTRUCTOR: Record<AgentFramework, string> = {\n strands: 'yield Agent($_)',\n langchain: 'create_agent($_)',\n};\n\n/** The metadata this generator records, which its predicates read. */\nexport interface PyAgentA2aConnectionMetadata {\n readonly framework: AgentFramework;\n}\n\n// Each framework's Layer-2 client needs its own A2A transport: Strands wraps\n// strands' `A2AAgent` (strands-agents[a2a]); LangChain drives the a2a SDK\n// directly (a2a-sdk) and must not pull Strands in.\nexport const DEPENDENCIES = declareDependencies<PyAgentA2aConnectionMetadata>()(\n {\n py: [\n { name: 'boto3' },\n { name: 'httpx' },\n { name: 'strands-agents[a2a]', when: (m) => m.framework === 'strands' },\n { name: 'a2a-sdk', when: (m) => m.framework === 'langchain' },\n ...ownedElsewhere(AGENT_CONNECTION_PY_DEPENDENCIES),\n ],\n },\n);\n\nexport const PY_AGENT_A2A_CONNECTION_GENERATOR_INFO: NxGeneratorInfo =\n getGeneratorInfo(import.meta.filename);\n\nexport const pyAgentA2aConnectionGenerator = async (\n tree: Tree,\n options: PyAgentA2aConnectionGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const sourceProject = readProjectConfigurationUnqualified(\n tree,\n options.sourceProject,\n );\n const targetProject = readProjectConfigurationUnqualified(\n tree,\n options.targetProject,\n );\n\n const agentComponent = options.sourceComponent;\n const targetAgentComponent = options.targetComponent;\n\n if (!agentComponent || !targetAgentComponent) {\n throw new Error(\n 'Both sourceComponent and targetComponent must be provided for py#agent -> a2a connections',\n );\n }\n\n if ((targetAgentComponent.protocol ?? '').toLowerCase() !== 'a2a') {\n throw new Error(\n `Target agent '${targetAgentComponent.name}' uses the ${targetAgentComponent.protocol ?? 'http'} protocol — only A2A agents can be connected as tools.`,\n );\n }\n\n if (\n targetAgentComponent.auth &&\n targetAgentComponent.auth.toLowerCase() !== 'iam'\n ) {\n throw new Error(\n `A2A agent connection currently only supports IAM authentication, but '${targetAgentComponent.name}' uses '${targetAgentComponent.auth}' authentication.`,\n );\n }\n\n const targetAgentComponentName = targetAgentComponent.name ?? 'agent';\n const targetAgentClassName = targetAgentComponent.rc as string;\n const targetAgentSnakeCase = snakeCase(targetAgentClassName);\n const targetAgentPort = targetAgentComponent.port ?? 9000;\n\n // The source agent's framework drives everything that varies: the client\n // naming + app template (shared with the mcp/gateway generators via\n // PY_CLIENT_NAMING), the A2A transport dep (see DEPENDENCIES), and the\n // agent.py transform below. Each framework wraps its own A2A Layer-2 client\n // (Strands' A2AAgent vs an a2a-sdk-only client) over the shared,\n // framework-agnostic A2A client config. Keyed on the framework, not a\n // boolean, so a third framework stays additive.\n const framework = resolveAgentFramework(agentComponent.framework);\n const naming = PY_CLIENT_NAMING[framework];\n\n // Recorded below and read by the declaration's predicates, so the packages\n // added here are exactly the ones the version sync will own.\n const metadata: PyAgentA2aConnectionMetadata = { framework };\n\n // 1. Ensure the shared Python agent-connection project exists + has the\n // A2A core client (framework Layer-2) and its shared SigV4 auth helper.\n await ensurePythonAgentConnectionProject(tree, DEPENDENCIES);\n await addPythonCoreClient(tree, 'a2a', DEPENDENCIES, framework);\n\n const agentConnectionProjectDir = getPythonAgentConnectionProjectDir(tree);\n const agentConnectionModuleName = getPythonAgentConnectionModuleName(tree);\n\n // A2A client config + shared auth helper deps, plus the framework's A2A\n // transport dep. These belong to the shared agent-connection project, which\n // holds the client.\n addPyDependencies(tree, DEPENDENCIES, {\n metadata,\n projectRoot: agentConnectionProjectDir,\n });\n\n // 2. Generate the per-connection client into the shared agent-connection project\n const appDir = joinPathFragments(\n agentConnectionProjectDir,\n agentConnectionModuleName,\n 'app',\n );\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', naming.appTemplateSubdir),\n appDir,\n {\n targetAgentSnakeCase,\n targetAgentClassName,\n targetAgentPort,\n agentConnectionModuleName,\n },\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n\n // Ensure app/__init__.py exists\n const appInitPath = joinPathFragments(appDir, '__init__.py');\n if (!tree.exists(appInitPath)) {\n tree.write(appInitPath, '');\n }\n\n // Add re-export to module __init__.py\n const moduleInitPath = joinPathFragments(\n agentConnectionProjectDir,\n agentConnectionModuleName,\n '__init__.py',\n );\n await addPythonReExport(\n tree,\n moduleInitPath,\n `.app.${targetAgentSnakeCase}_client_${naming.clientModuleSuffix}`,\n `${targetAgentClassName}Client${naming.clientClassSuffix}`,\n );\n\n // 3. Transform agent.py to add the A2A client import + wrap it as a tool\n const agentSourceDir = joinPathFragments(\n sourceProject.root,\n agentComponent.path ?? 'src',\n );\n const agentFilePath = joinPathFragments(agentSourceDir, 'agent.py');\n\n if (tree.exists(agentFilePath)) {\n const clientClassName = `${targetAgentClassName}Client${naming.clientClassSuffix}`;\n const clientVarName = targetAgentSnakeCase;\n const toolName = `ask_${targetAgentSnakeCase}`;\n\n await addImportToAgentFile(\n tree,\n agentFilePath,\n agentConnectionModuleName,\n clientClassName,\n framework,\n );\n await addToolToAgent(tree, agentFilePath, toolName, framework);\n await addClientToolToGetAgent(\n tree,\n agentFilePath,\n clientClassName,\n clientVarName,\n toolName,\n targetAgentClassName,\n framework,\n );\n }\n\n // 4. Add workspace dependency from agent project to agent-connection project\n addWorkspaceDependencyToPyProject(\n tree,\n sourceProject,\n getPythonAgentConnectionProject(tree),\n );\n\n // 5. Set up dev target dependencies — chain onto the target agent\n const agentName = agentComponent.name ?? 'agent';\n const devTargetName = `${agentName}-dev`;\n const targetDevTargetName = `${targetAgentComponentName}-dev`;\n\n if (sourceProject.targets?.[devTargetName]) {\n addDependencyToTargetIfNotPresent(sourceProject, devTargetName, {\n projects: [targetProject.name],\n target: targetDevTargetName,\n });\n updateProjectConfiguration(tree, sourceProject.name, sourceProject);\n }\n\n // Recorded so the version sync knows this connection's dependencies are ours.\n addComponentGeneratorMetadata(\n tree,\n sourceProject.name,\n PY_AGENT_A2A_CONNECTION_GENERATOR_INFO,\n toProjectRelativePath(sourceProject, agentFilePath),\n targetAgentClassName,\n // `sourcePath` names the source component this connection is made from, so\n // the pair is identifiable rather than just the two projects.\n { ...metadata, sourcePath: options.sourceComponent?.path },\n );\n\n await addGeneratorMetricsIfApplicable(tree, [\n PY_AGENT_A2A_CONNECTION_GENERATOR_INFO,\n ]);\n\n await formatFilesInSubtree(tree);\n return () =>\n installDependencies(tree, options.preferInstallDependencies, {\n languages: ['typescript'],\n });\n};\n\n/**\n * Add imports to agent.py:\n * - `tool` (from `strands`, or `langchain_core.tools` for LangChain agents)\n * - the per-connection client class from the shared agent-connection module\n */\nconst addImportToAgentFile = async (\n tree: Tree,\n filePath: string,\n agentConnectionModuleName: string,\n clientClassName: string,\n framework: AgentFramework,\n): Promise<void> => {\n await addPythonDestructuredImport(\n tree,\n filePath,\n ['tool'],\n TOOL_IMPORT_MODULE[framework],\n );\n await addPythonDestructuredImport(\n tree,\n filePath,\n [clientClassName],\n agentConnectionModuleName,\n );\n};\n\n/**\n * Add the A2A tool to the Agent's tools array using GritQL.\n *\n * Scoped to the framework's agent constructor so unrelated `tools=` keyword\n * arguments elsewhere in the file are not touched.\n */\nconst addToolToAgent = async (\n tree: Tree,\n filePath: string,\n toolName: string,\n framework: AgentFramework,\n): Promise<void> => {\n await appendToArrayViaGritQL(tree, filePath, 'tools=', toolName, {\n scope: AGENT_CONSTRUCTOR[framework],\n language: 'py',\n });\n};\n\n/**\n * Wrap the get_agent body so the remote A2A client is created and exposed\n * as a `@tool`-decorated closure before constructing the Agent.\n *\n * Unlike MCP clients, A2A clients aren't context managers — we don't need\n * a `with` block around them. Creation + tool definition happen at the\n * top of the function body, anchored on the framework's agent constructor.\n */\nconst addClientToolToGetAgent = async (\n tree: Tree,\n filePath: string,\n clientClassName: string,\n clientVarName: string,\n toolName: string,\n targetAgentClassName: string,\n framework: AgentFramework,\n): Promise<void> => {\n if (await matchGritQL(tree, filePath, py(`\\`${clientClassName}.create\\``))) {\n return;\n }\n\n // Both frameworks' tools are @tool-decorated callables (the decorator's\n // import differs, handled in addImportToAgentFile), defined inline in\n // get_agent. The client is directly callable, returning the remote's reply.\n const toolBlock = `${clientVarName} = ${clientClassName}.create()\n\n @tool\n def ${toolName}(prompt: str) -> str:\n \"\"\"Delegate a question to the remote ${targetAgentClassName} A2A agent and return its reply.\"\"\"\n return str(${clientVarName}(prompt))\n`;\n\n // Prepend client creation + tool definition to the function body, anchored on\n // the framework's agent constructor. This works whether or not a prior MCP\n // block is present since we insert before any existing statements.\n const anchor = AGENT_CONSTRUCTOR[framework];\n await applyGritQL(\n tree,\n filePath,\n py(`\\`def get_agent($params):\n $body\\` where {\n $body <: contains \\`${anchor}\\`,\n $body <: not contains \\`${clientClassName}.create\\`\n} => \\`def get_agent($params):\n ${toolBlock}\n $body\\``),\n );\n};\n\nexport default pyAgentA2aConnectionGenerator;\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","updateProjectConfiguration","addPyDependencies","AGENT_CONNECTION_PY_DEPENDENCIES","addPythonCoreClient","addPythonReExport","ensurePythonAgentConnectionProject","getPythonAgentConnectionModuleName","getPythonAgentConnectionProject","getPythonAgentConnectionProjectDir","PY_CLIENT_NAMING","resolveAgentFramework","addPythonDestructuredImport","appendToArrayViaGritQL","applyGritQL","matchGritQL","declareDependencies","ownedElsewhere","formatFilesInSubtree","installDependencies","addGeneratorMetricsIfApplicable","snakeCase","addComponentGeneratorMetadata","addDependencyToTargetIfNotPresent","getGeneratorInfo","readProjectConfigurationUnqualified","toProjectRelativePath","addWorkspaceDependencyToPyProject","py","pattern","TOOL_IMPORT_MODULE","strands","langchain","AGENT_CONSTRUCTOR","DEPENDENCIES","name","when","m","framework","PY_AGENT_A2A_CONNECTION_GENERATOR_INFO","filename","pyAgentA2aConnectionGenerator","tree","options","sourceProject","targetProject","agentComponent","sourceComponent","targetAgentComponent","targetComponent","Error","protocol","toLowerCase","auth","targetAgentComponentName","targetAgentClassName","rc","targetAgentSnakeCase","targetAgentPort","port","naming","metadata","agentConnectionProjectDir","agentConnectionModuleName","projectRoot","appDir","dirname","appTemplateSubdir","overwriteStrategy","KeepExisting","appInitPath","exists","write","moduleInitPath","clientModuleSuffix","clientClassSuffix","agentSourceDir","root","path","agentFilePath","clientClassName","clientVarName","toolName","addImportToAgentFile","addToolToAgent","addClientToolToGetAgent","agentName","devTargetName","targetDevTargetName","targets","projects","target","sourcePath","preferInstallDependencies","languages","filePath","scope","language","toolBlock","anchor"],"mappings":"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"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
FROM <%- pythonBaseImage %>
|
|
2
2
|
|
|
3
|
+
# Apply the distribution's security updates: base image tags lag behind the
|
|
4
|
+
# security suite, so its OS packages carry fixed HIGH/CRITICAL vulnerabilities
|
|
5
|
+
# flagged by the image scan.
|
|
6
|
+
RUN apt-get update && \
|
|
7
|
+
apt-get upgrade -y --no-install-recommends && \
|
|
8
|
+
rm -rf /var/lib/apt/lists/*
|
|
9
|
+
|
|
3
10
|
# Remove the base image's pip: the agent runs from the bundled environment and
|
|
4
11
|
# never needs it at runtime, and pip's vendored packages carry known
|
|
5
12
|
# HIGH/CRITICAL vulnerabilities flagged by the image scan.
|
|
File without changes
|
|
@@ -3,7 +3,8 @@ import os
|
|
|
3
3
|
from langchain.agents import create_agent
|
|
4
4
|
from langchain_aws import ChatBedrockConverse
|
|
5
5
|
from langchain_core.tools import tool
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
from .session import get_checkpointer
|
|
7
8
|
|
|
8
9
|
REGION = os.environ.get("AWS_REGION", "us-east-1")
|
|
9
10
|
MODEL_ID = os.environ.get("MODEL_ID", "global.anthropic.claude-haiku-4-5-20251001-v1:0")
|
|
@@ -17,7 +18,6 @@ def subtract(a: int, b: int) -> int:
|
|
|
17
18
|
|
|
18
19
|
def get_agent():
|
|
19
20
|
model = ChatBedrockConverse(model=MODEL_ID, region_name=REGION)
|
|
20
|
-
# Swap InMemorySaver for a durable checkpointer in a multi-replica deployment.
|
|
21
21
|
return create_agent(
|
|
22
22
|
model=model,
|
|
23
23
|
tools=[subtract],
|
|
@@ -26,5 +26,5 @@ You are a mathematical wizard.
|
|
|
26
26
|
Use your tools for mathematical tasks.
|
|
27
27
|
Refer to tools as your 'spellbook'.
|
|
28
28
|
""",
|
|
29
|
-
checkpointer=
|
|
29
|
+
checkpointer=get_checkpointer(),
|
|
30
30
|
)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
<%_ if (session === 's3') { _%>
|
|
4
|
+
import boto3
|
|
5
|
+
<%_ } _%>
|
|
6
|
+
import aiosqlite
|
|
7
|
+
from langgraph.checkpoint.base import BaseCheckpointSaver
|
|
8
|
+
<%_ if (session === 'in-memory') { _%>
|
|
9
|
+
from langgraph.checkpoint.memory import InMemorySaver
|
|
10
|
+
<%_ } _%>
|
|
11
|
+
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
|
|
12
|
+
<%_ if (session === 's3') { _%>
|
|
13
|
+
from <%- agentConnectionModuleName %>.core.s3_checkpoint_saver_langchain import S3CheckpointSaver
|
|
14
|
+
<%_ } else if (session === 'dynamodb-s3') { _%>
|
|
15
|
+
from langgraph_checkpoint_aws import DynamoDBSaver
|
|
16
|
+
<%_ } _%>
|
|
17
|
+
<%_ if (session === 's3' || session === 'dynamodb-s3') { _%>
|
|
18
|
+
from <%- agentConnectionModuleName %>.core.runtime_config import get_agentcore_runtime_config
|
|
19
|
+
<%_ } _%>
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_checkpointer() -> BaseCheckpointSaver:
|
|
23
|
+
"""Returns a LangGraph checkpointer for persisting conversation state across
|
|
24
|
+
invocations. Local development always uses a local SQLite database for
|
|
25
|
+
convenience, regardless of the configured session option.
|
|
26
|
+
"""
|
|
27
|
+
if os.environ.get("LOCAL_DEV") == "true":
|
|
28
|
+
os.makedirs("<%- localSessionsDir %>", exist_ok=True)
|
|
29
|
+
conn = aiosqlite.connect(os.path.join("<%- localSessionsDir %>", "checkpoints.sqlite"))
|
|
30
|
+
return AsyncSqliteSaver(conn)
|
|
31
|
+
|
|
32
|
+
<%_ if (session === 's3') { _%>
|
|
33
|
+
config = get_agentcore_runtime_config()
|
|
34
|
+
session_config = (config.get("agentRuntimes") or {}).get("<%- agentNameClassName %>", {}).get("session", {})
|
|
35
|
+
bucket_name = session_config.get("bucketName")
|
|
36
|
+
if not bucket_name:
|
|
37
|
+
raise RuntimeError(
|
|
38
|
+
"No S3 checkpoint bucket configured for this agent in runtime configuration."
|
|
39
|
+
)
|
|
40
|
+
return S3CheckpointSaver(
|
|
41
|
+
bucket_name=bucket_name,
|
|
42
|
+
prefix="checkpoints/",
|
|
43
|
+
s3_client=boto3.client("s3"),
|
|
44
|
+
)
|
|
45
|
+
<%_ } else if (session === 'dynamodb-s3') { _%>
|
|
46
|
+
config = get_agentcore_runtime_config()
|
|
47
|
+
session_config = (config.get("agentRuntimes") or {}).get("<%- agentNameClassName %>", {}).get("session", {})
|
|
48
|
+
table_name = session_config.get("tableName")
|
|
49
|
+
bucket_name = session_config.get("bucketName")
|
|
50
|
+
if not table_name or not bucket_name:
|
|
51
|
+
raise RuntimeError(
|
|
52
|
+
"No DynamoDB checkpoint table or S3 offload bucket configured for this agent in runtime configuration."
|
|
53
|
+
)
|
|
54
|
+
return DynamoDBSaver(
|
|
55
|
+
table_name=table_name,
|
|
56
|
+
region_name=os.environ.get("AWS_REGION"),
|
|
57
|
+
s3_offload_config={"bucket_name": bucket_name},
|
|
58
|
+
)
|
|
59
|
+
<%_ } else { _%>
|
|
60
|
+
return InMemorySaver()
|
|
61
|
+
<%_ } _%>
|
|
@@ -4,7 +4,7 @@ from contextlib import asynccontextmanager
|
|
|
4
4
|
|
|
5
5
|
from ag_ui.core import EventType, RunAgentInput, RunErrorEvent
|
|
6
6
|
from ag_ui.encoder import EventEncoder
|
|
7
|
-
from ag_ui_strands import StrandsAgent
|
|
7
|
+
from ag_ui_strands import StrandsAgent, StrandsAgentConfig
|
|
8
8
|
from fastapi import FastAPI, Request
|
|
9
9
|
from fastapi.middleware.cors import CORSMiddleware
|
|
10
10
|
from fastapi.responses import StreamingResponse
|
|
@@ -12,6 +12,7 @@ from <%- agentConnectionModuleName %> import get_current_session_id, session_id_
|
|
|
12
12
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
13
13
|
|
|
14
14
|
from .agent import get_agent
|
|
15
|
+
from .session import get_session_manager
|
|
15
16
|
|
|
16
17
|
logging.basicConfig(level=logging.INFO)
|
|
17
18
|
|
|
@@ -25,6 +26,9 @@ async def lifespan(app: FastAPI):
|
|
|
25
26
|
agent=agent,
|
|
26
27
|
name="<%= agentNameClassName %>",
|
|
27
28
|
description="A Strands Agent exposed via the AG-UI protocol.",
|
|
29
|
+
# A per-thread session manager, not the template Agent's own, since
|
|
30
|
+
# AG-UI caches one Strands agent per thread_id.
|
|
31
|
+
config=StrandsAgentConfig(session_manager_provider=lambda _input_data: get_session_manager()),
|
|
28
32
|
)
|
|
29
33
|
yield
|
|
30
34
|
|
|
@@ -2,7 +2,11 @@ from contextlib import contextmanager
|
|
|
2
2
|
|
|
3
3
|
from strands import Agent, tool
|
|
4
4
|
from strands_tools import current_time
|
|
5
|
-
from <%- agentConnectionModuleName %> import log_model_errors
|
|
5
|
+
from <%- agentConnectionModuleName %> import log_model_errors, log_tool_errors
|
|
6
|
+
<%_ if (protocol !== 'ag-ui') { _%>
|
|
7
|
+
|
|
8
|
+
from .session import get_session_manager
|
|
9
|
+
<%_ } _%>
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
@tool
|
|
@@ -21,5 +25,8 @@ Use your tools for mathematical tasks.
|
|
|
21
25
|
Refer to tools as your 'spellbook'.
|
|
22
26
|
""",
|
|
23
27
|
tools=[subtract, current_time],
|
|
24
|
-
hooks=[log_model_errors],
|
|
28
|
+
hooks=[log_model_errors, log_tool_errors],
|
|
29
|
+
<%_ if (protocol !== 'ag-ui') { _%>
|
|
30
|
+
session_manager=get_session_manager(),
|
|
31
|
+
<%_ } _%>
|
|
25
32
|
)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from strands.session import FileSessionManager, SessionManager<% if (session === 's3') { %>, S3SessionManager<% } %>
|
|
4
|
+
|
|
5
|
+
from <%- agentConnectionModuleName %> import get_current_session_id
|
|
6
|
+
<%_ if (session === 's3') { _%>
|
|
7
|
+
from <%- agentConnectionModuleName %>.core.runtime_config import get_agentcore_runtime_config
|
|
8
|
+
<%_ } _%>
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_session_manager() -> SessionManager | None:
|
|
12
|
+
"""Returns a SessionManager for persisting conversation state across
|
|
13
|
+
invocations. Local development always uses local file storage for
|
|
14
|
+
convenience, regardless of the configured session option. Without a
|
|
15
|
+
configured session option, conversation state is kept in memory only and
|
|
16
|
+
does not survive process restarts.
|
|
17
|
+
"""
|
|
18
|
+
session_id = get_current_session_id()
|
|
19
|
+
if not session_id:
|
|
20
|
+
raise RuntimeError(
|
|
21
|
+
"No current session id — cannot resolve a SessionManager outside of a request scope."
|
|
22
|
+
)
|
|
23
|
+
if os.environ.get("LOCAL_DEV") == "true":
|
|
24
|
+
return FileSessionManager(session_id=session_id, storage_dir="<%- localSessionsDir %>")
|
|
25
|
+
|
|
26
|
+
<%_ if (session === 's3') { _%>
|
|
27
|
+
config = get_agentcore_runtime_config()
|
|
28
|
+
bucket_name = (
|
|
29
|
+
(config.get("agentRuntimes") or {}).get("<%- agentNameClassName %>", {}).get("session", {}).get("bucketName")
|
|
30
|
+
)
|
|
31
|
+
if not bucket_name:
|
|
32
|
+
raise RuntimeError("No S3 bucket configured for this agent's session in runtime configuration.")
|
|
33
|
+
return S3SessionManager(session_id=session_id, bucket=bucket_name)
|
|
34
|
+
<%_ } else { _%>
|
|
35
|
+
return None
|
|
36
|
+
<%_ } _%>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { type GeneratorCallback, type Tree } from '@nx/devkit';
|
|
6
6
|
import { type NxGeneratorInfo } from '../../utils/nx';
|
|
7
7
|
import type { IacMetadata } from '../../utils/shared-constructs-constants';
|
|
8
|
-
import type { AgentProtocol, PyAgentFramework, PyAgentGeneratorSchema } from './schema';
|
|
8
|
+
import type { AgentProtocol, PyAgentFramework, PyAgentGeneratorSchema, PyAgentSession } from './schema';
|
|
9
9
|
/** The metadata this generator records, which its predicates read. */
|
|
10
10
|
export interface PyAgentMetadata extends IacMetadata {
|
|
11
11
|
readonly port: number;
|
|
@@ -17,6 +17,7 @@ export interface PyAgentMetadata extends IacMetadata {
|
|
|
17
17
|
* Strands vs LangChain Layer-2 client + agent.py transform.
|
|
18
18
|
*/
|
|
19
19
|
readonly framework: PyAgentFramework;
|
|
20
|
+
readonly session: PyAgentSession;
|
|
20
21
|
}
|
|
21
22
|
export declare const DEPENDENCIES: import("../../utils/declared-dependencies").DependencyDeclaration<readonly [{
|
|
22
23
|
readonly name: "agent-chat-cli";
|
|
@@ -120,6 +121,15 @@ export declare const DEPENDENCIES: import("../../utils/declared-dependencies").D
|
|
|
120
121
|
}, {
|
|
121
122
|
readonly name: "langgraph";
|
|
122
123
|
readonly when: (m: PyAgentMetadata) => boolean;
|
|
124
|
+
}, {
|
|
125
|
+
readonly name: "langgraph-checkpoint-sqlite";
|
|
126
|
+
readonly when: (m: PyAgentMetadata) => boolean;
|
|
127
|
+
}, {
|
|
128
|
+
readonly name: "aiosqlite";
|
|
129
|
+
readonly when: (m: PyAgentMetadata) => boolean;
|
|
130
|
+
}, {
|
|
131
|
+
readonly name: "langgraph-checkpoint-aws";
|
|
132
|
+
readonly when: (m: PyAgentMetadata) => boolean;
|
|
123
133
|
}, {
|
|
124
134
|
readonly name: "ag-ui-protocol";
|
|
125
135
|
readonly when: (m: PyAgentMetadata) => boolean;
|
|
@@ -6,7 +6,7 @@ import { ensureLicenseExceptions } from "../../license/config.js";
|
|
|
6
6
|
import { AG_UI_LANGGRAPH_EXCEPTIONS } from "../../license/known-exceptions.js";
|
|
7
7
|
import { addPyDependencies, addTsDependencies } from "../../utils/add-dependencies.js";
|
|
8
8
|
import { addAgentChatScripts } from "../../utils/agent-chat/agent-chat.js";
|
|
9
|
-
import { AGENT_CONNECTION_PY_DEPENDENCIES, addPythonFrameworkBase, ensurePythonAgentConnectionProject, getPythonAgentConnectionModuleName, getPythonAgentConnectionProject } from "../../utils/agent-connection/agent-connection.js";
|
|
9
|
+
import { AGENT_CONNECTION_PY_DEPENDENCIES, addPythonFrameworkBase, ensureLangchainS3CheckpointSaver, ensurePythonAgentConnectionProject, getPythonAgentConnectionModuleName, getPythonAgentConnectionProject } from "../../utils/agent-connection/agent-connection.js";
|
|
10
10
|
import { addAgentInfra } from "../../utils/agent-core-constructs/agent-core-constructs.js";
|
|
11
11
|
import { addPythonBundleTarget } from "../../utils/bundle/bundle.js";
|
|
12
12
|
import { resolveContainers } from "../../utils/containers.js";
|
|
@@ -22,7 +22,7 @@ import { kebabCase, toClassName, toSnakeCase } from "../../utils/names.js";
|
|
|
22
22
|
import { getNpmScope } from "../../utils/npm-scope.js";
|
|
23
23
|
import { addComponentDevTarget, addComponentGeneratorMetadata, addDependencyToTargetIfNotPresent, getGeneratorInfo, normalizeTargetKeyOrder, readProjectConfigurationUnqualified } from "../../utils/nx.js";
|
|
24
24
|
import { sortObjectKeys } from "../../utils/object.js";
|
|
25
|
-
import { toProjectRelativePath } from "../../utils/paths.js";
|
|
25
|
+
import { getRelativePathToRootByDirectory, toProjectRelativePath } from "../../utils/paths.js";
|
|
26
26
|
import { assignPort } from "../../utils/port.js";
|
|
27
27
|
import { addWorkspaceDependencyToPyProject } from "../../utils/py.js";
|
|
28
28
|
import { SHARED_CONSTRUCTS_DEPENDENCIES, sharedConstructsGenerator } from "../../utils/shared-constructs.js";
|
|
@@ -117,6 +117,22 @@ export const DEPENDENCIES = declareDependencies()({
|
|
|
117
117
|
name: 'langgraph',
|
|
118
118
|
when: (m)=>m.framework === 'langchain'
|
|
119
119
|
},
|
|
120
|
+
// Local dev uses a SQLite-backed checkpointer for convenience (parity
|
|
121
|
+
// with the strands framework's local FileSessionManager).
|
|
122
|
+
{
|
|
123
|
+
name: 'langgraph-checkpoint-sqlite',
|
|
124
|
+
when: (m)=>m.framework === 'langchain'
|
|
125
|
+
},
|
|
126
|
+
// AsyncSqliteSaver's driver, required alongside langgraph-checkpoint-sqlite.
|
|
127
|
+
{
|
|
128
|
+
name: 'aiosqlite',
|
|
129
|
+
when: (m)=>m.framework === 'langchain'
|
|
130
|
+
},
|
|
131
|
+
// Provides DynamoDBSaver (with S3 offloading for large checkpoints).
|
|
132
|
+
{
|
|
133
|
+
name: 'langgraph-checkpoint-aws',
|
|
134
|
+
when: (m)=>m.framework === 'langchain' && m.session === 'dynamodb-s3'
|
|
135
|
+
},
|
|
120
136
|
{
|
|
121
137
|
name: 'ag-ui-protocol',
|
|
122
138
|
when: (m)=>m.framework === 'langchain' && m.protocol === 'ag-ui'
|
|
@@ -131,25 +147,25 @@ export const DEPENDENCIES = declareDependencies()({
|
|
|
131
147
|
},
|
|
132
148
|
{
|
|
133
149
|
name: 'strands-agents[a2a]',
|
|
134
|
-
when: (m)=>m.framework
|
|
150
|
+
when: (m)=>m.framework === 'strands' && m.protocol === 'a2a'
|
|
135
151
|
},
|
|
136
152
|
{
|
|
137
153
|
name: 'strands-agents',
|
|
138
|
-
when: (m)=>m.framework
|
|
154
|
+
when: (m)=>m.framework === 'strands' && m.protocol !== 'a2a'
|
|
139
155
|
},
|
|
140
156
|
{
|
|
141
157
|
name: 'strands-agents-tools',
|
|
142
|
-
when: (m)=>m.framework
|
|
158
|
+
when: (m)=>m.framework === 'strands'
|
|
143
159
|
},
|
|
144
160
|
// Declared again here so a Strands AG-UI agent lists it in the same order
|
|
145
161
|
// the generated pyproject.toml had before.
|
|
146
162
|
{
|
|
147
163
|
name: 'ag-ui-protocol',
|
|
148
|
-
when: (m)=>m.framework
|
|
164
|
+
when: (m)=>m.framework === 'strands' && m.protocol === 'ag-ui'
|
|
149
165
|
},
|
|
150
166
|
{
|
|
151
167
|
name: 'ag-ui-strands',
|
|
152
|
-
when: (m)=>m.framework
|
|
168
|
+
when: (m)=>m.framework === 'strands' && m.protocol === 'ag-ui'
|
|
153
169
|
},
|
|
154
170
|
{
|
|
155
171
|
name: 'uvicorn'
|
|
@@ -193,9 +209,41 @@ export const pyAgentGenerator = async (tree, options)=>{
|
|
|
193
209
|
console.warn('Warning: auth is ignored when no infrastructure is configured (no infrastructure is generated)');
|
|
194
210
|
}
|
|
195
211
|
const auth = options.auth ?? 'iam';
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
|
|
212
|
+
// Session backends are framework-specific: Strands' SessionManager only has
|
|
213
|
+
// an S3-backed implementation here (via strands.session.S3SessionManager),
|
|
214
|
+
// while LangChain uses dedicated S3 and DynamoDB checkpointer libraries.
|
|
215
|
+
// 'in-memory' is valid for both.
|
|
216
|
+
const SESSIONS_BY_FRAMEWORK = {
|
|
217
|
+
strands: [
|
|
218
|
+
's3',
|
|
219
|
+
'in-memory'
|
|
220
|
+
],
|
|
221
|
+
langchain: [
|
|
222
|
+
's3',
|
|
223
|
+
'dynamodb-s3',
|
|
224
|
+
'in-memory'
|
|
225
|
+
]
|
|
226
|
+
};
|
|
227
|
+
if (options.session && !SESSIONS_BY_FRAMEWORK[framework].includes(options.session)) {
|
|
228
|
+
throw new Error(`Unsupported combination: session '${options.session}' is not implemented for the ${framework} framework (supported: ${SESSIONS_BY_FRAMEWORK[framework].join(', ')}).`);
|
|
229
|
+
}
|
|
230
|
+
const session = options.session ?? 's3';
|
|
231
|
+
// With infra=none there's no CDK/Terraform construct to provision a bucket
|
|
232
|
+
// or table or set RUNTIME_CONFIG_APP_ID, so session.py's non-local-dev
|
|
233
|
+
// branch has nothing to read from AppConfig at runtime — the generated code
|
|
234
|
+
// still honors `session`, but only works outside local dev if the caller
|
|
235
|
+
// wires up matching infra/runtime config themselves.
|
|
236
|
+
if (infra === 'none' && session !== 'in-memory') {
|
|
237
|
+
console.warn(`Warning: session '${session}' requires infrastructure to configure it automatically (no infrastructure is generated for infra=none) — outside local dev this will fail unless you configure matching runtime config yourself`);
|
|
238
|
+
}
|
|
239
|
+
// Local-dev session storage lives at the workspace root
|
|
240
|
+
// (`tmp/agents/<framework>/<agent-name>`), not inside the project, so each
|
|
241
|
+
// agent gets its own storage directory — shared by both frameworks
|
|
242
|
+
// (Strands' FileSessionManager, LangChain's SqliteSaver). The
|
|
243
|
+
// `-dev`/`-serve` targets run with cwd={projectRoot}, so compute that
|
|
244
|
+
// directory relative to the project root here rather than resolving it at
|
|
245
|
+
// runtime.
|
|
246
|
+
const localSessionsDir = joinPathFragments(getRelativePathToRootByDirectory(project.root), `tmp/agents/${framework}/${name}`);
|
|
199
247
|
// Ensure the shared agent-connection project exists so the server entry
|
|
200
248
|
// point can import `session_id_context` and propagate the AgentCore
|
|
201
249
|
// session ID to any downstream MCP / A2A clients a later connection
|
|
@@ -203,8 +251,8 @@ export const pyAgentGenerator = async (tree, options)=>{
|
|
|
203
251
|
await ensurePythonAgentConnectionProject(tree, DEPENDENCIES);
|
|
204
252
|
// The agent server imports the framework base helpers (session cache + model
|
|
205
253
|
// error logging) regardless of whether a connection client is wired in. The
|
|
206
|
-
//
|
|
207
|
-
// framework-agnostic session context), so this is a no-op for
|
|
254
|
+
// LangChain framework has no base layer (its AG-UI foundation reuses only the
|
|
255
|
+
// framework-agnostic session context), so this is a no-op for LangChain.
|
|
208
256
|
await addPythonFrameworkBase(tree, DEPENDENCIES, framework);
|
|
209
257
|
const agentConnectionModuleName = getPythonAgentConnectionModuleName(tree);
|
|
210
258
|
addWorkspaceDependencyToPyProject(tree, project, getPythonAgentConnectionProject(tree));
|
|
@@ -214,27 +262,28 @@ export const pyAgentGenerator = async (tree, options)=>{
|
|
|
214
262
|
agentNameClassName,
|
|
215
263
|
moduleName,
|
|
216
264
|
agentConnectionModuleName,
|
|
217
|
-
framework
|
|
265
|
+
framework,
|
|
266
|
+
protocol,
|
|
267
|
+
session,
|
|
268
|
+
localSessionsDir
|
|
218
269
|
};
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
//
|
|
270
|
+
// Files live under a per-framework dir (files/strands, files/langchain),
|
|
271
|
+
// mirrored one-for-one — including files that don't vary by framework
|
|
272
|
+
// (e.g. the empty package __init__.py) — so there's a single, uniform
|
|
273
|
+
// <framework>/<protocol> layout with no cross-framework fallbacks.
|
|
274
|
+
// `framework` is itself the directory name.
|
|
275
|
+
// Common files shared by both protocols: the agent module (Strands yields a
|
|
276
|
+
// contextmanaged Agent with a session.py sibling; LangChain returns a
|
|
277
|
+
// compiled create_agent graph) and the package __init__.py.
|
|
223
278
|
generateFiles(tree, joinPathFragments(import.meta.dirname, 'files', framework, 'common'), targetSourceDir, templateContext, {
|
|
224
279
|
overwriteStrategy: OverwriteStrategy.KeepExisting
|
|
225
280
|
});
|
|
226
|
-
if (framework === 'langchain') {
|
|
227
|
-
|
|
228
|
-
// framework-agnostic, so emit it from the strands `common` dir.
|
|
229
|
-
generateFiles(tree, joinPathFragments(import.meta.dirname, 'files', 'strands', 'common'), targetSourceDir, templateContext, {
|
|
230
|
-
overwriteStrategy: OverwriteStrategy.KeepExisting
|
|
231
|
-
});
|
|
281
|
+
if (framework === 'langchain' && session === 's3') {
|
|
282
|
+
ensureLangchainS3CheckpointSaver(tree);
|
|
232
283
|
}
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
// a compiled create_agent graph), so it comes from a per-framework,
|
|
237
|
-
// per-protocol dir: `files/<framework>/<protocol>`.
|
|
284
|
+
// Protocol-specific files. Each protocol's server entry point is
|
|
285
|
+
// framework-specific (Strands yields a contextmanaged Agent; LangChain
|
|
286
|
+
// drives a compiled create_agent graph).
|
|
238
287
|
const protocolLower = protocol.toLowerCase();
|
|
239
288
|
generateFiles(tree, joinPathFragments(import.meta.dirname, 'files', framework, protocolLower), targetSourceDir, templateContext, {
|
|
240
289
|
overwriteStrategy: OverwriteStrategy.KeepExisting
|
|
@@ -325,6 +374,7 @@ export const pyAgentGenerator = async (tree, options)=>{
|
|
|
325
374
|
auth,
|
|
326
375
|
protocol,
|
|
327
376
|
framework,
|
|
377
|
+
session,
|
|
328
378
|
...iac ? {
|
|
329
379
|
iac
|
|
330
380
|
} : {}
|