Skip to content

Add birdbench benchmark and bird_sql resource server - #1098

Merged
gwarmstrong merged 9 commits into
mainfrom
georgea/migrate-gym-birdbench
Apr 20, 2026
Merged

gwarmstrong merged 9 commits into
mainfrom
georgea/migrate-gym-birdbench

Conversation

@gwarmstrong

Copy link
Copy Markdown
Contributor

Migrates the birdbench (BIRD text-to-SQL) benchmark from NeMo Skills into
Gym. Uses execution-based verification against the per-db_id SQLite
databases, matching the Skills evaluator byte-for-byte (including the
"SELECT 1" no-answer filler and the --.*?$-with-DOTALL comment quirk).

Includes

  • resources_servers/bird_sql/ β€” execution-based SQL text-to-SQL resource server
    • Skills BirdEvaluator._extract_answer ported byte-for-byte in
      extract_sql_from_response (CODEBLOCK mode, SELECT 1 filler,
      --.*?$-with-DOTALL comment stripping)
    • SQL execution via asyncio.to_thread under an asyncio.Semaphore
      (max_concurrency=32, sql_execution_timeout_s=30.0), so the server
      works under contention with a vLLM-spawned Ray cluster on multi-node DP
    • Result-set comparison uses the same unordered set(pred) == set(gold) rule
      as Skills
    • Auto-installs BIRD dev DBs (1.4 GB, 11 SQLite files) from
      bird-bench.oss-cn-beijing.aliyuncs.com on server startup via
      ensure_bird_sql(); cached under .bird_sql/
    • compute_metrics() exposes pass@k/accuracy, pass@1[avg-of-k]/accuracy,
      majority@k/accuracy, and per-difficulty breakdowns via
      compute_subset_metrics(field="difficulty") (simple / moderate /
      challenging)
  • benchmarks/birdbench/ β€” benchmark config, prepare.py, prompt template
    • Data source: BIRD dev split (1534 tasks across 11 SQLite databases),
      downloaded from bird-bench.oss-cn-beijing.aliyuncs.com/dev.zip
    • Per-row schema {question, gt_sql, sql_context, difficulty, db_id, id} β€”
      identical to NeMo Skills dataset/birdbench/prepare.py::format_entries
    • sql_context is an iterdump()-style schema + truncated INSERT chain
      (≀10 INSERTs per run, VALUES (...), (...) chains collapsed after 10
      tuples) β€” byte-for-byte port of Skills' regex rules so the prompt context
      matches on both sides

Validated against NeMo Skills

Run on nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16, BIRD dev (1534 tasks),
4 rollouts per task (6136 rollouts per side), sampling
T=1.0 top_p=0.95 top_k=20 max_output_tokens=65536, on draco-oci. Skills'
BirdMetrics doesn't compute pass@k / majority@k β€” the Skills column
below shows pooled per-rollout accuracy, which is equivalent to
pass@1[avg-of-4].

=================================================================================
BIRD exec-based eval | 4 rollouts/task | T=1.0 top_p=0.95 | Nemotron-3-Nano-30B
=================================================================================
Metric                          Skills         Gym        Delta
---------------------------------------------------------------
pass@1[avg-of-4]/accuracy        27.88%      28.67%       +0.8%
simple/pass@1[avg-of-4]          34.89%      35.22%       +0.3%
moderate/pass@1[avg-of-4]        17.62%      19.34%       +1.7%
challenging/pass@1[avg-of-4]     16.03%      16.72%       +0.7%

Port the BIRD text-to-SQL benchmark from NeMo Skills to NeMo Gym:

- `resources_servers/bird_sql/` β€” execution-based resource server that
  runs the model's predicted SQL against per-`db_id` SQLite files from
  the BIRD dev split and compares result sets (unordered set equality).
  Mirrors the `spider2_lite` pattern: Ray remote execution, semaphore-
  bounded concurrency, per-query timeout, auto-download of `dev.zip` on
  first startup.

- `benchmarks/birdbench/` β€” benchmark config + `prepare.py` that
  replicates `nemo_skills/dataset/birdbench/prepare.py` exactly
  (schema dump via `iterdump()` + INSERT truncation) so Skills and
  Gym see identical rows. `prompts/default.yaml` is a char-for-char
  copy of `generic/text_to_sql.yaml`.

Extraction logic is a strict port of `BirdEvaluator._extract_answer`
including the "SELECT 1" no-answer filler and the `--.*?$` comment
quirk, so reward semantics match Skills at the rollout level.

Metrics use `compute_pass_majority_metrics` + `compute_subset_metrics(
field="difficulty")`, producing overall + simple/moderate/challenging
pass@k that map directly onto Skills' `total_acc`/`simple_acc`/
`moderate_acc`/`challenging_acc`.

43 unit tests, 97.83% coverage.

Signed-off-by: gwarmstrong <gwarmstrong@users.noreply.github.com>
Multi-node DP vLLM starts a Ray cluster on the head node at the version
shipped in the container (2.53.0). When Gym's ng_run attaches via
ray.init(), Ray's client-side version check rejects the mismatched pin
(2.52.1) with:

    RuntimeError: Version mismatch: The cluster was started with:
        Ray: 2.53.0
    This process on node ... was started with:
        Ray: 2.52.1

Bumping to ==2.53.0 unblocks DP Gym rollouts.

Signed-off-by: gwarmstrong <gwarmstrong@users.noreply.github.com>
When the bird_sql resource server runs alongside a multi-node DP vLLM,
Gym's ng_run attaches to the Ray cluster vLLM already started. vLLM
engines consume the Ray slots, so our @ray.remote(num_cpus=1) sqlite
tasks queue behind them, exceed the 30s per-query timeout, get
cancelled, and every rollout returns `gold_execution_error`.

SQLite queries are fast, local-only, and don't need cross-process
scheduling. Switching to `asyncio.to_thread` runs them on the event
loop's default thread pool, bounded by the existing concurrency
semaphore. Ray is no longer a dependency of this resource server.

Signed-off-by: gwarmstrong <gwarmstrong@users.noreply.github.com>
Signed-off-by: gwarmstrong <gwarmstrong@users.noreply.github.com>
…license

Regenerate example.jsonl as the first 5 rows of the BIRD dev split
(with baked-in prompt) so it is a faithful subset of the benchmark, then
commit the two artifacts the migration CI expects:

- data/example_metrics.json: produced by ng_prepare_data
  +mode=example_validation against the bird_sql server config.
- data/example_rollouts.jsonl: 5 rollouts (one per example task) sampled
  from the DFW parity run (rollouts per id=0..4).

Also correct the license declared on the example dataset from
"Creative Commons Attribution Non Commercial 4.0" (not in Gym's allowed
license list) to "Creative Commons Attribution-ShareAlike 4.0
International", which is BIRD's actual license.

Signed-off-by: gwarmstrong <gwarmstrong@users.noreply.github.com>
This reverts commit 8bf037e.

Signed-off-by: gwarmstrong <gwarmstrong@users.noreply.github.com>
@copy-pr-bot

copy-pr-bot Bot commented Apr 17, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@cmunley1

Copy link
Copy Markdown
Contributor

looks good, testing this on my end

Comment thread resources_servers/bird_sql/README.md Resolved
Comment thread benchmarks/birdbench/README.md Resolved
Addresses cmunley1's review on PR #1098 (#1098 (comment) / #discussion_r3112927035):
reviewers want copy-pasteable run commands in both the resource server
README and the benchmark README so they can smoke-test the PR without
hunting through skill or recipe files.

- resources_servers/bird_sql/README.md: "Example usage" block with
  ng_run (server + model config) and ng_collect_rollouts targeting
  the 5-row example.jsonl (+num_repeats=1).
- benchmarks/birdbench/README.md: "Running servers" + "Collecting
  rollouts" with the benchmark config and the prepared benchmark
  jsonl (+num_repeats=4), plus a cross-link to the server README's
  smoke-test block.

Both follow the pattern already established in
resources_servers/abstention/README.md.

Signed-off-by: gwarmstrong <gwarmstrong@users.noreply.github.com>
@cmunley1

Copy link
Copy Markdown
Contributor
ng_collect_rollouts     +agent_name=birdbench_bird_sql_simple_agent     +input_jsonl_fpath=resources_servers/bird_sql/data/example.jsonl   
  +output_jsonl_fpath=birdbench_rollouts.jsonl     +limit=1
Clearing output fpath since `resume_from_cache=False`!
Limiting the number of rows to 1
Using `birdbench_bird_sql_simple_agent` for rows that do not already have an agent ref
Repeating rows 1 times (in a pattern of abc to aabbcc)!
Reading rows: 0it [00:00, ?it/s]
INFO:     127.0.0.1:39692 - "GET /global_config_dict_yaml HTTP/1.1" 200 OK
Collecting rollouts: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 1/1 [00:06<00:00,  6.67s/it]
Sorting results to ensure consistent ordering
Computing aggregate metrics
INFO:     127.0.0.1:32700 - "GET /global_config_dict_yaml HTTP/1.1" 200 OK

Key metrics for birdbench_bird_sql_simple_agent:
{
    "mean/input_tokens": 4443.0,
    "mean/output_tokens": 99.0,
    "pass@1[avg-of-1]/accuracy": 100.0,
    "pass@1/accuracy": 100.0,
    "simple/pass@1[avg-of-1]/accuracy": 100.0
}
Finished rollout collection! View results at:
Fully materialized inputs: birdbench_rollouts_materialized_inputs.jsonl
Rollouts: birdbench_rollouts.jsonl
Aggregate metrics: birdbench_rollouts_aggregate_metrics.json

Single rollout test with openai model works for me smoothly

@gwarmstrong
gwarmstrong merged commit de4eba3 into main Apr 20, 2026
6 checks passed
@gwarmstrong
gwarmstrong deleted the georgea/migrate-gym-birdbench branch April 20, 2026 20:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants