core/sensorium/adapters/audio.py
Shay 7f619d44e5
feat(adr-0181-p3): audio_core_v1 pack + AudioProjectionHead (gate-closed) (#467)
PR-3 of ADR-0181. Externalises the PR-2 in-code operator registry to a
versioned, checksum-locked pack and wires the sensorium adapter. Mounts
gate-closed until the PR-4 eval gates pass. No core mutation (ADR-0013).

Pack (packs/audio/audio_core_v1/):
- manifest.json   canonical params, resampling config, gating defaults, ordering
- operators.jsonl the 8 elliptic operators (byte-equivalent to DEFAULT_OPERATOR_REGISTRY)
- basis_map.json  alias→index over the six elliptic planes {6,7,8,10,11,13}
- resample_fir_v1.npy  pinned odd-length symmetric windowed-sinc low-pass (unit DC)
- checksums.json  sha256 of every artifact

Loader (packs/audio/loader.py) — trust boundary (ADR-0051 / CLAUDE.md §Security):
- _validate_pack_id rejects traversal/separators/dotfiles before any path join
- fail-closed checksum verification: every file re-hashed vs checksums.json
- resolved path must stay under the packs root (defense in depth)
- JSON format (matches every other CORE pack loader; spec §6.1 TOML was
  illustrative; no tomllib dependency added)

Adapter (sensorium/adapters/audio.py):
- AudioProjectionHead wraps AudioCompiler; project(AudioSignal)->(32,) f32
- make_audio_pack loads+verifies the pack, builds a gate-closed ModalityPack

Tests (19): PR-2↔PR-3 registry byte-parity via manifest_sha256, elliptic-only
operators, FIR integrity, fail-closed checksum mismatch, path-traversal guard,
gate-closed refusal, mount-time unitarity (+ bad-unitarity blocks mount),
gate-engage requires checksum_verified, engaged pack projects (32,) f32.

All audio 32 + arch-invariants 40 + smoke 67 green.
2026-05-29 11:05:57 -07:00

101 lines
3.5 KiB
Python

"""
sensorium/adapters/audio.py — Audio modality adapter (ADR-0181 PR-3).
The thin, pack-governed ProjectionHead for audio. It wraps the deterministic
AudioCompiler (PR-2): a canonical AudioSignal is compiled to one
AudioCompilationUnit and the unit's (32,) float32 versor crosses the
Logos-recovery boundary.
The pack mounts **gate-closed** (``gate_engaged=False``) until the eval gates
(PR-4) pass — `ModalityRegistry.project` refuses a closed gate, so audio
contributes no field state until determinism/checksum/versor gates are green
(ADR-0181 §2.5). Adding audio touches no existing layer (ADR-0013).
"""
from __future__ import annotations
from dataclasses import dataclass
import numpy as np
from sensorium.audio.canonical import CANONICAL_SAMPLE_RATE, canonicalize
from sensorium.audio.compiler import AudioCompiler
from sensorium.audio.types import AudioSignal
from sensorium.protocol import (
CL41_DIM,
Modality,
ModalityPack,
ModalityVocabulary,
)
@dataclass(frozen=True, slots=True)
class AudioProjectionHead:
"""D1 projection head for audio.
Determinism class: D1 — deterministic given a pinned pack (operator
registry + basis). ``project`` accepts an already-canonical AudioSignal;
raw waveform is canonicalised upstream via ``canonicalize`` (a helper is
exposed as ``project_raw`` for convenience).
"""
compiler: AudioCompiler
modality: Modality = Modality.AUDIO
@property
def embedding_dim(self) -> int:
return CL41_DIM
def project(self, signal: AudioSignal) -> np.ndarray:
out = self.compiler.compile_signal(signal).versor
if out.shape != (CL41_DIM,):
raise ValueError(f"expected ({CL41_DIM},), got {out.shape}")
if out.dtype != np.float32:
raise TypeError(f"expected float32, got {out.dtype}")
return out
def project_batch(self, signals: list[AudioSignal]) -> np.ndarray:
return np.stack([self.project(s) for s in signals], axis=0)
def project_raw(
self, samples: np.ndarray, sample_rate: int, *, fir: np.ndarray | None = None
) -> np.ndarray:
"""Convenience: canonicalise raw samples then project."""
signal = canonicalize(samples, sample_rate, target_sr=CANONICAL_SAMPLE_RATE, fir=fir)
return self.project(signal)
def verify_unitarity(self, sample: AudioSignal) -> bool:
try:
return self.compiler.compile_signal(sample).versor_condition < 1e-6
except Exception:
return False
def make_audio_pack(
pack_id: str = "audio_core_v1",
*,
gate_engaged: bool = False,
checksum_verified: bool = False,
packs_root=None,
) -> ModalityPack:
"""Construct a gate-closed audio ModalityPack from a verified pack.
Loads + checksum-verifies the pack (fail-closed), builds the compiler over
the pack's operator registry, and wraps it in an AudioProjectionHead. The
pack ships gate-closed; engaging the gate is the PR-4 eval-gate decision.
"""
from packs.audio.loader import load_audio_pack
loaded = load_audio_pack(pack_id, packs_root=packs_root)
compiler = AudioCompiler(loaded.registry, pack_id=loaded.pack_id)
head = AudioProjectionHead(compiler)
return ModalityPack(
pack_id=loaded.pack_id,
modality_type=Modality.AUDIO,
projection=head,
decoder=None,
vocabulary=ModalityVocabulary(),
grammar_scaffold=None,
checksum_verified=checksum_verified,
gate_engaged=gate_engaged,
)