Audio-transcript layer schema for context graphs: Source, AudioSegment, Transcript, and Segment nodes with deterministic identity tuples and graph-node mapping, shared by the transcription, decomposition, and correction workflow cores.
Project description
cjm-transcript-graph-schema
Install
pip install cjm_transcript_graph_schema
Project Structure
nbs/
└── schema.ipynb # The audio-transcript layer schema (where-graph-begins locked layer schema): `Source -> AudioSegment(coarse boundary) -> AudioRendition(model-input: raw | vocals | …) -> Transcript(per-transcriber variants)` emitted by transcription, extended with the fine `Segment` spine (per-rendition) by decomposition. Deterministic identity tuples per the stage-5 ratified rule; the `AudioRendition` node lets raw + preprocessed model-inputs of one boundary coexist in one graph. `Document` from the pre-CR-18 era dissolves into `Source`.
Total: 1 notebook
Module Dependencies
graph LR
schema["schema<br/>schema"]
No cross-module dependencies detected.
CLI Reference
No CLI commands found in this project.
Module Overview
Detailed documentation for each module in the project:
schema (schema.ipynb)
The audio-transcript layer schema (where-graph-begins locked layer schema):
Source -> AudioSegment(coarse boundary) -> AudioRendition(model-input: raw | vocals | …) -> Transcript(per-transcriber variants)emitted by transcription, extended with the fineSegmentspine (per-rendition) by decomposition. Deterministic identity tuples per the stage-5 ratified rule; theAudioRenditionnode lets raw + preprocessed model-inputs of one boundary coexist in one graph.Documentfrom the pre-CR-18 era dissolves intoSource.
Import
from cjm_transcript_graph_schema.schema import (
TranscriptGraphLabels,
source_node_id,
audio_segment_node_id,
audio_rendition_node_id,
transcript_node_id,
segment_node_id,
SourceNode,
AudioSegmentNode,
AudioRenditionNode,
TranscriptNode,
TranscriptSliceRef,
SegmentNode
)
Functions
def source_node_id(
content_hash: str, # Content hash of the source media file ("algo:hexdigest")
) -> str: # Deterministic Source node id
"Source identity = the ingested file's content hash (Thread-1 ingested-root identity)."
def audio_segment_node_id(
source_id: str, # Owning Source node id
start: float, # Boundary start (source-coordinate seconds)
end: float, # Boundary end (source-coordinate seconds)
) -> str: # Deterministic AudioSegment node id
"""
AudioSegment identity = (source, boundary range).
Rendition-independent by design: an AudioSegment is a BOUNDARY of the source
(an audio fact), shared across every model-input rendition of it. The
boundary computation is pure and deterministic, so re-derivation reproduces
the id; the model-input WAV lives on the `AudioRendition` child, not here.
"""
def audio_rendition_node_id(
audio_segment_id: str, # Owning AudioSegment node id
chain: List[str], # Ordered preprocessing-chain descriptors; [] = the raw convert-only rendition
) -> str: # Deterministic AudioRendition node id
"""
AudioRendition identity = (audio segment, preprocessing chain).
A rendition is one model-input audio OF an AudioSegment. The chain is the
ordered list of preprocessing steps that produced it (each an opaque
descriptor string, e.g. ``"source_separation:cjm-media-plugin-demucs@<cfg>"``);
an EMPTY chain is the raw convert-only rendition, whose id is therefore
stable and chain-free. The universal 16k-mono resample is implicit (not a
chain step) — so raw + vocals are distinct renditions under one shared
AudioSegment, and the layer's identity-mismatch check never collides them.
Re-derivable from the manifest chain alone (extenders recompute, never
search).
"""
def transcript_node_id(
rendition_id: str, # Owning AudioRendition node id
transcriber: str, # Transcriber capability name (e.g. "cjm-transcription-plugin-whisper")
config_hash: str, # Transcriber config hash
) -> str: # Deterministic Transcript node id
"""
Transcript identity = (audio rendition, transcriber, config) — MIRRORS the
capability cache key UNIQUE(audio_path, config_hash), so the graph node is
the durable face of the cached row (the structural E13 fix). Keyed on the
RENDITION, so a raw vs vocals transcript of the same boundary are distinct.
"""
def segment_node_id(
rendition_id: str, # Owning AudioRendition node id
vad_config_hash: str, # VAD capability config hash (skeleton identity input)
chunk_start: float, # VAD chunk start (chunk-local seconds within the rendition WAV)
chunk_end: float, # VAD chunk end (chunk-local seconds)
) -> str: # Deterministic Segment node id
"""
Fine Segment identity = audio-side only (audio rendition, VAD config, chunk
range) — so the skeleton's ids are SHARED across transcribers by
construction (C4 "store agreement once" falls out of identity design). Keyed
on the RENDITION: each rendition has its own fine spine (vocals isolation can
yield different VAD chunk boundaries than raw).
"""
Classes
class TranscriptGraphLabels:
"Node labels of the audio-transcript layer schema."
def all(cls) -> list: # All schema labels
"All schema labels."
@dataclass
class SourceNode:
"The provenance root: one ingested media file."
content_hash: str # Content hash over the file ("algo:hexdigest"; the identity input)
path: str # Original media path (location, may dangle; identity is the hash)
title: Optional[str] # Display title; None = path stem
media_type: str = 'audio' # Media type
def id(self) -> str: # Deterministic node id
"""Deterministic node id."""
return source_node_id(self.content_hash)
def to_graph_node(self) -> Dict[str, Any]: # Node wire dict
"Deterministic node id."
def to_graph_node(self) -> Dict[str, Any]: # Node wire dict
"""Build the Source node wire dict (root_kind=ingested; FileRef provenance)."""
return {
"id": self.id,
"Build the Source node wire dict (root_kind=ingested; FileRef provenance)."
@dataclass
class AudioSegmentNode:
"""
Coarse ~5-min spine member: a BOUNDARY range of the Source (an audio fact),
shared across every model-input rendition of it.
Hashless by design: a boundary is not a materialized artifact — a hash over
the sliced ORIGINAL source bytes is not materializable without extracting
per-range artifacts (decoded ranges are not byte ranges). The materialized
model-input WAV lives on the `AudioRendition` child (which carries its
content hash); the structural chain to the Source rides the PART_OF edge +
the start/end properties. `segment_path` (the source-codec cut) stays here as
a rendition-independent archival pointer.
"""
source: str # Owning Source node id
index: int # Position in the coarse spine (0-based)
start: float # Boundary start (source-coordinate seconds)
end: float # Boundary end (source-coordinate seconds)
segment_path: Optional[str] # Source-codec cut file (rendition-independent archival pointer)
def id(self) -> str: # Deterministic node id
"""Deterministic node id."""
return audio_segment_node_id(self.source, self.start, self.end)
def to_graph_node(self) -> Dict[str, Any]: # Node wire dict
"Deterministic node id."
def to_graph_node(self) -> Dict[str, Any]: # Node wire dict
"""Build the AudioSegment node wire dict (hashless boundary; sources empty)."""
props: Dict[str, Any] = {
"Build the AudioSegment node wire dict (hashless boundary; sources empty)."
@dataclass
class AudioRenditionNode:
"""
A model-input rendition OF an AudioSegment — the materialized 16k-mono WAV
consumed by transcription/VAD/FA. OWNS the model-input (E14: the audio of
record); the AudioSegment above it is a hashless boundary.
Identity = (audio segment, preprocessing chain). The raw convert-only path is
an empty chain (its id is stable + chain-free); vocals isolation / future
speech-enhancement are non-empty chains. Raw + vocals are therefore distinct
renditions that COEXIST under one AudioSegment — the divergent model-input
hash that used to collide the AudioSegment now lives on distinct rendition
nodes. Connects UP to its AudioSegment by DERIVED_FROM (it is derived from the
segment's audio); the production act is recorded separately by a CR-18
`Derivation` event.
"""
audio_segment: str # Owning AudioSegment node id
model_input_path: str # The 16kHz mono WAV consumed by transcription/VAD/FA
model_input_hash: str # Content hash over that WAV (the rendition's identity-of-record artifact)
chain: List[str] = field(...) # Ordered preprocessing-chain descriptors; [] = raw convert-only
def id(self) -> str: # Deterministic node id
"""Deterministic node id."""
return audio_rendition_node_id(self.audio_segment, self.chain)
def to_graph_node(self) -> Dict[str, Any]: # Node wire dict
"Deterministic node id."
def to_graph_node(self) -> Dict[str, Any]: # Node wire dict
"""Build the AudioRendition node wire dict (owns the model-input SourceRef)."""
props: Dict[str, Any] = {
"Build the AudioRendition node wire dict (owns the model-input SourceRef)."
def derived_edge(self) -> Dict[str, Any]: # Edge wire dict
"DERIVED_FROM edge: this rendition derives from its AudioSegment's audio."
@dataclass
class TranscriptNode:
"""
One transcriber's text for one AudioRendition (per-transcriber variants at
the coarse layer — cross-transcriber divergence lives here, C4/C14).
"""
rendition: str # Owning AudioRendition node id
transcriber: str # Transcriber capability name
config_hash: str # Transcriber config hash
text: str # The transcript text (stored ONCE here; fine Segments slice into it)
audio_hash: str # Content hash of the consumed model-input WAV (the rendition's)
metadata: Dict[str, Any] = field(...) # Transcriber-reported metadata
asserted_at: Optional[float] # Derivation timestamp; None = now
def id(self) -> str: # Deterministic node id
"""Deterministic node id."""
return transcript_node_id(self.rendition, self.transcriber, self.config_hash)
def to_graph_node(self) -> Dict[str, Any]: # Node wire dict
"Deterministic node id."
def to_graph_node(self) -> Dict[str, Any]: # Node wire dict
"""Build the Transcript node wire dict (capability attribution included)."""
props: Dict[str, Any] = {
"Build the Transcript node wire dict (capability attribution included)."
def derived_edge(self) -> Dict[str, Any]: # Edge wire dict
"DERIVED_FROM edge: this Transcript derives from its AudioRendition."
@dataclass
class TranscriptSliceRef:
"""
One per-transcriber char-range reference for a fine Segment: where this
segment's text lives inside a Transcript node's text (Thread-1
slices-until-promoted — variants are slices, never duplicated fine text).
"""
transcript: str # Transcript node id
start_char: int # Slice start into the Transcript's text
end_char: int # Slice end
text: str # The sliced text (content-hashed for the ref)
def to_source_ref(self) -> Dict[str, Any]: # SourceRef wire dict
"Build the slice-shaped provenance ref."
@dataclass
class SegmentNode:
"""
Fine spine member: one VAD chunk — IMMUTABLE audio range + CORRECTABLE
text (the immutable-audio/mutable-text spine seam). PART_OF its AudioRendition
(each rendition has its own fine spine).
Layer-0 `text` is the ACCURACY transcriber's alignment; the designation is
per-segment provenance, not global config (`text_from` names the
authoritative Transcript; every transcriber's char range rides
`text_slices`, the authoritative one included).
"""
rendition: str # Owning AudioRendition node id
vad_config_hash: str # VAD config hash (skeleton identity input)
chunk_start: float # VAD chunk start (chunk-local seconds within the rendition WAV)
chunk_end: float # VAD chunk end (chunk-local seconds)
index: int # Source-wide fine-spine index (0-based)
start_time: float # Source-coordinate start (navigation)
end_time: float # Source-coordinate end
text: str = '' # Layer-0 text (accuracy alignment; "" = no aligned words, D14 class)
audio_hash: str = '' # Content hash of the owning rendition's model-input WAV
source: Optional[str] # Source node id (convenience for direct filters)
text_from: Optional[str] # Authoritative Transcript node id (None when text is empty)
text_slices: List[TranscriptSliceRef] = field(...) # All per-transcriber slice refs
def id(self) -> str: # Deterministic node id
"""Deterministic node id (audio-side identity; shared across transcribers, per-rendition)."""
return segment_node_id(self.rendition, self.vad_config_hash,
self.chunk_start, self.chunk_end)
def to_graph_node(self) -> Dict[str, Any]: # Node wire dict
"Deterministic node id (audio-side identity; shared across transcribers, per-rendition)."
def to_graph_node(self) -> Dict[str, Any]: # Node wire dict
"""Build the Segment node wire dict (audio ref + per-transcriber text slice refs)."""
props: Dict[str, Any] = {
"Build the Segment node wire dict (audio ref + per-transcriber text slice refs)."
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cjm_transcript_graph_schema-0.0.7.tar.gz.
File metadata
- Download URL: cjm_transcript_graph_schema-0.0.7.tar.gz
- Upload date:
- Size: 13.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a18ad6cadabdffbe594dbc7809929c8f438cd7b1739adba9b967898dac038a9
|
|
| MD5 |
2596eff461b788fefa3d12e3dbd4772d
|
|
| BLAKE2b-256 |
05fe5769b22298b6650a41e6e2d5ffe75d66b3d4b7d6bd00373c17ebc050095c
|
File details
Details for the file cjm_transcript_graph_schema-0.0.7-py3-none-any.whl.
File metadata
- Download URL: cjm_transcript_graph_schema-0.0.7-py3-none-any.whl
- Upload date:
- Size: 16.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
73374dcac325202571e4a674e7a15a6017b6681e4042ac2bd786f5858b7140c2
|
|
| MD5 |
9887fefc2eb8a968bd20ff14480beb6f
|
|
| BLAKE2b-256 |
9b075294772d4885dc402edac138f71351422285bc144675c3370a8985f8a436
|