Deepen layers interop via lairs; consolidate onto canonical models (0.7.0)#8
Merged
Merged
Conversation
Bump the minimum didactic to >=0.9.0 and panproto to >=0.56.0, and add lairs>=0.5.0 (resolved from the local sibling via tool.uv.sources until it is published). Importing bead does not import lairs.
Delete the hand-mirrored layers models and the snake<->camel / MirrorIso machinery, and re-point the corpus-record, graph, parse, and resource lenses onto the generated lairs.records models: each lens now returns a typed lairs model (or a small bead-side aggregate of them) as its view, with the bead-only remainder in a JsonValue complement, so the GetPut/PutGet laws still hold. Retire the redundant Node lexicon validator and the vendor/layers submodule; validation is now inherent in the lairs generated models. Migrate the interop tests accordingly.
Register a 'bead' lairs codec (lairs.codecs entry point) that round-trips an ItemCollection through the layers schema; map an Item's standoff spans and relations to span/relation AnnotationLayer records via ITEM_LAYERS; ingest a lairs Corpus into bead models and emit bead data as a corpus (materialize to Arrow/Parquet, commit to a repository, publish to a PDS). Add a 'bead layers' CLI group and a docker-PDS integration test for the publish path (gated behind --run-integration). Make the spaCy tokenizer test skip on any import failure (spaCy's pydantic.v1 raises ConfigError, not ImportError, on Python 3.14).
Rewrite the layers interop docs for the lairs-backed mapping, codec, item bridge, corpus IO, and CLI; drop the retired mirror-model and submodule references from the README and add a note on the --run-integration PDS tests. Bump the version to 0.7.0 with a changelog entry.
lairs 0.5.0 is not yet on PyPI and the pip-based CI jobs ignore the tool.uv.sources path redirect, so check out the public layers-pub/lairs repo and pip-install it before installing bead, satisfying the lairs>=0.5.0 requirement.
lairs 0.5.0 is now published on PyPI, so drop the tool.uv.sources local-path redirect and the CI source-checkout steps; the plain lairs>=0.5.0 requirement resolves from PyPI for both local and CI installs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Deepens the
bead.interop.layersintegration withlairs, the read/writeclient for the layers annotation format. bead now depends on
lairsdirectlyand builds its layers projections from the canonical
lairs.recordsmodels thatlairsgenerates from the layers lexicons, instead of a parallel hand-mirroredcopy of the schema.
The change has two halves:
Consolidation. Deletes the roughly 40 hand-mirrored layers models and the
snake/camel +
MirrorIsomachinery, and re-points the corpus-record, graph,parse, and resource lenses onto the generated
lairs.recordsmodels. Each lensnow returns a typed
lairsmodel (or a small bead-side aggregate of them) as itsview, with the bead-only remainder in a
JsonValuecomplement, so theGetPut/PutGet laws still hold. The redundant Node lexicon validator and the
vendor/layerssubmodule are retired (validation is now inherent in thegenerated models).
New surface. Plugs bead into the lairs ecosystem:
ItemLayersLensmaps anItem's standoff spans and relations to span andrelation
AnnotationLayerrecords (a span anchors bytokenRefSequencewithhead_indextoanchorTokenIndex, a Wikidatalabel_idto aknowledgeRef;a relation carries
ArgumentRefsource and target arguments).BeadCodec, registered on thelairs.codecsentry point, round-trips anItemCollectionthrough the layers schema.lairs.codec("bead")resolves itand
encode(decode(x)) == xis lossless.corpus_ioingests alairs.data.Corpusinto bead models and emits bead dataas a corpus (materialize to Arrow/Parquet, commit to a repository, publish to
a PDS).
bead layersCLI group (encode,decode,materialize, opt-inpublish).Raises the minimum
didacticto>=0.9.0andpanprototo>=0.56.0to matchlairs, and releases 0.7.0. Importingbeaddoes not importlairs; thedependency loads only when
bead.interop.layersis used.The five commits are scoped so the dependency bump is isolated first (so any
didactic 0.9 fallout is bisectable on its own), followed by the consolidation,
the new surface, the docs/release, and a ruff-format pass.
Motivation
lairsgenerates the canonical layers record models from the layers lexicons.Maintaining a second, hand-written copy of those models inside bead was
redundant and had already drifted from the source of truth (for example
Expression.eprint_ref: strversus the generatedeprintRefs: tuple[str, ...]).Consolidating onto
lairs.recordsremoves the duplication and lets beadparticipate in the lairs ecosystem as a first-class codec, rather than only
emitting layers-shaped JSON.
No associated issue.
Type of Change
The breaking change is scoped to the
bead.interop.layerspublic API (added in0.6.0): the hand-mirror model exports are removed and the lens view types change
from dicts to typed
lairsmodels.Checklist
uv run ruff check .anduv run ruff format .uv run pyrightwith no errorsuv run pytest tests/)Testing
uv run pyright bead/: 0 errors.uv run ruff check bead/ tests/anduv run ruff format --check: clean.uv run pytest tests/ -m "not slow_model_training": green. Theslow_model_trainingtests (which train ML models, unrelated to this change)are deselected as usual.
--run-integration).uv run pytest tests/interop/test_layers_publish_integration.py --run-integration: passed. The test stands up a real bluesky PDS in docker,publishes a bead corpus, reads the record back via
PdsClient, and verifiesidempotency. It skips cleanly when docker is unavailable.
import beaddoes not pulllairsintosys.modules, andlairs.codec("bead")resolves the registered codec.