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).

forward(graph_in: CorpusGraph) -> tuple[CorpusGraphLayers, JsonValue]

Project a graph to its layers view and bead complement.

backward(view: CorpusGraphLayers, complement: JsonValue) -> CorpusGraph

Reconstruct the graph from its layers view and 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).

forward(record: CorpusRecord) -> tuple[expression.Expression, JsonValue]

Project a record to a layers expression and bead complement.

backward(view: expression.Expression, complement: JsonValue) -> CorpusRecord

Reconstruct a record from its layers expression and 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.

forward(sentence: ParsedSentence) -> ParsedSentenceLayers

Project a parsed sentence to layers tokenization + annotations.

backward(view: ParsedSentenceLayers) -> ParsedSentence

Reconstruct a parsed sentence from its layers projection.

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.Expression per rendered element,
  • one :class:lairs.records.segmentation.Segmentation per tokenized element,
  • one span :class:lairs.records.annotation.AnnotationLayer per element whose spans anchor by tokenRefSequence (head_index maps to anchorTokenIndex; a Wikidata label_id maps to a knowledgeRef),
  • one relation AnnotationLayer whose annotations carry ArgumentRef source/target objectRef arguments.

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.

ItemLayersLens

Bases: Lens[Item, CorpusFragment, JsonValue]

Lossless lens Item <-> (layers corpus fragment, bead complement).

forward(item: Item) -> tuple[CorpusFragment, JsonValue]

Project an item to a layers fragment and bead complement.

backward(view: CorpusFragment, complement: JsonValue) -> Item

Reconstruct an item from its layers fragment and bead complement.

item_to_layers(item: Item) -> CorpusFragment

Return the standalone layers fragment view of an item.

Resource lenses

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 layers entry
  • Lexicon <-> a layers collection with its entry records
  • Template <-> a layers template (with its slots and constraints)

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, and the bead DSL constraint context) in the lens complement, so reconstruction is exact.

LexicalItemEntryLens

Bases: Lens[LexicalItem, Entry, JsonValue]

Lossless lens LexicalItem <-> (layers entry, complement).

forward(item: LexicalItem) -> tuple[resource.Entry, JsonValue]

Project a lexical item to a layers entry and complement.

backward(view: resource.Entry, complement: JsonValue) -> LexicalItem

Reconstruct a lexical item from its layers entry and 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).

forward(lexicon: Lexicon) -> tuple[LexiconLayers, JsonValue]

Project a lexicon to a layers collection + entry views.

backward(view: LexiconLayers, complement: JsonValue) -> Lexicon

Reconstruct a lexicon from its layers collection + complement.

TemplateLayersLens

Bases: Lens[Template, Template, JsonValue]

Lossless lens Template <-> (layers template, complement).

forward(template: Template) -> tuple[resource.Template, JsonValue]

Project a template to a layers template and complement.

backward(view: resource.Template, complement: JsonValue) -> Template

Reconstruct a template from its layers template and complement.

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.

decode(src: str | bytes, *, into: CorpusFragment | None = None) -> CorpusFragment

Decode a bead ItemCollection JSON document into a layers fragment.

encode(records: Iterable[FragmentRecord]) -> str

Encode layers fragment records back into ItemCollection JSON.

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_records stream a corpus's expressions as :class:~bead.corpus.records.CorpusRecord instances.
  • :func:corpus_to_graph builds a :class:~bead.corpus.graph.CorpusGraph from a corpus, deriving a parent edge from each expression's parentRef.
  • :func:corpus_to_items reconstructs best-effort :class:~bead.items.item.Item instances from a corpus's span and relation annotation layers.
  • :func:load_layers_corpus is a thin wrapper over :func:lairs.load_corpus.

Egress (bead to layers):

  • :func:items_to_corpus and :func:graph_to_corpus build a :class:lairs.data.Corpus from bead data, reusing the canonical lenses.
  • :func:materialize_corpus, :func:save_corpus_repo, and :func:publish_corpus delegate to the corresponding lairs store 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.