CoolFace
Apppublic

aananda/sql-agent-env

sourceHugging Facemitupdated 5mo agoView on Hugging Face
0likes
App README

๐Ÿ—„๏ธ SQL Agent OpenEnv

![OpenEnv](https://github.com/openenv) ![HF Space](https://huggingface.co/spaces)

An OpenEnv-compliant reinforcement learning environment for training and evaluating AI agents on SQL query generation โ€” one of the most practically important tasks in data engineering.

Agents must write correct SQLite queries from natural-language descriptions, iterating with exploratory queries before submitting a final answer.


๐ŸŽฏ Why SQL Generation?

Text-to-SQL is a real, high-value industry problem. Enterprises spend millions on analysts writing SQL by hand. This environment provides:

  • โ€”Deterministic grading โ€” SQL result sets are objectively comparable
  • โ€”Real-world schemas โ€” e-commerce, SaaS analytics, and financial data
  • โ€”Meaningful partial credit โ€” F1-score on result set overlap
  • โ€”Progressive difficulty โ€” from single JOIN to window functions

๐Ÿ“ Environment Design

Action Space

json
{
  "mode": "sql",
  "query": "SELECT name FROM customers WHERE ..."
}

Two modes:

  • โ€”`sql`: Run any SQL against the task database. Results returned immediately. Reward is discounted (0.5ร—) to encourage exploration before committing.
  • โ€”`submit`: Mark final answer. Full grader runs and episode ends.

Observation Space

json
{
  "task_id":          "task_1_easy",
  "task_description": "## Task: ...",
  "difficulty":       "easy",
  "schema_info":      { "customers": ["id INTEGER", ...] },
  "sample_data":      { "customers": [{...}, ...] },
  "last_query":       "SELECT ...",
  "last_result":      { "columns": [...], "rows": [[...]], "row_count": 3, "error": null },
  "steps_taken":      2,
  "max_steps":        10,
  "hint":             null,
  "done":             false
}

Reward Function

SituationReward
Exploration query โ€” full match0.5
Exploration query โ€” partial match (F1)0 โ€“ 0.4
Exploration query โ€” SQL error0.0
Submit โ€” full match1.0
Submit โ€” partial match (F1)0 โ€“ 0.8
Steps exhausted without submitEpisode ends, 0.0

After 3+ consecutive failed attempts, a natural-language hint is injected into the observation.


๐Ÿ“‹ Tasks

Task 1 โ€” Easy: Customers With Orders

Schema: customers, orders Goal: Return name & email of every customer with โ‰ฅ 1 order (any status). Challenge: Simple JOIN + DISTINCT. Filter out non-ordering customers. Expected baseline score (gpt-4o-mini): ~0.95

Task 2 โ€” Medium: Plan-level Usage Report

Schema: users, events Goal: Per plan, compute total events since 2024-01-01 and avg per user (including 0-event users). Challenge: LEFT JOIN to preserve zero-event users, date filtering, GROUP BY + ROUND. Expected baseline score (gpt-4o-mini): ~0.72

Task 3 โ€” Hard: Account Balance Milestones

Schema: accounts, transactions Goal: Per account, find the first date running balance exceeded 5000 (NULL if never) + total credits. Challenge: Window functions (SUM OVER), CTEs, LEFT JOIN for NULL milestones. Expected baseline score (gpt-4o-mini): ~0.48


๐Ÿš€ Setup

Local Development

bash
git clone <repo-url>
cd sql-agent-env
pip install -r requirements.txt
uvicorn app.main:app --host 0.0.0.0 --port 7860 --reload

Docker

bash
docker build -t sql-agent-env .
docker run -p 7860:7860 sql-agent-env

Docker Compose (env + inference together)

bash
export HF_TOKEN=sk-...
export MODEL_NAME=gpt-4o-mini
export API_BASE_URL=https://api.openai.com/v1
docker compose up

Running the Baseline Manually

bash
export API_BASE_URL=https://api.openai.com/v1
export MODEL_NAME=gpt-4o-mini
export HF_TOKEN=sk-...
export ENV_BASE_URL=http://localhost:7860

python inference.py

๐Ÿ”Œ API Reference

EndpointMethodDescription
/healthGETLiveness check
/tasksGETList all tasks with descriptions
/reset?task_id=task_1_easyPOSTStart new episode, returns session_id
/stepPOSTSubmit action (header: session-id)
/stateGETFull episode state (header: session-id)
/sessionDELETEClean up session

Quick Example

python
import httpx

http = httpx.Client(base_url="http://localhost:7860")

# 1. Start episode
r = http.post("/reset", params={"task_id": "task_1_easy"})
session_id = r.json()["session_id"]

# 2. Explore
r = http.post("/step",
    json={"mode": "sql", "query": "SELECT * FROM customers LIMIT 3"},
    headers={"session-id": session_id})
print(r.json()["observation"]["last_result"])

# 3. Submit final answer
r = http.post("/step",
    json={"mode": "submit",
          "query": "SELECT DISTINCT c.name, c.email FROM customers c JOIN orders o ON c.id = o.customer_id"},
    headers={"session-id": session_id})
print(r.json()["reward"])  # {"score": 1.0, "feedback": "Perfect!", ...}

๐Ÿ“Š Baseline Scores

Scores with gpt-4o-mini at temperature=0:

TaskDifficultyScore
task1easyeasy0.95
task2mediummedium0.72
task3hardhard0.48
Average0.72

๐Ÿ—๏ธ Project Structure

sql-agent-env/
โ”œโ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”œโ”€โ”€ main.py          # FastAPI app & OpenEnv HTTP endpoints
โ”‚   โ”œโ”€โ”€ environment.py   # Episode logic & in-memory SQLite session management
โ”‚   โ”œโ”€โ”€ models.py        # Pydantic: Action, Observation, Reward, State
โ”‚   โ””โ”€โ”€ tasks.py         # Task schemas, seed data, grader functions
โ”œโ”€โ”€ inference.py         # Baseline inference script (START/STEP/END logging)
โ”œโ”€โ”€ openenv.yaml         # OpenEnv spec metadata
โ”œโ”€โ”€ docker-compose.yml   # Local dev: env + inference runner
โ”œโ”€โ”€ Dockerfile
โ”œโ”€โ”€ requirements.txt
โ””โ”€โ”€ README.md

โš–๏ธ License

MIT