Skip to main content

Patient Treatment Planner Demonstration

A key use case for the VALAWAI architecture, as described in Medical Protocols, involves incorporating value considerations into medical protocols. This demonstration simplifies this use case, showcasing how a doctor can define patients, specify their current health status, and design a set of actions (a treatment plan) to achieve a desired outcome. After defining the treatment, the doctor receives feedback, including:

  • Whether the treatment adheres to the NIT protocol.
  • How well the treatment aligns with various values associated with the chosen actions.

This demonstration utilizes the Master Of VALAWAI (MOV) as the implementation of the VALAWAI Architecture, along with the following components:

Prerequisites

Before deploying this demonstration, ensure you have Docker installed on your system. Installation instructions and platform-specific guides are available on the official Docker website: https://www.docker.com/.

Build the Docker Containers

Instructions for building Docker containers for each VALAWAI software component are detailed in their respective documentation. To streamline this process, a buildContainers.sh script is provided below. This script automates the container build process, downloading the latest code and building the Docker images as needed.

buildContainers.sh
#!/bin/bash

# Define the components and their repositories
components=(
"MOV"
"C0_patient_treatment_ui"
"C1_nit_protocol_manager"
"C2_treatment_autonomy_valuator"
"C2_treatment_beneficence_valuator"
"C2_treatment_justice_valuator"
"C2_treatment_nonmaleficence_valuator"
)

function docker_git () {
(docker run -ti --rm -v ${HOME}:/root -v $(pwd):/git alpine/git "$@")
}

# Iterate through the components
for component_name in "${components[@]}"; do

if [ -d "$component_name" ]; then
echo "Directory $component_name exists. Checking for updates."
pushd "$component_name" >/dev/null
docker_git pull origin main || { echo "Error updating $component_name"; exit 1; }

else
echo "Directory $component_name does not exist. Cloning repository."
component_repo="https://github.com/VALAWAI/${component_name}.git"
docker run -ti --rm -v "${HOME}:/root" -v "$(pwd):/git" alpine/git clone $component_repo
pushd "$component_name" >/dev/null
fi

lowercase_component_name=$(echo "$component_name" | tr 'A-Z' 'a-z')
image_name="valawai/${lowercase_component_name}:latest"
if docker images -q $image_name >/dev/null; then

image_timestamp=$(docker inspect -f '{{ .Created }}' $image_name)
latest_commit_timestamp=$(docker_git shortlog -1 --since=\"$image_timestamp\")
popd >/dev/null

if [ $(echo "$latest_commit_timestamp" | wc -l) -gt 1 ] ; then
echo "Docker image $image_name is older than last commit. Rebuilding."
build_image=true
else
echo "Docker image $image_name is up to date. Skipping build."
build_image=false
fi
else
echo "Docker image $image_name does not exist. Building image."
build_image=true
fi


if [[ "$build_image" == true ]]; then
pushd "$component_name" >/dev/null
./buildDockerImages.sh -t latest || { echo "Error building $component_name"; exit 1; }
popd >/dev/null
echo "Built Docker image for $component_name"
fi

done

echo "Finished processing all components."

How to use the script:

  1. Save the script: Copy the code above and save it as buildContainers.sh in your VALAWAI project directory.

  2. Make it executable: Open a terminal and navigate to the directory where you saved the script. Then, make the script executable by running the following command:

chmod +x buildContainers.sh
  1. Run the script: Execute the script using the following command:
./buildContainers.sh

After that, the script will download the latest code of each component and build its Docker image if necessary.

Create Docker Compose file

To deploy the demo components, a docker-compose.yml file will be used. This file defines the services that Docker Compose will start to orchestrate the demonstration.
Create a file named docker-compose.yml in your project directory and copy the following code into it.

docker-compose.yml
services:
mov:
image: valawai/mov:${MOV_TAG:-latest}
container_name: treatment_demo_mov
profiles: [mov,all]
depends_on:
mongo:
condition: service_healthy
restart: true
mq:
condition: service_healthy
restart: true
ports:
- ${MOV_UI_PORT:-8080}:8080
networks:
- treatment_demo_net
environment:
RABBITMQ_HOST: ${MQ_HOST:-mq}
RABBITMQ_PORT: ${MQ_PORT:-5672}
RABBITMQ_USERNAME: ${MQ_USER:-mov}
RABBITMQ_PASSWORD: ${MQ_PASSWORD:-password}
QUARKUS_MONGODB_DATABASE: ${MOV_DB_NAME:-movDB}
QUARKUS_MONGODB_CREDENTIALS_USERNAME: ${MOV_DB_USER_NAME:-mov}
QUARKUS_MONGODB_CREDENTIALS_PASSWORD: ${MOV_DB_USER_PASSWORD:-password}
QUARKUS_MONGODB_HOSTS: ${DB_HOST:-mongo}:${MONGO_PORT:-27017}
MOV_UI_URL: http://localhost:${MOV_UI_PORT:-8080}
healthcheck:
test:
[
"CMD-SHELL",
"curl -s http://localhost:8080/q/health | grep -m 1 -P \"^[\\s|\\{|\\\"]+status[\\s|\\:|\\\"]+.+\\\"\" |grep -q \"\\\"UP\\\"\"",
]
interval: 1m
timeout: 10s
retries: 5
start_period: 1m
start_interval: 5s

mq:
image: rabbitmq:${RABBITMQ_TAG:-management}
container_name: treatment_demo_mq
profiles: [mov,all]
hostname: ${MQ_HOST:-mq}
ports:
- ${MQ_LOCAL_PORT:-5672}:5672
- ${MQ_LOCAL_UI_PORT:-8081}:15672
networks:
- treatment_demo_net
environment:
RABBITMQ_DEFAULT_USER: ${MQ_USER:-mov}
RABBITMQ_DEFAULT_PASS: ${MQ_PASSWORD:-password}
healthcheck:
test: ["CMD-SHELL", "rabbitmq-diagnostics -q ping"]
interval: 1m
timeout: 10s
retries: 5
start_period: 1m
start_interval: 5s

mongo:
image: mongo:${MONGODB_TAG:-latest}
container_name: treatment_demo_mongo
profiles: [mov,all]
hostname: ${DB_HOST:-mongo}
ports:
- ${MONGO_LOCAL_PORT:-27017}:27017
networks:
- treatment_demo_net
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USER:-root}
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:-password}
MONGO_INITDB_DATABASE: ${DB_NAME:-movDB}
MOV_DB_NAME: ${MOV_DB_NAME:-movDB}
MOV_DB_USER_NAME: ${MOV_DB_USER_NAME:-mov}
MOV_DB_USER_PASSWORD: ${MOV_DB_USER_PASSWORD:-password}
volumes:
- ${MONGO_LOCAL_DATA:-~/.mongo_data/treatment_demo_db}:/data/db
healthcheck:
test:
[
"CMD-SHELL",
"mongosh --quiet localhost/${DB_NAME:-movDB} --eval 'quit(db.runCommand({ ping: 1 }).ok ? 0 : 2)'",
]
interval: 1m
timeout: 10s
retries: 5
start_period: 1m
start_interval: 5s
configs:
- source: initialize-movDB.js
target: /docker-entrypoint-initdb.d/init-mongo.js

pg:
image: postgres:${POSTGRES_TAG:-17}
container_name: treatment_demo_C0_pg
profiles: [C0, all]
ports:
- ${PG_PORT:-5432}:5432
networks:
- treatment_demo_net
environment:
POSTGRES_USER: ${PG_USER_NAME:-c0_patient_treatment_ui}
POSTGRES_PASSWORD: ${PG_USER_PASSWORD:-password}
POSTGRES_DB: ${PG_DB_NAME:-c0_patient_treatment_ui_db}
volumes:
- ${PG_LOCAL_DATA:-~/.pg_data/treatment_demo_db}:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 1m
timeout: 10s
retries: 5
start_period: 1m
start_interval: 5s

patient_treatment_ui:
image: valawai/c0_patient_treatment_ui:${C0_PATIENT_TREATMENT_UI_TAG:-latest}
container_name: treatment_demo_c0_component
profiles: [C0, all]
networks:
- treatment_demo_net
ports:
- ${C0_PATIENT_TREATMENT_UI_PORT:-8082}:8080
depends_on:
pg:
condition: service_healthy
restart: true
mov:
condition: service_healthy
required: false
environment:
RABBITMQ_HOST: ${MQ_HOST:-mq}
RABBITMQ_PORT: ${MQ_PORT:-5672}
RABBITMQ_USERNAME: ${MQ_USER:-mov}
RABBITMQ_PASSWORD: ${MQ_PASSWORD:-password}
C0_PATIENT_TREATMENT_UI_URL: http://localhost:${C0_PATIENT_TREATMENT_UI_PORT:-8082}
healthcheck:
test:
[
"CMD-SHELL",
"curl -s http://localhost:8080/q/health | grep -m 1 -P \"^[\\s|\\{|\\\"]+status[\\s|\\:|\\\"]+.+\\\"\" |grep -q \"\\\"UP\\\"\"",
]
interval: 1m
timeout: 10s
retries: 5
start_period: 1m
start_interval: 5s

nit_protocol_manager:
image: valawai/c1_nit_protocol_manager:${C1_NIT_PROTOCOL_MANAGER_TAG:-latest}
container_name: treatment_demo_c1_component
profiles: [C1, all]
depends_on:
mov:
condition: service_healthy
required: false
networks:
- treatment_demo_net
environment:
RABBITMQ_HOST: ${MQ_HOST:-mq}
RABBITMQ_PORT: ${MQ_PORT:-5672}
RABBITMQ_USERNAME: ${MQ_USER:-mov}
RABBITMQ_PASSWORD: ${MQ_PASSWORD:-password}
healthcheck:
test:
[
"CMD-SHELL",
"curl -s http://localhost:8080/q/health | grep -m 1 -P \"^[\\s|\\{|\\\"]+status[\\s|\\:|\\\"]+.+\\\"\" |grep -q \"\\\"UP\\\"\"",
]
interval: 1m
timeout: 10s
retries: 5
start_period: 1m
start_interval: 5s

treatment_autonomy_valuator:
image: valawai/c2_treatment_autonomy_valuator:${C2_TREATMENT_AUTONOMY_VALUATOR_TAG:-latest}
container_name: treatment_demo_c2_component_autonomy
profiles: [C2, all]
depends_on:
mov:
condition: service_healthy
required: false
networks:
- treatment_demo_net
environment:
RABBITMQ_HOST: ${MQ_HOST:-mq}
RABBITMQ_PORT: ${MQ_PORT:-5672}
RABBITMQ_USERNAME: ${MQ_USER:-mov}
RABBITMQ_PASSWORD: ${MQ_PASSWORD:-password}
LOG_CONSOLE_LEVEL: ${LOG_LEVEL:-INFO}
healthcheck:
test: ["CMD-SHELL", "test -s /app/logs/component_id.json"]
interval: 1m
timeout: 10s
retries: 5
start_period: 1m
start_interval: 5s

treatment_beneficence_valuator:
image: valawai/c2_treatment_beneficence_valuator:${C2_TREATMENT_BENEFICENCE_VALUATOR_TAG:-latest}
container_name: treatment_demo_c2_component_beneficence
profiles: [C2, all]
depends_on:
mov:
condition: service_healthy
required: false
networks:
- treatment_demo_net
environment:
RABBITMQ_HOST: ${MQ_HOST:-mq}
RABBITMQ_PORT: ${MQ_PORT:-5672}
RABBITMQ_USERNAME: ${MQ_USER:-mov}
RABBITMQ_PASSWORD: ${MQ_PASSWORD:-password}
LOG_CONSOLE_LEVEL: ${LOG_LEVEL:-INFO}
healthcheck:
test: ["CMD-SHELL", "test -s /app/logs/component_id.json"]
interval: 1m
timeout: 10s
retries: 5
start_period: 1m
start_interval: 5s

treatment_justice_valuator:
image: valawai/c2_treatment_justice_valuator:${C2_TREATMENT_JUSTICE_VALUATOR_TAG:-latest}
container_name: treatment_demo_c2_component_justice
profiles: [C2, all]
depends_on:
mov:
condition: service_healthy
required: false
networks:
- treatment_demo_net
environment:
RABBITMQ_HOST: ${MQ_HOST:-mq}
RABBITMQ_PORT: ${MQ_PORT:-5672}
RABBITMQ_USERNAME: ${MQ_USER:-mov}
RABBITMQ_PASSWORD: ${MQ_PASSWORD:-password}
LOG_CONSOLE_LEVEL: ${LOG_LEVEL:-INFO}
healthcheck:
test: ["CMD-SHELL", "test -s /app/logs/component_id.json"]
interval: 1m
timeout: 10s
retries: 5
start_period: 1m
start_interval: 5s

treatment_nonmaleficence_valuator:
image: valawai/c2_treatment_nonmaleficence_valuator:${C2_TREATMENT_NONMALEFICENCE_VALUATOR_TAG:-latest}
container_name: treatment_demo_c2_component_nonmaleficence
profiles: [C2, all]
depends_on:
mov:
condition: service_healthy
required: false
networks:
- treatment_demo_net
environment:
RABBITMQ_HOST: ${MQ_HOST:-mq}
RABBITMQ_PORT: ${MQ_PORT:-5672}
RABBITMQ_USERNAME: ${MQ_USER:-mov}
RABBITMQ_PASSWORD: ${MQ_PASSWORD:-password}
LOG_CONSOLE_LEVEL: ${LOG_LEVEL:-INFO}
healthcheck:
test: ["CMD-SHELL", "test -s /app/logs/component_id.json"]
interval: 1m
timeout: 10s
retries: 5
start_period: 1m
start_interval: 5s

networks:
treatment_demo_net:

configs:
initialize-movDB.js:
content: |
db.createUser({
user: process.env.MOV_DB_USER_NAME,
pwd: process.env.MOV_DB_USER_PASSWORD,
roles: [{
role: 'readWrite',
db: process.env.MOV_DB_NAME
}]
})

On the code above, the docker-compose.yml, defines the following services:

Running the Demonstration

The following steps demonstrate the VALAWAI Architecture and the Master Of VALAWAI (MOV).

  1. Start MOV: Launch the MOV with: COMPOSE_PROFILES=mov docker compose up -d.
    Access the MOV user interface at http://localhost:8080. Initially, no components or connections will be visible.

  2. Start the Doctor UI: Launch the doctor's user interface with: COMPOSE_PROFILES=C0 docker compose up -d. Refresh the MOV UI (http://localhost:8080) to see the newly registered C0 component. No topology connections will be present yet.

  3. Using the Doctor UI: Open the doctor's user interface in your browser at http://localhost:8081 and perform the following steps:

3.1 Add a Patient: Go to "Patients" and add a new patient, providing the name and status.

3.2 Add a Treatment: Click the three dots next to the patient's name, select "Add treatment to patient," review the current status, click "Next," select the actions to apply, define the expected status after the treatment, and then add the treatment. Note that the treatment will not have any feedback information at this point because the C1 and C2 components are not yet running.

  1. Start the NIT Protocol Manager (C1): Launch the C1 component with: COMPOSE_PROFILES=C1 docker compose up -d. Refresh the MOV UI (http://localhost:8080) to see the registered C1 component and the connection between C1 and C0 in the topology.

  2. Check NIT Protocol Adherence: Once the C1 component is ready, return to the doctor's user interface (http://localhost:8081), go to the previously created treatment, and click the reload icon. After a few seconds, you will see whether the treatment actions follow the NIT protocol.

  3. Start the Value Evaluators (C2): Launch the C2 components with: COMPOSE_PROFILES=C2 docker compose up -d. Refresh the MOV UI (http://localhost:8080) to see the four registered C2 components and their connections to the C0 component.

  4. Check Value Alignment: Once the C2 components are ready, return to the doctor's UI (http://localhost:8081), go to the created treatment, and click the reload icon. After a few seconds, you will see the value alignment feedback for the treatment.

Convenience Commands:

  • Start All Components: COMPOSE_PROFILES=all docker compose up -d
  • Stop All Components: COMPOSE_PROFILES=all docker compose down (Note: this command will stop all services regardless of the profile used to start them). If you only want to stop the mov profile you can use COMPOSE_PROFILES=mov docker compose down