Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CareAsk — PHI-safe natural-language querying for skilled nursing data

A reference architecture for letting facility staff ask plain-English questions about their data and get answers back — without protected health information ever reaching the language model.

Synthetic data only. Everything in this repo runs against a generated database of fake residents (scripts/build_synthetic_db.py). No real resident data is included or required. The schema is deliberately realistic so the same code path works unchanged when a reviewed, real data source is swapped in.

Built by North Bridge Solutions — Madison, Wisconsin.


The idea

A user asks "How many residents had a fall last month?" in plain language. A language model writes the SQL, the query runs against the database, and the user gets an answer. The hard part in healthcare isn't the querying — it's keeping PHI away from the model. CareAsk does that with a de-identification gate that sits between the database and the LLM.

Database ──▶ De-identification gate ──▶ LLM (writes SQL only) ──▶ run locally ──▶ answer
              │ schema yes, PHI rows no

The model sees structure (table and column names), never real rows. The SQL it writes runs locally; results stay local.

What's here

careask/
  scripts/build_synthetic_db.py   build the fake SNF database
  scripts/demo.py                 run the full pipeline end to end
  scripts/test_paywall.py         exercise the gated request flow
  deident/gate.py                 the de-identification gate (the safety component)
  app/pipeline.py                 NL -> SQL -> run -> plain-language answer
  app/config.py                   how demo and production data sources swap
  auth/access.py                  subscription state + entitlement checks
  auth/billing.py                 Stripe checkout + webhook handling
  web/server.py                   login -> paywall -> pipeline (stdlib server)
  docs/COMPLIANCE.md              the PHI boundary and what legal review needs

Run it (beginner-friendly)

You'll need Python 3.9+. From the repo root:

# 1. (optional) create a virtual environment
python -m venv .venv && source .venv/bin/activate

# 2. build the synthetic database
python scripts/build_synthetic_db.py

# 3a. run the whole pipeline OFFLINE — no API key, canned SQL
CAREASK_FAKE_LLM=1 python scripts/demo.py

# 3b. run with a real model writing the SQL
pip install -r requirements.txt
export ANTHROPIC_API_KEY=sk-...
python scripts/demo.py

The offline mode (CAREASK_FAKE_LLM=1) lets you see the entire flow — gate, SQL validation, local execution, answer — without any external calls.

The paywall

web/server.py wires login → paywall → pipeline. The /ask endpoint is gated: it calls require_entitlement(email) before running any query, returning HTTP 402 if the user has no active subscription. The paywall gates access to the application — it never touches PHI, the de-identification gate, or the database. Billing and PHI handling stay deliberately separate.

# run the gated server fully offline (no API key, no real Stripe)
CAREASK_FAKE_LLM=1 CAREASK_FAKE_BILLING=1 python web/server.py

# in another terminal, exercise the flow: blocked before subscribing, ok after
python scripts/test_paywall.py

Stripe lives in auth/billing.py behind a fake-mode switch. For real billing, set STRIPE_SECRET_KEY, STRIPE_PRICE_ID, and STRIPE_WEBHOOK_SECRET, point your Stripe webhook at /webhook, and drop CAREASK_FAKE_BILLING. The subscription store in auth/access.py is an in-memory stub — swap the dict for your database in production; the webhook keeps it in sync with Stripe.

Note: web/server.py is a stdlib reference for the request flow, not a production server. For real use, put it behind FastAPI/Flask with a real session store and TLS.

DeidentGate takes an is_synthetic flag. Point it at a synthetic source and sample rows flow freely. Point it at a source not marked synthetic and it refuses to extract sample rows — you have to either supply a de-identified view or call extract(include_samples=False). The SQL-generation step also validates that the model returned a single read-only SELECT before anything runs.

Demo vs production

One backbone, swapped at app/config.py:

CAREASK_ENV Data source Gate behavior
demo (default) db/synthetic.db samples flow, rows may be summarized
production your reviewed DB no samples, no rows to the LLM

Important

This repo is an engineering reference, not a compliance certification. Before any real resident data flows through a deployment, the architecture needs review by a healthcare compliance attorney or HIPAA consultant, and Business Associate Agreements need to be in place — including with the LLM provider. See docs/COMPLIANCE.md.

About

PHI-safe natural-language querying for skilled nursing data — reference architecture on synthetic data

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages