speech-text-data-aligners / docs /ARCHITECTURE.md
pltobing's picture
chore(repo): finalize licensing and documentation
51e84d2
|
Raw History Blame Contribute Delete
7.33 kB

Architecture

Speech-Text Data Aligners is a library-first forced-alignment framework. The core contract accepts WAV audio plus a known, authoritative transcript and returns backend-neutral timed words. The CLI is only a composition and presentation layer.

The mandatory design rules are defined in the modular architecture contract and the aligner engineering contract.

Layers and Dependency Direction

CLI --------------------> Application --------------------> Core
                              |                              ^
                              +----------> Data -------------+
                              +----------> Benchmarking -----+
                              +----------> Runtime ----------+

CLI -- lazy composition --> Concrete Backends
                            |              |
                            +------------> Core
                            +------------> Runtime

In compact form:

CLI -> Application -> Core
                  -> Data
                  -> Benchmarking
                  -> Runtime

Concrete Backends -> Core / Runtime

The enforced consequences are:

  • Core does not import application, data, benchmarking, CLI, or concrete backends.
  • Data and benchmarking do not depend on concrete backends.
  • Application services receive an AlignmentBackend protocol instance; they do not branch on NeMo, MFA, or WhisperX names.
  • Concrete backend packages do not import one another.
  • The registry stores lazy dotted factories so unavailable optional runtimes do not break backend-neutral imports or sibling backends.
  • The CLI delegates annotation and benchmarking to reusable Python services.

Current Package Tree

src/speech_text_data_aligners/
|-- __init__.py                 public backend-neutral facade
|-- application/
|   |-- annotate.py             annotation orchestration and publication policy
|   `-- benchmark.py            identical-request benchmark orchestration
|-- backends/
|   |-- registry.py             immutable lazy backend composition
|   |-- nemo/
|   |   |-- backend.py          neutral adapter
|   |   |-- config.py           typed configuration
|   |   |-- manifest.py         reference JSONL and output-manifest parsing
|   |   |-- runner.py           isolated NFA execution
|   |   `-- ctm.py              word CTM parsing and mapping
|   |-- mfa/
|   |   |-- backend.py          neutral adapter
|   |   |-- config.py           typed configuration
|   |   |-- corpus.py           temporary WAV/LAB corpus
|   |   |-- runner.py           isolated MFA execution
|   |   `-- textgrid.py         word-tier parsing and mapping
|   `-- whisperx/
|       |-- backend.py          neutral adapter
|       |-- config.py           typed configuration
|       |-- segments.py         full-WAV authoritative segments
|       |-- provider.py         isolated-worker transport
|       |-- worker.py           lazy heavyweight native runtime
|       `-- parser.py           native word-dictionary parsing and mapping
|-- benchmarking/
|   |-- metrics.py              sparse-gold and pairwise metrics
|   `-- reports.py              JSON/CSV/Markdown report rendering
|-- cli/
|   |-- main.py                 command dispatch
|   |-- annotate.py             annotation argument adapter
|   `-- benchmark.py            benchmark argument adapter
|-- core/
|   |-- models.py               immutable neutral values and provenance
|   |-- protocols.py            injected backend contracts
|   |-- errors.py               project exception taxonomy
|   |-- normalization.py        lexical plans and monotonic reconciliation
|   |-- validation.py           result and publication-path invariants
|   `-- extraction.py           six first/last output fields
|-- data/
|   |-- audio.py                WAV resolution and lightweight probing
|   `-- metadata.py             preserving, atomic CSV I/O
`-- runtime/
    |-- process.py              shell-free injected subprocess boundary
    |-- artifacts.py            temporary and retained artifact lifecycle
    `-- receipts.py             hashes and run provenance

Package-marker __init__.py files not shown above are deliberately empty. The root __init__.py is the tested public facade.

Backend Isolation and Native Containment

PARENT PROCESS

Python caller or CLI
        |
        v
backend-neutral application API
        |
        v
AlignmentBackend protocol
        |
        v
concrete adapter (NeMo | MFA | WhisperX)
        |
        | injected process/provider boundary
        v
+------------------------------------------------------+
| ISOLATED NATIVE RUNTIME                              |
| NeMo NFA process | MFA process | WhisperX worker    |
| Heavy optional dependencies and model lifecycle     |
+------------------------------------------------------+
        |
        | native files / native dictionaries
        v
adapter-local builder, parser, and reconciliation
        |
        | only neutral records cross this point
        v
AlignmentResult | AlignmentFailure | diagnostics | provenance

NeMo manifests, output manifests, and CTM rows remain inside backends.nemo. MFA corpus labels and TextGrid objects remain inside backends.mfa. WhisperX segment payloads and native result dictionaries remain inside backends.whisperx. NeMo CTM, MFA TextGrid, and WhisperX native dictionaries never escape their adapters; callers receive only backend-neutral models.

The parent process does not import heavyweight NeMo, MFA, WhisperX, Torch, or Torchaudio packages. NeMo and MFA run through the shared shell-free process boundary. WhisperX runs in a dedicated worker that imports its heavy stack only after invocation.

Transcript-to-Result Data Flow

authoritative transcript
        |
        v
backend-specific reference representation
        |
        v
native timed words
        |
        v
shared monotonic reconciliation
        |
        v
shared validation
        |
        v
AlignmentResult

The authoritative en_transcript remains unchanged throughout. A backend may normalize or expand text only through a reversible plan that retains ownership of original lexical words. Shared reconciliation requires complete monotonic coverage, and shared validation rejects non-finite, negative, reversed, overlapping, incomplete, or out-of-audio timestamps. It never clamps, interpolates, or invents a timestamp.

Reuse and Composition

The root facade exposes reusable annotation and benchmark services. Applications may inject any object satisfying AlignmentBackend, including a lightweight test or custom backend. The default registry is a convenience for the CLI, not a dependency of the application layer.

Benchmarking consumes the same neutral results as annotation. Native parsing, transcript mapping, CSV I/O, metric calculation, report rendering, and process execution each have one owning layer, so adding another adapter does not require changes to established backend-neutral behavior.

Architecture is enforced by deterministic AST tests and Import Linter contracts. The accepted audit and complete dependency evidence are recorded in the Stage 5 final acceptance report.