diff --git a/bun.lock b/bun.lock index 8cc361b531..c0a6157ed2 100644 --- a/bun.lock +++ b/bun.lock @@ -835,7 +835,7 @@ "fast-uri": ["fast-uri@4.1.5", "", {}, "sha512-vZeoMRB4epNr7QfdHxel7te/RcX16CxyXI07JCCTFWZA2s4v1azGNESRj+2EoaHSaWFL/Z3GmKT2jF6A202jLg=="], - "fastify": ["fastify@5.12.1", "", { "dependencies": { "@fastify/ajv-compiler": "^4.0.5", "@fastify/error": "^4.0.0", "@fastify/fast-json-stringify-compiler": "^5.0.0", "@fastify/proxy-addr": "^5.0.0", "abstract-logging": "^2.0.1", "avvio": "^9.0.0", "fast-json-stringify": "^7.0.0", "find-my-way": "^9.6.0", "light-my-request": "^6.0.0", "pino": "^9.14.0 || ^10.1.0", "process-warning": "^5.1.0", "rfdc": "^1.3.1", "secure-json-parse": "^4.0.0", "semver": "^7.6.0", "toad-cache": "^3.7.0" } }, "sha512-FWi+tQvwxR/PeRX7Z2mhfEF5ozJ3jn9asiiclzKXNSzJRHAYcU924aIOKAdHFJ+YIKieh3cqr1IwCOvTr41B3Q=="], + "fastify": ["fastify@5.12.5", "", { "dependencies": { "@fastify/ajv-compiler": "^4.0.5", "@fastify/error": "^4.0.0", "@fastify/fast-json-stringify-compiler": "^5.0.0", "@fastify/proxy-addr": "^5.0.0", "abstract-logging": "^2.0.1", "avvio": "^9.0.0", "fast-json-stringify": "^7.0.0", "find-my-way": "^9.6.0", "light-my-request": "^6.0.0", "pino": "^9.14.0 || ^10.1.0", "process-warning": "^5.1.0", "rfdc": "^1.3.1", "secure-json-parse": "^4.0.0", "semver": "^7.6.0", "toad-cache": "^3.7.0" } }, "sha512-OB2k1dlxs5/NAABqeKV2FUHkSD2BbENsCak8yULVcymn3fHIPDVa9TI3SDnJSWYSllZmSYuZXy2gTnsT+Sut1A=="], "fastify-plugin": ["fastify-plugin@5.1.0", "", {}, "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw=="], diff --git a/src/assets/templates/a2a-python-strands/pyproject.toml b/src/assets/templates/a2a-python-strands/pyproject.toml index ede95b864b..fe05495bd4 100644 --- a/src/assets/templates/a2a-python-strands/pyproject.toml +++ b/src/assets/templates/a2a-python-strands/pyproject.toml @@ -11,9 +11,9 @@ requires-python = ">=3.10" dependencies = [ "a2a-sdk[all] ~= 0.3.0", "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore[a2a] ~= 1.9.1", + "bedrock-agentcore[a2a,strands-agents] >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", - "strands-agents ~= 1.15.0", + "strands-agents >= 1.15.0, < 2.0.0", ] [tool.hatch.build.targets.wheel] diff --git a/src/assets/templates/agent-python-minimal/pyproject.toml b/src/assets/templates/agent-python-minimal/pyproject.toml index c7b4e3ca28..381256f357 100644 --- a/src/assets/templates/agent-python-minimal/pyproject.toml +++ b/src/assets/templates/agent-python-minimal/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" requires-python = ">=3.10" dependencies = [ "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore ~= 1.9.1", + "bedrock-agentcore >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", ] diff --git a/src/assets/templates/agent-python-strands/Dockerfile.template b/src/assets/templates/agent-python-strands/Dockerfile.template index cb3569eff2..319ad9ba28 100644 --- a/src/assets/templates/agent-python-strands/Dockerfile.template +++ b/src/assets/templates/agent-python-strands/Dockerfile.template @@ -1,5 +1,7 @@ FROM public.ecr.aws/docker/library/python:3.12-slim-trixie +RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/* + RUN pip install --no-cache-dir uv ARG UV_DEFAULT_INDEX @@ -8,6 +10,7 @@ ARG UV_INDEX WORKDIR /app ENV UV_SYSTEM_PYTHON=1 \ + UV_NO_CACHE=1 \ UV_COMPILE_BYTECODE=1 \ UV_NO_PROGRESS=1 \ PYTHONUNBUFFERED=1 \ @@ -22,7 +25,7 @@ COPY pyproject.toml uv.lock ./ RUN uv sync --frozen --no-dev --no-install-project COPY --chown=bedrock_agentcore:bedrock_agentcore . . -RUN uv sync --frozen --no-dev +RUN uv sync --frozen --no-dev && /usr/local/bin/python -m pip uninstall -y uv USER bedrock_agentcore diff --git a/src/assets/templates/agent-python-strands/pyproject.toml b/src/assets/templates/agent-python-strands/pyproject.toml index e214929654..61ad4cf53d 100644 --- a/src/assets/templates/agent-python-strands/pyproject.toml +++ b/src/assets/templates/agent-python-strands/pyproject.toml @@ -10,13 +10,13 @@ readme = "README.md" requires-python = ">=3.10" dependencies = [ "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore ~= 1.9.1", + "bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", - {{#if (eq modelProvider "Anthropic")}}"strands-agents[anthropic] ~= 1.15.0", - {{else}}{{#if (eq modelProvider "OpenAI")}}"strands-agents[openai] ~= 1.15.0", - {{else}}{{#if (eq modelProvider "Gemini")}}"strands-agents[gemini] ~= 1.15.0", - {{else}}{{#if (eq modelProvider "LiteLLM")}}"strands-agents[litellm] ~= 1.15.0", - {{else}}"strands-agents ~= 1.15.0", + {{#if (eq modelProvider "Anthropic")}}"strands-agents[anthropic] >= 1.15.0, < 2.0.0", + {{else}}{{#if (eq modelProvider "OpenAI")}}"strands-agents[openai] >= 1.15.0, < 2.0.0", + {{else}}{{#if (eq modelProvider "Gemini")}}"strands-agents[gemini] >= 1.15.0, < 2.0.0", + {{else}}{{#if (eq modelProvider "LiteLLM")}}"strands-agents[litellm] >= 1.15.0, < 2.0.0", + {{else}}"strands-agents >= 1.15.0, < 2.0.0", {{/if}}{{/if}}{{/if}}{{/if}} ] diff --git a/src/assets/templates/agui-python-strands/pyproject.toml b/src/assets/templates/agui-python-strands/pyproject.toml index ab778db754..96eaa3e652 100644 --- a/src/assets/templates/agui-python-strands/pyproject.toml +++ b/src/assets/templates/agui-python-strands/pyproject.toml @@ -12,9 +12,9 @@ dependencies = [ "ag-ui-strands >= 0.3.0, < 0.4.0", "ag-ui-protocol >= 0.1.19, < 0.2.0", "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore ~= 1.9.1", + "bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", - "strands-agents ~= 1.15.0", + "strands-agents >= 1.15.0, < 2.0.0", "uvicorn >= 0.34.3, < 1.0.0", ] diff --git a/src/assets/templates/export-harness-python/pyproject.toml b/src/assets/templates/export-harness-python/pyproject.toml index 29262d7157..3649109d27 100644 --- a/src/assets/templates/export-harness-python/pyproject.toml +++ b/src/assets/templates/export-harness-python/pyproject.toml @@ -10,11 +10,11 @@ readme = "README.md" requires-python = ">=3.10" dependencies = [ "aws-opentelemetry-distro ~= 0.18.0", - "bedrock-agentcore ~= 1.9.1", + "bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0", "botocore[crt] ~= 1.43.0", - "mcp >= 1.23.0, < 2.0.0", + "mcp >= 1.28.1, < 2.0.0", {{#if bedrockMantle}}"aws-bedrock-token-generator >= 1.1.0, < 2.0.0", - {{/if}}"strands-agents{{#if strandsExtras}}[{{strandsExtras}}]{{/if}} ~= 1.54.0", + {{/if}}"strands-agents{{#if strandsExtras}}[{{strandsExtras}}]{{/if}} >= 1.54.0, < 2.0.0", ] diff --git a/src/assets/templates/mcp-python-fastmcp/pyproject.toml b/src/assets/templates/mcp-python-fastmcp/pyproject.toml index 960168bc5d..f9febaf96e 100644 --- a/src/assets/templates/mcp-python-fastmcp/pyproject.toml +++ b/src/assets/templates/mcp-python-fastmcp/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" requires-python = ">=3.10" dependencies = [ "aws-opentelemetry-distro ~= 0.18.0", - "mcp ~= 1.24.0", + "mcp >= 1.28.1, < 2.0.0", ] [tool.hatch.build.targets.wheel] diff --git a/src/core/project/manager.export.test.ts b/src/core/project/manager.export.test.ts index de3f458eca..8d70ddfef4 100644 --- a/src/core/project/manager.export.test.ts +++ b/src/core/project/manager.export.test.ts @@ -86,6 +86,16 @@ function exportInput(overrides: Partial = {}): ExportHarness } describe("FsProjectManager.exportHarness rendered tree", () => { + test("an exported harness permits the framework version selected by the SDK integration", async () => { + const { manager: subject } = manager(); + const project = await projectWithHarness(subject); + + const result = await drain(subject.exportHarness(project, exportInput())); + + const pyproject = await Bun.file(join(result.agentPath, "pyproject.toml")).text(); + expect(pyproject).toContain('"strands-agents >= 1.54.0, < 2.0.0"'); + }); + test("loads only the MCP tools allowedTools selects", async () => { const { manager: subject } = manager(); const project = await projectWithHarness(subject, { @@ -238,7 +248,7 @@ describe("FsProjectManager.exportHarness rendered tree", () => { expect(loadModel).toContain('params["temperature"] = 0.2'); expect(loadModel).toContain('params["top_p"] = 0.8'); const pyproject = await Bun.file(join(result.agentPath, "pyproject.toml")).text(); - expect(pyproject).toContain('"strands-agents[openai] ~= 1.54.0"'); + expect(pyproject).toContain('"strands-agents[openai] >= 1.54.0, < 2.0.0"'); expect(pyproject).not.toContain('"openai ~= 1.0.0"'); }); @@ -265,7 +275,7 @@ describe("FsProjectManager.exportHarness rendered tree", () => { expect(loadModel).toContain('params["top_p"] = 0.9'); expect(loadModel).toContain('params["top_k"] = 20'); expect(await Bun.file(join(result.agentPath, "pyproject.toml")).text()).toContain( - '"strands-agents[gemini] ~= 1.54.0"', + '"strands-agents[gemini] >= 1.54.0, < 2.0.0"', ); }); @@ -290,7 +300,7 @@ describe("FsProjectManager.exportHarness rendered tree", () => { expect(loadModel).toContain('params["top_p"] = 0.7'); expect(loadModel).toContain('json.loads("{\\"max_retries\\":2}")'); expect(await Bun.file(join(result.agentPath, "pyproject.toml")).text()).toContain( - '"strands-agents[litellm] ~= 1.54.0"', + '"strands-agents[litellm] >= 1.54.0, < 2.0.0"', ); }); @@ -310,7 +320,7 @@ describe("FsProjectManager.exportHarness rendered tree", () => { expect(main).toContain("from strands import AgentSkills"); expect(main).toContain('SlidingWindowConversationManager(**{"window_size":12}, per_turn=True)'); expect(await Bun.file(join(result.agentPath, "pyproject.toml")).text()).toContain( - '"strands-agents ~= 1.54.0"', + '"strands-agents >= 1.54.0, < 2.0.0"', ); }); diff --git a/src/core/project/templates/pythonSecurity.test.ts b/src/core/project/templates/pythonSecurity.test.ts new file mode 100644 index 0000000000..c5ff662258 --- /dev/null +++ b/src/core/project/templates/pythonSecurity.test.ts @@ -0,0 +1,44 @@ +import { expect, test } from "bun:test"; +import { FsAssetSource } from "../source"; +import { HandlebarsTemplateRenderer } from "./renderer"; + +const source = new FsAssetSource(); +const renderer = new HandlebarsTemplateRenderer(); + +test.each([ + ["agent-python-minimal", ["bedrock-agentcore >= 1.18.1, < 2.0.0"]], + ["agent-python-strands", ["bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0"]], + ["a2a-python-strands", ["bedrock-agentcore[a2a,strands-agents] >= 1.18.1, < 2.0.0"]], + ["agui-python-strands", ["bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0"]], + [ + "export-harness-python", + ["bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0", "mcp >= 1.28.1, < 2.0.0"], + ], + ["mcp-python-fastmcp", ["mcp >= 1.28.1, < 2.0.0"]], +] as const)("%s retains patched Python dependency ranges", async (template, dependencies) => { + const pyproject = renderer.render(await source.read(`templates/${template}/pyproject.toml`), { + name: "test_agent", + modelProvider: "Bedrock", + }); + + expect(Bun.TOML.parse(pyproject)).toMatchObject({ + project: { dependencies: expect.arrayContaining([...dependencies]) }, + }); +}); + +test("the Strands container updates OS packages and removes uv after installing dependencies", async () => { + const dockerfile = renderer.render( + await source.read("templates/agent-python-strands/Dockerfile.template"), + { entrypoint: "main" }, + ); + + expect(dockerfile).toMatch( + /RUN apt-get update && apt-get upgrade -y[\s\S]*USER bedrock_agentcore/, + ); + expect(dockerfile).toMatch( + /UV_NO_CACHE=1[\s\S]*RUN uv sync --frozen --no-dev --no-install-project/, + ); + expect(dockerfile).toMatch( + /RUN uv sync --frozen --no-dev && \/usr\/local\/bin\/python -m pip uninstall -y uv[\s\S]*USER bedrock_agentcore/, + ); +});