## What Converts `examples/integrations/agentcore` from unlocked `requirements.txt` files to uv projects (`pyproject.toml` + `uv.lock`), matching every other Python integration example in this repo. ## Why The example already used uv as an *installer* — the agent images are built from `ghcr.io/astral-sh/uv` and ran `uv pip install -r requirements.txt` — but nothing was locked, so each image build re-resolved transitive dependencies against whatever PyPI had that day. That had already drifted into a hard break. `langgraph==1.0.10rc1` resolved alongside a `langgraph-prebuilt` that reads `ExecutionInfo` off `langgraph.runtime`, which 1.0.x does not export, so the LangGraph agent raised `ImportError` at container start. ## The migration - Both agents get a `pyproject.toml` + `uv.lock`; the Dockerfiles install with `uv sync --locked` and run out of `/app/.venv`. - LangGraph agent moves to `langgraph==1.1.6` / `langchain==1.2.15`, the pair used by `examples/integrations/langgraph-python`, which resolves the import failure. `langchain` was previously `>=0.3.0` while the code uses the 1.x `create_agent` API. - Four packages that shipped code imports directly were declared nowhere and survived only as transitives: `boto3` (both agents, via `agents/utils/ssm.py`), `PyJWT` (langgraph, via `agents/utils/auth.py`), `langchain-core` (langgraph, via `tools/todos.py`), `botocore` (root, via `scripts/utils.py`). All now declared; no resolved version changed. - `aws-opentelemetry-distro` moves from a loose second `uv pip install` into the locked set. - The example root gets a project for the `scripts/` helpers. Their `requirements.txt` listed the dependencies but nothing installed it, so `uv run scripts/test-agent.py` — the command that script's own docstring gives — failed on a missing `requests`. - Deploy and local-dev scripts call `uv run` instead of bare `python3`; preflight checks for `uv` rather than `python3`. ## Terraform The docker-mode image hash read `patterns/<pattern>/requirements.txt`, `patterns/utils`, a root-level `gateway/` and `tools/`, and a root `pyproject.toml` — none of which exist here. `filesha256` on a missing file is a plan-time error. Repointed at `agents/<pattern>` and `agents/utils`, now hashing `pyproject.toml` and `uv.lock`. The hash then had to exclude the virtualenv the migration creates: `fileset(pattern_dir, "**/*.py")` saw 3114 files instead of 5, and the computed hash differed depending on whether a developer had ever run the agent locally — feeding `replace_triggered_by` and forcing a runtime replacement. Measured: `5f9a98ef…` with a venv present vs `bacab1e1…` without, on identical committed sources. The fix filters `.venv/` and `venv/` and produces the clean-checkout digest in both cases. ## Review Five review rounds plus a promotion audit, and nine defects this migration introduced were found and fixed before merge. Each fix was verified by running the thing, not by reading it: - The image hash sweeping the local virtualenv (above). - The undeclared direct dependencies (above). - `scripts/test-agent.py` piped the child's output and never drained it, deadlocking the agent, while the startup-timeout branch blocked forever on a read — the only channel carrying uv's lockfile-drift error. - `--local` treated any listener on port 8080 as the agent, so a foreign process produced "Agent started successfully" while the real child died. Adopting a running process is now explicit opt-in. - The documented invocation contract had drifted across the two READMEs and both scripts' usage text; a single reconciliation pass now owns all of them, and every documented command was executed to confirm it works. - The Terraform README named the CloudFormation deploy script, which cannot read a Terraform deployment. - Two `.env.example` entries parsed as their own trailing comment text under Docker Compose. - The build context shipped 898 MB of local-only artifacts (measured); now 11 kB. - Assorted comment and message inaccuracies, including a container env block whose comment attributed a runtime-critical variable to uv. ## Repository hygiene Two fixes the pre-merge gates surfaced rather than the review rounds: - `oxfmt` on the example README and `tofu fmt` on the Terraform locals file. The README break was introduced by this branch; the Terraform one predates it but sits in a file this branch edits. - The Terraform ignore rules were anchored to the top level, so the provider cache `terraform init` writes beside every *nested* module was fully stageable — a measured 834 MB one `git add -A` from being committed. Switched to unanchored patterns, matching what this branch already did for the virtualenv layout. Verified with `git check-ignore` that the nested cache and lock file are now covered, tfstate and tfvars still are, and no tracked file is caught by the wider patterns. ## Not addressed Roughly 90 further findings are real but pre-existing and belong to four follow-up subjects, listed in full in the review ledger: - **Terraform module repair** — `terraform validate` fails on five undeclared resources, so this module cannot plan at all. Zip mode references an entry point and a packager directory that do not exist. - **test-agent AG-UI correctness** — the request body fails `RunAgentInput.model_validate` on both agents, and the response decoder parses a pre-AG-UI format, so failures print nothing and exit 0. - **README accuracy** — a `docs/` directory that does not exist, a wrong Node floor, a teardown block whose second command never runs. - **Deploy-script hardening** — `infra-terraform/scripts/deploy-frontend.py` requires a Terraform output nothing declares, so it exits 1 every run; the README now says so rather than presenting it as a working path. ## Verification Both images built for `linux/arm64` and the agent module imported inside each, after every fix cycle. `uv lock --check` clean on all three projects. Every documented command executed from its documented directory. The virtualenv hash filter, the build-context reduction, the pipe deadlock, the port-adoption fix and the env-file parsing were each verified by measurement with before/after output. No repo CI job builds or lints this example, so these local runs are the only coverage that exists. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
CopilotKit + AWS AgentCore
Chat UI with generative charts, shared-state todo canvas, and inline tool rendering — deployed on AWS Bedrock AgentCore. Pick LangGraph or Strands.
Prerequisites
| Tool | Version |
|---|---|
| AWS CLI | configured (aws configure) |
| Node.js | 18+ |
| uv | any recent release |
| Docker | running |
The Python side is managed entirely by uv — it provisions the interpreter, so there is no separate Python install step.
Deploy to AWS
-
Create your config:
cp config.yaml.example config.yaml # Edit config.yaml — set stack_name_base and admin_user_email -
Deploy:
./deploy-langgraph.sh # LangGraph agent (infra + frontend) ./deploy-langgraph.sh --skip-frontend # infra/agent only ./deploy-langgraph.sh --skip-backend # frontend only # or ./deploy-strands.sh # AWS Strands agent ./deploy-strands.sh --skip-frontend ./deploy-strands.sh --skip-backend -
Open the Amplify URL printed at the end. Sign in with your email.
Local Development
cd docker
cp .env.example .env
# Fill in AWS creds — STACK_NAME, MEMORY_ID, and aws-exports.json are auto-resolved
./up.sh --build
- Frontend → hot reloads on save (volume mount + Vite)
- Agent → rebuild on changes:
docker compose up --build agent - Browser →
http://localhost:3000, auth redirects back to localhost
The full chain runs locally: browser:3000 → bridge:3001 → agent:8080. AWS is only used for Memory and Gateway (SSM/OAuth2).
See docs/LOCAL_DEVELOPMENT.md for full details.
Agent dependencies
Each single-agent directory under agents/ — langgraph-single-agent/ and
strands-single-agent/ — is its own uv project with its own uv.lock, and the
Dockerfiles install with uv sync --locked — so the image gets exactly the
dependency set in the lockfile, not whatever resolves that day. agents/utils/
is the exception: it is shared source that both Dockerfiles COPY in, not a
project, so it has no pyproject.toml or lockfile of its own and anything it
imports must be declared in each agent that copies it.
cd agents/langgraph-single-agent
uv add some-package # or edit pyproject.toml, then: uv lock
Either way, commit the updated uv.lock alongside pyproject.toml. Terraform
hashes both, so a dependency change retriggers the image build on the next apply.
What's inside
| Piece | What it does |
|---|---|
frontend/ |
Vite + React with CopilotKit chat, charts, todo canvas |
agents/langgraph-single-agent/ |
LangGraph agent with tools + shared todo state |
agents/strands-single-agent/ |
Strands agent with tools + shared todo state |
pyproject.toml / uv.lock |
Dependencies for the scripts/ helpers |
infra-cdk/ |
CDK: Cognito, AgentCore, CopilotKit Lambda bridge, Amplify |
infra-terraform/ |
Terraform equivalent — see infra-terraform/README.md |
docker/ |
Local dev via Docker Compose |
docs/ |
LOCAL_DEVELOPMENT.md, LOCAL_DOCKER_TESTING.md |
Architecture
Browser → API Gateway → CopilotKit Lambda (Node.js, AG-UI bridge)
↓
AgentCore Runtime
↓
langgraph_agent.py / strands_agent.py
↓ MCP (OAuth2 M2M)
AgentCore Gateway → Lambda tools
Auth: Cognito OIDC → Bearer token forwarded from browser through Lambda to AgentCore.
Tear down
cd infra-cdk && npx cdk@latest destroy --all --output ../cdk.out-lg # LangGraph stack
cd infra-cdk && npx cdk@latest destroy --all --output ../cdk.out-st # Strands stack