Skip to content

BYOC creation method, manylinux_2_28 dependency resolution, free-form runtime replies, list-first agent management - #128

Open
alex-sample-code wants to merge 4 commits into
aws-samples:mainfrom
alex-sample-code:feat/byoc
Open

alex-sample-code wants to merge 4 commits into
aws-samples:mainfrom
alex-sample-code:feat/byoc

Conversation

@alex-sample-code

Copy link
Copy Markdown

Summary

Four commits, each independently reviewable:

  1. feat(byoc): Bring Your Own Code creation method — a fourth wizard card that deploys code the member's developers wrote themselves. Three artifact kinds behind one spec.byoc block: code_zip (direct-code Runtime), container_source (Dockerfile → the shared launchpad-agent-builder CodeBuild project → ECR → Runtime), container_image (an existing private-ECR image in this account, verified with DescribeImages). Developers need no IAM: uploads and deploys carry perm:agents.deploy; each agent gets its own least-privilege execution role whose bedrock:InvokeModel covers exactly spec.byoc.allowed_models (primary injected as MODEL_ID, list as ALLOWED_MODEL_IDS). Samples under samples/byoc/, lab chapter 13, architecture/API docs (en + zh-CN).
  2. feat(deps): manylinux_2_28 resolution target, strict requirements.txt handling, upload-time pre-resolve — the zip paths resolved wheels for manylinux2014 (the docs' conservative example). Measured from inside a deployed direct-code Runtime (2026-09-18): AL2023, aarch64, glibc 2.34 — so packages shipping only manylinux_2_26/2_28 aarch64 wheels (e.g. google-re2, pulled in by chromadb) were unsolvable. One resolve/install target (app/core/runtime_target.py, default manylinux_2_28, LAUNCHPAD_RUNTIME_PYTHON_PLATFORM falls back to manylinux2014); pip gets the full tag ladder because --platform is an exact match, uv widens itself. Uploaded requirements.txt is parsed in pip format (continuations, comments, markers; --hash dropped and re-locked), supply-chain entries refused (-r/-c, -e, local paths, URL/VCS, index options), and POST /api/agents/uploads pre-resolves so the wizard names the offending package before a deploy is attempted.
  3. fix(runtime): render free-form JSON replies instead of a blank turn — the Runtime HTTP contract requires JSON or SSE and names no key, but the chat parser only read {"result"}. A BYOC agent answering {"answer", "session_id", …} produced an empty turn with no error while CloudWatch showed the invocation succeeding. Conventional text keys are now read (response, answer, output, text, message, …), anything else is shown as compact JSON, {"error"} still surfaces as a failed turn. Docs state the response contract.
  4. feat(agents): list-first agent management/create split into /agents (list + stats), /agents/new (wizard, system presets beneath the four cards), /agents/import (discovery), /agents/:id (detail, live while deploying), /agents/:id/edit; /create[?…] redirects with the query kept. Row actions collapse into a portalled ··· menu; failed rows expose the reason.

Verification

  • env -u AWS_REGION make verify on this branch after rebase onto main: ruff / pytest (4497) / infra ruff / infra pytest (47) / eslint / tsc / vite build / i18n_check / i18n_zh_punct — all OK.
  • Fresh clone → uv sync + npm cimake verify also green (no dependency or lockfile changes; the only new setting has a default; no schema migration).
  • Deployed end-to-end on a us-west-2 Launchpad: BYOC code_zip and container_source both reach READY and answer through Chat; a requirements.txt with \ continuations + --hash + google-re2 pre-resolves (21 packages), deploys in ~50 s, and import re2 succeeds at invocation time (glibc 2.34); a --index-url entry is refused at upload with the reason. A probe agent answering {"answer": …} renders in Chat after (3). The /agents* routes and redirects were exercised in a browser.

Notes for reviewers

  • container_image needs the spoke role to ecr:DescribeImages on repositories other than launchpad-agents; the template gains a read-only statement for that (the agent's own execution role still receives the pull grant scoped to its repository).
  • Backend assistant texts still mention /create; they resolve through the redirect.

A fourth wizard card deploys code the member's developers wrote themselves,
already wrapped with the AgentCore SDK (BedrockAgentCoreApp) or any HTTP
server satisfying the Runtime contract (ARM64, :8080, POST /invocations +
GET /ping). One spec.byoc block, three artifact kinds:

- code_zip          zip of Python source staged via POST /api/agents/uploads,
                    resolved for linux/aarch64 and deployed as a direct-code
                    Runtime (codeConfiguration, member's Python version +
                    entrypoint)
- container_source  zip carrying a Dockerfile, built by the shared
                    launchpad-agent-builder CodeBuild project (the platform
                    buildspec is injected into the zip) -> ECR -> Runtime
- container_image   an existing image in this account's private ECR,
                    verified with DescribeImages, deployed as-is

Security model: developers need no IAM — uploads and deploys carry
perm:agents.deploy. Each agent gets its own least-privilege execution role;
bedrock:InvokeModel covers exactly spec.byoc.allowed_models (1–20 ids,
primary injected as env MODEL_ID, full list as ALLOWED_MODEL_IDS). Uploads
are workspace-scoped under byoc/{workspace_id}/{upload_id}/ with provenance
stamped server-side. The spoke role gains read-only ecr:DescribeImages on
the account's repositories for container_image.

Wizard: upload with detected entrypoints / requirements / SDK markers,
allowed-models list, BYOC provenance on the detail view. Samples under
samples/byoc (hello-http, hello-container), lab chapter 13, architecture
and API docs (en + zh-CN).
… handling, upload-time pre-resolve

The zip deploy paths (byoc code_zip + platform zip runtimes) resolved
Python dependencies for manylinux2014, the docs' conservative
recommendation. Measured from inside a deployed AgentCore Runtime
direct-code agent (2026-09-18): AL2023, aarch64, glibc 2.34 — so
packages that only ship manylinux_2_26/2_28 aarch64 wheels (google-re2,
pulled in by chromadb) were unsolvable despite running fine there.

- app/core/runtime_target.py: one definition of the resolve/install
  target, default manylinux_2_28, configurable via
  runtime_python_platform (LAUNCHPAD_RUNTIME_PYTHON_PLATFORM;
  manylinux2014 stays the documented fallback). pip gets the full tag
  ladder down to manylinux2014 because it treats --platform as exact
  strings; uv widens from one tag itself. Consumed by zip_runtime,
  byoc and schemas/requirements.resolve_pins — still --only-binary,
  user code is never built or executed on the control plane.
- app/services/requirements_txt.py: pip-format parsing for uploaded
  requirements.txt (continuations, inline comments, markers; --hash
  dropped — the platform re-locks with its own hashes), supply-chain
  rejections (-r/-c, -e, local paths, URL/VCS, index options, >500
  entries), and a resolver-failure summarizer that names the offending
  package + reason + fix hints instead of dumping uv's derivation tree.
- POST /api/agents/uploads pre-resolves the zip's requirements.txt
  against the target (optional ?python_version=, 90s bound, threadpool)
  and returns detected.requirements {status, package_count, error}; the
  BYOC wizard shows the verdict and re-checks on Python version change.
- Docs: architecture (+zh), api (+zh), lab 13, samples/byoc README.
The Runtime HTTP contract requires JSON or SSE and names no key, but
_runtime_payload_events only read {"result"} (BedrockAgentCoreApp's
convention), the delta/tool/complete envelope and Converse events. A BYOC
agent answering its own JSON — measured 2026-09-18: a CrewAI agent
returning {"answer", "session_id", "turns", "latency_ms"} — produced
an empty chat turn with no error while CloudWatch showed the invocation
succeeding.

- Bodies with none of the known keys now take the first conventional text
  key (response, answer, output, output_text, text, message, content,
  completion, reply; a nested {"text"} block under one counts), and a
  body with none of those is shown as compact JSON (4000-char cap) with a
  warning log, never as a blank turn. {"result"} still wins; {"error"}
  still raises; Converse bookkeeping events stay silent.
- Tests for each shape; docs (architecture en/zh, lab 13, samples/byoc
  README) now state the response contract.
…ents/:id

The management page at /create hosted the 3-step wizard and the agent list
on one route, with discovery hidden behind ?view=discover. Split it into
routes without touching the wizard's state machine:

  /agents            list landing: + New Agent / Import existing Runtime,
                     stats strip (total / running / deploying / failed), the
                     agent table — name links to the detail, CHAT + DETAILS
                     visible, EDIT / CONVERT / DELETE in a per-row "···" menu
                     (portalled so the last row is never clipped), FAILED rows
                     carry the error as tooltip + VIEW REASON, relative
                     UPDATED time, empty state with a create CTA
  /agents/new        the wizard: four method cards (equal height), a button
                     to the import page, the system-preset cards beneath
  /agents/import     discovery of existing Runtime / Harness resources
  /agents/:id        the agent's detail (launch sequence, versions, BYOC
                     provenance; live polling while deploying; OPEN CHAT /
                     OBSERVABILITY / EDIT). A deploy from /agents/new hands
                     over here when it goes active
  /agents/:id/edit   the wizard preloaded for a re-publish
  /create[?...]      redirects (?view=discover -> /agents/import, otherwise
                     /agents/new with the query kept for Registry prefill)

Sidebar target, ROUTE_PATHS, every in-app navigate()/Link, the preset
settings mock script and the docs (architecture en/zh, lab 02/03/13,
studio-integration) follow. en + zh-CN keys under agents.*.
@alex-sample-code
alex-sample-code marked this pull request as ready for review September 18, 2026 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant