fiftyone.core.label_schema_docs#

Named label-schema documents: storage, CRUD and tier resolution.

A label schema document is a self-contained, named schema for a dataset. The Schema Manager authors documents and the App’s “schema lens” reads them: viewing a dataset through a schema hides its hidden fields and attributes from the sidebar and from the App’s queries. In FiftyOne the lens is a visibility preference chosen in the client; FiftyOne Enterprise additionally enforces a schema server-side for annotation tasks, which reference a document by id. The @voxel51/label-schemas plugin exposes the CRUD below as operators.

Each document is stored as two SEPARATE halves (kept separable on purpose — visibility can later lift into shared, named policy docs without a migration of label_schema):

{
    "_id": str, "dataset_id": ObjectId, "name": str,
    "description": str, "created_by": str,
    "created_at": int, "updated_at": int, "version": int,

    # The annotation CONTENT: today's ``dataset.label_schema`` entry
    # shape, verbatim per field — byte-compatible with the dataset's
    # own stored schemas so copies/fallbacks are pure synthesis.
    "label_schema": {
        "<field_path>": {type, component, classes, attributes[],
                         read_only, bbox, label, ...},
    },

    # The VISIBILITY policy: structured, no dotted keys.
    "visibility": {
        "default": "explore" | "hidden",     # unlisted fields
        "fields": {
            "<field_path>": {
                "tier": "annotate" | "explore" | "hidden",
                # omitted attribute => "annotate"
                "attributes": {"<attr>": "annotate"|"explore"|"hidden"},
            },
        },
    },
}

Storage is the label_schemas collection (COLLECTION), scoped by dataset_id. Every store call takes the scope explicitly (dataset_id=) or derives it from a loaded dataset (ctx=: any object whose dataset is a fiftyone.core.dataset.Dataset, such as an operator’s execution context).

Resolution rules: an explicit tier wins; label_schema membership defaults a field to annotate; annotate without content degrades to explore; hidden (and a hidden default expanded over the dataset universe) feeds the silent field-exclusion channel. Attribute tiers mask attributes at resolution; bbox/label content knobs stamp the geometry/class-input locks the App applies.

The dataset DEFAULT schema is not a document: synthesize_default() builds it from the dataset’s own stored schemas (content fields annotatable, everything else explore) and it is never persisted.

Copyright 2017-2026, Voxel51, Inc.

Data:

COLLECTION

Mongo collection holding the documents (dataset-scoped by dataset_id).

MAX_LABEL_SCHEMA_BYTES

Upper bound on a document's serialized label_schema content.

PROTECTED_PATHS

Required/default sample and frame fields a schema can NEVER hide.

PROTECTED_ATTRIBUTES

identity (id), tagging (tags), instance linking (index) and the geometry the renderer needs.

LABEL_LIST_KEYS

hidden ATTRIBUTE values are excluded from sample payloads at <field>.<list_key>.<attr> for list types and <field>.<attr> for singular types.

Functions:

is_protected(path[, protected])

Whether a schema can never hide path: it is in protected (protected_paths()) or nested under a protected field, like metadata.width or group.name.

resolve_dataset_id(*[, ctx, dataset_id])

The dataset ObjectId that scopes a call: dataset_id when given, else the id of ctx.dataset.

snapshot_content(dataset)

Today's stored per-field label schemas, copied verbatim.

protected_paths(dataset)

The paths no schema can hide on dataset: PROTECTED_PATHS plus its group field and the group's id and name, which tie a group dataset's slices together.

schema_universe(dataset)

The dataset's top-level field paths (sample fields, plus frames.-prefixed frame fields of a video dataset or of a group dataset's video slices), minus protected paths (protected_paths()) — what a hidden visibility default expands over.

normalize_visibility(raw)

Validated copy of a structured visibility block; invalid entries dropped.

validate_label_schema(raw)

The content block a doc may store: a mapping of field path to a mapping (today's per-field label schema entry shape), JSON-serializable and no larger than MAX_LABEL_SCHEMA_BYTES.

create(*, name[, description, label_schema, ...])

Creates a schema doc; raises ValueError on a duplicate name or invalid content.

list_(*[, ctx, dataset_id])

[{id, name, description, updated_at, version}] for the dataset.

get(doc_id, *[, ctx, dataset_id])

The full public doc (label_schema + visibility), or None.

update(doc_id, *[, name, description, ...])

Partial update; returns the updated public doc or None when it does not exist.

propagate_field(path[, entry, ...])

Propagates a newly created dataset field across every custom schema of the dataset: the schema it was created in (source_doc_id) receives entry as annotate-tier content; every OTHER schema gets an explicit hidden tier for it.

delete(doc_id, *[, ctx, dataset_id])

Deletes a doc; True when a document was removed.

resolve(doc, universe[, protected])

Resolves a schema doc into the render payload.

synthesize_default(dataset)

The virtual "dataset default" doc: the dataset's own stored schemas as content (annotate by membership), everything else explore.

fiftyone.core.label_schema_docs.COLLECTION = 'label_schemas'#

Mongo collection holding the documents (dataset-scoped by dataset_id).

fiftyone.core.label_schema_docs.MAX_LABEL_SCHEMA_BYTES = 1048576#

Upper bound on a document’s serialized label_schema content.

fiftyone.core.label_schema_docs.PROTECTED_PATHS = frozenset({'created_at', 'filepath', 'frames', 'frames.created_at', 'frames.frame_number', 'frames.id', 'frames.last_modified_at', 'id', 'last_modified_at', 'media_reference', 'metadata', 'tags'})#

Required/default sample and frame fields a schema can NEVER hide. The exclusion stage rides with _allow_missing (which skips ExcludeFields.validate), and stripping e.g. filepath from sample docs crashes the server’s sample serialization outright. Their children are protected too (is_protected()): hiding e.g. metadata.width makes the server re-read every sample’s media for its dimensions, and a failed read renders it square at 30 fps.

fiftyone.core.label_schema_docs.is_protected(path: str, protected: frozenset = frozenset({'created_at', 'filepath', 'frames', 'frames.created_at', 'frames.frame_number', 'frames.id', 'frames.last_modified_at', 'id', 'last_modified_at', 'media_reference', 'metadata', 'tags'})) → bool#

Whether a schema can never hide path: it is in protected (protected_paths()) or nested under a protected field, like metadata.width or group.name. Fields under frames are only protected when listed themselves.

fiftyone.core.label_schema_docs.PROTECTED_ATTRIBUTES = frozenset({'bounding_box', 'id', 'index', 'mask', 'mask_path', 'points', 'tags'})#

identity (id), tagging (tags), instance linking (index) and the geometry the renderer needs. A stored hidden tier on one of these normalizes to annotate.

Type:

Label attributes a schema can never hide

fiftyone.core.label_schema_docs.LABEL_LIST_KEYS = {'classifications': 'classifications', 'detections': 'detections', 'keypoints': 'keypoints', 'polylines': 'polylines', 'temporaldetections': 'detections'}#

hidden ATTRIBUTE values are excluded from sample payloads at <field>.<list_key>.<attr> for list types and <field>.<attr> for singular types.

Type:

Label-list db keys per content type

fiftyone.core.label_schema_docs.resolve_dataset_id(*, ctx=None, dataset_id=None) → ObjectId#

The dataset ObjectId that scopes a call: dataset_id when given, else the id of ctx.dataset.

fiftyone.core.label_schema_docs.snapshot_content(dataset) → dict#

Today’s stored per-field label schemas, copied verbatim.

The seed for “New schema from dataset fields”: every stored field schema becomes doc content (= annotate tier by membership). Best-effort {} on any failure.

fiftyone.core.label_schema_docs.protected_paths(dataset) → frozenset#

The paths no schema can hide on dataset: PROTECTED_PATHS plus its group field and the group’s id and name, which tie a group dataset’s slices together.

Parameters:

dataset – a fiftyone.core.dataset.Dataset, or None

Returns:

a frozenset of paths

fiftyone.core.label_schema_docs.schema_universe(dataset) → tuple#

The dataset’s top-level field paths (sample fields, plus frames.-prefixed frame fields of a video dataset or of a group dataset’s video slices), minus protected paths (protected_paths()) — what a hidden visibility default expands over.

The universe must be the dataset’s ACTUAL fields: stored label-schema entries only exist for fields someone configured for annotation, so expanding over them silently missed every other field (a default-hidden schema left segmentations et al fully visible on datasets with no stored schemas). Stored entries remain the fallback when the field schema is unavailable. Protected paths never enter (a born-hidden schema once excluded filepath via a stale stored entry and crashed sample serialization); any failure yields an empty universe, which only narrows wildcard expansion.

fiftyone.core.label_schema_docs.normalize_visibility(raw: Any) → dict#

Validated copy of a structured visibility block; invalid entries dropped.

fiftyone.core.label_schema_docs.validate_label_schema(raw: Any) → dict#

The content block a doc may store: a mapping of field path to a mapping (today’s per-field label schema entry shape), JSON-serializable and no larger than MAX_LABEL_SCHEMA_BYTES. Raises ValueError otherwise.

fiftyone.core.label_schema_docs.create(*, name: str, description: str = '', label_schema: Mapping | None = None, visibility: Mapping | None = None, created_by: str = '', ctx=None, dataset_id=None) → dict#

Creates a schema doc; raises ValueError on a duplicate name or invalid content.

fiftyone.core.label_schema_docs.list_(*, ctx=None, dataset_id=None) → list[dict]#

[{id, name, description, updated_at, version}] for the dataset.

fiftyone.core.label_schema_docs.get(doc_id: str, *, ctx=None, dataset_id=None) → dict | None#

The full public doc (label_schema + visibility), or None.

fiftyone.core.label_schema_docs.update(doc_id: str, *, name: str | None = None, description: str | None = None, label_schema: Mapping | None = None, visibility: Mapping | None = None, expected_version: int | None = None, ctx=None, dataset_id=None) → dict | None#

Partial update; returns the updated public doc or None when it does not exist.

expected_version makes the write conditional: when the stored doc’s version differs (someone else saved since this caller loaded it), nothing is written and ValueError is raised.

fiftyone.core.label_schema_docs.propagate_field(path: str, entry: Mapping | None = None, source_doc_id: str | None = None, *, ctx=None, dataset_id=None) → int#

Propagates a newly created dataset field across every custom schema of the dataset: the schema it was created in (source_doc_id) receives entry as annotate-tier content; every OTHER schema gets an explicit hidden tier for it. Schemas that already model the field (content or an explicit tier) are left untouched. The dataset default (the dataset’s own stored schemas) is written by the field creation itself, not here. Returns the number of docs updated.

fiftyone.core.label_schema_docs.delete(doc_id: str, *, ctx=None, dataset_id=None) → bool#

Deletes a doc; True when a document was removed.

fiftyone.core.label_schema_docs.resolve(doc: Mapping, universe: Iterable[str], protected: Iterable[str] | None = None) → dict#

Resolves a schema doc into the render payload.

Returns {id, name, label_schemas, active, excluded_paths, excluded_attr_paths, excluded_attr_db_paths} where label_schemas is the get_label_schemas-envelope meta map the Annotate sidebar renders from, active is the annotate-tier field list, and the excluded_* lists feed the silent Explore exclusion channels. universe (the dataset’s field paths) expands a hidden default. protected (protected_paths(), default PROTECTED_PATHS) are never hidden.

fiftyone.core.label_schema_docs.synthesize_default(dataset) → dict#

The virtual “dataset default” doc: the dataset’s own stored schemas as content (annotate by membership), everything else explore. A dataset with no stored schemas yields the empty permissive doc. Never persisted — run it through resolve() like any stored doc.