AI-Driven Multi-Role Clinical Intelligence System powered by LangGraph agents, ChromaDB, and Claude/OpenAI models.
ClinicalIQ adapts clinical responses based on the user's role — Patient, Clinician, Radiologist, or Care Coordinator. It uses a multi-agent LangGraph orchestrator that routes queries to specialized agents (lab interpreter, radiology analyzer, allergy safety checker) and retrieves context from a ChromaDB vector store.
Tech Stack
| Layer | Technology |
|---|---|
| Frontend | React 18, Vite, Redux Toolkit, TailwindCSS |
| Backend | FastAPI, LangGraph, LangChain, Python 3.12 |
| AI Providers | Anthropic Claude / OpenAI GPT (switchable) |
| Vector Store | ChromaDB Cloud |
| Observability | Langfuse |
| AWS Infra | Lambda (container), ECR, S3, CloudFront, Terraform |
- Python 3.12+
- Node.js 20+
- Docker & Docker Compose (optional, for container-based setup)
- Anthropic API key (or OpenAI API key)
- ChromaDB Cloud account
- Langfuse account (optional — app runs without it)
git clone <repo-url>
cd clinicaliq
cp .env.example .envEdit .env and fill in your credentials:
# Required: choose one provider
AI_PROVIDER=anthropic # or openai
AI_MODEL=claude-haiku-4-5-20251001
ANTHROPIC_API_KEY=sk-ant-... # if using Anthropic
OPENAI_API_KEY=sk-... # if using OpenAI
# ChromaDB Cloud (required)
CHROMA_API_KEY=ck-...
CHROMA_TENANT=<your-tenant-id>
CHROMA_DATABASE=health-care-clinical-data
CHROMA_COLLECTION_NAME=clinical_documents
# Langfuse (optional)
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_BASE_URL=https://us.cloud.langfuse.comStarts both backend and frontend with hot reload.
docker compose up --build| Service | URL |
|---|---|
| Frontend | http://localhost:6153 |
| Backend API | http://localhost:12000 |
| API Docs (Swagger) | http://localhost:12000/docs |
| Health check | http://localhost:12000/health |
To stop:
docker compose downBackend
cd backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# Run from project root so .env is found automatically
cd ..
uvicorn backend.app.main:app --host 0.0.0.0 --port 12000 --reloadFrontend (in a separate terminal)
cd frontend
npm install
npm run devOpen http://localhost:6153 in your browser.
The backend auto-ingests documents on startup. To manually trigger ingestion:
cd backend
python scripts/generate_data.pyThis generates synthetic clinical documents into data/synthetic/clinical_documents.json and upserts them into ChromaDB.
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Health check |
| POST | /api/v1/clinical/query |
Submit a clinical query |
Example query:
curl -X POST http://localhost:12000/api/v1/clinical/query \
-H "Content-Type: application/json" \
-d '{
"query": "What do elevated troponin levels indicate?",
"role": "clinician",
"patient_id": "P001"
}'Supported roles: patient, clinician, radiologist, coordinator
The infrastructure is fully managed by Terraform. The architecture is:
- Backend: Docker container image → ECR → Lambda (Function URL)
- Frontend: Vite build → S3 → CloudFront
- CloudFront: routes
/api/*to Lambda, everything else to S3
- AWS CLI configured (
aws configure) - Terraform >= 1.6 installed
- Docker installed (for building the Lambda image)
cd terraform
terraform initCreate a terraform.tfvars file (do not commit this file):
anthropic_api_key = "sk-ant-..."
openai_api_key = "" # leave empty if unused
chroma_api_key = "ck-..."
chroma_tenant = "<your-tenant-id>"
langfuse_secret_key = "sk-lf-..."
langfuse_public_key = "pk-lf-..."
aws_region = "us-east-1"Apply the infrastructure:
terraform apply -var-file="terraform.tfvars"Note the outputs — you will need them in the next steps:
cloudfront_url = "https://xxxx.cloudfront.net"
ecr_repository_url = "123456789.dkr.ecr.us-east-1.amazonaws.com/clinicaliq-prod-backend"
s3_bucket_name = "clinicaliq-prod-frontend-123456789"
lambda_function_url = "https://xxxx.lambda-url.us-east-1.on.aws/"
export AWS_REGION=us-east-1
export ECR_URL=<ecr_repository_url from terraform output>
# Authenticate Docker with ECR
aws ecr get-login-password --region $AWS_REGION | \
docker login --username AWS --password-stdin $ECR_URL
# Build for Lambda (AMD64)
docker build --platform linux/amd64 -t clinicaliq-backend ./backend
# Tag and push
docker tag clinicaliq-backend:latest $ECR_URL:latest
docker push $ECR_URL:latestAfter pushing, update the Lambda to use the new image:
aws lambda update-function-code \
--function-name clinicaliq-prod-backend \
--image-uri $ECR_URL:latest \
--region $AWS_REGIONexport CLOUDFRONT_URL=<cloudfront_url from terraform output>
export S3_BUCKET=<s3_bucket_name from terraform output>
# Build with the production API base URL
cd frontend
VITE_API_BASE_URL=$CLOUDFRONT_URL npm run build
# Upload to S3
aws s3 sync dist/ s3://$S3_BUCKET --delete
# Invalidate CloudFront cache
aws cloudfront create-invalidation \
--distribution-id <your-cloudfront-distribution-id> \
--paths "/*"The CloudFront distribution ID can be retrieved with:
cd ../terraform
terraform output cloudfront_url
# Then find the distribution ID:
aws cloudfront list-distributions --query \
"DistributionList.Items[?DomainName=='<domain-from-output>'].Id" \
--output text# Health check
curl https://<cloudfront_url>/health
# Test a query
curl -X POST https://<cloudfront_url>/api/v1/clinical/query \
-H "Content-Type: application/json" \
-d '{"query": "Explain CBC results", "role": "patient", "patient_id": "P001"}'Open https://<cloudfront_url> in your browser to use the full application.
To remove all AWS resources:
cd terraform
terraform destroy -var-file="terraform.tfvars"Note: This deletes the S3 bucket and all its contents (force_destroy = true), the ECR repository and all images, and the Lambda function.
clinicaliq/
├── backend/
│ ├── app/
│ │ ├── agents/ # LangGraph agents (orchestrator, lab, radiology, allergy)
│ │ ├── api/routes/ # FastAPI route handlers
│ │ ├── data/ # Document ingestion pipeline
│ │ ├── knowledge_graph/ # Medical ontology graph
│ │ ├── models/ # Pydantic schemas
│ │ ├── observability/ # Langfuse client
│ │ ├── vectorstore/ # ChromaDB client
│ │ ├── config.py
│ │ └── main.py
│ ├── scripts/
│ │ └── generate_data.py
│ ├── Dockerfile # Production Lambda image
│ ├── Dockerfile.dev # Local dev image
│ ├── lambda_handler.py # Mangum ASGI adapter for Lambda
│ └── requirements.txt
├── frontend/
│ └── src/
│ ├── components/ # React UI components
│ ├── store/slices/ # Redux state (role, clinical)
│ └── services/api.js # Axios API client
├── terraform/ # AWS infrastructure (Lambda, ECR, S3, CloudFront)
├── data/synthetic/ # Sample clinical documents
├── docker-compose.yml
├── .env.example
└── README.md
| Variable | Required | Description |
|---|---|---|
AI_PROVIDER |
Yes | anthropic or openai |
AI_MODEL |
Yes | Model ID (e.g. claude-haiku-4-5-20251001) |
ANTHROPIC_API_KEY |
If using Anthropic | API key |
OPENAI_API_KEY |
If using OpenAI | API key |
CHROMA_API_KEY |
Yes | ChromaDB Cloud API key |
CHROMA_TENANT |
Yes | ChromaDB tenant UUID |
CHROMA_DATABASE |
Yes | Database name |
CHROMA_COLLECTION_NAME |
Yes | Collection name |
LANGFUSE_SECRET_KEY |
Optional | Langfuse tracing |
LANGFUSE_PUBLIC_KEY |
Optional | Langfuse tracing |
LANGFUSE_BASE_URL |
Optional | Langfuse endpoint |
ALLOWED_ORIGINS |
Yes | Comma-separated CORS origins |
APP_ENV |
No | development or production |
APP_PORT |
No | Backend port (default: 12000) |