EKOS · RFC 0240 / 0241 · 2026-11-04

BIRD Mini-Dev on EKOS

Text-to-SQL on the BIRD Mini-Dev benchmark (release “Mini-Dev 1024”, SQLite, 500 questions over 11 databases), answered by Claude Haiku 5.5 through OpenCode Zen, with and without the knowledge EKOS compiles from each database. Every number below is read from the saved run reports and the model router’s billing log.

EKOS cell G2 · hold-out
66.9%
execution accuracy, 302 questions · CI 61.9–72.2
Raw DDL baseline A-4k
53.3%
same model, prompt and output limit
Difference
+13.6 pts
95% CI +8.0 to +19.5 · McNemar p < 0.0001
Price per question
$0.00099
EKOS cell, uncached · baseline $0.00067
Total billed
$2.39
2,865 calls · 10,182,225 in / 2,737,094 out tokens
Read with these caveats. G2’s hold-out run came after an analysis of hold-out results (formula_1), so it is not a clean hold-out number; the clean one is cell G, 63.9%. Every EKOS cell measured here ran without BIRD’s column descriptions: a bug in the SQL analyzer dropped every COMMENT ON on quoted identifiers. It is fixed in v1.2.0, and a run with descriptions has not been made yet (section 1). Mini-Dev 1024 is 92% “challenging” questions, so its numbers are not comparable with older Mini-Dev releases.

1 · Source databases and what EKOS compiled from them

Each database is its own EKOS project, built by ekos eval bench prepare. The pipeline reads the database’s catalogue and its dictionary (database_description/*.csv): one generated schema.sql per project, holding CREATE TABLE statements plus one COMMENT ON COLUMN per dictionary row. That file goes through the normal pipeline (build → recover → resolve → compile → commit). Question files and gold SQL never leave the archive. After the build, every ledger is scanned for the text of all 500 questions, gold statements and evidence hints; the scan was clean (1,349 texts, no hit).

DatabaseTablesColumnsRowsMBDictionary rowsLedger objects / edgesLedger fingerprintQuestionsHold-out
california_schools38929,94111.1824 / 235495712d6ca4930
card_games6115803,445261.81087 / 443c6b940e58d6941
codebase_community871740,646481.4719 / 11c063ce1abe1f3119
debit_card_specializing521423,05034.6206 / 1c0523524574a148
european_football_27199222,796597.81038 / 7503457f2e9273119
financial8551,079,68071.3529 / 829f17c9a07514628
formula_11394493,25722.49414 / 195e0d7f2c4bb85533
student_club84842,5112.6489 / 813e0be5f92cf3723
superhero103110,6140.23111 / 98e3bf8c453915030
thrombosis_prediction36415,2527.3614 / 2c7b307cdc1728652
toxicology41136,9222.7115 / 4b39fe42d87803219

The schema text below is exactly what a cell showed the model, printed by ekos eval bench context. Three views per database: the EKOS pack as the measured G2 runs saw it (tables, keys and join paths from the ledger, plus column profiles: rows, nulls, empty strings, distinct values, examples, list columns); the same pack after the v1.2.0 analyzer fix, now carrying the dictionary’s descriptions; and the raw DDL that cells A and A-4k got.

2 · Every hold-out question, the query EKOS generated, and its result

302 hold-out questions. The SQL is cell G2’s answer; ✓ means its result set equals the gold result set (execution accuracy, as BIRD’s official scorer computes it). The A-4k and G columns show whether those cells got the same question right. Each prompt was the template below, with the database’s schema text from section 1, the matched values, and the question. No cell here used BIRD’s evidence hint.

Prompt template used by G and G2 (ekos-v1)
Task Overview:
You are a data science expert. Below, you are provided with a database schema and a natural language question. Your task is to understand the schema and generate a valid SQL query to answer the question.

Database Engine:
{engine}

Database Schema:
{schema}This schema describes the database's structure, including tables, columns, primary keys, foreign keys, and any relevant relationships or constraints. Column comments give each column's meaning, its null and distinct counts, and example values.

Question:
{question_block}

Instructions:
- Return exactly the columns the question asks for, in the order it asks for them, and nothing else.
- The generated query should return all of the information asked in the question without any missing or extra information.
- Before counting or averaging, check the grain of each table from its row and distinct counts: when an entity appears in several rows (e.g. one row per printing or per year), count DISTINCT entities if the question is about entities.
- Use the exact spelling of values shown in the schema comments; a comma-separated list column is matched with LIKE '%value%'.
- Keep computations in the form the question states (e.g. a rate as a count divided by a count).

Output Format:
Give the SQL query first, in one code block:
```sql
-- Your SQL query
```
Then explain it in at most three sentences.
IDDatabaseDifficultyQuestionGenerated SQL (G2)G2Soft-F1A-4kGTokens in / outSeconds

3 · Tokens and money

All metered calls went to Claude Haiku 5.5 through OpenCode Zen ($0.10 per million input tokens, $0.50 per million output tokens). The billing log (data/llm/llm-usage.jsonl) records each call that reached the provider. Repeated identical prompts were served from EKOS’s LLM cache and are not billed, so a report’s processed tokens can exceed its billed tokens.

ModelBilled callsInput tokensOutput tokensCostMean latency
Claude Haiku 5.5 (Zen)2,86510,182,2252,737,094$2.38684.6 s
qwen2.5-coder:3b (local Ollama)668889,540254,521$0 (local GPU)17.9 s

Every run

Cost covers billed calls only. “$ per 1k” is the cost of 1,000 questions at that run’s rate. Runs before the cache accounting was added (the first five) show processed tokens only.

ModelCellSplitnEX %95% CISoft-F1R-VESCallsCachedProcessed in / outBilled in / outCost$ per 1kDurationLatency mean / P95 sNote
Haiku 5.5Ahold10052.042–6253.148.8145—285,414 / 125,511—$0.09130.91311m 49s6.4 / 10.3100-question sample
Haiku 5.5Chold10048.039–5851.245.0146—286,097 / 124,654—$0.09090.90911m 36s6.4 / 10.6100-question sample
Haiku 5.5Bhold10047.038–5747.843.5141—285,066 / 122,310—$0.08970.8978m 28s4.7 / 10.4100-question sample
Haiku 5.5Dhold10046.036–5647.542.1149—299,123 / 127,218—$0.09350.9358m 54s5.1 / 10.7100-question sample
Haiku 5.5C-primehold10053.044–6355.650.3143—269,832 / 122,912—$0.08840.8848m 53s4.5 / 10.4100-question sample
Haiku 5.5Etune19856.650–6463.653.11990895,989 / 222,821895,989 / 222,821$0.20101.01519m 19s5.4 / 9.4
Haiku 5.5Ftune19854.047–6161.850.719988904,682 / 223,835575,753 / 121,260$0.11820.59713m 26s3.3 / 9.1
Haiku 5.5Gtune19865.759–7270.361.52050964,193 / 189,196964,193 / 189,196$0.19100.96519m 50s5.0 / 9.4
Haiku 5.5Atune19845.539–5248.341.82880564,266 / 257,706564,266 / 257,706$0.18530.93624m 21s6.8 / 10.7
Haiku 5.5Htune19864.158–7169.958.46162052,672,408 / 569,9721,708,215 / 380,776$0.36121.82437m 35s10.6 / 20.1
Haiku 5.5G-nvtune19863.657–7068.959.120890971,271 / 188,991621,690 / 104,480$0.11440.57813m 36s3.2 / 9.5
Haiku 5.5Ghold30263.959–6967.759.732001,509,241 / 295,4051,509,241 / 295,405$0.29860.98927m 53s5.1 / 9.4
Haiku 5.5A-4khold30253.348–5957.649.530455572,686 / 350,683468,612 / 308,078$0.20090.66528m 54s5.3 / 10.4
Haiku 5.5G2tune19867.761–7473.862.920567978,518 / 188,935648,344 / 123,659$0.12670.64014m 21s3.4 / 7.9
Haiku 5.5G2hold30266.962–7270.262.03181081,525,399 / 300,615985,856 / 200,425$0.19880.65821m 11s3.7 / 8.7after the formula_1 analysis
qwen2.5-coder:3bAhold1005.01–97.14.7150—194,591 / 56,457—$0.00000.00043m 57s25.8 / 40.6100-question sample
qwen2.5-coder:3bChold1002.00–53.51.9159—207,604 / 60,289—$0.00000.00047m 02s28.1 / 45.6100-question sample
qwen2.5-coder:3bBhold1004.01–86.94.1152—202,315 / 57,918—$0.00000.00034m 39s20.2 / 42.0100-question sample
qwen2.5-coder:3bDhold1003.00–64.63.0159—211,132 / 59,949—$0.00000.00042m 13s25.0 / 64.7100-question sample
qwen2.5-coder:3bC-primehold1003.00–65.53.0155—196,148 / 58,589—$0.00000.00029m 52s17.6 / 38.6100-question sample

4 · Average price per question

Over every Zen run
$0.00085
$2.39 billed for 2,792 answered questions
EKOS cell G, uncached
$0.00099
5,976 tokens per question
EKOS cell G2
$0.00066
108 of 318 calls from the cache
Raw DDL A-4k
$0.00067
3,058 tokens per question

The EKOS prompt is about three times longer on input (1,509,241 against 572,686 input tokens for 302 questions), because the profiles describe every column. Output is the same size, and output carries five times the per-token price, so the EKOS cell costs about 1.5× as much per question: about one tenth of a cent.

5 · Accuracy and speed on the hold-out split

Execution accuracy, 302 questions

020406080100A-4k53.3%G63.9%G266.9%

Bars are execution accuracy; whiskers are the bootstrap 95% interval (2,000 resamples).

Averages

A-4kGG2
Execution accuracy53.3%63.9%66.9%
Soft-F157.667.770.2
R-VES (3 timed runs)49.559.762.0
Answers that executed100%100%100%
Mean latency per question5.3 s5.1 s3.7 s
P95 latency10.4 s9.4 s8.7 s
Wall-clock for 302 questions28m 54s27m 53s21m 11s

By database

DatabasenA-4kGG2
california_schools3037%53%53%
card_games4146%61%66%
codebase_community1953%47%42%
debit_card_specializing8100%100%100%
european_football_21974%84%84%
financial2857%57%57%
formula_13379%55%79%
student_club2343%78%78%
superhero3057%50%47%
thrombosis_prediction5233%65%67%
toxicology1968%95%95%

By difficulty

DifficultynA-4kGG2
challenging27552.4%63.6%67.6%
moderate2161.9%66.7%57.1%
simple666.7%66.7%66.7%

How answers failed

OutcomeA-4kGG2
correct161193202
wrong values998479
wrong rows412420
wrong columns111
exec error000
no sql000

How the EKOS cell was chosen (tune split, 198 questions)

Everything was tuned on a separate tune split (40% of each database × difficulty stratum, fixed seed). The hold-out split was used once per cell.

CellEX %95% CICost
A45.539–52$0.185
F54.047–61$0.118
E56.650–64$0.201
G-nv63.657–70$0.114
H64.158–71$0.361
G65.759–72$0.191
G267.761–74$0.127

A = raw DDL with a 1,024-token output limit · E = EKOS pack + column profiles · F = E + value lookup · G = F + the ekos-v1 prompt + result checks · G-nv = G without value lookup · H = G with three varied-context candidates (voting) · G2 = G + empty-string profiles.

6 · Code added to run the benchmark

6,927 lines in 32 files, all in the public EKOS repository (alexeyban/EKOS, v1.2.0). Nothing outside EKOS is needed to run a benchmark. The official BIRD scorer is used only by the parity test, from a separate checkout, and is never copied into the repository.

FileLines
ekos/crates/cli/src/commands/eval_bench.rs663
ekos/crates/cli/tests/bench.rs246
ekos/crates/evals/src/bench/bird.rs1,191
ekos/crates/evals/src/bench/context.rs1,107
ekos/crates/evals/src/bench/fixture.rs389
ekos/crates/evals/src/bench/leakage.rs265
ekos/crates/evals/src/bench/mod.rs499
ekos/crates/evals/src/bench/report.rs608
ekos/crates/evals/src/bench/split.rs124
ekos/crates/evals/src/bench/sqlexec.rs496
ekos/crates/evals/src/bench/stats.rs137
ekos/crates/evals/src/bench/suite.rs232
ekos/crates/evals/tests/bench_fixture.rs427
ekos/crates/evals/tests/bird_parity.rs170
evals/bench/bird-mini-dev/configs/a.toml17
evals/bench/bird-mini-dev/configs/b.toml17
evals/bench/bird-mini-dev/configs/c-prime.toml17
evals/bench/bird-mini-dev/configs/c.toml17
evals/bench/bird-mini-dev/configs/d.toml17
evals/bench/bird-mini-dev/configs/zen-haiku/a-4k.toml19
evals/bench/bird-mini-dev/configs/zen-haiku/a.toml19
evals/bench/bird-mini-dev/configs/zen-haiku/b.toml19
evals/bench/bird-mini-dev/configs/zen-haiku/c-prime.toml19
evals/bench/bird-mini-dev/configs/zen-haiku/c.toml19
evals/bench/bird-mini-dev/configs/zen-haiku/d.toml19
evals/bench/bird-mini-dev/configs/zen-haiku/e.toml24
evals/bench/bird-mini-dev/configs/zen-haiku/f.toml24
evals/bench/bird-mini-dev/configs/zen-haiku/g-nv.toml24
evals/bench/bird-mini-dev/configs/zen-haiku/g.toml24
evals/bench/bird-mini-dev/configs/zen-haiku/g2.toml25
evals/bench/bird-mini-dev/configs/zen-haiku/h.toml24
evals/bench/bird-mini-dev/suite.toml29
  • bench/mod.rs: the BenchAdapter trait, the experiment cell (ExperimentConfig), the run loop with its budget stop, the ledger fingerprint.
  • bench/bird.rs: the BIRD adapter. It extracts files from the archive by allowlist, generates the project schema, builds the prompt, runs repair rounds and result checks, votes between candidates, scores, and exports BIRD’s prediction format.
  • bench/context.rs: raw DDL, the EKOS pack from a ledger, column profiles, value lookup, and the SQL shape measures (schema-linking and join-path recall).
  • bench/sqlexec.rs: read-only SQLite under a deadline; EX, Soft-F1 and R-VES as the official scorer computes them; SQLite’s math functions.
  • bench/leakage.rs, split.rs, stats.rs, report.rs, suite.rs: the leakage guards, the fixed-seed split, bootstrap and McNemar, reports with the contamination check, and suite files with pinned checksums.
  • cli/commands/eval_bench.rs: ekos eval bench list | prepare | run | context | compare | export.
  • Tests: a two-database, ten-question fixture with a scripted model (bench_fixture.rs); the real binary end to end against a stand-in Ollama server (cli/tests/bench.rs); parity with the official scorer (bird_parity.rs).

One fix outside the benchmark code was needed: recovery/src/sql_analyzer.rs (COMMENT ON on quoted identifiers).

7 · Configuration

The suite pins its downloads by SHA-256. A cell is one TOML file: what the model is shown, the prompt, the generation strategy and the model route.

evals/bench/bird-mini-dev/suite.toml

# RFC 0240 — BIRD Mini-Dev, SQLite, the "Mini-Dev 1024" release (2026-10-28 update).
#
# Questions come from the Hugging Face dataset at a pinned revision; the databases from the
# original Mini-Dev package (the SQLite databases did not change in the update — the README says
# the original dev databases serve the SQLite version). `ekos eval bench prepare` downloads both,
# checks these checksums and extracts only <db>/<db>.sqlite and <db>/database_description/*.csv:
# the package's own question and gold files never leave the archive.
name = "bird-mini-dev"
adapter = "bird"
version = "Mini-Dev 1024"
dialect = "sqlite"
new-dev = false
notes = "birdsql/bird_mini_dev @ 5b256b5a (data/manifest.json revision colleague_review_fixes_20261025)"

[questions]
file = "mini_dev_sqlite.jsonl"
url = "https://huggingface.co/datasets/birdsql/bird_mini_dev/resolve/5b256b5a9c17478f06ed6912612e9a14d17065c8/data/mini_dev_sqlite.jsonl"
sha256 = "926af2a0d1b47e5d0d15a7aa8a34d4721007f4550a881555b6a89496b54a2606"

[databases]
file = "minidev.zip"
url = "https://bird-bench.oss-cn-beijing.aliyuncs.com/minidev.zip"
sha256 = "cc48ba16838204e4e214512030cb572eeb5f7bcdd999bae4b9b6ff12ec13b92f"
prefix = "minidev/MINIDEV/dev_databases/"

# Fixed seed, stratified by database and difficulty: 40% tune / 60% hold-out (RFC 0240 §6).
[split]
seed = 240
tune-fraction = 0.4

evals/bench/bird-mini-dev/configs/zen-haiku/g2.toml

# RFC 0241 — G with profile v2. Motivated by the formula_1 hold-out analysis; validate on the tune split.
name = "G2"
description = "G with profile v2: empty-string counts, and that '' means missing"
context = "ekos-pack"
evidence = false
value-lookup = true
few-shot = 0
n = 1
r = 1
max-tokens = 4096
ves-iterations = 3
timeout-seconds = 30
profiles = true
profile-version = 2
value-lookup-version = 2
prompt = "ekos-v1"
result-checks = true
vary-context = false

[llm]
# OpenCode Zen, Claude Haiku 5.5 ($0.10 / $0.50 per million tokens). Metered: needs --allow-metered.
provider = "zen"
model = "claude-haiku-5-5"
api-key-env = "OPENCODE_API_KEY"
budget-usd = 0.6

evals/bench/bird-mini-dev/configs/zen-haiku/a-4k.toml

# RFC 0241 — the baseline for the final comparison: cell A with max-tokens 4096, so the difference to G/H is EKOS alone.
name = "A-4k"
description = "Cell A (raw DDL, baseline prompt, no evidence, no lookup) with the output allowance of the RFC 0241 cells"
context = "raw-ddl"
evidence = false
value-lookup = false
few-shot = 0
n = 1
r = 1
max-tokens = 4096
ves-iterations = 3
timeout-seconds = 30

[llm]
# OpenCode Zen, Claude Haiku 5.5 ($0.10 / $0.50 per million tokens). Metered: needs --allow-metered.
provider = "zen"
model = "claude-haiku-5-5"
api-key-env = "OPENCODE_API_KEY"
budget-usd = 1.0

Commands, from the repository root

ekos eval bench prepare --suite bird-mini-dev           # download, verify, extract, build 11 projects, leakage scan
ekos eval bench context --suite bird-mini-dev --cell g2 --db formula_1   # print what the model is shown
ekos eval bench run     --suite bird-mini-dev --cell evals/bench/bird-mini-dev/configs/zen-haiku/g2.toml \
                        --split hold --allow-metered    # needs OPENCODE_API_KEY
ekos eval bench compare <report-g2.json> <report-a-4k.json>
ekos eval bench export  <report.json> --target bird-submission
ekos eval history                                       # every run, newest last

8 · How it works, and what it costs to run

Pipeline

  1. Prepare. Check the archive checksums. Extract only <db>.sqlite and its dictionary CSVs. Write one schema.sql per database, from SQLite’s own catalogue and the dictionary. Build one EKOS project per database through the standard pipeline (child processes, with API keys removed, so preparing never calls a model). Record each ledger’s fingerprint, and scan every ledger for leaked benchmark text. Build a column profile and a distinct-value index from the database file; these are kept beside the project, never in a ledger.
  2. Context. For an EKOS cell, the schema text is rendered from the project’s compiled ledger: Table objects with columns, types, keys and descriptions, plus ForeignKey edges as join paths. The column profile is added, and the values the question mentions are looked up.
  3. Generate. One model call with the cell’s prompt. If the SQL does not run, the error goes back once. If it returns no rows or a single NULL (result checks), that fact goes back once. The model is never shown anything about the gold answer.
  4. Score. The predicted and gold SQL run against the same read-only database with a 30-second limit. Result sets are compared with Python’s equality, as the official scorer does (1 == 1.0). Each answer gets an outcome category. Three timed runs feed R-VES.
  5. Report. Each report stores the cell’s configuration and its hash, the prompt hash, ledger fingerprints, the split and a hash of its question ids, and n with a bootstrap interval. It also stores tokens (billed and cached), cost, latency, and every question’s SQL and verdict. compare pairs two runs question by question.

Machine and resources

StepWall clockPeak memory (RSS)Notes
prepare --force, all 11 projects1 min 32 s48 MB35 s user + 36 s system CPU; 1.4 GB of SQLite databases read; value index and profiles rebuilt
run, 30 questions, all model calls cached39 s45 MBThe harness alone: SQL execution, gold execution, 3 timed runs per correct answer
run, cell G2, 302 questions21m 11s≈ 45 MB3.7 s mean per question, mostly waiting on the model
All Zen runs (15)4h 30m16,394,949 tokens processed
All local qwen2.5-coder:3b runs (5 × 100)3h 17mGTX 1650 4 GB; about 30 s per question; 2–5% accuracy

The machine was a 12-core Linux workstation with 62 GB RAM and a 4 GB GTX 1650. The model calls run remotely (Zen) or on the local GPU (Ollama). The harness itself is single-threaded and light.

What the results say