Skip to content

Developer Guide

This guide describes how to build the LMA project from source code, run local development, and contribute.

You need the following installed on your machine:

DependencyVersion
bashLinux, macOS, or Windows WSL
Node.js>= 22.22.2 (the exact pin is in .nvmrc; make setup-node installs it via nvm)
npmBundled with Node.js
DockerRunning (required for SAM builds). On macOS, use Docker Desktop.
zipAny version
Python 3With pip3
virtualenvpip3 install virtualenv
AWS CLIConfigured with credentials
AWS SAM CLI>= 1.118.0
lma-main.yaml # Main CloudFormation orchestration template
publish.sh # Build and publish script
VERSION # Current version (0.3.0)
lma-ai-stack/ # Core: Lambdas, AppSync, DynamoDB, UI
├── deployment/ # CloudFormation templates
├── source/
│ ├── lambda_functions/ # 19 Python Lambda functions
│ ├── lambda_layers/ # Shared Python/Node layers
│ ├── appsync/ # GraphQL schema and 39 resolvers
│ └── ui/ # React web application
├── Makefile # Build orchestration
└── config.mk # Build configuration
lma-websocket-transcriber-stack/ # WebSocket server (TypeScript/Fastify on Fargate)
lma-virtual-participant-stack/ # VP (TypeScript/Playwright + CloakBrowser on ECS)
lma-vpc-stack/ # VPC networking
lma-cognito-stack/ # Cognito auth
lma-meetingassist-setup-stack/ # Strands agent config
lma-bedrockkb-stack/ # Bedrock Knowledge Base
lma-llm-template-setup-stack/ # LLM prompt templates
lma-chat-button-config-stack/ # Chat button config
lma-nova-sonic-config-stack/ # Nova Sonic config
docs/ # Documentation (you are here)

Set up your development environment (installs Node.js, Python venv, SDK, and CLI):

Terminal window
make setup
Terminal window
lma-cli check-prereqs

The simplest way to build from source and deploy:

Terminal window
lma-cli deploy --stack-name LMA --from-code . --admin-email user@example.com --wait

This builds all stacks, publishes artifacts to S3, and deploys the CloudFormation stack in one command. Use --wait to monitor progress with real-time event streaming.

Terminal window
lma-cli publish --source-dir . --region us-east-1

This packages all sub-stacks, uploads to S3, and outputs a CloudFormation template URL you can use later.

Terminal window
lma-cli deploy --stack-name LMA --template-url <template-url> --admin-email user@example.com --wait

See the LMA CLI Reference for the full list of options.

Both lma-cli publish and lma-cli deploy --from-code use content-hash-based checksums to skip rebuilding unchanged stacks on subsequent runs.

Publishing and deploying from source works on both Linux and macOS (including Apple Silicon). On macOS:

  • Docker Desktop must be installed and running. Docker Desktop handles x86_64 emulation via Rosetta — no additional QEMU setup is needed.
  • Enable Rosetta emulation: Open Docker Desktop → Settings → General → Enable “Use Rosetta for x86_64/amd64 emulation on Apple Silicon”, then restart Docker Desktop.
  • SAM CLI container preference: If SAM CLI is configured to use Finch (via /Library/Preferences/com.amazon.samcli.plist), but Finch is not installed, builds will fail. Fix with:
    Terminal window
    sudo plutil -replace DefaultContainerRuntime -string docker /Library/Preferences/com.amazon.samcli.plist
    Or remove the preference entirely to let SAM CLI auto-detect Docker: sudo rm /Library/Preferences/com.amazon.samcli.plist

The React UI is in lma-ai-stack/source/ui/. The simplest way to start the UI dev server is:

Terminal window
make ui-start STACK_NAME=<your-stack-name>

This automatically retrieves the .env configuration from your deployed stack’s CloudFormation outputs, installs dependencies, and starts the development server at http://localhost:3000. The page reloads on edits.

Other npm scripts (run from lma-ai-stack/source/ui/):

  • npm test — Run Jest tests in watch mode
  • npm run build — Production build to build/

The WebSocket transcription server is in lma-websocket-transcriber-stack/source/app/.

Terminal window
cd lma-websocket-transcriber-stack/source/app
npm install
npm run build # TypeScript compilation
npm test # Jest tests

The server is a TypeScript/Fastify application deployed as a Docker container on ECS Fargate behind an Application Load Balancer.

The VP backend is in lma-virtual-participant-stack/backend/.

Terminal window
cd lma-virtual-participant-stack/backend
npm install
npm run build # TypeScript compilation

The simplest way to run the VP locally against a deployed LMA stack is via the Makefile:

Terminal window
make vp-start STACK_NAME=<your-stack-name> PLATFORM=WEBEX MEETING_ID=<meeting-id>

This wraps lma-virtual-participant-stack/backend/local-test.sh: it fetches configuration from CloudFormation, writes .env.local, builds the Docker image, and runs the container with VNC on ports 5900 (VNC client) and 5901 (noVNC web browser).

Other useful targets:

TargetPurpose
make vp-start-dev ...Dev mode: source-mounted, auto-reloads on TS changes
make vp-start-reuse ...Reuse existing .env.local (preserves manually-added secrets like ELEVENLABS_API_KEY)
make vp-stopStop and remove the lma-vp-local-test container
make vp-logsTail container logs (dev mode)
make vp-shellOpen a shell inside the running container

Because the VP runs on Linux in ECS, the most production-like local environment is a Linux EC2 instance edited via VSCode Remote-SSH with VNC previewed on your laptop. For the full workflow (EC2 setup, secret handling, dev-mode auto-reload, and the VSCode stale-port-forwarding gotcha), see Virtual Participant Local Development.

Execute the VP scheduler Step Function directly:

Terminal window
aws stepfunctions start-execution \
--state-machine-arn arn:aws:states:us-east-1:123456789012:stateMachine:SchedulerStateMachine-XXXX \
--input '{"apiInfo":{"httpMethod":"POST"},"data":{"meetingPlatform":"Zoom","meetingID":"12345678","meetingPassword":"a1b2c3","meetingName":"Test","meetingTime":"","userName":"Bob"}}' # pragma: allowlist secret

Supported httpMethod values: POST (join/schedule), GET (list scheduled), DELETE (cancel scheduled).

A test utility in utilities/websocket-client/ streams WAV file audio to the WebSocket server:

Terminal window
cd utilities/websocket-client
npm run setup # Install dependencies (first time)
npm run build # Build TypeScript

Configure environment variables (export or .env file):

SAMPLE_RATE=8000
BYTES_PER_SAMPLE=2
CHUNK_SIZE_IN_MS=200
CALL_FROM_NUMBER='LCA-Client'
CALL_TO_NUMBER='+8001112222'
AGENT_ID='TestAgent'
LMA_ACCESS_JWT_TOKEN=<access_token>
LMA_ID_JWT_TOKEN=<id_token>
LMA_REFRESH_JWT_TOKEN=<refresh_token>

Get the JWT tokens from an authenticated LMA user session. Then run:

Terminal window
npm run start -- --uri <WebSocket_Server_Endpoint> --wavfile <file.wav>

The WebSocket server endpoint is in the CloudFormation stack Outputs.

From lma-ai-stack/, the Makefile provides linting targets (requires CONFIG_ENV environment variable):

TargetToolWhat it checks
make lint-cfn-lintcfn-lintCloudFormation templates
make lint-yamllintyamllintYAML syntax
make lint-pylintpylintPython code (100 char lines)
make lint-mypymypyPython type annotations
make lint-banditbanditPython security
make lint-validateSAM CLITemplate validation

Code style conventions:

  • Python: Black formatter, Flake8, Pylint. 100-character line limit. Config in .pylintrc, .flake8.
  • JavaScript/TypeScript: ESLint (airbnb-base) + Prettier. 120-character line limit, single quotes, trailing commas. Config in .eslintrc.json, .prettierrc.

Two pipelines run the same set of no-AWS checks:

PipelineFileTriggers
GitHub Actions Code Checks.github/workflows/code-checks.ymlPull requests to develop or main, and pushes to those branches
GitLab code_checks.gitlab-ci.ymlEvery branch push and merge request
GitLab nightly_integ_tests.gitlab-ci.ymlA pipeline schedule only — see Scheduled Integration Tests

Both take the Node.js version from .nvmrc at the repository root (22.23.2;

= 22.22.2 is required by jsdom 30). That file is the single pin — GitHub Actions reads it via node-version-file, the GitLab job downloads the version it names, and make setup-node installs it with nvm. The pipelines run, in order:

Terminal window
make setup-python && make setup-cli-dev
make lint-cfn # cfn-lint on all CloudFormation templates
make test-sdk && make test-cli # LMA SDK + CLI unit tests
make test-lambdas # Lambda unit suites (each dir isolated)
make lint-ui-force && make test-ui-force # React UI eslint + vitest
make lint-typescript # tsc + eslint (transcriber, Virtual Participant)
make test-vp && make test-vp-template # Virtual Participant unit + template tests
make test-ai-stack # AppSync contract + AI stack CFN invariants
make test-integ-plumbing # Scheduled integ-run machinery (no AWS)
make test-asr # ASR MicroVM runtime tests + ruff
cd lma-websocket-transcriber-stack/source/app && npm ci && npm test && npm run smoke
cd lma-browser-extension-stack && npm install && CI=true npm test && CI=false npm run build

None of these need AWS credentials or Docker. The local image builds (make docker-build-check) do, so they are run manually. The integration tests do too, and run on a nightly GitLab schedule against a long-lived stack — make integ-tests remains the way to run them by hand, and Scheduled Integration Tests covers the scheduled run and the one-time setup it needs.

Line coverage is measured by make test-coverage, which is deliberately not part of this pipeline — it reports numbers rather than enforcing a threshold, and runs every suite. See Test Coverage.

The GitLab pipeline additionally runs the security review job on merge requests targeting develop — see Security Scanning.

What to customizeHowDocs
LLM summary promptsDynamoDB or admin UITranscript Summarization
Chat shortcut buttonsAdmin UIMeeting Assistant
MCP server integrationsAdmin UIMCP Servers
Knowledge Base documentsS3 bucket or web crawlingMeeting Assistant
Bedrock GuardrailsCloudFormation parameterMeeting Assistant
Transcript processingCustom Lambda functionLambda Hook Functions
Voice assistant promptsDynamoDB or admin UINova Sonic 2 Setup

See CONTRIBUTING.md for guidelines on reporting bugs, requesting features, and submitting pull requests.