ORA / Documentation
GitHub Discord Open Workspace →

Ora Documentation

The FastAPI of NL2SQL — speak to any database with any LLM in 3 lines.

Quickstart

The simplest possible Ora query — no schema setup, no vector store, no API key tricks:

python
import ora

db = ora.connect("postgresql://localhost/mydb")
result = db.query("Show me top 10 customers by revenue")

# result.dataframe → pandas DataFrame
# result.sql       → the generated SQL
# result.summary   → plain-English explanation
Zero-dependency demo: Works with SQLite and Ollama — no API key required. Run pip install ora-sql and use sqlite:///path/to/db.sqlite.

Installation

shell
# Core install
pip install ora-sql

# With database extras
pip install ora-sql[postgres]     # asyncpg
pip install ora-sql[snowflake]    # snowflake-connector
pip install ora-sql[bigquery]     # google-cloud-bigquery
pip install ora-sql[all]          # everything

# With Langfuse tracing
pip install ora-sql[langfuse]

Requirements: Python 3.10+. Core dependencies: litellm, sqlalchemy 2.0, sqlglot, chromadb, pydantic 2.

LLM Providers

Ora uses LiteLLM under the hood — every provider works identically:

ProviderModelConfig
OpenAIgpt-4o, gpt-4o-miniORA_LLM_PROVIDER=openai
Anthropicclaude-sonnet-4, claude-haiku-3ORA_LLM_PROVIDER=anthropic
AWS BedrockNova Pro, Claude on BedrockORA_LLM_PROVIDER=bedrock
Google Geminigemini-2.5-pro, flashORA_LLM_PROVIDER=gemini
Ollamallama3.3, deepseek-r1ORA_LLM_PROVIDER=ollama
vLLMAny HuggingFace modelORA_LLM_PROVIDER=vllm
python
import ora
from ora.config import OraConfig

# Anthropic Claude
db = ora.connect("postgresql://...", model="claude-sonnet-4")

# Local Ollama — no API key
db = ora.connect("sqlite:///mydb.db", model="ollama/llama3.3")

# Via env vars
# ORA_LLM_MODEL=gpt-4o-mini ORA_LLM_PROVIDER=openai

Database Support

DatabaseConnection StringStatus
PostgreSQLpostgresql://user:pass@host/dbStable
SQLitesqlite:///path/to/db.sqliteStable
MySQLmysql://user:pass@host/dbStable
DuckDBduckdb:///path/to/db.duckdbStable
Snowflakesnowflake://user@account/dbBeta
BigQuerybigquery://project/datasetBeta
Redshiftredshift+psycopg2://user@host/dbPreview
CSV / XLSX / Parquetpath/to/file.csvStable

The Pipeline

Every query runs through a 6-stage pipeline. Each stage is independently configurable and testable:

1
Intent Classification
Routes simple queries to the fast path, complex or cross-source queries to accurate. Uses a lightweight classifier — no LLM call.
2
Schema Linking — CHESS LSH
Prunes your schema from hundreds of columns to ~8 relevant ones using locality-sensitive hashing. Scales to 1,000+ table schemas without token overflow.
3
SQL Generation
Three generators run in parallel (fewshot, plan-based, decompose). A pairwise LLM selector picks the best candidate — CHASE-SQL technique.
4
Self-Correction — ReFoRCE
Four correction layers: syntax → schema validation → execution errors → semantic sanity. Up to 3 attempts per layer before escalation.
5
Execution
Sandboxed, read-only, timeout-enforced. AST firewall rejects any non-SELECT statement before it reaches the database.
6
Result Formatting
Returns a PipelineResult with .dataframe, .sql, .summary (NL), .chart_config, and .follow_ups.

Schema Linking

Ora's schema linker uses the CHESS LSH algorithm (Stanford, 2024) to prune massive schemas before the LLM ever sees them. This is why Ora works at enterprise scale where other tools fail.

python
# Schema pruning happens automatically — but you can inspect it:
result = db.query("top customers by revenue")
print(result.schema_used)
# → SchemaSnapshot(tables=['customers','orders'], columns=8, pruned_from=347)

# Add business context to improve linking accuracy:
db.add_description("customers.lifetime_value", "Total revenue from customer since sign-up")
db.add_metric("revenue", "SUM(orders.total_amount) WHERE status = 'paid'")

Self-Correction

The correction loop runs automatically. It catches ~95% of generation errors before they reach your users:

LayerWhat it catchesMechanism
SyntaxParse errors, bad SQL structureSQLGlot parse + error message fed back to LLM
SchemaHallucinated table/column namesVerify all identifiers exist in DB metadata
ExecutionType mismatches, division by zeroRun query, catch runtime errors, retry with context
SemanticLogically wrong but valid SQLLLM checks if result plausibly answers the question
python
# Configure correction depth
db = ora.connect("postgresql://...",
    max_corrections=5,         # default: 3
    strategy="accurate",        # fast | balanced | accurate
)

Multi-Source Queries

The feature nobody else has shipped. Ora decomposes cross-source questions into sub-queries, runs them in parallel, then synthesises the results inside DuckDB in-memory — no data leaves your environment.

python
with ora.connect(
    sales="postgresql://prod/sales",
    inventory="snowflake://warehouse/inventory",
    staff="headcount.csv",
) as db:
    result = db.ask("which stores have highest revenue per employee?")
    print(result.dataframe)
    # → One DataFrame. Three databases. Zero manual join keys.
How it works: Ora's DecomposeAgent splits the query into sub-problems, runs each against the appropriate source in parallel, then SynthesisAgent joins the results using DuckDB's in-memory engine. The join keys are inferred — you never write them.

Security

Security defaults are non-negotiable and cannot be accidentally disabled:

ControlDefaultOverride
Read-only enforcementAlways onNot possible by design
AST SQL firewallSELECT onlyNot possible by design
Row limit1,000 rowsmax_rows=5000
Query timeout30 secondstimeout_seconds=60
Input sanitizationAlways onNot configurable
PII maskingOffmask_pii=True

Training & Feedback

Ora gets smarter with every query. Successful NL→SQL pairs are stored in the vector store and surfaced as few-shot examples for similar future queries.

python
# Train a specific pair (thumbs-up equivalent)
await db.train_sql(
    question="top 10 stores by revenue last quarter",
    sql="SELECT store, SUM(total) FROM sales WHERE ...",
)

# Bootstrap from real query logs
from ora.integrations.query_log import SnowflakeHarvester
result = await SnowflakeHarvester(db).harvest(lookback_days=30)
# → HarvestResult(queries_scanned=847, examples_added=312, duplicates_skipped=535)

SOUL Layer

After 20+ queries, Ora builds a SOUL profile — a persistent mental model of how each user thinks about their data. This improves generation accuracy for that specific user over time.

SOUL is always in a try/except. It never breaks a query. If SOUL inference fails, it's silently skipped and the pipeline continues normally.
python
# Inspect what SOUL has inferred
profile = await db.get_soul(user_id="ash")
print(profile.cadence)      # "weekly"
print(profile.entity_focus) # ["store", "revenue", "employee"]
print(profile.vocabulary)  # {"revenue": "SUM(total_amount)", ...}

dbt Integration

Load column descriptions, metric definitions, and lineage directly from your dbt project. No dbt-core import required.

python
from ora.integrations.dbt import load_dbt_docs

result = await load_dbt_docs(db, dbt_project_path="/path/to/dbt")
print(result.models_loaded)      # 47
print(result.columns_described)  # 284
print(result.examples_added)    # 47 (auto-generated questions)

MCP Server

Expose Ora as an MCP server — lets Claude, Cursor, and Windsurf query your database directly from the chat interface.

shell
pip install ora-sql
ora serve --mcp --db postgresql://localhost/mydb --port 8080

Then add to your Claude Desktop / Cursor config:

json
{
  "mcpServers": {
    "ora": {
      "command": "ora",
      "args": ["serve", "--mcp", "--db", "postgresql://localhost/mydb"]
    }
  }
}

Workspace UI

shell
ora serve --db postgresql://localhost/mydb --port 8080
# Open http://localhost:8080

The workspace gives you:

  • Ask view — chat-first interface with live execution traces (node-by-node)
  • Data view — interactive knowledge map with FK relationships
  • Tasks view — full history of every query with full execution traces
  • Learn view — training progress, accuracy trend, improvement suggestions

Benchmarks

Ora's target scores with GPT-4o (launch day results):

Spider 1.0 EX
85%+
target: >85%
BIRD EX
65%+
target: >65%
Spider 2.0 EX
40%+
target: >40%
CHESS LSH schema pruning is the key accuracy driver — it ensures the LLM receives only the 8 most relevant columns out of potentially thousands, eliminating the #1 failure mode (wrong table/column selection) in production NL2SQL.

Eval Harness

python
from ora.eval import Evaluator

eval = Evaluator(engine=db, dataset="bird")
results = eval.run(model="gpt-4o", n=100)

print(results.execution_accuracy)  # 0.67
print(results.latency_p95)         # 2.4s
print(results.cost_per_query)      # $0.006
results.save_benchmark_card()      # → benchmark.md (README-ready)

API Reference — connect()

python
ora.connect(
    connection_string: str,         # or named kwargs for multi-source
    model: str = "gpt-4o-mini",
    strategy: str = "balanced",    # fast | balanced | accurate
    max_rows: int = 1000,
    timeout_seconds: int = 30,
    max_corrections: int = 3,
    trace: bool = False,
    mask_pii: bool = False,
) → OraEngine

db.query() / db.ask()

python
result = db.query(
    question: str,
    strategy: str | None = None,  # overrides engine default
) → PipelineResult

# PipelineResult attributes:
result.dataframe    # pandas DataFrame
result.sql          # generated SQL string
result.summary      # plain-English answer
result.follow_ups   # list[str] — suggested next questions
result.chart_config # Chart.js config if applicable
result.trace        # PipelineTrace — timing per stage
result.succeeded    # bool
result.row_count    # int
result.cost_usd     # float — LLM cost for this query

Quick one-liner: ask()

python
from ora import ask

result = ask("top customers by revenue", db="postgresql://localhost/mydb")
print(result.dataframe)

train_sql() / train_docs()

python
await db.train_sql(question="...", sql="...")
await db.train_docs(documentation="...")  # free-text business context

REST API

MethodEndpointDescription
POST/queryRun a NL query, returns JSON result
POST/query/streamSSE stream — live execution trace events
GET/schemaFull schema metadata
GET/schema/graphKnowledge map (nodes + edges)
POST/schema/refreshForce re-introspect database
POST/train/sqlAdd a NL→SQL training pair
GET/healthHealth check + system status
GET/metricsPrometheus metrics endpoint
Apache 2.0 · GitHub · Discord
Open Workspace →