Skip to content

Latest commit

 

History

48 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

title MedDataOps
emoji 🩺
colorFrom blue
colorTo green
sdk docker
app_port 7860
tags
openenv
rl-environment
healthcare
data-engineering

MedDataOps

Clinical data engineering RL environment for training agents to clean hospital data and repair production SQL under realistic constraints.

Quick navigation:

1. Motivation

Clinical data pipelines are not abstract spreadsheet problems. They drive triage views, medication safety dashboards, and ICU capacity planning. When these pipelines fail, clinicians make decisions on bad information.

MedDataOps exists to train and evaluate agents on the exact failure modes that appear in real hospital analytics:

  • messy, heterogeneous, and partially corrupted tabular data
  • broken SQL logic in operational reporting queries
  • pressure to produce correct answers with limited steps and auditability

The objective is simple: build agents that can safely recover high-integrity analytics from noisy clinical data.

2. Environment Overview

MedDataOps is an episodic, session-scoped environment exposed through an OpenEnv-style HTTP API.

Story setup:

  • You are an on-call clinical data engineer.
  • A hospital analytics report is wrong.
  • You must clean the working dataset and fix the SQL query.
  • You submit only when both are correct.

Core characteristics:

  • deterministic task reset via optional seed
  • explicit action space (clean_data / run_query / fix_query / submit)
  • structured observation payloads for agent planning
  • reward decomposition for cleaning quality, query quality, efficiency, and step discipline

Local web UI defaults:

  • keyboard-first navigation with a skip link and visible focus indicators
  • screen-reader status announcements for demo lifecycle states
  • sanitized client rendering path (no direct HTML interpolation of API payloads)
  • session identifier validation before local persistence

3. Action Space

The API accepts action payloads via POST /step.

action_type parameters description example
clean_data {"operations": [...]} Apply cleaning transforms to the working dataset (normalize strings, type fixes, null handling, dedupe). {"action_type":"clean_data","parameters":{"operations":[{"operation":"normalize_strings","columns":["drug_name"],"case":"lower"}]}}
run_query {"query":"SELECT ..."} Execute SQL against current episode tables to inspect correctness. {"action_type":"run_query","parameters":{"query":"SELECT ward, COUNT(*) AS n FROM patients GROUP BY ward"}}
fix_query {"query":"SELECT ..."} Replace broken SQL with corrected SQL candidate. {"action_type":"fix_query","parameters":{"query":"WITH x AS (...) SELECT ..."}}
submit {} Finalize episode scoring using current cleaned data and current SQL. {"action_type":"submit","parameters":{}}

Notes:

  • unsupported actions return validation errors
  • /step requires an active session initialized by /reset

4. Observation Space

Observation payload returned by /reset and included in /step response:

field type description
current_dataset_state list[object] Current snapshot of episode working rows visible to the agent.
current_sql_query string Current SQL query under repair/evaluation.
error_messages list[string] Validation or execution errors from previous action/query attempt.
task_description string Natural-language objective for the current task.
step_number int Zero-based step index in the current episode.

5. Tasks

Three benchmark tasks represent increasing operational complexity.

task_id difficulty data_challenge sql_challenge max_score
triage_report easy Ward casing drift, duplicate patients, mixed date formats, null/N/A age values. Fix malformed triage aggregation query and return accurate ward counts. 1.0
medication_summary medium Drug-name normalization, dosage parsing, orphan prescription rows, mixed timestamp formats. Replace invalid cross-join pattern with correct patient-medication join and grouped counts. 1.0
icu_capacity hard Merge heterogeneous schemas across hospital systems, map ward codes, deduplicate occupancy events. Rewrite expensive correlated-subquery design into set-based CTE aggregation for capacity metrics. 1.0

6. Reward Function

Per-step reward is decomposed and explicitly reported.

Formula:

$$ R_{total} = (0.4 \cdot S_{clean}) + (0.4 \cdot S_{sql}) + (0.2 \cdot B_{eff}) + P_{step} $$

Where:

  • $S_{clean} \in [0,1]$: cell-level cleaning correctness against expected cleaned rows
  • $S_{sql} \in [0,1]$: query result correctness against expected SQL output (exact + partial matching)
  • $B_{eff} \in {0, 0.1}$: efficiency bonus when query plan cost is below threshold
  • $P_{step} = -0.02 \times N_{unnecessary}$: penalty for unnecessary action churn

Reward object fields:

  • data_clean_score
  • query_correct_score
  • efficiency_bonus
  • step_penalty
  • total

7. Quick Start

Python local setup

python -m venv .venv
# Windows PowerShell
.venv\Scripts\Activate.ps1
pip install -r requirements.txt

Run with Docker (recommended)

docker build -t meddataops .
docker run --rm -p 7860:7860 meddataops

Run from Docker Hub image

Image: https://hub.docker.com/r/lazerai/meddataops

docker pull lazerai/meddataops:latest
docker run --rm -p 7860:7860 lazerai/meddataops:latest

Pinned tag for the latest validated release:

docker pull lazerai/meddataops:f313e2a
docker run --rm -p 7860:7860 lazerai/meddataops:f313e2a

Then open:

  • root UI: http://localhost:7860/
  • health: http://localhost:7860/health

Run baseline inference client

inference.py expects a chat-completions compatible endpoint.

$env:API_BASE_URL = "https://router.huggingface.co/v1"
$env:MODEL_NAME = "meta-llama/Llama-3.1-8B-Instruct"
$env:HF_TOKEN = "<your_hf_token>"
python inference.py

If you are on a low-TPM provider tier and see repeated 429 responses, you can tune retries and prompt/output budgets:

$env:MAX_API_RETRIES = "8"
$env:ACTION_TOKEN_BUDGET = "600"
$env:MODEL_MAX_OUTPUT_TOKENS = "180"
python inference.py

Quick smoke test

curl -s http://localhost:7860/health
curl -s http://localhost:7860/tasks

Open the local UI

  1. Start the service with Docker or Python.
  2. Open http://localhost:7860/ in your browser.
  3. Use the "Run Demo Episode" control to execute reset -> clean_data -> fix_query -> submit.
  4. Inspect /state to confirm final step details.

8. API Reference

GET /

Serves the landing page (index.html) with task cards, demo runner, and endpoint examples.

GET /health

Response:

{
  "status": "ok",
  "version": "1.0.0"
}

GET /tasks

Response:

{
  "tasks": [
    {
      "id": "triage_report",
      "name": "Morning Triage Report",
      "difficulty": "easy",
      "description": "The morning triage report is broken...",
      "hints": ["Standardize ward strings before grouping."],
      "dirty_row_count": 500,
      "has_expected_sql": true
    }
  ]
}

POST /reset

Request:

{
  "task_id": "triage_report",
  "seed": 101
}

Response headers:

  • X-Session-Id: <uuid>
  • Set-Cookie: session_id=<uuid>; HttpOnly; SameSite=Lax

Response body (observation):

{
  "current_dataset_state": [{"patient_id": "P100001", "ward": "icu"}],
  "current_sql_query": "SELECT ward, COUNT(*) as patient_count FROM patients ...",
  "error_messages": [],
  "task_description": "The morning triage report is broken...",
  "step_number": 0
}

POST /step

Request:

{
  "action_type": "fix_query",
  "parameters": {
    "query": "SELECT ward, COUNT(*) AS patient_count FROM patients GROUP BY ward"
  }
}

Header required (or cookie from /reset):

  • X-Session-Id: <uuid>

Response:

{
  "observation": {
    "current_dataset_state": [{"patient_id": "P100001", "ward": "ICU"}],
    "current_sql_query": "SELECT ward, COUNT(*) AS patient_count FROM patients GROUP BY ward",
    "error_messages": [],
    "task_description": "...",
    "step_number": 1
  },
  "reward": {"value": 0.0},
  "done": false,
  "info": {"action_type": "fix_query", "query_valid": true}
}

GET /state

Header required:

  • X-Session-Id: <uuid>

Response:

{
  "done": false,
  "step_number": 1,
  "max_steps": 20,
  "task": {
    "id": "triage_report",
    "name": "Morning Triage Report",
    "difficulty": "easy",
    "description": "...",
    "hints": ["..."]
  },
  "observation": {"current_dataset_state": [], "current_sql_query": "..."},
  "latest_reward": {"value": 0.0},
  "last_info": {}
}

9. Baseline Scores

Measured baseline from inference.py:

task model score steps status
triage_report llama-3.1-8b-instant 0.6000 20 ok
medication_summary llama-3.1-8b-instant 0.0000 20 ok
icu_capacity llama-3.1-8b-instant 0.0000 3 ok

A 0.0000 score with status=ok indicates model-capability limits (not environment/runtime failure); the deterministic reference solver below validates that the benchmark pipeline itself is functioning.

Scores measured on 2026-04-04 against lazerai-meddataops.hf.space using an 8B-class model (llama-3.1-8b-instant, equivalent scale to meta-llama/Llama-3.1-8B-Instruct). Medium and hard tasks exceeded this model's capability at default settings. Larger models (70B+) are expected to score meaningfully on all three tasks. Phase 2 evaluation uses a standard frontier model.

Run note: this benchmark completed without llm_failed status. Free-tier TPM limits can still introduce multiple 429 retries and increase runtime.

Reference Solver (deterministic):

task solver score steps status
triage_report reference_solver 1.0000 2 ok
medication_summary reference_solver 1.0000 3 ok
icu_capacity reference_solver 0.6343 2 ok

These values come from reference_solver.py against the live Space (https://lazerai-meddataops.hf.space).

10. Project Structure

MedDataOps/
├─ Dockerfile
├─ docker-compose.yml
├─ entrypoint.sh
├─ README.md
├─ index.html
├─ inference.py
├─ openenv.yaml
├─ requirements.txt
├─ scripts/
│  ├─ api_server.py
│  ├─ run_env.py
│  └─ seed_db.py
└─ src/
   └─ meddataops/
      ├─ __init__.py
      ├─ config.py
      ├─ data_cleaning.py
      ├─ db.py
      ├─ env.py
      ├─ models.py
      ├─ scoring.py
      ├─ sql_query.py
      ├─ sql/
      │  ├─ schema.sql
      │  └─ seed.sql
      └─ tasks/
         ├─ __init__.py
         ├─ triage_report.py
         ├─ medication_summary.py
         ├─ icu_capacity.py
         ├─ easy.py
         ├─ medium.py
         └─ hard.py

11. Contributing

We welcome contributions that improve realism, reliability, and evaluation rigor.

Recommended workflow:

  1. Open an issue describing the clinical/technical gap.
  2. Implement with tests and deterministic seed behavior.
  3. Run local API smoke checks (/health, /tasks, /reset, /step, /state).
  4. Submit a PR with before/after behavior and benchmark impact.

Contribution priorities:

  • richer clinical noise patterns and schema drift cases
  • stronger reward calibration and error attribution
  • additional benchmark tasks and baseline trajectories

12. License

This project is released under the MIT License. See LICENSE for full terms.

About

A clinical data engineering RL environment for OpenEnv — clean hospital datasets and fix broken PostgreSQL queries

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages