Skip to main content

Safe natural-language-to-SQL: validated, read-only queries across many SQL engines.

Project description

askql — Safe Natural-Language → SQL

Translate plain-English questions into validated, read-only SELECT queries and run them against a relational database. Designed for QA / dev environments only, with defense-in-depth guardrails so an AI agent can never modify data or leak PII.

⚠️ Non-production by design. This system executes only read-only SELECT statements through a least-privilege DB user. Do not point it at a production database without the Phase-3 approval workflow (see ARCHITECTURE.md).


How it works

question ─▶ compress (pick ~10 relevant tables) ─▶ LLM writes SQL
        ─▶ validate (AST guardrails)  ─▶ execute (read-only, rollback, timeout)
        ─▶ markdown/CSV + audit log

Five defense layers: AST validation → read-only DB user → connection hygiene → output caps → audit trail. See ARCHITECTURE.md for the full design and the security hardening applied on top of the source playbook.

Credentials: use whatever your org gives you — the tool runs with any user. A read-only user is a recommended nice-to-have (defense in depth), not a requirement; if the connected user can write, you get a one-line advisory, never a block.


Quick start

# 1. Install (Python 3.11+). Pick your driver extra: [postgres] / [oracle] / [mssql] / [jdbc]
pip install "askql[postgres]"               # in this repo: pip install -e ".[postgres,dev]"

# 2. Scaffold a project in an empty dir — no T2S_HOME needed; askql auto-discovers it from here
cd my-askql-project
askql init                       # creates config/, docs/, .env + a .askql marker
#   edit .env                    # DB_USER / DB_PASSWORD / DB_CONNECTION (host:port/service)
#   edit config/databases.yaml + config/settings.yaml   # dialect + schemas
askql doctor                     # verify connectivity

# 3. Build the metadata pipeline (one command)
askql scrape --schemas public --build-graph     # or: python scripts/scrape_schema.py …

# 4. Ask a question (the orchestrator runs compress→validate→execute)
askql ask "show me the 10 newest active accounts"

# …or drive the steps manually:
python scripts/compress_metadata.py --question "active accounts" --graph docs/schema-graph.json
python scripts/validate_sql.py build/query.sql --max-rows 100
python scripts/execute_sql.py build/query.sql --limit 50

No database handy? Everything except execute runs fully offline, and the executor has an offline render mode (--rows-json) used by the test suite.

Verify your setup any time with the built-in health check:

python scripts/doctor.py        # config, credentials, transport, live connectivity, graph

Run in Docker (zero local install)

The image bundles Python and a JRE, so the universal JDBC transport works without installing Java or native drivers on your machine. Mount your config, .env, and vendor JDBC jars at runtime:

docker build -t askql .

# health check
docker run --rm --env-file .env \
  -v "$PWD/config:/app/config" -v "$PWD/jdbc-drivers:/app/jdbc-drivers" askql doctor

# or via compose
docker compose run --rm askql doctor
docker compose run --rm askql execute build/query.sql --limit 50

Vendor JDBC jars aren't baked into the image (licensing) — drop them in jdbc-drivers/ and they're mounted in. This is the "usable by all" path: one image, any database engine/version.


REST API (connectivity-as-a-service)

Run askql as a service so clients (a web UI, a Slack bot, scripts, other teams) hold no drivers, JVM, or credentials — they just call HTTP. The service applies the same validator, RBAC, read-only execution, and audit.

pip install -e ".[api,postgres,jdbc]"
export T2S_API_KEYS="devkey:alice@corp"       # identity drives RBAC; omit + ALLOW_OPEN for dev
askql-api                                         # or: uvicorn askql.api:app --port 8000
#   or containerized:  docker compose up api
Method & path Auth Purpose
GET /health public liveness
GET /api/v1/databases public registry names (no creds/conn strings)
POST /api/v1/validate required {sql, database?} → guardrail check
POST /api/v1/compress required {question} → focused schema slice
POST /api/v1/query required {sql, database?, max_rows?, format?} → execute read-only
POST /api/v1/ask required {question, database?} → compress→LLM→validate→retry→execute (needs ANTHROPIC_API_KEY)
curl -s -X POST localhost:8000/api/v1/query -H "X-API-Key: devkey" \
  -H "Content-Type: application/json" \
  -d '{"sql":"SELECT first_name, salary FROM HR.EMPLOYEES WHERE ROWNUM <= 3","database":"oracle-prod"}'
# → {"ok":true,"columns":["FIRST_NAME","SALARY"],"rows":[["Steven",24000], ...],"latency_ms":104}

Auth: T2S_API_KEYS="key1:alice@corp,key2:bob@corp" maps a key → identity (→ role in config/access-control.yaml). Writing endpoints refuse to run unless keys are set (or T2S_API_ALLOW_OPEN=true for local dev). A non-SELECT returns 400 before any DB work.

Choosing the model (provider-agnostic / BYOM)

/ask (and askql ask) generates SQL through a pluggable provider — you bring your own model. The safety pipeline is identical regardless of provider; only T2S_LLM_PROVIDER changes. With nothing configured, generation falls back to the BYO-LLM / IDE lane (your Copilot / Claude Code agent does it — no backend model, no key).

T2S_LLM_PROVIDER What it uses Config
anthropic (auto) Anthropic API ANTHROPIC_API_KEY · pip install '.[llm]'
bedrock Claude on AWS AWS creds/role · '.[llm-aws]' · T2S_LLM_MODEL=anthropic.claude-…
vertex Claude on GCP GCP creds · '.[llm-vertex]'
openai OpenAI GPT OPENAI_API_KEY · '.[llm-openai]'
azure-openai Azure OpenAI AZURE_OPENAI_API_KEY/_ENDPOINT/OPENAI_API_VERSION · '.[llm-openai]'
openai-compatible any self-hosted / OSS (vLLM, Ollama, LM Studio, OpenRouter, GitHub Models) T2S_LLM_BASE_URL=… + T2S_LLM_MODEL=… · '.[llm-openai]'
custom your own gateway T2S_LLM_FACTORY=module:callable
unset / none BYO-LLM — IDE agent (Copilot/Claude Code) nothing

Copilot note: Copilot has no server-side completions API, so it can't power the /ask service — but it is the model in the IDE lane (the agent runs the scripts/skill with its own model). For a token-based API in the GitHub/MS ecosystem, use Azure OpenAI or "GitHub Models" via the openai-compatible provider.


Use as a library

askql is a clean, typed, importable package (py.typed) — build it into your own app. The public API is everything in askql.__all__; heavy/optional deps (DB drivers, LLM SDKs, FastAPI) load lazily, so import askql is light.

from askql import validate, compress, execute_sql_text, ask, Settings, load_graph

s = Settings(dialect="postgres", max_rows=100)

# 1) Guardrail check (pure, no DB)
r = validate("SELECT id, name FROM app.users LIMIT 10", settings=s)
assert r.ok, r.errors

# 2) Pick relevant tables for a question
slice_ = compress(load_graph("schema-graph.json"), "active users this week", max_tables=8)

# 3) Full NL->SQL (compress -> LLM -> validate -> retry -> execute), provider via env
result = ask("how many active users this week", settings=s)   # needs an LLM provider configured

Everything is config-injectable (pass Settings / DatabaseEntry / a custom SqlGenerator) — no checked-out repo required. For pip-installed use, set T2S_HOME (config/data root) and/or T2S_DATA_DIR (writable dir for the audit log + caches) so nothing is written next to the installed package. Packaging/publishing details: SHIPPING.md.

Repository map

Path What it is
src/askql/ The library: validator, compressor, executor, drivers, scraper
scripts/ Thin CLI wrappers (the commands the agent/humans call)
config/ settings.yaml, sensitive-columns.yaml, databases.yaml
docs/domain-rules.md Business logic hints the LLM reads
tests/ Offline unit tests (validator / compressor / graph)
.claude/ Claude Code skill + permission settings
CLAUDE.md Operating instructions for the AI agent
QUICKSTART.md Install + first run in your org (≈15 min)
SHIPPING.md How to package, ship, and install (wheel / index / Docker)
PB/ The original source playbook (reference)

Safety rules (non-negotiable)

  • Only SELECT (incl. UNION/INTERSECT/EXCEPT). No DDL/DML, no procedural code.
  • No SELECT * — explicit columns only (COUNT(*) is fine).
  • A row limit is required and capped at max_rows.
  • No system schemas, no sensitive columns (SSN, PASSWORD, …), no dangerous functions.
  • Every execution rolls back, times out, and is audited.

See CLAUDE.md for the agent workflow and tests/ for the enforced behavior.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

askql-0.3.1.tar.gz (77.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

askql-0.3.1-py3-none-any.whl (71.3 kB view details)

Uploaded Python 3

File details

Details for the file askql-0.3.1.tar.gz.

File metadata

  • Download URL: askql-0.3.1.tar.gz
  • Upload date:
  • Size: 77.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for askql-0.3.1.tar.gz
Algorithm Hash digest
SHA256 07608ebbf509d1175a4b58ecf1e7fb84739bffd3f44a7f947f0b60b90293defa
MD5 55d674d9bbb2844ab4a7ff4f4f80bf65
BLAKE2b-256 2a355a1bce92aa27edbe67bb67d4a05fb29db7a80a9372cf73c8492c0f2ec562

See more details on using hashes here.

Provenance

The following attestation bundles were made for askql-0.3.1.tar.gz:

Publisher: release.yml on TestAutomationArchitect/askql

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file askql-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: askql-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 71.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for askql-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 98121a1bc2b14c97e29e6d2c8b641295fba0a249660252c82b2fa1b5d628ba24
MD5 26c662f0286564e6a6e217c8a81935b2
BLAKE2b-256 7ed2c23aa6416837d372926c78af943f24f0a23404d6a9c3c6ce30062c61b165

See more details on using hashes here.

Provenance

The following attestation bundles were made for askql-0.3.1-py3-none-any.whl:

Publisher: release.yml on TestAutomationArchitect/askql

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page