Skip to content

feat(spartqa): add SpartQA spatial-reasoning benchmark#2072

Open
pachmu wants to merge 2 commits into
NVIDIA-NeMo:mainfrom
pachmu:mbagdasarova/startqa-integr
Open

feat(spartqa): add SpartQA spatial-reasoning benchmark#2072
pachmu wants to merge 2 commits into
NVIDIA-NeMo:mainfrom
pachmu:mbagdasarova/startqa-integr

Conversation

@pachmu

@pachmu pachmu commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

SpartQA — spatial-reasoning benchmark

Adds a native gym port of SpartQA
— a spatial-reasoning QA benchmark where the model is shown a scene-description
query and must return the matching answer phrase.

What it does

Every task is single-turn generation — no agent fork, no judge model.
verify() is pure, rule-based scoring (stdlib re / string only).

The model is prompted to end its response with Final answer: <phrase>.
verify() extracts that phrase, normalizes it, and compares against every
accepted phrase in all_targets:

Outcome reward exact
Extracted phrase == any accepted phrase (normalized) 1.0 true
Extracted phrase contains any accepted phrase (normalized) 1.0 false
No match 0.0 false
Empty / unparsed output 0.0 false

compute_metrics's mean_reward equals corpus-level accuracy (the headline
metric). exact_match_rate and parse_rate are reported alongside for
downstream inspection.

Scoring details

_extract_answer handles:

  • <think>…</think> reasoning-block stripping (reasoning models)
  • Final answer: … / Selected answer: … / Answer: … patterns with
    fallback to last non-empty line when no explicit marker is present
  • Markdown artifacts (**, `, *, bullet prefixes)

Field placement and all_targets survival

The primary accepted answer phrase lives at target (a top-level scalar,
forwarded intact by the nemo-evaluator gym://...protocol=native driver). The
full accepted set (all_targets) is a list and dropped by the driver — it
is mirrored into verifier_metadata (forwarded intact), so verify() always
sees every phrase via the fallback chain:

body.all_targets → body.verifier_metadata["all_targets"] → [body.target]

Contents

  • resources_servers/spartqa/app.pySpartqaResourcesServer (/verify +
    compute_metrics / get_key_metrics) with answer extraction, normalization,
    and multi-target scoring.
  • prepare_spartqa.py — joins mteb/SpartQA (HF datasets) into
    data/spartqa_test.jsonl. Committed data/example.jsonl is the 5-row smoke
    set.
  • configs/spartqa.yaml — server + simple_agent wiring for local
    eval/training.
  • configs/spartqa_serve.yaml — serve-only scorer for nemo-evaluator via the
    gym:// adapter (rule-based verify needs no model on the gym side).
  • requirements.txt — no extra runtime deps; -e nemo-gym[dev] editable
    install only.
  • tests/test_app.py25 tests over _extract_answer, _strip_reasoning,
    _normalize, verify() (exact / contains / empty / multi-target /
    metadata-fallback), compute_metrics, get_key_metrics, _response_text,
    and acceptance parity against every example.jsonl row.

Testing

gym env test --resources-server spartqa
ruff check resources_servers/spartqa/app.py resources_servers/spartqa/prepare_spartqa.py resources_servers/spartqa/tests/

Notes

  • No API keys required — all scoring is rule-based.
  • Reasoning modelsverify() strips leading <think>…</think> blocks
    before extracting the answer, so thinking-model outputs score correctly.
  • verified: false — not yet baselined. Reward-profiling across an
    open-instruct / open-thinking / closed-source model suite is a follow-up
    before flipping to verified: true.
  • Heavy dataset-prep deps (datasets) are opt-in and not in requirements.txt
    — install only when running prepare_spartqa.py.

pachmu added 2 commits July 17, 2026 13:59
Native gym port of SpartQA (mteb/SpartQA) — a spatial-reasoning benchmark
where the model is shown a scene-description query and must return the matching
answer phrase. The scorer is pure rule-based: extract the model's "Final
answer: <phrase>" line, normalize (lowercase, strip punctuation, collapse
whitespace), and compare against every accepted phrase in all_targets (exact
match sets reward 1.0 and exact=true; substring containment sets reward 1.0
with exact=false; no match → 0.0).

- resources_servers/spartqa/app.py — SpartqaResourcesServer.verify() extracts
  the answer via _extract_answer (strips <think> reasoning blocks, matches
  "Final answer:" / "Answer:" patterns with fallbacks, cleans markdown
  artifacts), normalizes, and compares against all_targets (falling back to
  verifier_metadata when the nemo-evaluator native driver drops the top-level
  list field). compute_metrics reports mean_reward (= corpus accuracy),
  exact_match_rate, and parse_rate; get_key_metrics surfaces mean_reward and
  exact_match_rate.
- prepare_spartqa.py joins the MTEB mteb/SpartQA queries/corpus/qrels splits
  into one row per query, porting build_records from byob_spartqa.py. The
  all_targets list is mirrored into verifier_metadata so the native driver can
  forward it intact to verify(). Prompt and scoring logic are ported verbatim
  from byob_spartqa.py so the metric is identical to the BYOB baseline.
- spartqa.yaml wires the server + simple_agent for local eval/training;
  spartqa_serve.yaml serves the scorer for nemo-evaluator via the gym://
  adapter (rule-based verify needs no model on the gym side).
- No extra runtime deps — scoring is pure stdlib. Dataset prep requires the HF
  datasets package (prep-time only, not in requirements.txt).
- 25 unit tests covering _extract_answer, _strip_reasoning, _normalize,
  verify() exact/contains/empty/multi-target/metadata-fallback paths,
  compute_metrics, get_key_metrics, _response_text, and acceptance parity
  against every example.jsonl row.

Signed-off-by: mbagdasarova <mbagdasarova@nvidia.com>
…ce tests

Signed-off-by: mbagdasarova <mbagdasarova@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Jul 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.

@github-actions github-actions Bot added the sla:triage-overdue Review assignment is over the one-business-day SLA label Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sla:triage-overdue Review assignment is over the one-business-day SLA

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant