bead.interop¶
Lossless, law-verified interoperability mappings between bead models and the
layers linguistic-annotation schema. The
layers subpackage maps bead's corpus, annotation, and resource data to and from
the canonical lairs.records models via didactic lenses (dx.Iso / dx.Lens);
round-trip fidelity is guaranteed by the GetPut/PutGet lens laws.
See the Layers Interoperability guide for usage.
Bridge lenses (bead-native <-> layers)¶
graph_lens
¶
Lens between a CorpusGraph and the layers property graph.
The lens projects a :class:~bead.corpus.graph.CorpusGraph to a
:class:CorpusGraphLayers view (a bundle of canonical
:class:lairs.records.expression.Expression and
:class:lairs.records.graph.GraphNode records plus a
:class:lairs.records.graph.GraphEdgeSet) and keeps a complement holding the
information layers' graph does not express directly (the bead framework
identity, the bead node ids, edge directedness, and exact float confidence).
Together the view and complement reconstruct the graph exactly.
CorpusGraphLayers
¶
Bases: Model
A layers view of a corpus graph: expressions, graph nodes, and an edge set.
The records are ordered tuples; the bead node ids that key them live in the
lens complement (layers records carry no bead node id). An empty graph
projects to empty tuples and an empty edge set.
CorpusGraphLayersLens
¶
Bases: Lens[CorpusGraph, CorpusGraphLayers, JsonValue]
Lossless lens CorpusGraph <-> (layers graph view, bead complement).
graph_to_layers(graph_in: CorpusGraph) -> CorpusGraphLayers
¶
Return the standalone layers-shaped view of a corpus graph.
bridges
¶
Bridge lens between a bead CorpusRecord and a layers expression.
A :class:~bead.corpus.records.CorpusRecord projects to a canonical
:class:lairs.records.expression.Expression; the lens complement holds the
bead-only remainder (framework identity, the source name, and the record index,
which layers has no slot for), so the round-trip is exact and the
GetPut/PutGet laws hold.
RecordExpressionLens
¶
Bases: Lens[CorpusRecord, Expression, JsonValue]
Lossless lens CorpusRecord <-> (layers expression, complement).
record_to_expression(record: CorpusRecord) -> expression.Expression
¶
Return the standalone layers expression view of a corpus record.
parse_lens
¶
Lossless iso between a dependency parse and layers annotation records.
A :class:~bead.tokenization.parsers.ParsedSentence maps to a
:class:ParsedSentenceLayers view: a canonical
:class:lairs.records.segmentation.Tokenization plus two
:class:lairs.records.annotation.AnnotationLayer records (a part-of-speech
token-tag layer and a dependency relation layer).
ParsedToken/ParsedSentence carry no framework identity, so the mapping is
a true bijection (dx.Iso).
The layers token has no spaceAfter slot, so each token's space-after flag
travels in its part-of-speech annotation's features alongside the lemma, xpos,
and morphology. The annotation layers require an expression reference and a
timestamp that the parse does not carry; these are emitted as fixed constants and
ignored on the way back.
ParsedSentenceLayers
¶
Bases: Model
A layers view of a parse: a tokenization plus pos and dependency layers.
ParsedSentenceLayersIso
¶
Bases: Iso[ParsedSentence, ParsedSentenceLayers]
Lossless ParsedSentence <-> layers tokenization + annotation layers.
parse_to_layers(sentence: ParsedSentence) -> ParsedSentenceLayers
¶
Return the layers tokenization + annotation-layer view of a parse.
item_bridge
¶
Lens between a bead Item and canonical layers annotation records.
A bead :class:~bead.items.item.Item carries standoff span and relation
annotations over one or more rendered elements. This lens projects an item to a
:class:lairs.integrations.codecs.CorpusFragment of canonical lairs records:
- one :class:
lairs.records.expression.Expressionper rendered element, - one :class:
lairs.records.segmentation.Segmentationper tokenized element, - one span :class:
lairs.records.annotation.AnnotationLayerper element whose spans anchor bytokenRefSequence(head_indexmaps toanchorTokenIndex; a Wikidatalabel_idmaps to aknowledgeRef), - one relation
AnnotationLayerwhose annotations carryArgumentRefsource/targetobjectRefarguments.
The layers token has no space-after slot and an Item carries many fields
layers cannot express, so the round-trip is a dx.Lens: the view captures
the text, tokenization, and a faithful annotation projection; the complement
carries the bead-only remainder (framework identity, the item-construction
fields, the space-after flags, and the spans and relations verbatim), so the
GetPut/PutGet laws hold for every item.
Resource lenses¶
Lenses between bead's resource models and their layers counterparts: lexical items and lexicons to entries and collections, templates to layers templates, and filled templates to layers fillings.
resource_lens
¶
Lenses between bead resource models and layers resource records.
Maps bead's lexical and template resources to their canonical
:mod:lairs.records.resource counterparts:
LexicalItem<-> a layersentryLexicon<-> a layerscollectionwith itsentryrecordsTemplate<-> a layerstemplate(with its slots and constraints)FilledTemplate<-> a layersfilling(with its per-slot fillings)
Each lens produces a layers-shaped view from the generated models and keeps the
fields that have no layers equivalent (the bead framework identity, tags, the
LexicalItem original form / free-text source, the bead DSL
constraint context, and the filled-template slot requirement map) in the lens
complement, so reconstruction is exact.
LexicalItemEntryLens
¶
Bases: Lens[LexicalItem, Entry, JsonValue]
Lossless lens LexicalItem <-> (layers entry, complement).
LexiconLayers
¶
Bases: Model
A layers view of a lexicon: a collection plus its entry records.
LexiconCollectionLens
¶
Bases: Lens[Lexicon, LexiconLayers, JsonValue]
Lossless lens Lexicon <-> (layers collection + entries, complement).
TemplateLayersLens
¶
Bases: Lens[Template, Template, JsonValue]
Lossless lens Template <-> (layers template, complement).
FilledTemplateFillingLens
¶
Bases: Lens[FilledTemplate, Filling, JsonValue]
Lossless lens FilledTemplate <-> (layers filling, complement).
The layers filling record (resource.Filling) is the canonical
representation of a filled template: it carries the template reference, the
per-slot fillings, the rendered text, and the filling strategy. The bead-only
remainder (identity, the source template name, the slot requirement map, and
the exact lexical-item fillers) travels in the lens complement.
Judgment and list lenses¶
Lenses between bead's response and list-composition models and their layers counterparts: annotation records to judgments (and judgment sets), bead list constraints to layers list constraints, and experiment lists to layers collections with their memberships and constraints.
judgment_lens
¶
Lenses between bead annotation records and layers judgment records.
Maps bead's reliability inputs to their canonical
:mod:lairs.records.judgment counterparts:
AnnotationRecord<-> a layersjudgment- a tuple of
AnnotationRecordsharing one annotator <-> a layersjudgmentSet
A single layers judgment has no slot for the annotator or the question name;
those belong to the parent judgmentSet and experiment. The lens therefore
keeps the bead framework identity, the annotator id, the question name, and the
raw item id / response label in the lens complement so reconstruction is exact.
The aggregate is modelled as two plain functions (a tuple is not a single
dx.Model), each one delegating to the per-record lens so every record
round-trips exactly.
AnnotationRecordJudgmentLens
¶
Bases: Lens[AnnotationRecord, Judgment, JsonValue]
Lossless lens AnnotationRecord <-> (layers judgment, complement).
The layers judgment record (:class:lairs.records.judgment.Judgment) is
the canonical representation of a single annotator response: it carries the
item reference and the categorical value. The bead-only remainder (identity,
annotator id, question name, and the raw item id / response label) travels in
the lens complement, since a single judgment has no slot for an annotator
or a question name.
records_to_judgment_set(records: tuple[AnnotationRecord, ...], participant: Participant | None = None) -> tuple[judgment.JudgmentSet, JsonValue]
¶
Project records sharing one annotator to a layers judgment set.
Groups a homogeneous tuple of annotation records (all from one annotator)
into a layers judgmentSet, projecting each record through
:data:ANNOTATION_RECORD_JUDGMENT. The set's agent carries the shared
annotator id and its createdAt is the earliest record creation time. The
complement carries the shared annotator id and the per-record complements, so
:func:judgment_set_to_records inverts it exactly.
When the annotator's :class:~bead.participants.models.Participant is
supplied, the set's agent is that participant's agentRef and its
features carries the participant's study fields (demographics, sessions,
consent), which the judgmentSet schema documents as the home for
"annotator demographics, session metadata, completion time, payment info".
The participant is supplementary provenance on the view: the records
reconstruct from the complement alone, so judgment_set_to_records is
unaffected.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
records
|
tuple[AnnotationRecord, ...]
|
Annotation records that all share one |
required |
participant
|
Participant | None
|
The annotator behind |
None
|
Returns:
| Type | Description |
|---|---|
tuple[JudgmentSet, JsonValue]
|
The layers judgment set view and the lens complement. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
See Also
judgment_set_to_records : Invert this projection.
judgment_set_to_records(view: judgment.JudgmentSet, complement: JsonValue) -> tuple[AnnotationRecord, ...]
¶
Reconstruct annotation records from a layers judgment set + complement.
Inverts :func:records_to_judgment_set by zipping each layers judgment
in the set with its per-record complement and delegating to
:data:ANNOTATION_RECORD_JUDGMENT, so every record round-trips exactly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
view
|
JudgmentSet
|
The layers judgment set produced by :func: |
required |
complement
|
JsonValue
|
The matching lens complement (per-record complements plus the shared annotator id). |
required |
Returns:
| Type | Description |
|---|---|
tuple[AnnotationRecord, ...]
|
The reconstructed annotation records, in their original order. |
See Also
records_to_judgment_set : The forward projection.
list_lens
¶
Lenses between bead list-composition models and layers judgment records.
Maps bead's experimental list models to their canonical
:mod:lairs.records counterparts:
ListConstraint<-> a layerslistConstraint(judgment.ListConstraint)ExperimentList<-> a layerscollectionwith itscollectionMembershiprecords and per-listlistConstraintrecords, bundled as the bead-side :class:ExperimentListLayersaggregate view
The bead ListConstraint union is far richer than the four scalar fields of the
layers listConstraint record, so the lens carries the entire bead constraint
(serialized with model_dump_json) in the lens complement and reconstructs the
exact subclass with ListConstraint.model_validate_json on the way back. The
ExperimentList lens keeps the bead framework identity, the numeric list id, the
ordered item references, the presentation order, the list and balance metadata, the
per-constraint satisfaction records, and the per-constraint complements in its own
complement, so reconstruction is exact.
ListConstraintLens
¶
Bases: Lens[ListConstraint, ListConstraint, JsonValue]
Lossless lens ListConstraint <-> (layers list constraint, complement).
The layers listConstraint record (judgment.ListConstraint) keeps only a
kind slug, an optional targetProperty, an optional formal constraint
expression, and an optional parameters map. The bead union carries many more
variant-specific fields, so the projection is lossy on its own; the complement
stores the entire bead constraint as model_dump_json and reconstruction
resolves the exact subclass through the discriminated-union root.
forward(constraint: ListConstraint) -> tuple[judgment.ListConstraint, JsonValue]
¶
Project a list constraint to a layers list constraint and complement.
backward(view: judgment.ListConstraint, complement: JsonValue) -> ListConstraint
¶
Reconstruct a list constraint from its complement.
The layers view is purely a projection; the exact bead constraint comes from the serialized model in the complement, whose discriminated-union root resolves the correct subclass.
ExperimentListLayers
¶
Bases: Model
A layers view of an experiment list.
Attributes:
| Name | Type | Description |
|---|---|---|
collection |
Collection
|
The list itself, projected to a layers |
memberships |
tuple[CollectionMembership, ...]
|
One membership per item reference, in presentation-independent item order, carrying the item ordinal. |
list_constraints |
tuple[ListConstraint, ...]
|
The per-list constraints, projected via :data: |
ExperimentListLens
¶
Bases: Lens[ExperimentList, ExperimentListLayers, JsonValue]
Lossless lens ExperimentList <-> (layers collection aggregate, complement).
The layers view bundles a collection (the list), one
collectionMembership per item reference (preserving order via the membership
ordinal), and the per-list listConstraint records. The bead-only remainder
(framework identity, the numeric list id, the ordered item references, the
presentation order, the list and balance metadata, the constraint-satisfaction
records, and the per-constraint complements) travels in the lens complement, so
a round-trip reconstructs the original list exactly.
Codec and corpus I/O¶
codec
¶
A lairs codec that round-trips a bead ItemCollection.
:class:BeadCodec binds the :class:lairs.integrations.ports.Codec port so a
downstream user can reach it through lairs.codec("bead") once both packages
are installed (the codec is registered via the lairs.codecs entry point in
bead's project metadata).
The codec's external format is a bead ItemCollection serialized as JSON.
decode runs each item through :data:~bead.interop.layers.item_bridge.ITEM_LAYERS
and concatenates the resulting canonical layers records, threading each item's
lens complement and the collection's own fields as private records under the
bead.interop.complement NSID (a layers consumer ignores unknown NSIDs).
Because the complement rides along, encode(decode(x)) == x holds losslessly
for every collection, and a single canonical mapping (the lens) drives both
directions.
BeadCodec
¶
Bidirectional codec ItemCollection JSON <-> layers corpus fragment.
corpus_io
¶
Ingest a layers Corpus into bead models and emit bead data as a corpus.
Ingest (layers to bead):
- :func:
expression_to_record/ :func:corpus_to_recordsstream a corpus's expressions as :class:~bead.corpus.records.CorpusRecordinstances. - :func:
corpus_to_graphbuilds a :class:~bead.corpus.graph.CorpusGraphfrom a corpus, deriving aparentedge from each expression'sparentRef. - :func:
corpus_to_itemsreconstructs best-effort :class:~bead.items.item.Iteminstances from a corpus's span and relation annotation layers. - :func:
load_layers_corpusis a thin wrapper over :func:lairs.load_corpus.
Egress (bead to layers):
- :func:
items_to_corpusand :func:graph_to_corpusbuild a :class:lairs.data.Corpusfrom bead data, reusing the canonical lenses. - :func:
materialize_corpus, :func:save_corpus_repo, and :func:publish_corpusdelegate to the correspondinglairsstore and publish entry points; the network-bound publish path is opt-in and defaults to a dry run.
expression_to_record(expr: expression.Expression, *, source_name: str, record_index: int = 0) -> CorpusRecord
¶
Build a :class:CorpusRecord from a layers expression.
corpus_to_records(corpus: Corpus, *, source_name: str | None = None) -> Iterator[CorpusRecord]
¶
Stream a corpus's member expressions as corpus records.
corpus_to_graph(corpus: Corpus, *, source_name: str | None = None) -> CorpusGraph
¶
Build a corpus graph from a corpus, with a parent edge per parentRef.
corpus_to_items(corpus: Corpus, *, item_template_id: UUID) -> Iterator[Item]
¶
Reconstruct best-effort items from a corpus's annotation layers.
Third-party corpora carry no bead complement, so item-construction fields take their defaults; the rendered text, spans, and relations are recovered from the expression and its span and relation annotation layers.
load_layers_corpus(uri: str, *, source: str = 'pds', pds_client: PdsClient | None = None, follow_refs: bool = True) -> Corpus
¶
Load a layers corpus by AT-URI (a thin wrapper over lairs.load_corpus).
items_to_corpus(collection: ItemCollection, *, corpus_name: str, authority: str = 'local') -> Corpus
¶
Build a layers corpus from a bead item collection.
Each item's rendered elements become expressions, its tokenizations become
segmentations, and its spans and relations become annotation layers (reusing
:data:~bead.interop.layers.item_bridge.ITEM_LAYERS); a membership links each
expression to the corpus, with one corpus record describing the dataset.
graph_to_corpus(graph: CorpusGraph, *, corpus_name: str, authority: str = 'local') -> Corpus
¶
Build a layers corpus from a corpus graph's record-bearing nodes.
materialize_corpus(corpus: Corpus, out_dir: Path) -> list[Path]
¶
Materialize a corpus to Arrow/Parquet views (delegates to lairs).
save_corpus_repo(corpus: Corpus, path: Path) -> str
¶
Commit a corpus to a local lairs repository and return the revision.
publish_corpus(repo: Repository, revision: str, *, to: str, endpoint: str | None = None, client: httpx.Client | None = None, dry_run: bool = True) -> object
¶
Publish a committed corpus revision to a PDS (opt-in; default dry run).
Returns the lairs PublishPlan. endpoint is the PDS base URL and
client an authorized httpx.Client; both are required for an actual
write (dry_run=False). The network-bound publish entry point is imported
lazily so importing this module never pulls the publish stack.