Skip to content

CHARM-BDF/CHARMTwinsights

Repository files navigation

CHARMTwinsight

Table of Contents

  1. Overview
  2. Architecture
  3. Installation and Usage
  4. Troubleshooting
  5. Model Development
  6. MCP Server
  7. Development
  8. Miscellaneous

Overview

CHARMTwinsight is part of the CHARM suite of tools, focused on health data storage, predictive analytics, and synthetic data generation.

It is under active development, and currently supports the following features via REST API:

  • Synthetic patient data generation with Synthea.
  • Internal FHIR data storage and cohort management with HAPI FHIR.
  • Data summary statistics and querying.
  • Predictive analytics, supporting health risk models and synthetic data generation with GAN and similar models.
  • Ingestion of FHIR data from external sources such as FHIR-HOSE.

Architecture

CHARMTwinsight is designed as a microservices architecture managed with docker compose.

Note: this architecture represents implemented data flows; future features may connect HAPI FHIR to the model server, etc.

router: Externally-facing REST API providing access to backend services. Built with FastAPI for flexibility.

hapi: HAPI FHIR server for efficient and flexible storage and querying of FHIR data.

hapi_db: Postgres database providing storage for HAPI.

synthea_server: REST API frontend to the Synthea synthetic data generator; generates FHIR data and corresponding cohort IDs for storage in HAPI.

stat_server_r: REST API for R-based analytics (summary statistics etc.) on stored FHIR or other data. Uses plumbr and fhircrackr R libraries, amongst others.

stat_server_py: REST API for Python-based analytics (summary statistics etc.) on stored FHIR or other data. Uses FastAPI, fhiry, and other Python libraries.

model_server: REST API hosting and serving arbitrary ML and statistical models packaged as Docker containers.

model_server_db: MongoDB database for storing metadata on hosted models.

mcp_server: Model Context Protocol (MCP) server for cohort generation, patient querying, and model access

All services are containerized, most developed with python>=3.13.

Installation and Usage

0. Prerequisites

You will need docker; if using a Mac install Docker Desktop.

  • Mac users may also wish to install GNU versions of coreutils
  • Mac users may also need to enable Docker volume storage under System Preferences -> Privacy and Security -> Files and Folders -> Docker

1. Build and Start Application

New simplified process: CHARMTwinsights now automatically builds and registers all components in one step.

# working dir: app
./build_all.sh
docker compose up --detach

The build_all.sh script builds both the application images and the built-in ML models. All docker compose commands need to be run in the same directory as the docker-compose.yml file.

REST-API

When running, public-facing REST endpoints are found at http://localhost:8000/, with documentation at http://localhost:8000/docs (documentation link, for the bots reading this).

2. Generate Synthetic Data

Synthetic patient data in FHIR format may be generated via a POST request to http://localhost:8000/synthetic/synthea/generate-synthetic-patients, with url parameters num_patients, num_years, and cohort_id. The patients will be simulated with Synthea, and their records will be tagged with the provided cohort_id (defaulting to default).

It is possible to re-use the same cohort ID across multiple generations, in which case newly generated patients will be added to the cohort. It is also possible to specify the US states patients should be sampled from, and large cohort generations is broken into batches managed by an internal job queue.

A testing script demonstrates this (retrying until the Synthea and HAPI services are up and running), creating a cohort1 with 6 members, and a cohort2 with 3. Each generation takes a few seconds.

# working dir: app
synthea_server/gen_patients.sh

These data are pushed to the FHIR server accessible at http://localhost:8080 for development purposes.

3. Test External FHIR Ingestion

External FHIR data from sources like mobile apps, wearables, or EHRs can be ingested via POST request to http://localhost:8000/ingest/fhir. The endpoint accepts a FHIR Bundle and automatically:

  • Prefixes patient IDs with ext- to prevent conflicts with synthetic data
  • Updates all references throughout the bundle
  • Applies CHARM tags for cohort organization
  • Supports updates to existing patient data (re-ingesting updates rather than duplicates)

A comprehensive test script demonstrates ingestion, validation, and update capabilities:

# working dir: app
synthea_server/test_external_ingest.sh

This script tests basic ingestion, custom parameters, validation, and verifies that patient data is properly updated rather than duplicated. See the API documentation for example FHIR bundles and detailed usage.

4. Test Models

Predictive model capabilities are accessed under endpoints at http://localhost:8000/modeling. Example CURLs are available via script:

# working dir: app
model_server/models/test_predict_models.sh

As above, skip if you are not developing or using models.

5. Test Analytics

Summary statistics about generated patient data are available under endpoints at http://localhost:8000/stats. Examples CURLs are available via script:

# working dir: app
stat_server_py/test_stats.sh

5a. Run CI Checks Locally

CI entrypoints are in ci/, and GitHub Actions calls the same scripts. This lets you test workflow logic locally before pushing.

# project root
./ci/run.sh model-validation
./ci/run.sh router-validation
./ci/run.sh all

See ci/README.md for the full list of targets, how to keep services running for debugging, and how to add new CI targets.

6. Stopping and Cleaning Up

Basic Stop

To stop the application but keep generated/imported FHIR data:

# working dir: app
docker compose down

Full Reset

To completely reset everything (useful if you encounter issues):

# working dir: app
# Stop all services
docker compose down

# Remove stored FHIR data (optional - this data takes time to regenerate)
rm -rf hapi/postgres_data/*

# For a completely fresh start, you can also remove model metadata
# (models will be automatically re-registered on next startup)
docker volume rm app_shared_tmp 2>/dev/null || true

Note: The FHIR database data is persisted in hapi/postgres_data/ to avoid having to regenerate synthetic patients every time. Model metadata is stored in MongoDB and will be automatically recreated when you restart.

Nuclear Option (Use with Caution)

If you're experiencing persistent Docker issues, you can use the cleanup script:

# working dir: project root
scripts/docker_clean.sh

WARNING: This script removes ALL Docker containers and networks on your system, not just CHARMTwinsights!

Troubleshooting

Having Docker issues? See DOCKER_TIPS.md for detailed troubleshooting help.

Quick fixes:

  • Won't start: Make sure Docker Desktop is running
  • Port conflicts: Run docker compose down first
  • Build errors: Try ./build_all.sh --no-cache
  • Still broken: docker compose down && ./build_all.sh && docker compose up --detach

Model Development

CHARMTwinsights provides templates and tools to help developers create new machine learning models without needing Docker expertise.

Quick Start for Model Developers

  1. Choose a template:

    # For Python models
    cp -r model-templates/python-model my-new-model
    
    # For R models  
    cp -r model-templates/r-model my-new-model
  2. Customize your model:

    • Edit predict.py or predict.R with your model logic
    • Update README.md with model documentation
    • Update examples.json with test data
    • Add dependencies to pyproject.toml or DESCRIPTION
  3. Validate and build:

    # Validate Dockerfile (prevents common mistakes)
    python model-templates/validate-dockerfile.py my-new-model/Dockerfile
    
    # Build Docker image
    docker build -t my-new-model:latest my-new-model/
  4. Register with CHARMTwinsights:

    curl -X POST http://localhost:8000/modeling/models \
      -H "Content-Type: application/json" \
      -d '{
        "image": "my-new-model:latest",
        "title": "My New Model",
        "short_description": "What your model does",
        "authors": "Your Name"
      }'

Key Features for Model Developers

  • Templates: Ready-to-use Python and R model templates
  • Container-based metadata: Include README.md and examples.json in your model container
  • Validation tools: Prevent common Docker mistakes with automated validation
  • File-based I/O: Models read JSON input files and write JSON output files
  • Example models: Working examples to learn from

Resources

MCP Server

CHARMTwinsights includes a Model Context Protocol (MCP) server that allows AI assistants to interact with the platform programmatically.

The MCP server is automatically started with docker compose up and runs on port 8006.

Available capabilities:

  • Patient Data Access: Search patients, retrieve demographics, get structured clinical data (Observations, Conditions, Procedures, Medications), access narrative reports
  • Predictive Models: List available models, view model documentation and requirements, execute predictions

Example configuration for Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "charmtwinsight": {
      "command": "npx",
      "args": ["mcp-remote", "http://localhost:8006/mcp"]
    }
  }
}

For more information see app/mcp_server/README.md.

Development

Recommendations

Ideally, features are added to the service they are most aligned with, adding additional services for feature sets sufficiently unique. To keep with a microservice architecture, services should not share disk storage, but communicate over the docker-internal network via REST.

Iterating

As noted above, FHIR data stored in the HAPI server is persisted across runs; this allows the developer to generate synthetic data (a time-consuming process) once while while iterating on other features.

For development purposes, each service is exposed to the localhost on independent ports (applied automatically via app/docker-compose.override.yaml):

  • router: localhost:8000
  • hapi: localhost:8080
  • stat_server_py: localhost:8001
  • stat_server_r: localhost:8002
  • synthea_server: localhost:8003
  • model_server: localhost:8004
  • mcp_server: localhost:8006

Accessing HAPI FHIR Server

Development/Demo Mode (default):

Production Mode:

# Skip override file to prevent external HAPI access
docker compose -f docker-compose.yml up -d
  • HAPI port not exposed externally for security
  • FHIR data accessible only through controlled router endpoints at /stats
  • Internal services (stat servers, synthea) can still access HAPI directly

Individual services can be rapidly iterated on even if they depend on others. After the application has been initialized and databases populated, a typical workflow would be:

  1. Initialize application as above in a detached state.

  2. Make changes to service code (e.g. in app/stat_server_py/pyserver/main.py).

    • For Python services, add packages by running poetry add <package-name> next to the pyproject.toml file; for R services, add the package to the Dockerfile.
  3. Rebuild relevant containers with docker compose build --with-dependencies <service_name>

    • This should only rebuild services that have changed and that are dependent on the service; Dockerfiles have also been designed to maximize use of layer caching for fast rebuilds. If you run into trouble and think caching or persistent data are causing issues, try these approaches in order:

      1. Simple rebuild: docker compose build <service_name> --no-cache
      2. Reset service data: docker compose down && docker compose up --detach
      3. Full reset: docker compose down && rm -rf hapi/postgres_data/* && ./build_all.sh --no-cache && docker compose up --detach
      4. Nuclear option: Use scripts/docker_clean.sh (removes ALL Docker containers/networks on your system)
  4. Restart the services with docker compose up <service_name>.

    • Re-upping only the relevant service will cause only it and dependent services that have changed to be re-initialized; you won't have to wait for HAPI or other dependent services to restart. Bringing up an an attached state, with a given target, will only show logs for the specific service (useful for print-debugging).

A common development loop is thus simply docker compose build --with-dependencies <service_name> && docker compose up <service_name>, using Ctrl-C and rerunning to effectuate code changes, accessing services directly on their local port for testing (e.g. http://localhost:8001 for stat_server_py).

Miscellaneous

Additional Documentation

Useful Scripts

The scripts folder contains a few useful scripts:

  • docker_status.sh: list various running docker resources
  • docker_clean.sh: clobber running docker resources (containers, networks, volumes)
  • mimic_fetch_decrypt.sh: fetch MIMIC IV FHIR data and decrypt it (MIMIC IV access approval required, contact Shawn for password)
  • mimic_sample_push_hapi.py: pushes a rich subsample of the MIMIC IV FHIR data to the running HAPI server for testing

About

CHARMTwinsight is part of the CHARM suite of tools, focused on predictive analytics via flexible model hosting and synthetic data generation and storage in via standards-compliant FHIR.

Topics

Resources

License

Contributing

Stars

3 stars

Watchers

3 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors