Spaces:
Sleeping
Sleeping
| title: Data Cleaning Environment | |
| emoji: π§Ή | |
| colorFrom: blue | |
| colorTo: green | |
| sdk: docker | |
| app_port: 8000 | |
| base_path: /web | |
| # Data Cleaning Environment | |
| A real-world OpenEnv environment where an AI agent learns to clean tabular data | |
| through the standard `step()` / `reset()` / `state()` API. | |
| The agent receives a dirty CSV-style dataset and must apply cleaning operations | |
| step-by-step to maximise a data-quality score (0.0 β 1.0). | |
| --- | |
| ## Tasks | |
| | Task ID | Difficulty | Description | Max Steps | | |
| |----------|------------|-----------------------------------------------------------------|-----------| | |
| | `easy` | Easy | Fix 3 missing values (name / age) in a 5-row table | 10 | | |
| | `medium` | Medium | Remove 2 duplicate rows **and** fix 2 type errors in `age` | 15 | | |
| | `hard` | Hard | Full pipeline: missing values + duplicates + outliers + normalise text | 25 | | |
| --- | |
| ## Action Space | |
| ```json | |
| { | |
| "operation": "<string>", | |
| "column": "<string | null>" | |
| } | |
| ``` | |
| | Operation | Task(s) | Effect | | |
| |---------------------|------------------|-----------------------------------------------------| | |
| | `impute_mean` | easy | Fill numeric `None` with column mean | | |
| | `impute_mode` | easy | Fill string `None` with column mode | | |
| | `drop_missing_rows` | easy / medium | Drop all rows containing any `None` | | |
| | `remove_duplicates` | medium | Drop exact-duplicate rows | | |
| | `fix_type_errors` | medium | Coerce non-numeric values to `float` (or `None`) | | |
| | `remove_outliers` | hard | Drop rows where `price β€ 0` or `price β₯ 500` | | |
| | `normalize_text` | hard | Strip whitespace + title-case all string columns | | |
| | `fill_quantity_mean`| hard | Fill missing `quantity` with column mean | | |
| --- | |
| ## Observation Space | |
| | Field | Type | Description | | |
| |-------------------|---------|-----------------------------------------------------------| | |
| | `current_text` | `str` | Human-readable table of current rows | | |
| | `is_normalized` | `bool` | True when no missing values, duplicates, or outliers left | | |
| | `remaining_typos` | `int` | Composite issue count (missing + dupes + outliers) | | |
| | `html_found` | `bool` | Always `False` (compatibility field) | | |
| | `done` | `bool` | Episode ended (score=1.0 or step limit reached) | | |
| | `reward` | `float` | Ξ quality score from this step (partial progress signal) | | |
| | `metadata` | `dict` | Full row data, per-field diagnostics, valid ops list | | |
| --- | |
| ## Reward Function | |
| ``` | |
| reward = (quality_score_after - quality_score_before) + 0.01 # if improvement | |
| reward = (quality_score_after - quality_score_before) - 0.01 # if no improvement | |
| ``` | |
| Each task has its own grader: | |
| - **Easy**: fraction of the 3 original missing values that are resolved | |
| - **Medium**: average of (dedup score, type-fix score) | |
| - **Hard**: average of 4 sub-scores (no missing, no dupes, no outliers, normalised text) | |
| Invalid operations incur a β0.05 penalty but do **not** end the episode. | |
| --- | |
| ## Baseline Scores | |
| Deterministic rule-based agent (reproducible, seed=42): | |
| | Task | Score | | |
| |--------|-------| | |
| | easy | 1.0 | | |
| | medium | 1.0 | | |
| | hard | 1.0 | | |
| --- | |
| ## API Endpoints | |
| | Method | Path | Description | | |
| |--------|-------------|--------------------------------------------------| | |
| | POST | `/reset` | Start new episode. Body: `{"task":"easy"}` | | |
| | POST | `/step` | Apply action. Body: `{"action":{...}}` | | |
| | GET | `/state` | Current episode state | | |
| | GET | `/schema` | Action + Observation JSON schemas | | |
| | GET | `/tasks` | Task list + action schema per difficulty | | |
| | POST | `/grader` | Score the current episode (0.0β1.0) | | |
| | POST | `/baseline` | Run baseline agent on all 3 tasks | | |
| | GET | `/health` | Health check | | |
| | WS | `/ws` | WebSocket for low-latency persistent sessions | | |
| --- | |
| ## Quick Start | |
| ### 1. Reset to a task | |
| ```bash | |
| curl -X POST http://localhost:8000/reset \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"task": "easy"}' | |
| ``` | |
| ### 2. Apply a cleaning operation | |
| ```bash | |
| curl -X POST http://localhost:8000/step \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"action": {"operation": "impute_mean"}}' | |
| ``` | |
| ### 3. Get current state | |
| ```bash | |
| curl http://localhost:8000/state | |
| ``` | |
| ### 4. Score the episode | |
| ```bash | |
| curl -X POST http://localhost:8000/grader | |
| ``` | |
| ### 5. Run baseline on all tasks | |
| ```bash | |
| curl -X POST http://localhost:8000/baseline | |
| ``` | |
| --- | |
| ## Setup | |
| ### Run locally | |
| ```bash | |
| uv run server | |
| # or | |
| uvicorn server.app:app --reload --host 0.0.0.0 --port 8000 | |
| ``` | |
| ### Run with Docker | |
| ```bash | |
| docker build -t data-cleaning-env:latest -f server/Dockerfile . | |
| docker run -p 8000:8000 data-cleaning-env:latest | |
| ``` | |
| ### Deploy to Hugging Face Spaces | |
| ```bash | |
| openenv push --repo-id your-username/data-cleaning-env | |
| ``` | |
| --- | |
| ## Project Structure | |
| ``` | |
| data_cleaning_env/ | |
| βββ README.md # This file | |
| βββ openenv.yaml # OpenEnv manifest | |
| βββ pyproject.toml # Project metadata & dependencies | |
| βββ uv.lock # Locked dependencies | |
| βββ models.py # Action + Observation Pydantic models | |
| βββ client.py # DataCleaningEnv HTTP/WS client | |
| βββ server/ | |
| βββ app.py # FastAPI app + /tasks /grader /baseline | |
| βββ data_cleaning_env_environment.py # Core environment logic + graders | |
| βββ Dockerfile # Container image | |
| ``` | |
| --- | |
| ## Dependencies | |
| - `openenv-core[core] >= 0.2.2` β OpenEnv runtime | |
| - Python 3.10+ | |
| - No external data dependencies β all datasets are generated synthetically at runtime |