Shared libraries

Five libraries live in libs/. Each is an independent uv project with its own pyproject.toml, and each is packaged as a Lambda layer by the SAM template.

Library Import prefix Layer Purpose

morpheus-common

morpheus_common

MorpheusCommonLayer

Event models, the SQS publisher, the cached secrets helper, Sentry

morpheus-llm

morpheus_llm

MorpheusLlmLayer

Strands agent services, Bedrock, MCP tools, model ids

morpheus-data-layer

morpheus_data_layer

MorpheusDataLayer

SQLAlchemy models, repositories, session management

morpheus-bigquery

morpheus_bigquery

MorpheusBigQueryLayer

BigQuery client factory

pyb-hubspot

pyb_hubspot

PybHubspotLayer

The HubSpot client and every HubSpot API call

Who uses what

Consumer common llm data-layer bigquery pyb-hubspot

enrichment-scraper

✓

✓

✓

enrichment-scorer

✓

✓

✓

✓

morpheus-hubspot

✓

✓

✓

✓

email-agent

✓

✓

✓

phone-agent

✓

✓

✓

agentcore runtime

✓

MorpheusCommonLayer is always first in a function’s layer list — it provides Sentry, which every handler configures at cold start.

The AgentCore runtime depends on morpheus-llm as an editable path dependency ([tool.uv.sources] in agentcore/pyproject.toml), not as a layer, since it is a container rather than a Lambda.

Design rules

Libraries never read the environment

create_hubspot_mcp(), create_tavily_mcp(), create_bigquery_client(), and HubSpotSyncClient.from_*() all take credentials as explicit keyword arguments. The caller fetches them from its own secrets loader. This keeps credential provenance visible at the call site and makes the library testable without environment manipulation.

Libraries never open transactions

morpheus_data_layer repositories require a Session argument and never commit. The caller owns the transaction boundary via get_session().

One responsibility each

pyb_hubspot owns every HubSpot API call — no consumer imports hubspot.* directly. morpheus_llm owns every Bedrock and Strands construction. That is what makes rate-limit handling, auth modes, and prompt caching changeable in one place.

Lazy imports where the dependency is heavy

morpheus_llm/init.py uses PEP 562 getattr, so import morpheus_llm.utils does not pull in Bedrock, Strands, or bedrock_agentcore. Consumers additionally defer imports inside @lru_cache functions so modules stay importable without the layer installed.

Cross-library dependencies are allowed, cross-unit imports are not

pyb_hubspot imports morpheus_common.events.models for property mapping, and morpheus_llm.utils.hubspot re-exports pyb_hubspot.auth.refresh_hubspot_access_token. Both are fine. What is prohibited is agentcore/, lambdas/, and libs/ importing each other’s application code.

Dependency management

cd libs/morpheus-llm
uv add <package>                                   # updates pyproject.toml + uv.lock
uv pip compile pyproject.toml -o requirements.txt   # the manifest SAM builds from

Never pip install or poetry. Each layer is built by SAM with BuildMethod: python3.12 against the library’s requirements.txt, so an unexported dependency will not be in the deployed layer.

Layer details, including size and retention: Lambda layers.

Tests

cd libs/morpheus-common     && uv run pytest tests -v    # event model validation
cd libs/morpheus-data-layer && uv run pytest tests -v    # repositories (integration/ needs a DB)
cd libs/pyb-hubspot         && uv run pytest tests -v    # HubSpot helpers, mocked SDK
cd libs/morpheus-bigquery   && uv run pytest tests -v    # client factory

morpheus-llm has no test suite of its own; it is exercised through the functions that use it.