Team Ai
Apppublic

barathSV/sqldebug-env

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

๐Ÿ› ๏ธ SQL Query Debugger โ€” OpenEnv Environment

An agentic environment where AI models learn to diagnose and fix broken SQL queries through iterative feedback.

![OpenEnv](https://github.com/meta-pytorch/OpenEnv) ![HF Space](https://huggingface.co/spaces/barathSV/sqldebug-env) ![Docker](./Dockerfile)


๐ŸŽฏ Why SQL Debugging?

SQL query repair is a high-value, high-frequency real-world task:

  • โ€”Data analysts and engineers debug queries every day in production systems
  • โ€”It has clear, deterministic success/failure criteria (output matches or it doesn't)
  • โ€”Partial progress is meaningful โ€” a query that runs but returns wrong columns is closer to correct than one that errors
  • โ€”Bug taxonomy maps naturally to difficulty levels: wrong joins โ†’ aggregation bugs โ†’ window functions
  • โ€”Directly evaluates an agent's ability to reason about schemas, relational algebra, and query semantics

This environment is novel in the OpenEnv ecosystem and immediately useful for evaluating and training agents that assist data teams.


๐ŸŒ Environment Overview

The agent receives a broken SQL query along with:

  • โ€”The full database schema (CREATE TABLE DDL)
  • โ€”Expected output column names and row count
  • โ€”A sample of the first 3 expected rows (on easy/medium tasks)
  • โ€”Its last query's execution result and any SQL errors
  • โ€”A human-readable hint after 3 failed attempts

The agent submits corrected SELECT queries. Each is executed against an in-memory SQLite database pre-seeded with realistic data. Reward is shaped to give partial credit for incremental progress toward the correct answer.


๐Ÿ“ Action Space

json
{
  "query": "SELECT ... (your corrected SQL)",
  "explanation": "optional string describing what you fixed"
}
  • โ€”Must be a SELECT statement
  • โ€”Write operations (DROP, DELETE, INSERT, etc.) return 0.0 reward with a penalty flag
  • โ€”No length limit; must be valid SQLite syntax

๐Ÿ‘๏ธ Observation Space

json
{
  "task_id": "easy_wrong_join",
  "schema_ddl": "CREATE TABLE employees ...",
  "broken_query": "SELECT e.name, d.name FROM employees e, departments d ...",
  "expected_columns": ["name", "department"],
  "expected_row_count": 3,
  "expected_sample": [{"name": "Alice", "department": "Engineering"}, ...],
  "last_execution_result": {
    "columns": ["name", "department"],
    "rows": [["Alice", "Engineering"]],
    "total_rows": 3
  },
  "last_error": null,
  "step_number": 1,
  "max_steps": 10,
  "hint": null
}

๐Ÿ† Reward Function

Reward is shaped โ€” partial credit accumulates across four components:

ComponentWeightCondition
executes0.1Query runs without SQL error
columns_match0.2Output column names match expected exactly
row_count_match0.2Number of rows matches expected
data_match0.5Full result set matches (order-independent)

Total range: [0.0, 1.0]

Example reward trajectory

Step 1 โ€” Cartesian product bug:    reward = 0.30  (runs + right columns, wrong rows)
Step 2 โ€” Wrong column alias:       reward = 0.80  (runs + right rows, wrong col name)
Step 3 โ€” Correct fix:              reward = 1.00  โœ“ done

๐Ÿ“‹ Tasks

Task 1 โ€” easy_wrong_join ๐ŸŸข Easy

Bug: Missing JOIN condition causes a Cartesian product.

sql
-- โŒ Broken
SELECT e.name, d.name AS department
FROM employees e, departments d
WHERE e.salary > 70000;

-- โœ… Fixed
SELECT e.name, d.name AS department
FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE e.salary > 70000;

Schema: employees, departments (6 employees, 3 departments) Expected: 3 rows โ€” high earners with their department name Broken query reward: 0.30 | Correct reward: 1.00


Task 2 โ€” medium_aggregation_bug ๐ŸŸก Medium

Bugs (multiple):

  1. 1.Missing GROUP BY clause โ€” SUM() has nothing to aggregate over
  2. 2.HAVING applied before any grouping exists
  3. 3.COUNT(o.id) missing an alias, causing column name mismatch
sql
-- โŒ Broken (3 bugs)
SELECT c.name, c.country, SUM(o.quantity * o.unit_price) AS revenue, COUNT(o.id)
FROM orders o JOIN customers c ON o.customer_id = c.id
HAVING revenue > 500
ORDER BY revenue DESC;

-- โœ… Fixed
SELECT c.name, c.country, SUM(o.quantity * o.unit_price) AS revenue, COUNT(o.id) AS order_count
FROM orders o JOIN customers c ON o.customer_id = c.id
GROUP BY c.id, c.name, c.country
HAVING revenue > 500
ORDER BY revenue DESC;

Schema: orders, customers, products (12 orders, 5 customers) Expected: 4 high-revenue customers Broken query reward: 0.10 | Correct reward: 1.00


Task 3 โ€” hard_window_cte ๐Ÿ”ด Hard

Bugs: PARTITION BY and ORDER BY are swapped inside a window function CTE, causing every partition to have exactly 1 member, making the outer WHERE dept_rank > 1 return zero rows.

sql
-- โŒ Broken (swapped PARTITION/ORDER)
WITH ranked AS (
    SELECT e.name, e.department, e.salary, pr.score,
           ROW_NUMBER() OVER (
               PARTITION BY pr.score      -- โŒ should be e.department
               ORDER BY e.department      -- โŒ should be pr.score DESC
           ) AS dept_rank
    FROM employees e
    JOIN performance_reviews pr ON e.id = pr.employee_id
    WHERE pr.review_year = 2023
)
SELECT name, department, salary, score, dept_rank
FROM ranked WHERE dept_rank > 1 ORDER BY department, dept_rank;

-- โœ… Fixed
WITH ranked AS (
    SELECT e.name, e.department, e.salary, pr.score,
           ROW_NUMBER() OVER (
               PARTITION BY e.department
               ORDER BY pr.score DESC
           ) AS dept_rank
    ...

Schema: employees, performance_reviews (10 employees, 3 departments) Expected: 7 rows โ€” non-top performers per department Broken query reward: 0.30 | Correct reward: 1.00


๐Ÿ“Š Baseline Scores

Evaluated with Qwen/Qwen2.5-72B-Instruct via HuggingFace router (10 steps max):

TaskDifficultyScoreSteps to Solve
easy_wrong_join๐ŸŸข Easy1.001
medium_aggregation_bug๐ŸŸก Medium1.002
hard_window_cte๐Ÿ”ด Hard0.804
Average0.93

๐Ÿš€ Setup & Usage

Run locally

bash
cd server
pip install -r requirements.txt
python app.py
# API available at http://localhost:7860

Run with Docker

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

API Reference

bash
# List available tasks
curl http://localhost:7860/tasks

# Start an episode
curl -X POST http://localhost:7860/reset \
  -H "Content-Type: application/json" \
  -d '{"task_id": "easy_wrong_join"}'

# Submit a query
curl -X POST http://localhost:7860/step \
  -H "Content-Type: application/json" \
  -d '{"query": "SELECT e.name, d.name AS department FROM employees e JOIN departments d ON e.department_id = d.id WHERE e.salary > 70000"}'

# Check current state
curl http://localhost:7860/state

# Interactive API docs
open http://localhost:7860/docs

Run inference

bash
export API_KEY=your_key
export API_BASE_URL=https://router.huggingface.co/v1
export MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
export SQLDEBUG_ENV_URL=http://localhost:7860

python inference.py

๐Ÿ“ Project Structure

sqldebug-env/
โ”œโ”€โ”€ Dockerfile              # Docker build (python:3.11-slim, non-root)
โ”œโ”€โ”€ openenv.yaml            # OpenEnv metadata spec
โ”œโ”€โ”€ pyproject.toml          # Python package config with [project.scripts]
โ”œโ”€โ”€ uv.lock                 # Locked dependencies
โ”œโ”€โ”€ inference.py            # Baseline inference script
โ”œโ”€โ”€ local_test.py           # 40-test validation suite
โ”œโ”€โ”€ README.md               # This file
โ””โ”€โ”€ server/
    โ”œโ”€โ”€ app.py              # FastAPI server (reset/step/state/health)
    โ”œโ”€โ”€ environment.py      # Core env: models, grader, task definitions
    โ””โ”€โ”€ requirements.txt    # Server dependencies

โœ… Validation

Run the local test suite (40 tests) before submitting:

bash
python local_test.py
# Results: 40/40 passed โœ“

Built for the Meta PyTorch Hackathon x Scaler School of Technology โ€” Round 1