Lambda layers
Each library in libs/ is published as a Lambda layer by the SAM template. A function
attaches only the layers it needs.
| Logical id | Layer name | Source |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
All five declare CompatibleRuntimes: [python3.12], CompatibleArchitectures: [x86_64],
BuildMethod: python3.12, and RetentionPolicy: Retain.
RetentionPolicy: Retain keeps every published version alive after a stack update. Layer
versions are immutable and a function pins one, so deleting old versions would break any
function still referencing them — including during a rollback.
Attachment
| Function | Layers, in order |
|---|---|
|
common, data-layer, bigquery |
|
common, data-layer, llm, pyb-hubspot |
|
common, data-layer, pyb-hubspot, bigquery |
|
common, data-layer, llm |
|
common, data-layer, bigquery |
|
The conventional order after that is data-layer, then llm, then the integration layers. |
morpheus-common is on all five functions; morpheus-data-layer is too. morpheus-llm is
attached only to the two functions that call Bedrock, morpheus-bigquery only to the three
that query the warehouse, and pyb-hubspot only to the two that talk to the CRM.
Keeping the set minimal is not cosmetic: layers are extracted at cold start, and every
megabyte of unused dependency is latency paid on every cold invocation. morpheus-llm pulls
in the Strands SDK and its transitive dependencies, which is why phone-agent — which needs
none of it — does not carry it.
How they are built
BuildMethod: python3.12 tells sam build to install each layer’s dependencies with pip into
a python/ directory inside the layer archive, which is where the Lambda runtime looks.
The dependency list itself comes from the requirements.txt generated ahead of the build by
lambdas/Makefile:
uv export --no-dev --no-hashes -o requirements.txt
make deploy runs that export for every function before sam build --no-cached && sam deploy.
|
Libraries are developed with That is why a hand-edited |
--no-cached forces a clean build. Layer builds cache aggressively, and a cached layer that
predates a source change is a genuinely confusing failure: the deploy succeeds and the old
code runs.
Importing from a layer
A layer’s package is a normal top-level import — the extraction path is already on
sys.path:
from morpheus_common.sentry import configure as configure_sentry
from morpheus_data_layer.repositories import SellerRepository
from morpheus_data_layer.session import get_session, init
from morpheus_llm.providers import ClaudeAgentService
from morpheus_bigquery import create_bigquery_client
from pyb_hubspot import ...
Never import from agentcore.* or from another Lambda’s package. The deployment units are
independent, and the only shared code is what is packaged as a layer.
Testing without a layer
Tests run on a developer machine where no layer is installed. The pattern that makes that work
is a deferred import inside an @lru_cache function:
@lru_cache(maxsize=1)
def load_secrets() -> Secrets:
import boto3 (1)
...
| 1 | Imported when first called, not at module load. |
The module can then be imported by a test that mocks load_secrets() entirely, without the
layer’s dependencies being present. The caching is doing double duty: one Secrets Manager call
per container lifetime, and one import. See Testing Lambdas.
Adding a layer
-
Create the library under
libs/<name>/with apyproject.toml. Manage dependencies withuv add. -
Add an
AWS::Serverless::LayerVersiontolambdas/template.yamlwithContentUri,CompatibleRuntimes: [python3.12],CompatibleArchitectures: [x86_64],BuildMethod: python3.12, andRetentionPolicy: Retain. -
Add a
sync-<name>step tolambdas/Makefileso the export runs before the build. -
Reference it from each function’s
Layerslist, afterMorpheusCommonLayer. -
Document it in Shared libraries and add a row to this page.
Constraints
- Layers are versioned and immutable
-
sam deploypublishes a new version and repoints the functions. Rolling back republishes an earlier one. - Five layers is the hard limit
-
AWS allows a maximum of five layers per function.
enrichment-scorerandmorpheus-hubspotcarry four. A sixth shared library cannot be attached to those functions without merging two. - 250 MB unzipped, all layers plus code
-
The Lambda deployment size limit is shared across the function and every layer.
morpheus-llmis the largest contributor. - Architecture must match
-
Layers declare
x86_64, matching the SAMGlobals. A function switched toarm64will fail to load every one of them.