Inclusive journey planning that preserves accessibility, health, timing and budget constraints when conditions change.
AdaptSG is a Singapore-focused proof of concept for caregivers travelling with an elderly or mobility-limited family member. It creates a timed plan of at most three stops, monitors weather and disruption signals, then proposes the smallest safe change. It never silently relaxes a hard constraint.
The LLM proposes; deterministic code validates.
A caregiver planning a day out must manually reconcile wheelchair access, walking limits, rain, PSI, flood conditions, rest, meals, fixed times, disruptions and budget. One change can make the original plan unsafe or unusable.
AdaptSG:
- extracts hard constraints and soft preferences into typed state;
- selects a small set of curated venues;
- obtains route, walking, cost and environmental values from tools;
- builds and deterministically validates a timed itinerary;
- monitors or receives a change;
- preserves the unaffected prefix and scores a small set of alternatives;
- shows a before/after diff and asks for approval when cost rises materially.
The model cannot approve its own output. If no safe option exists, the service stops and asks the caregiver instead of inventing a route.
Start with the pre-filled request:
Plan a 10 am-5 pm day for me and my 72-year-old mother, starting from Toa Payoh. She uses a wheelchair, should not walk more than 400 metres at once, needs lunch before 1 pm, and we have a $70 transport and activity budget. We would like to visit Gardens by the Bay.
The deterministic demo produces National Gallery Singapore, an accessible lunch stop and Gardens by the Bay with route provenance, rest seating, walking and cost values.
Then:
- select Simulate heavy rain + flood;
- review the indoor replacement and 67% retained-plan metric;
- apply it;
- select Mum is more tired;
- review the shorter activity/taxi proposal and explicit cost approval.
The demo is reproducible without cloud credentials. Demo values are labelled and must not be presented as live.
Requirements: Python 3.12 and Git.
py -3.12 -m venv .venv
./.venv/Scripts/Activate.ps1
python -m pip install --upgrade "pip>=26.2"
python -m pip install -r requirements.txt
Copy-Item .env.example .env
uvicorn adaptsg.web_api:app --reload --port 8000python3.12 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade 'pip>=26.2'
python -m pip install -r requirements.txt
cp .env.example .env
uvicorn adaptsg.web_api:app --reload --port 8000Open http://localhost:8000. The default ADAPTSG_MODE=demo requires no network or secrets, and
public/index.html is served same-origin with the API: /runtime-config.json 404s locally, so the
client skips Cognito sign-in entirely and boots straight into the deterministic demo. Open
http://localhost:8000/docs for the OpenAPI schema. This same FastAPI service backs Vercel's JSON
API and AWS Lambda.
docker build -t adaptsg .
docker run --rm -p 8000:8000 adaptsgor:
docker compose up --buildTo pass a live configuration, use an explicit environment file:
docker run --rm -p 8000:8000 --env-file .env adaptsgThe image runs as a non-root user and serves the page and API on :8000 with an /api/health
check.
Copy .env.example to .env. .env is ignored by Git.
| Variable | Default | Purpose |
|---|---|---|
ADAPTSG_MODE |
demo |
demo for deterministic adapters; live for Bedrock and official APIs |
AWS_REGION |
us-east-1 |
Bedrock region used by this hackathon account |
AWS_PROFILE |
empty | Preferred local AWS CLI/SSO profile |
AWS_ACCESS_KEY_ID |
empty | Temporary credential when a profile is unavailable |
AWS_SECRET_ACCESS_KEY |
empty | Temporary credential; never commit it |
AWS_SESSION_TOKEN |
empty | Required with hackathon temporary credentials |
BEDROCK_MODEL_ID |
Claude Haiku 4.5 global profile | Configured Bedrock Converse model |
ONEMAP_API_TOKEN |
empty | Required for live OneMap routing |
ONEMAP_BFA_ENABLED |
false |
Enable BFA walking routes only after SLA approval |
DATA_GOV_SG_API_KEY |
empty | Optional for higher data.gov.sg rate limits |
LTA_ACCOUNT_KEY |
empty | Required for live train and PUB flood alerts |
ADAPTSG_APPROVAL_COST_INCREASE_SGD |
8 |
Cost increase that requires caregiver approval |
ADAPTSG_MAX_REPLANS |
2 |
Bounded replanning cycles; maximum accepted value is three |
Hackathon AWS access credentials expire regularly. Refresh all three temporary values, including AWS_SESSION_TOKEN, or use the configured AWS profile. Verify identity before a live run:
aws sts get-caller-identity --profile workshopDo not put secrets in source, screenshots, videos, Vercel client code or GitHub Actions logs.
In live mode BedrockPreferenceParser calls the Bedrock Converse API at temperature zero. It requests one strict JSON object and records input/output token counts. The result still passes through Pydantic, the deterministic planner and ItineraryValidator.
If Bedrock extraction fails, the local app uses a conservative fallback and displays a warning. Routing or environment failures do not become live claims: the current plan is retained and live verification is reported as failed.
The deployed workshop stack uses ap-southeast-1. Keep all regional resources and GitHub variables
on that region; Bedrock stays disabled for this demo phase.
Requirements: AWS CLI v2, AWS SAM CLI, and an AWS SSO/profile or short-lived hackathon credentials. Bedrock access is not required for the default deployment.
sam validate --lint --template-file infra/aws/template.yaml
sam build --template-file infra/aws/template.yaml
sam deploy --guided --region ap-southeast-1 --capabilities CAPABILITY_NAMED_IAMThe custom SAM Makefile builds a lean API artifact without dev-only or notebook dependencies. The
complete one-time GitHub OIDC bootstrap, Secrets Manager setup, deployment variables, manual
commands, verification, and teardown procedure is in infra/aws/README.md.
The stack contains:
- Python 3.12 Lambda;
- Mangum/FastAPI handler;
- CloudFront HTTPS delivery from a private static-web S3 bucket;
- same-origin
/api/*proxy to the Cognito-scoped API Gateway HTTP API; - Cognito Managed Login with email signup and authorization-code/PKCE support;
- IAM-authenticated operations Function URL;
- encrypted on-demand DynamoDB journey/idempotency storage with TTL;
- private encrypted/versioned S3 catalog and evaluation-evidence storage;
- reserved concurrency, X-Ray, retained logs, alarms, an operations dashboard, and optional SNS email delivery;
- optional exact-resource Bedrock permission, disabled by default.
BedrockModelArns=DISABLED is the safe default: no inference permission is attached and the
GitHub pipeline asserts zero model tokens in its deterministic Lambda/DynamoDB smoke test. The
main-branch deploy job uses GitHub OIDC rather than stored AWS access keys. Provider values are
resolved from Secrets Manager when configured. Delete the application and bootstrap stacks after
the hackathon to stop resource use:
sam delete --stack-name adaptsg-demopublic/index.html is the one client, with no build step: the same file is served same-origin by
uvicorn adaptsg.web_api:app locally and in Docker, and published as-is to CloudFront on AWS. The
AWS production path uses:
- private S3 plus CloudFront for the static browser client;
- Cognito Managed Login and API Gateway/Lambda for authenticated application calls.
On every main-branch deployment, CI uploads public/ (it now always contains index.html; the
infra/aws/web/index.html placeholder is only a fallback if it is ever missing). CI also generates
/runtime-config.json with the public Cognito client/domain, PKCE endpoints, callback URL, scopes,
and same-origin API base. No password, token, provider key, or client secret belongs in static files.
The client validates that file's schema at boot and falls back to the local no-auth demo only on
the expected local 404; every other config error fails closed rather than silently disabling login.
The final WebAppUrl CloudFormation output is the public AWS URL. Bedrock is connected only through
an explicitly disabled permission condition and is not called by the deterministic demo.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/health |
Process health and current mode; no live tool call |
POST |
/api/plan |
Parse, plan and validate one journey |
POST |
/api/replan |
Validate and score a typed replan trigger |
POST |
/api/journeys |
Create a server-owned draft journey |
GET |
/api/journeys/{id} |
Retrieve journey state for refresh/recovery |
POST |
/api/journeys/{id}/monitor |
Retrieve conditions and detected triggers |
POST |
/api/journeys/{id}/replan |
Store a validated replan proposal |
POST |
/api/journeys/{id}/decision |
Approve or reject an initial plan or proposal |
Example:
curl -X POST http://localhost:8000/api/plan \
-H 'content-type: application/json' \
-d '{"prompt":"Plan a 10 am-5 pm wheelchair day from Toa Payoh, lunch before 1 pm, budget $70.","journey_date":"2026-09-01"}'No booking, payment or medical endpoint exists.
- OneMap public routing for walk, public-transport and drive routes.
- OneMap Barrier-Free Access routing, which requires approved access and currently covers selected areas.
- data.gov.sg real-time APIs for NEA 24-hour weather and PSI.
- LTA DataMall dynamic datasets for train service and PUB flood alerts.
src/adaptsg/data/venues.json contains 18 curated demonstration venues. Opening hours, costs and accessibility fields are intentionally version-controlled for demo reliability; they are not a substitute for production verification.
.
|-- public/index.html Static browser client with Cognito PKCE auth; no build step
|-- api/index.py Dormant legacy serverless compatibility entry point
|-- src/adaptsg/
| |-- agent.py Bounded LangGraph and service facade
| |-- domain.py Strict immutable journey state
| |-- preference_parser.py Bedrock extraction and safe fallback
| |-- planning.py Planner and minimal-change replanner
| |-- validation.py Deterministic hard-constraint authority
| |-- presentation.py Pure formatting helpers the browser client's JS mirrors
| |-- web_api.py Shared FastAPI routes; mounts public/ same-origin
| |-- aws_handler.py Lambda/Mangum adapter
| |-- tools/catalog.py Curated venue access
| |-- tools/routing.py Demo and OneMap routing clients
| |-- tools/environment.py Demo and official condition clients
| `-- data/venues.json Curated 18-venue demo dataset
|-- tests/ Unit, contract and 20 scenario tests
|-- scripts/check.* Local equivalents of CI gates
|-- scripts/check_web.mjs Browser client syntax/accessibility/invariant checks
|-- scripts/test_web_auth.mjs Executable PKCE/callback/refresh/401 state-machine tests
|-- Dockerfile Non-root uvicorn image serving public/ and the API on :8000
|-- vercel.json JSON-API-only deployment config; no outputDirectory
|-- infra/aws/template.yaml CloudFront/S3/Cognito/API/Lambda SAM stack
|-- infra/aws/web/index.html Fallback page published only if public/index.html is missing
|-- Makefile Lean SAM artifact builder
|-- ARCHITECTURE.md Flows, trust boundaries and deployment
|-- AGENTS.md Safety, coding and Git rules for agents
`-- PROGRESS.md Verified progress, blockers and next work
Install development dependencies, then run the complete local gate:
python -m pip install -r requirements-dev.txt
./scripts/check.shPowerShell:
python -m pip install -r requirements-dev.txt
./scripts/check.ps1Current verified baseline:
- 71 passing tests;
- 20 named caregiver/disruption scenarios;
- 98.1% branch coverage, with CI failing below 90%;
- strict mypy, Ruff lint/format and Bandit;
- dependency audit with no known vulnerabilities;
- API and browser-client drift-guard smoke tests;
- Docker build and AWS SAM validate/build jobs in GitHub Actions.
The 20 scenarios include heavy rain, high PSI, flood, closure, train disruption, fatigue, reduced budget, early lunch/finish constraints, unverified accessibility and the replan loop cap.
- Hard constraints live in typed state, not conversation history.
- Only verified accessibility is eligible when wheelchair access is mandatory.
- Route destinations must match catalog coordinates and include provenance.
- Tool-derived total cost is recomputed and compared with the declared total.
- Every candidate is validated before it can be shown as safe.
- Unaffected segments dominate the deterministic change score.
- Material cost increases require explicit approval.
- No feasible route means stop and ask, never fabricate.
- Replanning is bounded and tool requests have timeouts.
- Demo, stale and live data are visibly distinguished.
See ARCHITECTURE.md for full flows and AGENTS.md for contributor rules.
The supplied training PDF asks for a concise proof of concept whose README explains execution, files, environment, paths, secrets and testing. This repository covers those items and mirrors the presentation methodology in code.
The complete submission still needs:
- project files/workflow, under the stated 5 GB limit;
- a presentation deck of at most 10 slides;
- a digital solution or simulation video of at most five minutes;
- testing/evaluation results in the slides.
Suggested slide flow: problem, caregiver evidence, solution, plan-act-adapt flow, architecture, smallest-change innovation, measured benefits, demo, roadmap and call to action.
- The venue catalog is curated demo data and needs owner/official verification.
- BFA access must be requested from SLA; it is not a universally available endpoint.
- Demo transport fares use a deterministic policy and are not live fare quotations.
- OneMap BFA currently applies to walking routes; end-to-end accessible public transport requires deeper first/last-mile verification.
- Local/demo journey state is process memory; the AWS stack uses DynamoDB conditional writes and TTL.
- The AWS operations Function URL requires IAM/SigV4; browsers use CloudFront, Cognito, and API Gateway.
- No booking, payment, international travel or medical interpretation is in scope.
Every feature must use feature/<name>, receive incremental Conventional Commits, pass the full gate and merge non-fast-forward. Do not commit directly to main. Update PROGRESS.md when scope, blockers or metrics change.
MIT. See LICENSE.