CoolFace
Apppublic

SujalVaishya/dataquality-openenv

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
App README

๐Ÿงน DataQuality OpenEnv

A real-world OpenEnv environment for training and evaluating AI agents on data quality triage.

Data engineers spend a significant portion of their time identifying and fixing data quality issues โ€” missing values, type errors, duplicate records, format violations, outliers, and referential integrity problems. This environment simulates that work as a structured, episodic agent task with partial-progress rewards and deterministic graders.


Why This Environment?

Data quality triage is a genuinely hard, high-value real-world task:

  • โ€”It requires inspection (understanding what's wrong), planning (deciding what to fix first), and execution (applying the right operations in the right order)
  • โ€”It has rich partial progress signals โ€” each fix measurably improves the dataset
  • โ€”It scales in difficulty: a simple contacts CSV vs. a production healthcare dataset
  • โ€”It maps cleanly to agent capabilities: tool use, state tracking, multi-step reasoning

This fills a gap in the OpenEnv ecosystem โ€” no existing environment models data engineering tasks.


Action Space

Agents issue JSON actions with an action_type and parameters dict:

Action TypeParametersDescription
inspect_columncolumn (optional)Get dtype, null count, unique samples for a column. No column โ†’ list all columns.
fill_missingcolumn, fill_value, strategy (constant/forward_fill/mean)Fill null values in a column
drop_duplicatessubset (list of cols or null), keep (first/last)Remove duplicate rows
fix_typecolumn, target_type (float/int/str/date/datetime), strip_charsConvert column dtype; strips currency symbols etc.
flag_outliercolumn, min_val, max_val, action (drop/clip/flag)Handle out-of-range numeric values
apply_regex_fixcolumn, normalize_map OR pattern+replacementNormalize categorical values or apply regex substitution
drop_rowscolumn, condition (isnull/notin/lessthan/greaterthan), value OR row_indicesRemove rows matching a condition
rename_columnold_name, new_nameRename a column
submit(none)End the episode and trigger final scoring

Example action:

json
{
  "action_type": "fix_type",
  "parameters": {
    "column": "revenue",
    "target_type": "float",
    "strip_chars": "$,"
  }
}

Observation Space

Each step returns an Observation with:

FieldTypeDescription
dataset_previewList[Dict]First 20 rows of the current dataset
schemaDict[str, str]Column names โ†’ inferred Python type
issue_registryList[IssueRecord]All detected issues with resolved flag
action_historyList[str]Last 10 actions taken (with results)
step_countintNumber of steps taken this episode
task_descriptionstrNatural language description of the objective
metricsDataMetricsFour quality scores: completeness, consistency, validity, uniqueness (each 0โ€“1)
last_action_resultstrHuman-readable result of the most recent action

IssueRecord fields: issue_id, issue_type (missing/duplicate/typeerror/format/outlier), `column`, `rowindices, severity (critical/major/minor), description, resolved`


Tasks

Task 1: Basic Completeness Fix (task_easy)

Difficulty: Easy | Max steps: 15 | Target score: โ‰ฅ 0.85

A customer contacts CSV (35 rows) has:

  • โ€”~30% of email values missing โ†’ fill with unknown@example.com
  • โ€”~25% of phone values missing โ†’ fill with N/A
  • โ€”5 exact duplicate rows โ†’ remove

Grader weights: email completeness 40%, phone completeness 30%, no duplicates 30%

Optimal solution (4 actions):

json
[
  {"action_type": "fill_missing", "parameters": {"column": "email", "fill_value": "unknown@example.com", "strategy": "constant"}},
  {"action_type": "fill_missing", "parameters": {"column": "phone", "fill_value": "N/A", "strategy": "constant"}},
  {"action_type": "drop_duplicates", "parameters": {}},
  {"action_type": "submit", "parameters": {}}
]

Task 2: Type Errors & Format Violations (task_medium)

Difficulty: Medium | Max steps: 20 | Target score: โ‰ฅ 0.80

A sales transactions dataset (40 rows) has:

  • โ€”revenue stored as strings with currency symbols ("$1,234.56") โ†’ convert to float
  • โ€”date column in 5 mixed non-ISO formats (MM/DD/YYYY, DD-MM-YYYY, Jan 15 2024, etc.) โ†’ normalize to YYYY-MM-DD
  • โ€”~15% of quantity values are negative โ†’ drop those rows
  • โ€”Duplicate transaction_id values โ†’ deduplicate

Grader weights: revenue numeric 35%, dates ISO 25%, no negative qty 20%, unique transaction IDs 20%


Task 3: Multi-Issue Production Dataset (task_hard)

Difficulty: Hard | Max steps: 30 | Target score: โ‰ฅ 0.75

A healthcare appointments dataset (65 rows) with 7 simultaneous issue types:

  1. 1.Missing `patient_id` (~15% of rows) โ†’ drop those rows
  2. 2.`appointment_date` format violations (~40% non-ISO) โ†’ standardize to YYYY-MM-DD
  3. 3.`created_timestamp` format violations (~35% non-ISO) โ†’ standardize to YYYY-MM-DDThh:mm:ss
  4. 4.Age outliers (negative ages, age > 130) โ†’ drop those rows
  5. 5.Invalid `status` values ("Scheduled", "cancled", "no show") โ†’ normalize to {scheduled, completed, cancelled, no_show}
  6. 6.Invalid `department` values ("ORTHO", "neuro", "kids") โ†’ normalize to valid set
  7. 7.Duplicate `appointment_id` โ†’ remove duplicates

Grader weights: patientid 20%, apptdate 15%, created_ts 15%, age 15%, status 15%, dept 10%, uniqueness 10%

This task challenges frontier models because all 7 issues must be identified and addressed in the right order, with some interdependencies (e.g., dropping null patient_ids first reduces the duplicate count).


Reward Function

The reward at each step is a combination of:

ComponentWeightDescription
metric_improvement_delta0.50Change in average quality metric (completeness, consistency, validity, uniqueness)
issue_resolution_delta0.30Fraction of issues resolved this step
efficiency_bonus0.02 ร— stepsremainingratioSmall bonus for resolving issues quickly
penaltyvariableโˆ’0.02 for failed actions, โˆ’0.05 for timeout

Key properties:

  • โ€”Dense signal: Every step that improves the dataset yields a positive reward
  • โ€”No reward hacking: inspect_column actions yield exactly 0.0 reward (no state change)
  • โ€”Timeout penalty: Exceeding max_steps yields a โˆ’0.05 penalty on the terminal reward
  • โ€”Efficiency incentive: Solving issues in fewer steps earns a small bonus

Setup & Usage

Local installation

bash
git clone https://huggingface.co/spaces/your-username/dataquality-openenv
cd dataquality-openenv
pip install -r requirements.txt

Python API

python
from dataquality_env import DataQualityEnv, Action, ActionType

env = DataQualityEnv(task_id="task_hard", seed=42)
obs = env.reset()

print(obs.task_description)
print(f"Issues: {[(i.issue_id, i.description) for i in obs.issue_registry]}")

# Fix missing patient IDs
action = Action(
    action_type=ActionType.DROP_ROWS,
    parameters={"column": "patient_id", "condition": "is_null"}
)
obs, reward, done, info = env.step(action)
print(f"Reward: {reward.total:+.4f}")
print(f"Result: {obs.last_action_result}")

# Get full internal state
state = env.state()
print(f"Score so far: {state.score}")

HTTP API (after docker run or HF Space)

bash
# Create a session
curl -X POST http://localhost:7860/env/reset \
  -H 'Content-Type: application/json' \
  -d '{"task_id": "task_medium", "seed": 42}'
# โ†’ {"session_id": "abc-123", "observation": {...}}

# Step
curl -X POST http://localhost:7860/env/step \
  -H 'Content-Type: application/json' \
  -d '{
    "session_id": "abc-123",
    "action": {
      "action_type": "fix_type",
      "parameters": {"column": "revenue", "target_type": "float", "strip_chars": "$,"}
    }
  }'
# โ†’ {"observation": {...}, "reward": {...}, "done": false, "info": {...}}

# Get state
curl http://localhost:7860/env/state/abc-123

Docker

bash
docker build -t dataquality-env .
docker run -p 7860:7860 dataquality-env

# Interactive docs at: http://localhost:7860/docs

Run baseline agent

bash
export OPENAI_API_KEY=your_key_here
python baseline/run_baseline.py

# Optional: use a different model
BASELINE_MODEL=gpt-4o python baseline/run_baseline.py

Baseline Scores

Reproducible scores using seed=42, model gpt-4o-mini:

TaskDifficultyBaseline ScoreOracle Score
task_easyEasy~0.781.0000
task_mediumMedium~0.721.0000
task_hardHard~0.551.0000
Average~0.681.0000

Oracle scores obtained by running the deterministic optimal action sequence (verified in tests).

The gap between baseline and oracle on the hard task reflects the genuine difficulty of coordinating 7 simultaneous issue types under a step budget.


Project Structure

dataquality-env/
โ”œโ”€โ”€ dataquality_env/
โ”‚   โ”œโ”€โ”€ __init__.py         # Package exports
โ”‚   โ”œโ”€โ”€ env.py              # DataQualityEnv โ€” step/reset/state
โ”‚   โ”œโ”€โ”€ models.py           # Typed Pydantic models (Action, Observation, Reward, EpisodeState)
โ”‚   โ””โ”€โ”€ tasks.py            # Dataset generators + graders for all 3 tasks
โ”œโ”€โ”€ baseline/
โ”‚   โ””โ”€โ”€ run_baseline.py     # OpenAI-client baseline agent
โ”œโ”€โ”€ tests/
โ”‚   โ””โ”€โ”€ test_env.py         # 30 tests covering spec compliance, actions, graders, rewards
โ”œโ”€โ”€ app.py                  # FastAPI server for HF Spaces / HTTP API
โ”œโ”€โ”€ openenv.yaml            # OpenEnv metadata
โ”œโ”€โ”€ Dockerfile              # Container definition
โ”œโ”€โ”€ requirements.txt
โ”œโ”€โ”€ setup.py
โ””โ”€โ”€ README.md

OpenEnv Spec Compliance

  • โ€”โœ… Typed Action, Observation, Reward, EpisodeState Pydantic models
  • โ€”โœ… step(action) โ†’ (Observation, Reward, bool, Dict)
  • โ€”โœ… reset() โ†’ Observation
  • โ€”โœ… state() โ†’ EpisodeState
  • โ€”โœ… openenv.yaml with full metadata
  • โ€”โœ… 3 tasks with difficulty progression (easy โ†’ medium โ†’ hard)
  • โ€”โœ… Graders produce scores in [0.0, 1.0], deterministic, seed-reproducible
  • โ€”โœ… Meaningful partial-progress reward function (dense, not sparse)
  • โ€”โœ… Baseline inference script using OpenAI API client
  • โ€”โœ… Dockerfile + HF Spaces deployment

License

MIT