Skip to main content
This is a clean fit for structured output: you send the database schema and a question, and you get back a JSON object with the query. Because the output is schema-constrained, you can pull the sql field straight out and execute it. The example uses the OpenAI SDK pointed at the gateway, so the same code runs against any model. The example uses an in-memory SQLite database so it runs as-is.
python app.py

You › Who are the two highest-paid people in Engineering?

SQL › SELECT name, salary FROM employees WHERE department = ‘Engineering’ ORDER BY salary DESC LIMIT 2;

[(‘Dan’, 151000), (‘Alice’, 145000)]

The assistant

import os, json, sqlite3
from openai import OpenAI

client = OpenAI(
    base_url="https://api.opper.ai/v3/compat",
    api_key=os.environ["OPPER_API_KEY"],
)

# A sample database
db = sqlite3.connect(":memory:")
db.executescript("""
CREATE TABLE employees (id INTEGER, name TEXT, department TEXT, salary INTEGER, hire_date TEXT);
INSERT INTO employees VALUES
 (1,'Alice','Engineering',145000,'2021-03-01'),
 (2,'Bjorn','Engineering',132000,'2022-07-15'),
 (3,'Chloe','Sales',98000,'2020-01-10'),
 (4,'Dan','Engineering',151000,'2019-11-20'),
 (5,'Eva','Sales',105000,'2023-02-05');
""")

def schema(con):
    rows = con.execute("SELECT sql FROM sqlite_master WHERE type='table'").fetchall()
    return "\n".join(r[0] for r in rows)

QUERY_SCHEMA = {
    "type": "object",
    "properties": {
        "reasoning": {"type": "string", "description": "Brief reasoning about how to answer."},
        "sql": {"type": "string", "description": "A single valid SQLite query."},
    },
    "required": ["reasoning", "sql"],
}

def to_sql(question, db_schema):
    r = client.chat.completions.create(
        model="openai/gpt-5-mini",
        messages=[
            {"role": "system", "content": "Given a SQLite schema and a question, write one SQLite query that answers it. Return only the query in `sql`."},
            {"role": "user", "content": f"Schema:\n{db_schema}\n\nQuestion: {question}"},
        ],
        response_format={"type": "json_schema", "json_schema": {"name": "query", "schema": QUERY_SCHEMA}},
    )
    return json.loads(r.choices[0].message.content)

question = "Who are the two highest-paid people in Engineering? Show name and salary."
out = to_sql(question, schema(db))

print("SQL:", out["sql"])
print("Rows:", db.execute(out["sql"]).fetchall())
Run it:
pip install openai
export OPPER_API_KEY="your-api-key"
python app.py

How it works

  • The schema forces the model to return a sql string (plus its reasoning). With structured output you parse the response once and run out["sql"] directly, no regex.
  • Passing the live schema(db) means the model writes queries against your real tables, not guessed ones.
  • The reasoning field is optional but useful: it gives you a plain-English trace of why the query looks the way it does, which shows up on the call’s trace.

Make it more robust

  • Retry on error. Catch SQLite exceptions, send the error message back as input, and ask for a corrected query.
  • Read-only safety. Run the generated SQL against a read-only connection so a bad query can’t modify data.
  • Score it. Add an Observe rule that checks the query ran and returned rows.

What’s next

Structured output

JSON Schema, enums, and field descriptions.

Route

Set a default model per project without touching code.

Streaming

Stream the query as it generates.

Observe

Score generated SQL automatically.