Readme updates and minor fixes
This repository is an entry for the InterSystems Programming Contest: AI Agents + FHIR.
It implements one of the suggested tasks directly:
Natural Language to FHIR Query Explorer.
The project lets users explore FHIR data conversationally by translating clinical questions into transparent, executable SQL over InterSystems IRIS for Health FHIR SQL Builder projections.
Unlike a typical NL-to-SQL demo, this project does not let the model generate arbitrary SQL directly. The flow is:
natural language -> structured query plan -> schema grounding -> deterministic SQL generation -> execution
That makes the system easier to inspect, explain, and debug.

FHIR Query Copilot showing the extracted query plan, schema grounding, generated SQL, and execution results for a natural language question.
User: Show diabetic patients System: Found matching patientsUser: Only females System: Refined the existing query
User: Born after 1950 System: Refined the existing query again
User: Count them System: Returns the count for the refined cohort
This stateful refinement is one of the main differentiators of the project. The conversation updates a shared query plan instead of restarting from scratch on every turn.

Multi-turn query refinement. Each user message updates the existing SQL query plan instead of starting a new session.
FHIR Query Copilot does not simply fail when a question cannot be answered from the current FHIR SQL Builder projection.
Instead, it identifies the missing resources or fields and recommends what should be projected.
Example:
Question:
Show diabetic patients that are alive
Response:
I can’t fully answer that from the indexed schema
Missing: (concept ‘admission’ is not in the coding dictionary; time window ‘recent’ has no date field in the schema)
Extend your FHIR projection with:
Re-index after extending the projection, or rephrase the question.

Projection recommendations help users understand why a question cannot be answered and which FHIR fields should be added to the projection.
uvgit clone https://github.com/blockpass253/iris-health-fhir-query-explorer.git
cd iris-health-fhir-query-explorer
cp .env.example .env
Edit .env and set at least:
OPENAI_API_KEY=your_key_here
The default IRIS connection settings already point to the included demo container.
make iris-up
This starts the prebuilt IRIS for Health demo image defined in compose.yaml.
make install
make run
This runs a simple SELECT $ZVERSION smoke test.
uv run iris index-schema MEDICATIONS --namespace FHIRSERVER
This builds the local semantic registry used at runtime and writes it to data/schema_registry.json.
The demo image ships with three projections (see Available projections):
| Projection | Resources | Notes |
|---|---|---|
BASIC |
Patient, Encounter, Observation, Condition | Minimal fields only |
BASICWITHDATES |
Patient, Encounter, Observation, Condition | Includes dates and other useful fields |
MEDICATIONS |
Patient, Encounter, Observation, Condition, MedicationRequest | Same as BasicWithDates plus medications |
Use MEDICATIONS for the fullest demo experience. Use BASIC if you only want to verify connectivity.
make tui
Then try any of the questions in the next section.
All questions below work with the MEDICATIONS projection (the recommended starting point).
Multi-turn refinement works in the TUI — each follow-up message refines the active query plan rather than starting a new one.
Show diabetic patients
Show diabetic patients taking metformin
Show patients with recent encounters
These work well as a multi-turn sequence:
Show diabetic patients
Only females
Born after 1950
Count them
The following will trigger a clarifying question about how to compute age — answer it and the query will proceed:
Show female diabetic patients over 65
Show diabetes conditions
Show active conditions
Show COPD conditions
Show conditions recorded in the last year
Show conditions associated with female patients
Show A1c observations
Show observations recorded in the last 90 days
Show observations for diabetic patients
Show encounters in the last 30 days
Show encounters for diabetic patients
Show encounters involving female patients
Show COPD-related encounters
Show metformin prescriptions
Show prescriptions for diabetic patients
The following triggers a clarifying question about how to compute age — the TUI will ask and continue once you answer:
Show prescriptions for patients over 65
FHIR Query Copilot detects when the current projection is missing fields needed to answer a question, explains what is missing, and suggests what to add. Here is a quick way to see that feature end-to-end:
Step 1 — Index a minimal projection:
uv run iris index-schema BASIC --namespace FHIRSERVER
The BASIC projection has no date fields, so time-window queries cannot be answered from it.
Step 2 — Ask a question that requires dates:
make tui
Show encounters in the last 30 days
The system will report that it cannot answer the question, identify the missing date field (Encounter.period.start), and suggest adding it to the projection.
Step 3 — Switch to a projection that includes dates:
Exit the TUI, re-index with BASICWITHDATES or MEDICATIONS, and try the same question:
uv run iris index-schema BASICWITHDATES --namespace FHIRSERVER
make tui
Show encounters in the last 30 days
This time the query succeeds, showing the generated SQL and results.
The contest suggested building a natural-language FHIR query experience. This project does that by putting a conversational layer on top of FHIR SQL Builder.
The key point is that the app is not just translating text into SQL. It is:
Traditional NL -> SQL systems often assume:
FHIR Query Copilot instead:
The model never generates SQL directly.
The system does not behave like a black box. Users can see:
FHIR SQL Builder projections are customizable. Table names, available resources, and fields can differ between environments. This project does not rely on a fixed handcrafted SQL template per question. It first indexes the actual projection and then binds queries against that indexed registry.
The LLM helps with semantic interpretation and schema binding, but SQL generation itself is deterministic. That reduces hallucination risk and makes failures easier to understand.
In the TUI, users can refine a previous question naturally, for example:
Show diabetic patients
Only females
Born after 1950
Count them
The system does not silently guess when a query is ambiguous.
If multiple resources or fields could satisfy a request, FHIR Query Copilot asks the user for clarification before generating SQL.
This keeps query generation transparent and allows users to remain in control of the final query.
This is not limited to patient cohort queries. The runtime can anchor the query on different FHIR root resources depending on what the user is asking for, including:
PatientConditionObservationEncounterMedicationRequestExamples:
Show diabetic patients -> root resource PatientShow active conditions -> root resource ConditionShow A1c observations from the last year -> root resource ObservationShow recent encounters -> root resource EncounterTop 5 medications prescribed in the last 6 months -> root resource MedicationRequestIf the current projection cannot answer a question, the app can explain why and suggest which FHIR fields or resources should be added.
For example, a question like:
Show diabetic patients with A1c above 9 in the last year
may fail if the projection can ground the A1c value filter but has no projected date field for the Observation time window. In that case, FHIR Query Copilot treats the query as not answerable from the current schema and can suggest projection additions such as:
Observation.effectiveDateTimeObservation.issuedThe goal is not to guess. The system stops, explains the gap, and makes the missing projection actionable.
The current runtime pipeline is:
The demo is currently strongest at:
Current limitations:
Connection settings are loaded from .env or the shell environment:
| Variable | Default | Description |
|---|---|---|
IRIS_HOST |
localhost |
IRIS hostname |
IRIS_PORT |
1972 |
IRIS superserver port |
IRIS_NAMESPACE |
FHIRSERVER |
Namespace to connect to |
IRIS_USERNAME |
_SYSTEM |
Username |
IRIS_PASSWORD |
SYS |
Password |
LLM settings:
| Variable | Default | Description |
|---|---|---|
OPENAI_API_KEY |
unset | Required for extraction and binding |
OPENAI_MODEL |
gpt-5.4-nano |
Model override |
| Command | Purpose |
|---|---|
make iris-up |
Start the demo IRIS container |
make iris-down |
Stop the demo container |
make iris-logs |
Show IRIS container logs |
make install |
Install Python dependencies |
make run |
IRIS connectivity smoke test |
make tui |
Launch the interactive Textual UI |
uv run iris index-schema <PROJECTION> |
Build the semantic registry for a projection |
uv run iris query "<question>" |
Run a one-shot query from the CLI |
The Docker image is a prebuilt IRIS snapshot that ships with three FHIR SQL Builder projections in the FHIRSERVER namespace:
| Projection | Resources | Description |
|---|---|---|
BASIC |
Patient, Encounter, Observation, Condition | A minimal projection with a small set of fields. Good for verifying connectivity. |
BASICWITHDATES |
Patient, Encounter, Observation, Condition | Extends BASIC with date fields (e.g. effectiveDateTime, period.start) and other useful attributes. Enables time-window queries. |
MEDICATIONS |
Patient, Encounter, Observation, Condition, MedicationRequest | Everything in BASICWITHDATES plus the MedicationRequest resource. Enables medication queries such as “top 5 medications prescribed in the last 6 months”. |
Run uv run iris index-schema <PROJECTION> --namespace FHIRSERVER to build the registry for the projection you want to use.
This repository focuses on the query-exploration layer:
The goal of this project is to make FHIR SQL Builder more accessible for analysts, developers, and data consumers who want to explore healthcare data without manually writing SQL or knowing every detail of the projected schema.