Skip to content

feat(nucleus): Expose dataset_item_id on exported items, annotations and predictions - #473

Open
luke-e-schaefer wants to merge 5 commits into
masterfrom
lukeschaefer/export-dataset-item-id
Open

feat(nucleus): Expose dataset_item_id on exported items, annotations and predictions#473
luke-e-schaefer wants to merge 5 commits into
masterfrom
lukeschaefer/export-dataset-item-id

Conversation

@luke-e-schaefer

@luke-e-schaefer luke-e-schaefer commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Batch exports only ever returned reference_id on the exported item, so keying an exported prediction back to a Nucleus dataset item meant a second lookup.

This is an inconsistency fix, not a new field: the single-item endpoints (/dataset/:id/loc, /refloc, /iloc) already return dataset_item_id, and the public API docs (ApiDocsPage/slices/batch-export.md) already document it in the batch-export response. The batch export was the odd one out.

Pairs with scaleapi PR: luke/nucleus-export-dataset-item-id.

What changed

dataset_item_id now appears everywhere reference_id already does:

  • DatasetItem gains the field, wired through from_json — the single deserialization entry point, so every SDK path that returns an item picks it up, not just exports.
  • Every Annotation / Prediction subclass gains it (box, line, polygon, keypoints, cuboid, category, multicategory, segmentation). convert_export_payload stamps it down from the item exactly as it already does for reference_id.
  • The scene/video export docstring documents it on each track frame.

Read-only semantics

The field is server-assigned, and mirrors how DatasetItem.phash was done:

behaviour
from_json populated
locally constructed None
__eq__ excluded (compare=False)
to_payload absent — uploads unchanged

The __eq__ exclusion matters: without it, every existing test that compares a locally-built annotation against its round-tripped twin would start failing.

Exports from a backend that doesn't return the field leave it None rather than throwing, so this SDK version is safe against an un-upgraded backend.

Verification

The test suite requires live API keys (conftest.py hard-asserts on NUCLEUS_PYTEST_API_KEY), so it could not be run locally. Verified offline instead by driving convert_export_payload directly:

  • every geometry type on both the annotation and prediction paths carries the id
  • a local object still compares equal to its round-tripped self
  • to_payload output is unchanged
  • an item payload with no dataset_item_id yields None everywhere and does not raise

Formatted with the repo-pinned black 23.12.1 (a newer local black introduced unrelated reformatting, which was reverted).

🤖 Generated with Claude Code

Greptile Summary

The PR exposes the server-assigned dataset item ID throughout SDK export objects while preserving existing upload and equality behavior.

  • Adds dataset_item_id deserialization to dataset items, annotations, and predictions.
  • Propagates item-level IDs through batch-export and single-item response conversion.
  • Keeps the field out of upload payloads and positional constructor signatures.
  • Adds offline coverage for propagation, backward compatibility, and read-only semantics.
  • Updates the package version, changelog, and scene-export documentation.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
nucleus/annotation.py Adds a shared keyword-only, read-only dataset item ID field and deserializes it across all annotation types.
nucleus/prediction.py Extends prediction constructors and deserializers with the keyword-only dataset item ID without changing upload payloads.
nucleus/dataset_item.py Adds the server-assigned ID to dataset-item deserialization while excluding it from equality and serialization.
nucleus/utils.py Propagates each enclosing item's dataset item ID to converted annotations and predictions for batch and single-item responses.
tests/test_utils.py Adds offline coverage for ID propagation, absent backend fields, payload exclusion, equality, and keyword-only construction.
nucleus/dataset.py Documents the dataset item ID available on scene and video track frames.
pyproject.toml Bumps the client package version from 0.20.1 to 0.20.2.
CHANGELOG.md Documents the exported dataset item ID and its read-only compatibility semantics.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    A[API response item] -->|dataset_item_id| B[DatasetItem.from_json]
    A --> C[Response converter]
    C -->|stamp item ID| D[Annotation.from_json]
    C -->|stamp item ID| E[Prediction.from_json]
    B --> F[SDK caller]
    D --> F
    E --> F
    B -. omitted .-> G[to_payload]
    D -. omitted .-> G
    E -. omitted .-> G
Loading

Reviews (4): Last reviewed commit: "Merge remote-tracking branch 'origin/mas..." | Re-trigger Greptile

…ctions

Batch exports only ever returned reference_id, so keying an exported
prediction back to a Nucleus dataset item meant a second lookup. The
single-item endpoints (/loc, /refloc, /iloc) already return
dataset_item_id, and the public batch-export docs already document it —
the batch export was the odd one out.

Adds the field everywhere reference_id already appears:

- DatasetItem gains dataset_item_id, wired through from_json (the single
  deserialization entry point, so every SDK path that returns an item
  picks it up).
- Every Annotation/Prediction subclass gains it, stamped down from the
  item by convert_export_payload exactly as reference_id already is.
- Scene/video exports carry it on each track frame.

It is server-assigned and read-only, mirroring DatasetItem.phash:
populated by from_json, None on locally-constructed objects, excluded
from __eq__ so a local object still compares equal to its round-tripped
self, and absent from to_payload so uploads are unchanged. A backend
that does not return it leaves the field None.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@luke-e-schaefer luke-e-schaefer changed the title [DE-XXXX] Expose dataset_item_id on exported items, annotations and predictions feat(nucleus): Expose dataset_item_id on exported items, annotations and predictions Aug 17, 2026
@luke-e-schaefer
luke-e-schaefer marked this pull request as ready for review August 17, 2026 17:04
@luke-e-schaefer luke-e-schaefer self-assigned this Aug 17, 2026
@luke-e-schaefer
luke-e-schaefer requested a review from a team August 17, 2026 17:06
@edwinpav

Copy link
Copy Markdown
Contributor

👀

@edwinpav edwinpav left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Couple comments, aside from that, can / should any unit tests that test these existing functions be made or updated?

Comment thread nucleus/prediction.py Outdated
for ann in payload.get(ANNOTATIONS_KEY, [])
],
reference_id=payload[REFERENCE_ID_KEY],
dataset_item_id=payload.get(DATASET_ITEM_ID_KEY, None),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

does the docs comment above (lines 93-122) need to be updated to include this key? Also do we have to fall back to None here, doesn't seem like we do that in the from_json in dataset_item.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

idt the other docstrings list dataset_item_id since it's server-assigned/read-only and not a user-supplied param (idt we document _task_id)

Comment thread nucleus/prediction.py
Comment thread CHANGELOG.md Outdated
Comment thread nucleus/annotation.py Outdated
Comment thread nucleus/dataset.py Outdated
Comment thread nucleus/dataset_item.py
Comment thread nucleus/utils.py
luke-e-schaefer and others added 4 commits August 18, 2026 09:42
… scene comment

Addresses the mechanical review feedback:
- Replace the 9 repeated `field(default=None, repr=False, compare=False)` +
  comment blocks in annotation.py with a single `_server_assigned_id_field()`
  helper that carries the note once (per edwinpav's suggestion). Behaviour is
  identical; verified from_json populates, to_payload omits, and __eq__ ignores
  the field. (Suppresses pylint invalid-field-call, a false positive for a
  helper that returns a field() into a dataclass body.)
- CHANGELOG 0.20.1 release date 2026-08-14 -> 2026-08-18.
- Make the scene-frame dataset_item_id docstring comment more specific
  ("internally generated id of the frame's dataset item").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ayload lint

- Add offline unit tests (tests/test_utils.py) for the behavior this PR ships,
  which previously had only live-API coverage: convert_export_payload stamps
  dataset_item_id onto every annotation and prediction type, DatasetItem.from_json
  populates it, it's excluded from __eq__ and omitted from to_payload, and a
  backend that omits it yields None without raising.
- Fix the pre-existing build_test failure: the PR's per-type stamping pushed
  convert_export_payload to 56 statements, tripping pylint R0915. Extend the
  function's existing too-many-branches suppression to cover too-many-statements
  (it enumerates every annotation type by design).

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

Addresses the remaining review feedback:
- Make dataset_item_id keyword-only everywhere it is a constructor param: add
  kw_only=True to the _server_assigned_id_field() helper (covers all 9
  annotation dataclasses) and a `*` marker to the 7 prediction __init__s. It is
  server-assigned and dropped by to_payload, so a positional value could only
  silently disagree with what was uploaded — now it must be passed by keyword.
- Standardize the from_json reads to bare `payload.get(DATASET_ITEM_ID_KEY)`
  (the `, None` was redundant and inconsistent with dataset_item.py).
- Stamp dataset_item_id onto annotations in format_dataset_item_response too, so
  the single-item loc/refloc/iloc path carries it like batch exports do.
- Tests for the single-item stamping (present + absent) and the keyword-only
  guard (positional value rejected).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-dataset-item-id

# Conflicts:
#	CHANGELOG.md
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