Skip to content

update sdk with new adds - #467

Open
luke-e-schaefer wants to merge 13 commits into
masterfrom
update-nuc-sdk-for-new-eval-stuff-pt1
Open

update sdk with new adds#467
luke-e-schaefer wants to merge 13 commits into
masterfrom
update-nuc-sdk-for-new-eval-stuff-pt1

Conversation

@luke-e-schaefer

@luke-e-schaefer luke-e-schaefer commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary — v0.19.0: EvaluationV2, benchmark paradigm

Brings the SDK to parity with the Nucleus EvaluationV2 UI. Evaluation creation is benchmark-scoped only — dataset/slice-scoped creation (an unreleased earlier iteration of this branch) was removed before release as the platform moved to the benchmark paradigm.

Added

  • Benchmarks. create_benchmark() (members from item_ids, (dataset_id, ref_id) items pairs, a slice_id, or a dataset_id — exactly one required; membership frozen at creation), list_benchmarks(), get_benchmark(), update_benchmark(), delete_benchmark(), list_benchmark_items(), plus the Benchmark resource (refresh() / update() / delete() / items() / create_evaluation_v2()) in nucleus/benchmark.py.
  • Benchmark evaluations. create_benchmark_evaluation_v2(benchmark_id, model_run_id, ...) — every benchmark item is scored (uncovered items count as FN, keeping leaderboard scores comparable). EvaluationV2 exposes benchmark_id, rollup_groups, exclusion_rules, exclusion_stats. No only_items_with_predictions / eval-scope slice_id (invalid in the benchmark paradigm; the server rejects the former).
  • Rollup groups. RollupGroup (class_name + labels) as the primary label configuration on benchmark eval create and presets (create/update_evaluation_v2_preset(rollup_groups=...), mutually exclusive with legacy allowed_label_matches; parsed from both key casings). Preset seeding prefers rollup_groups, falling back to legacy matches.
  • Exclusion rules. MetadataExclusionRule / LabelExclusionRule / BoxAreaExclusionRule via exclusion_rules on eval create and presets.
  • Presets. list/create/update/delete_evaluation_v2_preset + EvaluationV2Preset resource; preset= seeds benchmark eval creation (explicit args override).
  • Results. EvaluationV2.charts() (mAP, per-class AP, confusion matrix, PR/F1 curves, TIDE, AP by size) and examples() (paginated TP/FP/FN; match_type optional) with EvaluationV2FilterArgs filtering; cancel() / retry(); Dataset.evaluation_label_schema().
  • Benchmark leaderboards. leaderboard_ranking(metric_type, benchmark_ids, ...) (metrics: MAP_50, MAP_50_95, AP_SMALL/MEDIUM/LARGE, PRECISION, RECALL, F1; scope/collapse) and leaderboard_f1_curve(benchmark_ids, ..., top_n=5).
  • Filter schema discovery. EvaluationV2.filter_schema() / get_evaluation_v2_filter_schema() return the evaluation's filter vocabulary (gt_labels, pred_labels, metadata_fields with inferred value types).
  • New DTOs: EvaluationV2FilterSchema, LeaderboardRankingEntry, LeaderboardF1CurveEntry, BenchmarkItemsPage.

Removed (relative to earlier commits on this branch — never released)

  • create_evaluation_v2 (dataset/slice-scoped), create_evaluations_v2_batch, BatchEvaluationResult, only_items_with_predictions. Reading existing evals (including old slice-scoped ones) still works.

Fixed

  • CI build_test pylint failure (pre-existing W0718 on the per-job catch) — suppressed with a black-stable # pylint: disable-next line.

Tests / Version

  • tests/test_benchmarks.py, tests/test_leaderboard.py, plus rollup/benchmark coverage in the eval/preset test files (59 unit tests, mock-based).
  • pyproject.toml0.19.0; single consolidated CHANGELOG entry (the unreleased 0.18.9 section was folded in).

Server dependency: the leaderboard and filter-schema methods call new REST mirrors (POST /nucleus/leaderboard/ranking, POST /nucleus/leaderboard/f1Curve, GET /nucleus/evaluationsV2/:id/filterSchema) that ship separately in scaleapi. Unit tests pass regardless; live calls 404 until that deploys. All benchmark CRUD / benchmark-eval / rollup-group / preset endpoints are already live (DE-8209).

resolves https://linear.app/scale-epd/issue/DE-8209

Greptile Summary

This PR brings the Python SDK to parity with the Nucleus EvaluationV2 UI under a benchmark paradigm: benchmarks are frozen ground-truth item sets that model runs are evaluated against, enabling comparable leaderboard scores across runs.

  • Benchmarks (nucleus/benchmark.py, NucleusClient): full CRUD (create_benchmark, list_benchmarks, get_benchmark, update_benchmark, delete_benchmark, list_benchmark_items) plus a Benchmark resource object with refresh/update/delete/items/create_evaluation_v2.
  • Benchmark evaluations (create_benchmark_evaluation_v2): supports rollup_groups (new primary label config), legacy allowed_label_matches*, exclusion rules, and preset seeding; adds cancel, retry, filter_schema to EvaluationV2; migrates charts() from GET to POST.
  • Presets, exclusion rules, leaderboard (evaluation_v2_preset.py, evaluation_v2_exclusions.py): new EvaluationV2Preset with per-user CRUD; MetadataExclusionRule, LabelExclusionRule, BoxAreaExclusionRule; leaderboard_ranking and leaderboard_f1_curve client methods backed by new server endpoints.

Confidence Score: 5/5

  • Safe to merge. The change is a well-structured additive surface — new dataclasses, resource methods, and client methods — with 59 mock-based unit tests. No existing behavior is broken; the charts() GET→POST migration matches the server's new requirement.
  • All findings are style or API-usability nits (empty-list fallback in preset parsing, missing BoxAreaExclusionRule guard, update_benchmark unable to clear optional fields, inconsistent key constants). None affect correctness of the live happy-path flows. The core benchmark CRUD, evaluation create, leaderboard, and preset flows are logically correct and covered by tests.
  • nucleus/evaluation_v2_preset.py (allowed_label_matches fallback with or) and nucleus/evaluation_v2_exclusions.py (BoxAreaExclusionRule missing bound guard) are worth a second look before the next SDK release, but neither blocks this one.

Important Files Changed

Filename Overview
nucleus/benchmark.py New file — clean Benchmark dataclass with from_json, refresh, update, delete, items, and create_evaluation_v2 instance methods that delegate to NucleusClient; no logic issues found.
nucleus/evaluation_v2.py Adds RollupGroup, _parse_rollup_groups, _parse_allowed_label_matches, _parse_json_field helpers; extends EvaluationV2 with benchmark_id, rollup_groups, exclusion_rules, exclusion_stats; adds cancel, retry, filter_schema; migrates charts from GET+QS to POST; makes match_type optional in examples. Logic is sound.
nucleus/evaluation_v2_exclusions.py New file with MetadataExclusionRule, LabelExclusionRule, BoxAreaExclusionRule. BoxAreaExclusionRule lacks the client-side guard its docstring implies ("at least one bound required"), so an empty rule reaches the server before failing.
nucleus/evaluation_v2_preset.py New EvaluationV2Preset dataclass with from_json, update, delete; handles both camelCase/snake_case response keys and JSON-encoded fields. allowed_label_matches fallback uses or rather than an explicit is not None check, which incorrectly treats an empty list as absent.
nucleus/init.py Large addition of benchmark CRUD, benchmark evaluation create, preset CRUD, and leaderboard methods. update_benchmark cannot clear optional fields (None-guard silently no-ops); create_benchmark_evaluation_v2 uses inline string literals for legacy label-match keys where constants exist.
nucleus/data_transfer_object/evaluation_v2.py Adds EvaluationV2FilterSchema, LeaderboardRankingEntry, LeaderboardF1CurveEntry, BenchmarkItemsPage; extends filter args with gt_area_range and slice_ids. Required fields on schema models look correct.

Sequence Diagram

sequenceDiagram
    participant User
    participant NucleusClient
    participant API

    User->>NucleusClient: "create_benchmark(name, slice_id=...)"
    NucleusClient->>API: POST /benchmarks
    API-->>NucleusClient: "{benchmark_id, ...}"
    NucleusClient-->>User: Benchmark

    User->>NucleusClient: "create_benchmark_evaluation_v2(benchmark_id, model_run_id, rollup_groups=..., preset=...)"
    Note over NucleusClient: Seed from preset if no explicit label config
    NucleusClient->>API: "POST /benchmarks/{id}/evaluationsV2"
    API-->>NucleusClient: "{evaluation_id}"
    NucleusClient->>API: "GET /evaluationsV2/{evaluation_id}"
    API-->>NucleusClient: EvaluationV2 payload
    NucleusClient-->>User: EvaluationV2

    User->>NucleusClient: evaluation.wait_for_completion()
    loop Poll status
        NucleusClient->>API: "GET /evaluationsV2/{id}"
        API-->>NucleusClient: status
    end

    User->>NucleusClient: "evaluation.charts(iou_threshold=0.5)"
    NucleusClient->>API: "POST /evaluationsV2/{id}/charts"
    API-->>NucleusClient: EvaluationV2Charts

    User->>NucleusClient: leaderboard_ranking(metric_type, benchmark_ids)
    NucleusClient->>API: POST /leaderboard/ranking
    API-->>NucleusClient: List[LeaderboardRankingEntry]
    NucleusClient-->>User: ranked entries
Loading

Reviews (10): Last reviewed commit: "Route benchmark eval create through the ..." | Re-trigger Greptile

@luke-e-schaefer
luke-e-schaefer requested a review from edwinpav June 25, 2026 21:41
@luke-e-schaefer luke-e-schaefer self-assigned this Jun 25, 2026
@luke-e-schaefer
luke-e-schaefer requested a review from vinay553 June 25, 2026 21:42
Comment thread nucleus/evaluation_v2_preset.py Outdated
Comment thread nucleus/__init__.py Outdated
luke-e-schaefer and others added 4 commits June 25, 2026 17:41
Benchmark-paradigm parity with the EvaluationV2 UI:
- Benchmark resource + client CRUD (create from item_ids/items/slice/dataset,
  list, get, update, delete, paginated items)
- create_benchmark_evaluation_v2 (uncovered items score as FN; label config
  via rollup_groups / legacy matches / preset)
- RollupGroup as the primary label configuration, wired through presets
- EvaluationV2 exposes benchmark_id + rollup_groups
- Benchmark leaderboard_ranking / leaderboard_f1_curve and evaluation
  filter_schema (require the scaleapi REST mirrors to be deployed)
- v0.19.0 + changelog

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pre-existing W0718 on the batch-create per-job catch made pylint exit
nonzero. Use disable-next on its own line so black's 79-char wrap can't
displace the pragma.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The platform moved to the benchmark paradigm; dataset/slice-scoped eval
creation never shipped. Removes create_evaluation_v2,
create_evaluations_v2_batch, BatchEvaluationResult, and
only_items_with_predictions from the SDK surface, and consolidates the
unreleased 0.18.9 changelog section into the single 0.19.0 entry.
Reading old evals (slice_id, exclusion fields) still works.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Same pass as the weights branch: the docstrings are published API docs, so
they shouldn't explain how the platform stores or validates things.

- `evaluation_v2_exclusions`: dropped the reference to the server-side
  validator by name (`parseEvaluationV2ExclusionRulesWithDiagnostics`) — a
  TypeScript function name has no business in the Python docs — and said what
  a caller can actually observe: invalid rules are reported with a reason
  instead of silently excluding nothing.
- `_parse_json_field`: no longer describes JSONB columns, raw DB rows, or
  driver behaviour; it just normalizes a field that may arrive as a string.
- `_parse_allowed_label_matches` / `_parse_rollup_groups`: made private and
  reworded off "the shapes the backend may return". They had public names, so
  autoapi would have published these internal parsers regardless of wording.
- Reworded the leftover storage vocabulary in user-visible text: "row offset"
  → "offset", "one row of a ranking" → "one entry", "match rows" → "results".
- CHANGELOG: "requires a scaleapi server with the REST leaderboard endpoints
  deployed" → "requires a Nucleus deployment with leaderboard support". The
  constraint is real and worth keeping; the internals aren't.

pylint 10.00/10, mypy clean, ruff/black/isort clean, 59 tests passing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@jaypsiri jaypsiri left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had a few questions, but mostly looks good to me! I would defer to @vinay553 and @edwinpav's review on this though

Comment thread nucleus/evaluation_v2_exclusions.py Outdated
Comment thread nucleus/benchmark.py
Comment thread nucleus/dataset.py Outdated
luke-e-schaefer and others added 4 commits August 7, 2026 17:56
Master's #468 (DE-8304) made uploads async-only and deleted
nucleus/upload_response.py; this branch predated it, so every test
fixture that appended items failed at the old sync-upload assertion.

Resolve the CHANGELOG collision (both sides claimed 0.19.0) by releasing
this feature set as 0.19.1 and bump pyproject.toml to match.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ey constants

- evaluation_v2_exclusions: ExclusionScope / ExclusionTarget / MetadataOp are
  now Literal unions instead of bare str aliases, so the valid values are
  checked rather than only documented in a comment. StrEnum is unavailable
  here (the package supports Python 3.10).
- dataset.evaluation_label_schema: use the existing NucleusClient.get()
  wrapper instead of a raw make_request(..., requests.get).
- constants: add the Evaluation V2 / benchmark / preset / leaderboard payload
  keys and reference them from the new from_json / to_api_dict / request
  builders, per CLAUDE.md ('All API payload keys are constants here').
  Where the backend takes camelCase on request but returns snake_case, both
  spellings get a constant (_CAMEL_ infix).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
evaluation_label_schema() now routes through NucleusClient.get(), which
passes requests_command by keyword, so the test's positional args[2] lookup
raised IndexError. Assert on call.kwargs instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Consistency pass over the new Evaluation V2 / benchmark surface:

- create_benchmark_evaluation_v2 used a bare make_request for what is a
  plain POST; it now uses self.post() like every other new call site, and
  reads the response id via EVALUATION_ID_KEY.
- Tests for wrapper-routed calls now all use the same idiom the rest of the
  file uses: mock client.connection.<verb>, then unpack (payload, route)
  from call_args[0]. This replaces the one-off kwargs assertion added for
  the label-schema test.
- Drop _stub_create and the empty 'Batch create' header, dead since the
  batch-create surface was removed from this branch.

The four remaining make_request calls (delete_evaluation_v2_preset,
delete_benchmark, EvaluationV2.delete / .cancel) stay as they are: they all
pass return_raw_response=True, which the Connection wrappers cannot express.
Their tests keep the positional-args idiom, matching the call they assert on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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