
Initial Release
AI-Powered Clinical Reasoning Agent for Hospital Readmission Risk Assessment
Author: Carlos Eduardo Dias Duarte | Email: kcedd34@gmail.com | GitHub: @kcedd34
# 1. Clone repository git clone https://github.com/kcedd34/smart-discharge-navigator.git cd smart-discharge-navigator2. (Optional) Enable AI features - create .env file
echo "OPENAI_API_KEY=sk-your-key-here" > .env echo "AI_MODEL=gpt-4o" >> .env
3. Start application (automated script)
chmod +x start.sh ./start.sh
4. Access
- Frontend: http://localhost:3000
- Backend API: http://localhost:8000/docs
- IRIS Portal: http://localhost:52773/csp/sys/UtilHome.csp (SuperUser/SYS)
Windows Users (or if start.sh fails):
# Start containers docker-compose up -dWait 60 seconds for IRIS initialization
sleep 60
Install FHIR endpoint (interactive terminal)
docker exec -it smart-discharge-iris iris session IRIS
Then paste the commands from Step 5 below and wait for completion
Configure CSP gateway
docker cp config/iris/setup_fhir_post_install.sh smart-discharge-iris:/tmp/setup_fhir_post_install.sh docker exec smart-discharge-iris bash /tmp/setup_fhir_post_install.sh
Load sample data (from backend container)
docker cp data/load_sample_data.py smart-discharge-backend:/app/ docker exec smart-discharge-backend pip install requests docker exec -e FHIR_BASE_URL=http://iris:52773/fhir/r4 smart-discharge-backend python /app/load_sample_data.py
Note: Without OPENAI_API_KEY, the system runs in rule-based mode (fully functional, no AI features).
Smart Discharge Navigator tackles hospital readmissions ($17B annual cost in US) through a hybrid AI Agent + rule-based system that:
| Layer | Purpose | When Used |
|---|---|---|
| AI Agent (GPT-4o) | Deep clinical reasoning, identifies non-obvious risks, conversational interface | When OPENAI_API_KEY configured |
| Rule-Based Engine | Evidence-based deterministic scoring (6 factors weighted) | Always (provides baseline + fallback) |
Key Differentiator: AI Agent identifies risks missed by rules (medication interactions, comorbidity patterns, social determinants) while maintaining explainable step-by-step reasoning.
| Feature | Description |
|---|---|
| AI Clinical Chat | Conversational interface for patient queries with FHIR data context |
| AI Risk Analysis | LLM-powered assessment with step-by-step clinical reasoning |
| AI Discharge Plans | Personalized, AI-generated discharge narratives |
| AI Patient Comparison | Multi-patient triage with AI prioritization rationale |
| Graceful Degradation | Auto-fallback to rule-based mode when AI unavailable |
| Requirement | Version | Notes |
|---|---|---|
| Docker | 20.10+ | Allocate minimum 6 GB RAM to Docker |
| Docker Compose | 2.0+ | |
| Python | 3.8+ | For sample data loading script |
| Git | Latest | |
| OpenAI API Key | - | Optional - enables AI features |
Critical: InterSystems IRIS requires significant memory. Ensure Docker has at least 6 GB RAM allocated (Docker Desktop β Settings β Resources).
Generates comprehensive, personalized discharge plans using LLM reasoning:
git clone https://github.com/kcedd34/smart-discharge-navigator.git cd smart-discharge-navigatorOptional: enable AI features
echo "OPENAI_API_KEY=sk-your-key-here" > .env echo "AI_MODEL=gpt-4o" >> .env
Start everything
chmod +x start.sh ./start.sh
The script automatically:
/fhir/r4)Access Points:
git clone https://github.com/kcedd34/smart-discharge-navigator.git
cd smart-discharge-navigator
Create .env file in project root:
OPENAI_API_KEY=sk-your-key-here
AI_MODEL=gpt-4o
AI_ENABLED=true
Important: Use
gpt-4oorgpt-4-turbo. Basegpt-4lacks JSON mode support and will fall back to rule-based mode.
docker-compose up -d
This starts 3 services:
# Wait until health check passes (2-5 minutes)
docker inspect --format='{{.State.Health.Status}}' smart-discharge-iris
# Should output: healthy
Critical first-run step. The FHIR endpoint is not pre-configured.
Open interactive IRIS terminal:
docker exec -it smart-discharge-iris iris session IRIS
Inside IRIS terminal, copy and paste ALL commands below:
// Unexpire passwords (development only) set $NAMESPACE = "%SYS" do ##class(Security.Users).UnExpireUserPasswords("*")// Create FHIRSERVER namespace set $NAMESPACE = "HSLIB" do ##class(HS.Util.Installer.Foundation).Install("FHIRSERVER")
// Install FHIR R4 endpoint set $NAMESPACE = "FHIRSERVER" do ##class(HS.FHIRServer.Installer).InstallNamespace() do ##class(HS.FHIRServer.Installer).InstallInstance("/fhir/r4", "HS.FHIRServer.Storage.Json.InteractionsStrategy", "hl7.fhir.r4.core@4.0.1")
// Configure search limits set strategy = ##class(HS.FHIRServer.API.InteractionsStrategy).GetStrategyForEndpoint("/fhir/r4") set configData = strategy.GetServiceConfigData() set configData.DefaultSearchPageSize = 1000 set configData.MaxSearchPageSize = 10000 set configData.MaxSearchResults = 10000 do strategy.SaveServiceConfigData(configData)
write "FHIR installed at /fhir/r4",!
halt
Wait 3-8 minutes for FHIR package download and class compilation. Youβll see progress messages.
Required to enable HTTP access to /fhir/r4/* endpoints.
Copy and run setup script:
docker cp config/iris/setup_fhir_post_install.sh smart-discharge-iris:/tmp/setup_fhir_post_install.sh
docker exec smart-discharge-iris bash /tmp/setup_fhir_post_install.sh
This script:
/fhir to CSP gateway routing tableVerify FHIR endpoint:
curl http://localhost:52773/fhir/r4/metadata
# Should return JSON CapabilityStatement
The script must run from inside the backend container to access the Docker network:
# Copy script to backend container docker cp data/load_sample_data.py smart-discharge-backend:/app/Install requests library (if not already installed)
docker exec smart-discharge-backend pip install requests
Execute script with correct FHIR URL
docker exec -e FHIR_BASE_URL=http://iris:52773/fhir/r4 smart-discharge-backend python /app/load_sample_data.py
Creates 8 synthetic patients:
If containers stopped (not destroyed):
docker-compose startRe-apply CSP gateway config (ephemeral, must re-run after restart)
docker cp config/iris/setup_fhir_post_install.sh smart-discharge-iris:/tmp/setup_fhir_post_install.sh docker exec smart-discharge-iris bash /tmp/setup_fhir_post_install.sh
If containers destroyed (docker-compose down): repeat full installation (Steps 3-7).
Access: http://localhost:3000
Navigate: Click βπ€ AI Chatβ tab
Example Questions:
From Dashboard: Click βπ€ AI Analysisβ on any patient
View:
From Dashboard: Click βπ€ AI Discharge Planβ on any patient
Generates personalized plan including:
Base URL: http://localhost:8000/api/v1
Interactive Docs: http://localhost:8000/docs
| Method | Endpoint | Description |
|---|---|---|
GET |
/health |
Health check |
GET |
/patients |
List all patients with risk scores |
GET |
/patients/{id} |
Get patient details |
GET |
/patients/{id}/risk-assessment |
Rule-based risk assessment |
POST |
/patients/{id}/discharge-plan |
Generate discharge plan (creates FHIR CarePlan + Tasks) |
GET |
/statistics |
Population statistics |
| Method | Endpoint | Description |
|---|---|---|
GET |
/ai/status |
Check AI availability |
POST |
/ai/chat |
Clinical chat with patient context |
GET |
/patients/{id}/ai-analysis |
AI-powered risk analysis |
POST |
/patients/{id}/ai-discharge-plan |
AI-generated discharge plan |
POST |
/ai/compare-patients |
Multi-patient AI triage |
| Method | Endpoint | Description |
|---|---|---|
GET |
/analytics/sql-stats |
Population analytics via SQL |
GET |
/analytics/readmission-sql |
Readmission candidates (SQL-based) |
GET |
/analytics/sql-builder-info |
FHIR SQL Builder feature info |
InterSystems Programming Contest: AI Agents for FHIR 2026
Contest Task: #9 β Hospital Readmission Risk Workbench
| Requirement | Implementation |
|---|---|
| AI Agent for FHIR | β GPT-4o LLM with clinical reasoning over FHIR R4 data |
| FHIR Resources | β Patient, Encounter, Condition, Observation, MedicationRequest, AllergyIntolerance, CarePlan, Task |
| Platform Features | β FHIR API, FHIR SQL Builder, AI Hub pattern (OpenAI-compatible) |
| MVP Criteria | β Rule-based + AI scoring, FHIR Task generation, discharge plans |
| Conversational AI | β Chat interface with patient context switching |
| Explainable AI | β Step-by-step reasoning, confidence scores, risk attribution |
intersystemsdc/irishealth-community:latestMost common issue on fresh install. The embedded httpd doesnβt know about /fhir routes.
Fix:
docker cp config/iris/setup_fhir_post_install.sh smart-discharge-iris:/tmp/setup_fhir_post_install.sh
docker exec smart-discharge-iris bash /tmp/setup_fhir_post_install.sh
Verify:
curl http://localhost:52773/fhir/r4/metadata
# Should return 200 with CapabilityStatement
Endpoint was never installed (first run or after docker-compose down).
Fix - Open interactive IRIS terminal:
docker exec -it smart-discharge-iris iris session IRIS
Paste these commands inside IRIS:
set $NAMESPACE = "%SYS"
do ##class(Security.Users).UnExpireUserPasswords("*")
set $NAMESPACE = "HSLIB"
do ##class(HS.Util.Installer.Foundation).Install("FHIRSERVER")
set $NAMESPACE = "FHIRSERVER"
do ##class(HS.FHIRServer.Installer).InstallNamespace()
do ##class(HS.FHIRServer.Installer).InstallInstance("/fhir/r4", "HS.FHIRServer.Storage.Json.InteractionsStrategy", "hl7.fhir.r4.core@4.0.1")
set strategy = ##class(HS.FHIRServer.API.InteractionsStrategy).GetStrategyForEndpoint("/fhir/r4")
set configData = strategy.GetServiceConfigData()
set configData.DefaultSearchPageSize = 1000
set configData.MaxSearchPageSize = 10000
set configData.MaxSearchResults = 10000
do strategy.SaveServiceConfigData(configData)
write "FHIR installed",!
halt
Wait 3-8 minutes, then configure gateway:
docker cp config/iris/setup_fhir_post_install.sh smart-discharge-iris:/tmp/setup_fhir_post_install.sh
docker exec smart-discharge-iris bash /tmp/setup_fhir_post_install.sh
FHIR search index classes not fully generated during installation.
Fix - reinstall FHIR endpoint (use interactive method above).
Write operations require authentication. Backend handles this automatically. For manual testing:
curl -u SuperUser:SYS -X POST http://localhost:52773/fhir/r4/Patient \
-H "Content-Type: application/fhir+json" \
-d '{"resourceType":"Patient","name":[{"family":"Test"}]}'
CORS_ORIGINS must be JSON array. In docker-compose.yml:
- CORS_ORIGINS=["http://localhost:3000","http://localhost:3001"] # Correct
- CORS_ORIGINS=http://localhost:3000 # Wrong - causes pydantic error
docker-compose ps # Check all containers running
docker-compose logs iris # Check IRIS startup
docker-compose logs backend --tail=30
Backend uses internal Docker hostname iris (not localhost):
FHIR_BASE_URL=http://iris:52773/fhir/r4 # Correct for container-to-container
Check AI status:
curl http://localhost:8000/api/v1/ai/status
Common causes:
OPENAI_API_KEY not set β create .env file in project rootAI_MODEL=gpt-4 (base) β change to gpt-4o or gpt-4-turbo (JSON mode required)AI_ENABLED=false β set to true in docker-compose.ymlCheck logs:
docker-compose logs backend --tail=50
IRIS returns HTTP 201 with empty body on successful resource creation. Update to latest data/load_sample_data.py which extracts resource ID from Location header.
Resource creation failed. Verify FHIR endpoint accepts writes:
curl -s -o /dev/null -w "%{http_code}" -u SuperUser:SYS \
-X POST http://localhost:52773/fhir/r4/Patient \
-H "Content-Type: application/fhir+json" \
-d '{"resourceType":"Patient","name":[{"family":"Test"}]}'
# Expected: 201
Note: The automated scripts handle this. Use manual setup only for customization/debugging.
pip (for data loading script)Important: InterSystems IRIS for Health requires significant memory during FHIR schema installation. Ensure Docker has at least 6 GB RAM available. The
config/iris/merge.cpffile already setsglobals=256to allocate 256 MB for IRIS globals buffer.
git clone https://github.com/kcedd34/smart-discharge-navigator.git
cd smart-discharge-navigator
The system works fully without an API key in rule-based fallback mode. To enable AI features, create a .env file in the project root:
# .env (never commit this file β it is already in .gitignore)
OPENAI_API_KEY=sk-your-key-here
AI_MODEL=gpt-4o
AI_ENABLED=true
Important: Use
gpt-4o(orgpt-4-turbo) β notgpt-4base. The basegpt-4model does not support JSON mode (response_format=json_object), which is required for structured clinical analysis, and will fall back to rule-based mode.
Alternatively, export the key in the shell before docker-compose up:
# Linux / macOS / WSL export OPENAI_API_KEY=sk-your-key-hereWindows CMD
set OPENAI_API_KEY=sk-your-key-here
# Recommended: use the automated start script
./start.sh
The script handles everything: starts containers, waits for IRIS to become healthy, installs the FHIR R4 endpoint, and loads sample data.
On Windows (without WSL), run manually:
docker-compose up -d
Then follow Steps 4 and 5 below.
start.sh is not available)Step 3a β Start containers
docker-compose up -d
This starts three services:
Step 3b β Wait for IRIS to become healthy
IRIS requires 2β5 minutes to fully initialise. Wait until the healthcheck passes:
docker inspect --format='{{.State.Health.Status}}' smart-discharge-iris
# Wait until the output is: healthy
Step 3c β Install the FHIR R4 Endpoint
This step is mandatory on first run and after
docker-compose down(data reset). The FHIR endpoint is not pre-configured in the image β it must be installed into IRIS using ObjectScript.
Open interactive IRIS terminal:
docker exec -it smart-discharge-iris iris session IRIS
Paste all commands below inside IRIS terminal:
set $NAMESPACE = "%SYS"
do ##class(Security.Users).UnExpireUserPasswords("*")
set $NAMESPACE = "HSLIB"
do ##class(HS.Util.Installer.Foundation).Install("FHIRSERVER")
set $NAMESPACE = "FHIRSERVER"
do ##class(HS.FHIRServer.Installer).InstallNamespace()
do ##class(HS.FHIRServer.Installer).InstallInstance("/fhir/r4", "HS.FHIRServer.Storage.Json.InteractionsStrategy", "hl7.fhir.r4.core@4.0.1")
set strategy = ##class(HS.FHIRServer.API.InteractionsStrategy).GetStrategyForEndpoint("/fhir/r4")
set configData = strategy.GetServiceConfigData()
set configData.DefaultSearchPageSize = 1000
set configData.MaxSearchPageSize = 10000
set configData.MaxSearchResults = 10000
do strategy.SaveServiceConfigData(configData)
write "FHIR installed at /fhir/r4",!
halt
Wait 3-8 minutes for package download and class compilation.
Expected output (success indicators):
Saving hl7.fhir.r4.core@4.0.1 β package data loading (takes 2-5 minutes)
INFO: New strategy created...
FHIR installed at /fhir/r4 β installation successful
Step 3d β Configure CSP gateway routing and authentication
This step is required on every fresh container install. The embedded IRIS web gateway (httpd) needs to be told that /fhir requests should be forwarded to IRIS, and the IRIS security settings must allow HTTP Basic auth on the FHIR endpoint.
docker cp config/iris/setup_fhir_post_install.sh smart-discharge-iris:/tmp/setup_fhir_post_install.sh
docker exec smart-discharge-iris bash /tmp/setup_fhir_post_install.sh
This script:
/fhir to the CSP gateway routing table (CSP.ini) β without this, all /fhir/r4/* URLs return 404 Not FoundSuperUser authentication to HTTP Basic with password SYS/fhir/r4 CSP application to accept HTTP Basic + delegated auth (AutheEnabled=96)Verify the FHIR endpoint is working:
curl http://localhost:52773/fhir/r4/metadata
# Should return a large JSON CapabilityStatement (resourceType: "CapabilityStatement")
The following is the detailed manual setup procedure that was adapted for this application. This approach creates a FHIR server from scratch using IRIS terminal commands. This is useful if you need to customize the FHIR server configuration or troubleshoot installation issues.
Note: The interactive installation method (Step 5 above) uses this approach. Use this section to understand what each command does or to customize the configuration.
docker exec -it smart-discharge-iris iris session IRIS
By default, Community editions of IRIS Health have the password SYS for every account, but these passwords have expired. This step prevents authentication issues.
Warning: This is only suitable for development environments. Production environments should use robust authentication.
set $NAMESPACE = "%SYS"
do ##class(Security.Users).UnExpireUserPasswords("*")
set $NAMESPACE = "HSLIB"
do:'##class(%SYS.Namespace).Exists("fhirdemo") ##class(HS.Util.Installer.Foundation).Install("fhirdemo")
set $NAMESPACE = "fhirdemo"
Note: For this application, we use the namespace
HSSYS(configured in IRIS container). The namespacefhirdemois shown here as an example if you need to create a custom namespace.
Configure and install the FHIR R4 server endpoint:
// Make the path prefix for the FHIR server set fhirServerPath = "/fhir/r4"// Create Strategy set strategyClass = "HS.FHIRServer.Storage.Json.InteractionsStrategy"
// Create metadata info for FHIR version r4 set metadataPackage = "hl7.fhir.r4.core@4.0.1"
// You can allow metadata packages for multiple FHIR versions with the following: // set metadataPackage = $LISTBUILD("hl7.fhir.r4.core@4.0.1","hl7.fhir.us.core@3.1.0")
// Ensure namespace is FHIR enabled do ##class(HS.FHIRServer.Installer).InstallNamespace() do ##class(HS.FHIRServer.Installer).InstallInstance(fhirServerPath, strategyClass, metadataPackage)
Note: This application uses
/fhir/r4as the FHIR server path (not/demo/fhiras shown in some InterSystems examples).
Set limits on search results to avoid transferring huge amounts of data:
set strategy = ##class(HS.FHIRServer.API.InteractionsStrategy).GetStrategyForEndpoint(fhirServerPath)
set configData = strategy.GetServiceConfigData()
set configData.DefaultSearchPageSize = 1000
set configData.MaxSearchPageSize = 10000
set configData.MaxSearchResults = 10000
do strategy.SaveServiceConfigData(configData)
Load FHIR resource files from a directory into the FHIR server:
// Location of our FHIR data set fhirdata = "/usr/irissys/output/fhir"
// Load FHIR resource files do ##class(HS.FHIRServer.Tools.DataLoader).SubmitResourceFiles(fhirdata, "FHIRServer", fhirServerPath, 1, "^fhirlogs")
Arguments explanation:
FHIRServer or HTTP/demo/fhir)1 (yes) or 0 (no)^fhirlogs (optional)Note: For this application, sample data is loaded via the Python script
data/load_sample_data.pywhich uses HTTP POST requests to the FHIR endpoint.
Check that the endpoint is running by visiting the metadata endpoint:
curl http://localhost:52773/demo/fhir/metadata
It should return a CapabilityStatement resource (XML or JSON depending on Accept header).
Now that data is loaded, you can query the endpoint with HTTP requests.
Example GET request:
GET http://localhost:52773/demo/fhir/Patient/1
Authorization: Basic SuperUser SYS
Accept: application/fhir+json
Using curl:
curl -u "SuperUser:SYS" \
-H "Accept: application/fhir+json" \
http://localhost:52773/fhir/r4/Patient \
-o response.json
Using Python Requests library:
import requests from requests.auth import HTTPBasicAuthheaders = {"Content-Type": "application/fhir+json"} uri = "http://localhost:52773/demo/fhir/Patient/1"
username = "SuperUser" password = "SYS" res = requests.get(uri, headers=headers, auth=HTTPBasicAuth(username, password)) print(res) print(res.json())
Execute from within the backend container to access the Docker network:
# Copy script to backend container docker cp data/load_sample_data.py smart-discharge-backend:/app/Install requests library
docker exec smart-discharge-backend pip install requests
Execute with FHIR URL pointing to 'iris' container
docker exec -e FHIR_BASE_URL=http://iris:52773/fhir/r4 smart-discharge-backend python /app/load_sample_data.py
This creates 8 synthetic patients categorised by risk level:
| Service | URL | Credentials |
|---|---|---|
| Application | http://localhost:3000 | β |
| AI Chat | http://localhost:3000 | Click βAI Chatβ tab |
| Backend API | http://localhost:8000 | β |
| Swagger Docs | http://localhost:8000/docs | β |
| IRIS Portal | http://localhost:52773/csp/sys/UtilHome.csp | SuperUser / SYS |
| FHIR Endpoint | http://localhost:52773/fhir/r4/metadata | β (no auth for GET) |
If Docker containers are stopped and restarted (without docker-compose down), the FHIR endpoint persists and no reinstallation is needed. However, the CSP gateway routing and authentication configuration must be re-applied after every container restart, because the CSP.ini changes made inside the container are ephemeral:
docker-compose up -dRe-apply CSP gateway + auth configuration (required after every restart)
docker cp config/iris/setup_fhir_post_install.sh smart-discharge-iris:/tmp/setup_fhir_post_install.sh docker exec smart-discharge-iris bash /tmp/setup_fhir_post_install.sh
Tip: The
start.shscript handles this automatically β use it instead of runningdocker-compose up -ddirectly.
If the containers are destroyed (docker-compose down), the IRIS data is lost and the full installation (Steps 3bβ3d) must be repeated.
The docker-compose.yml uses intersystemsdc/irishealth-community:latest. This application was developed and tested with irishealth-community:2026.1.0.235.2. If you encounter issues with a newer image, pin the version:
image: intersystemsdc/irishealth-community:2026.1.0.235.2
Environment variables are set directly in docker-compose.yml. The file ships with sensible defaults β the only variable you need to supply is OPENAI_API_KEY for AI features.
docker-compose.ymlenvironment:
# FHIR / IRIS connection
- FHIR_BASE_URL=http://iris:52773/fhir/r4
- IRIS_USERNAME=SuperUser
- IRIS_PASSWORD=SYS
CORS β must be a JSON array (pydantic-settings v2 requirement)
- CORS_ORIGINS=["http://localhost:3000","http://localhost:3001"]
AI Agent (optional) β set values in .env file (recommended) or in shell
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
- AI_MODEL=${AI_MODEL:-gpt-4o} # gpt-4o required for JSON mode support
AI_ENABLED=${AI_ENABLED:-true}
CORS format:
CORS_ORIGINSmust be a valid JSON array string. A plain URL like
CORS_ORIGINS=http://localhost:3000will cause a pydantic parse error and prevent the
backend from starting.
| Variable | Required | Default | Description |
|---|---|---|---|
FHIR_BASE_URL |
Yes | http://iris:52773/fhir/r4 |
Internal FHIR endpoint (container-to-container) |
IRIS_USERNAME |
Yes | SuperUser |
IRIS user for authenticated FHIR writes |
IRIS_PASSWORD |
Yes | SYS |
IRIS password |
CORS_ORIGINS |
Yes | ["http://localhost:3000","http://localhost:3001"] |
Allowed CORS origins (JSON array) |
OPENAI_API_KEY |
No | (empty) | OpenAI key. Without it, system runs in rule-based fallback mode |
AI_MODEL |
No | gpt-4o |
LLM model. Use gpt-4o or gpt-4-turbo |
AI_TEMPERATURE |
No | 0.3 |
Sampling temperature |
AI_MAX_TOKENS |
No | 2000 |
Maximum tokens in AI response |
AI_ENABLED |
No | true |
Master toggle for AI |
HIGH_RISK_THRESHOLD |
No | 0.7 |
Risk score above which is high risk |
MODERATE_RISK_THRESHOLD |
No | 0.4 |
Risk score above which is moderate risk |
MIT License - see https://github.com/kcedd34/smart-discharge-navigator/blob/main/LICENSE
Built with β€οΈ for healthcare innovation | Carlos Eduardo Dias Duarte | kcedd34@gmail.com
| IRIS_PASSWORD | Yes | SYS | IRIS user password |
| CORS_ORIGINS | Yes | ["http://localhost:3000","http://localhost:3001"] | Allowed CORS origins (JSON array) |
| OPENAI_API_KEY | No | (empty) | OpenAI key. Without it, system runs in rule-based fallback mode. Set via .env file (recommended) |
| AI_MODEL | No | gpt-4o | LLM model. Use gpt-4o or gpt-4-turbo β base gpt-4 does not support JSON mode and will cause AI to fall back to rule-based mode |
| AI_TEMPERATURE | No | 0.3 | Sampling temperature (lower = more deterministic) |
| AI_MAX_TOKENS | No | 2000 | Maximum tokens in AI response |
| AI_ENABLED | No | true | Master toggle for AI features |
| HIGH_RISK_THRESHOLD | No | 0.7 | Score threshold for HIGH risk classification |
| MODERATE_RISK_THRESHOLD | No | 0.4 | Score threshold for MODERATE risk classification |
| Resource | Purpose | AI Agent Usage |
|---|---|---|
| Patient | Demographics, age | Context for AI reasoning |
| Encounter | Admission history | Pattern analysis by AI |
| Condition | Active diagnoses | Comorbidity reasoning |
| Observation | Vital signs, labs | Trend detection by AI |
| MedicationRequest | Active medications | Interaction analysis |
| AllergyIntolerance | Drug/food allergies | Allergy-aware discharge planning |
| Resource | Purpose |
|---|---|
| CarePlan | Structured discharge plan (rule-based + AI-enhanced) |
| Task | Discharge checklist items |
The application leverages the FHIR SQL Builder built into InterSystems IRIS for Health to run standard SQL queries directly against FHIR resource projections. Instead of parsing JSON bundles in application code, the backend executes analytical SQL via the IRIS REST SQL endpoint.
How it works:
InstallInstance. The schema is named HSFHIR_X000x_S where x is the installation sequence number (e.g., HSFHIR_X0001_S on a clean first install, HSFHIR_X0008_S if reinstalled several times).fhir_sql_analytics.py) sends SQL queries to IRIS via POST /api/atelier/v1/{namespace}/_query/sql.The SQL examples below use
HSFHIR_X0001_Sas a representative first-install schema name.
Example SQL queries executed:
-- Patient demographics distribution SELECT Age_group, Gender, COUNT(*) as patient_count FROM HSFHIR_X0001_S.Patient GROUP BY Age_group, Gender-- Multi-resource JOIN: encounter frequency per patient SELECT p.Key as patient_id, COUNT(e.Key) as encounter_count FROM HSFHIR_X0001_S.Patient p JOIN HSFHIR_X0001_S.Encounter e ON e.subject = p.Key GROUP BY p.Key ORDER BY encounter_count DESC
-- 3-table JOIN: high-risk population analysis SELECT p.Key, c.code, e.status FROM HSFHIR_X0001_S.Patient p JOIN HSFHIR_X0001_S.Condition c ON c.subject = p.Key JOIN HSFHIR_X0001_S.Encounter e ON e.subject = p.Key WHERE c.code IN ('44054006','73211009','38341003')
-- Readmission candidates via encounter history SELECT subject as patient_id, COUNT() as admissions FROM HSFHIR_X0001_S.Encounter WHERE status = 'finished' GROUP BY subject HAVING COUNT() > 1
-- Polypharmacy analysis (5+ active medications) SELECT subject as patient_id, COUNT() as med_count FROM HSFHIR_X0001_S.MedicationRequest WHERE status = 'active' GROUP BY subject HAVING COUNT() >= 5
API Endpoints:
| Endpoint | Description |
|---|---|
GET /analytics/sql-stats |
Population-level analytics via FHIR SQL Builder |
GET /analytics/readmission-sql |
Readmission candidates identified by SQL |
GET /analytics/sql-builder-info |
Platform feature info and query catalog |
The AI layer follows the InterSystems AI Hub integration pattern, providing a clean abstraction for LLM access that is compatible with the IRIS AI ecosystem:
βββββββββββββββββββββββββββββββββββ
β Smart Discharge Navigator β
β (FastAPI Backend) β
βββββββββββββββββββββββββββββββββββ€
β AIAgentService β
β ββ OpenAI-compatible API βββββββ AI Hub pattern (model gateway)
β ββ Structured clinical promptsβ
β ββ FHIR context injection β
β ββ Graceful degradation β
βββββββββββββββββββββββββββββββββββ€
β InterSystems IRIS for Health β
β ββ FHIR Repository (R4) β
β ββ FHIR SQL Builder β
β ββ IntegratedML (production) β
βββββββββββββββββββββββββββββββββββ
Key design decisions:
The React frontend includes a dedicated Analytics Dashboard tab that visualizes data from the FHIR SQL Builder queries:
intersystemsdc/irishealth-community:latestsmart-discharge-navigator/
βββ backend/
β βββ app/
β β βββ core/
β β β βββ config.py # Config (incl. AI settings)
β β β βββ fhir_client.py # FHIR API wrapper
β β βββ models/
β β β βββ patient.py # Models (incl. AI models)
β β βββ services/
β β β βββ ai_agent_service.py # π€ AI Agent (NEW)
β β β βββ risk_calculator.py # Rule-based scoring
β β β βββ care_plan_generator.py # Discharge planning
β β β βββ fhir_service.py # FHIR orchestration
β β βββ api/
β β βββ routes.py # API endpoints (incl. AI)
β βββ main.py
β βββ requirements.txt
β βββ Dockerfile
βββ frontend/
β βββ src/
β β βββ App.js # React app (incl. AI Chat)
β β βββ index.js
β β βββ index.css # Styles (incl. AI UI)
β βββ public/
β βββ package.json
β βββ Dockerfile
βββ data/
β βββ load_sample_data.py
βββ .env.example # Environment template
βββ docker-compose.yml
βββ start.sh
βββ AI_AGENT_GUIDE.md # π€ AI Agent documentation
βββ ARCHITECTURE.md
βββ README.md
cd backend pip install -r requirements.txtSet AI configuration
export OPENAI_API_KEY=sk-your-key-here
uvicorn main:app --reload --port 8000
cd frontend
npm install
npm start
Duration: 5β7 minutes
AI_ENABLED=falseMIT License β see https://github.com/kcedd34/smart-discharge-navigator/blob/main/LICENSE
Carlos Eduardo Dias Duarte
This is the most common 404 cause on a fresh install. The IRIS embedded web gateway (httpd) does not know to route /fhir requests to IRIS unless /fhir is listed in CSP.ini. Run the post-install setup script to fix this:
docker cp config/iris/setup_fhir_post_install.sh smart-discharge-iris:/tmp/setup_fhir_post_install.sh
docker exec smart-discharge-iris bash /tmp/setup_fhir_post_install.sh
Verify:
curl http://localhost:52773/fhir/r4/metadata
# Expected: 200 with CapabilityStatement JSON
If the FHIR R4 endpoint was never installed (e.g. first run or after docker-compose down), install it:
# Open interactive IRIS terminal docker exec -it smart-discharge-iris iris session IRISPaste the installation commands from Step 5 (Installation section)
Wait 3-8 minutes for completion, then configure CSP Gateway
docker cp config/iris/setup_fhir_post_install.sh smart-discharge-iris:/tmp/setup_fhir_post_install.sh docker exec smart-discharge-iris bash /tmp/setup_fhir_post_install.sh
Wait 3β8 minutes for class compilation to complete, then run the post-install setup:
docker cp config/iris/setup_fhir_post_install.sh smart-discharge-iris:/tmp/setup_fhir_post_install.sh
docker exec smart-discharge-iris bash /tmp/setup_fhir_post_install.sh
This means the FHIR search index classes were not fully generated during installation. The error looks like:
<PROPERTY DOES NOT EXIST>AddToSearchTableEntry *family,HSFHIR.X000x.S.Patient
Fix: uninstall and reinstall the FHIR endpoint. Run this in an IRIS session:
// In HSSYS namespace β uninstall then reinstall via the install script // Step 1: Uninstall set sc = ##class(HS.FHIRServer.Installer).UninstallInstance("/fhir/r4") write "Uninstall sc=",sc,!
// Step 2: Re-run the installation commands from Step 5 // Exit and reopen interactive terminal, then paste all commands
Or use the interactive installation method from Step 5:
docker exec -it smart-discharge-iris iris session IRIS
# Paste all installation commands from Step 5
IRIS needs at least 4 GB RAM. Confirm Docker resource limits and check that config/iris/merge.cpf contains:
[config]
globals=256,0,0,0,0,0
Check container health and logs:
docker inspect --format='{{.State.Health.Status}}' smart-discharge-iris
docker logs smart-discharge-iris --tail=50
Write operations require HTTP Basic Auth. The backend handles this automatically. If testing manually:
# Correct: include credentials for write operations curl -u SuperUser:SYS -X POST http://localhost:52773/fhir/r4/Patient \ -H "Content-Type: application/fhir+json" \ -d '{"resourceType":"Patient","name":[{"family":"Test"}]}'Note: GET /fhir/r4/metadata works WITHOUT credentials (by design)
The IRIS community image may set ChangePassword=1 on the SuperUser account. Fix via ObjectScript:
echo 'set props("ChangePassword")=0
do ##class(Security.Users).Modify("SuperUser",.props)
halt' | docker exec -i smart-discharge-iris bash -c 'iris session IRIS -U %SYS'
Then log in at http://localhost:52773/csp/sys/UtilHome.csp with SuperUser / SYS.
The CORS_ORIGINS variable must be a JSON array. In docker-compose.yml, ensure:
- CORS_ORIGINS=["http://localhost:3000","http://localhost:3001"]
A plain URL string (CORS_ORIGINS=http://localhost:3000) will cause a pydantic-settings v2 parse error.
docker-compose ps # check all containers are running
docker-compose logs iris # look for IRIS startup errors
docker-compose logs backend --tail=30
The backend connects to IRIS using the internal Docker hostname iris (not localhost).
This is set via FHIR_BASE_URL=http://iris:52773/fhir/r4 in docker-compose.yml.
IRIS returns HTTP 201 Created with an empty response body for successful resource creations. The sample data loader (data/load_sample_data.py) handles this by extracting the new resource ID from the Location response header. If you see this error on an old copy of the script, update from the repository.
This means patient creation succeeded but linked resource creation failed. Check that the FHIR endpoint accepts writes:
curl -s -o /dev/null -w "%{http_code}" -u SuperUser:SYS \
-X POST http://localhost:52773/fhir/r4/Patient \
-H "Content-Type: application/fhir+json" \
-d '{"resourceType":"Patient","name":[{"family":"Test"}]}'
# Expected: 201
AI features not working:
# Check AI status endpoint
curl http://localhost:8000/api/v1/ai/status
AI returns fallback responses:
OPENAI_API_KEY is set β preferred method is a .env file in the project rootAI_MODEL=gpt-4o (or gpt-4-turbo) β base gpt-4 does not support JSON mode and silently falls back to rule-based modeAI_ENABLED=true in docker-compose.ymldocker-compose logs backendDischarge plan button shows βFailed to generate discharge planβ (HTTP 500):
IRIS returns HTTP 201 with an empty response body when creating FHIR resources such as CarePlan and Task. If you see JSONDecodeError: Expecting value: line 1 column 1 (char 0) in backend logs, the create_resource() method in fhir_client.py is calling .json() on an empty body. The fix extracts the resource ID from the Location response header instead. Ensure you are running the latest code:
docker-compose up -d --no-deps --force-recreate backend
Frontend shows CORS errors:
CORS errors on the frontend are almost always caused by a backend 500 error β the backend does not emit CORS headers on error responses. Fix the underlying backend error first (check docker-compose logs backend), then the CORS error resolves automatically.
# Verify CORS_ORIGINS matches frontend URL
# Must be JSON array: CORS_ORIGINS=["http://localhost:3000","http://localhost:3001"]
Sample data fails to load:
# Ensure IRIS has fully started (wait 60 seconds after docker-compose up) sleep 60Copy script to backend container and execute
docker cp data/load_sample_data.py smart-discharge-backend:/app/ docker exec smart-discharge-backend pip install requests docker exec -e FHIR_BASE_URL=http://iris:52773/fhir/r4 smart-discharge-backend python /app/load_sample_data.py
Note: The script uses FHIR_BASE_URL environment variable. When running from the host (outside Docker network), use http://localhost:52773/fhir/r4. When running inside containers, use http://iris:52773/fhir/r4.
Built with β€οΈ and π€ by Carlos Eduardo Dias Duarte for InterSystems Programming Contest: AI Agents for FHIR 2026