Files
local-transcriber/tests/test_streaming_sortformer_experiment.py
Dmitry Dementiev 4c4a8be07b fix(experiment): уточнено определение активной подкачки
- Зачем:
  - занятые, но неизменные swap-страницы ошибочно блокировали канонический Ryzen-прогон.
- Что:
  - active swap определяется изменением счетчиков sin/sout во время ячейки.
  - сохранены memory gates для OOM, RSS plateau, линейного роста и пикового RSS.
- Проверка:
  - uv run pytest -q tests/test_streaming_sortformer_experiment.py tests/test_benchmark_experiment.py tests/test_speaker_diarizer.py tests/test_diarization.py.
2026-09-04 18:50:31 +03:00

1651 lines
57 KiB
Python

import copy
import ctypes
import hashlib
import json
import tomllib
from datetime import UTC, datetime
from pathlib import Path
from types import SimpleNamespace
import numpy as np
import pytest
from local_transcriber import streaming_sortformer_experiment as experiment
def test_experiment_module_is_excluded_from_production_wheel():
project = tomllib.loads(
(Path(__file__).parents[1] / "pyproject.toml").read_text(encoding="utf-8")
)
excluded = project["tool"]["hatch"]["build"]["targets"]["wheel"]["exclude"]
assert "src/local_transcriber/streaming_sortformer_experiment.py" in excluded
def _write(root: Path, name: str, payload: bytes) -> dict[str, object]:
path = root / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(payload)
return {
"path": name,
"sha256": hashlib.sha256(payload).hexdigest(),
"size_bytes": len(payload),
}
def _record(
root: Path, prefix: str, *, duration: float, reference: bool
) -> dict[str, object]:
media = _write(root, f"media/{prefix}.wav", f"audio-{prefix}".encode())
sidecar = _write(root, f"sidecars/{prefix}.json", f"words-{prefix}".encode())
result = {
**media,
"duration_seconds": duration,
"sidecar_path": sidecar["path"],
"sidecar_sha256": sidecar["sha256"],
"expected_cluster_count": 2,
}
if reference:
reference_record = _write(
root, f"references/{prefix}.json", f"ref-{prefix}".encode()
)
result.update(
reference_path=reference_record["path"],
reference_sha256=reference_record["sha256"],
)
return result
def _manifest(root: Path) -> dict[str, object]:
inputs = []
for index in range(3):
source = _write(root, f"sources/{index}.wav", f"source-{index}".encode())
inputs.append(
{
"source_path": source["path"],
"source_sha256": source["sha256"],
"source_size_bytes": source["size_bytes"],
"fragment": _record(
root, f"fragment-{index}", duration=300.0, reference=True
),
"full_recording": _record(
root, f"full-{index}", duration=900.0, reference=False
),
}
)
return {
"schema": "local-transcriber.streaming-sortformer-experiment.v1",
"fresh_basis": {
"marker": "issue-46-fresh-owner-reviewed-basis",
"approval": {
"decision": "approved",
"timestamp": "2026-09-01T09:00:00+00:00",
},
},
"source": {"commit": "a" * 40},
"machine": _machine(),
"inputs": inputs,
"recipes": _recipes(),
"schedule": experiment.build_fragment_schedule(
[item["fragment"]["sha256"] for item in inputs],
[item["fragment"]["sidecar_sha256"] for item in inputs],
),
"rss_sample_interval_ms": 10,
}
def _machine() -> dict[str, object]:
return {
"machine_sha256": "1" * 64,
"physical_cores": 8,
"logical_cores": 16,
"affinity": list(range(8)),
"cpuid_isa_sha256": "2" * 64,
"ram_bytes": 16 * 1024**3,
"os_sha256": "3" * 64,
"compiler_sha256": "4" * 64,
"runtime_versions_sha256": "5" * 64,
"power_mode": "balanced",
"governor": "powersave",
"turbo": True,
}
def _recipes() -> dict[str, object]:
return {
"W0-FRESH": {
"sherpa_onnx_version": "1.13.6",
"sherpa_onnx_core_version": "1.13.6",
"segmentation_model_sha256": "220ad67ca923bef2fa91f2390c786097bf305bceb5e261d4af67b38e938e1079",
"embedding_model_sha256": "e9848563da86f263117134dfd7ad63c92355b37de492b55e325400c9d9c39012",
"window_shift_ratio": 0.1,
"clustering": {
"algorithm": "FastClustering",
"mode": "auto",
"threshold": 0.89,
},
"segmentation_provider": "cpu",
"embedding_provider": "cpu",
"segmentation_debug": True,
"embedding_debug": True,
"min_duration_on": 0.3,
"min_duration_off": 0.5,
"workers": 1,
"sessions": 1,
"intra_op_threads": 8,
"inter_op_threads": 1,
"batch_size": 1,
"process_variant": "upstream-unpatched",
},
"SORTFORMER-V2-Q8": {
"runtime_repo": "NVIDIA/NeMo-Speech.cpp",
"runtime_commit": "4f9676226f667d14608487df744f375db87127f8",
"runtime_license": "Apache-2.0",
"model_repo": "nvidia/diar_streaming_sortformer_4spk-v2",
"model_revision": "5240a64075176943f677d30fa2171c780229f341",
"model_name": "diar_streaming_sortformer_4spk-v2.q8_0.gguf",
"model_size_bytes": 147075776,
"model_sha256": "0679cfeb1ce356d0dea9470b31274f4bfc7eb927497d82005483770666da998a",
"model_license": "CC-BY-4.0",
"quantization": "Q8_0",
"preset": "cpu-diar",
"diarization_preset": "streaming",
"backend": "cpu",
"gpu": -1,
"processes": 1,
"streams": 1,
"internal_cpu_threads": 4,
"submodule_command": "git submodule update --init ggml",
"configure_command": "scripts/configure.sh cpu-diar",
"build_command": "cmake --build --preset cpu-diar",
"cmake_options": {
"build_type": "Release",
"generator": "Ninja",
"diarization_only": True,
"patched_ggml": False,
},
"geometry": {
"chunk_frames": 340,
"right_context_frames": 40,
"left_context_frames": 0,
"fifo_frames": 40,
"spkcache_frames": 188,
"update_period_frames": 300,
},
"post_processing": {
"onset": 0.641,
"offset": 0.561,
"pad_onset_sec": 0.229,
"pad_offset_sec": 0.079,
"min_duration_sec": 0.511,
"min_gap_sec": 0.296,
},
"num_speakers": 4,
"seconds_per_frame": 0.08,
"submodule_commits": {"ggml": "6" * 40},
"compiler": "pinned-compiler",
"cmake_version": "pinned-cmake",
"executable_sha256": "7" * 64,
"library_sha256": "8" * 64,
},
}
def _environment() -> dict[str, object]:
return {"commit": "a" * 40, "clean": True}
def test_preflight_validates_fresh_exact_schema_before_loading(tmp_path):
manifest = _manifest(tmp_path)
calls = []
outcome = experiment.preflight_and_load(
manifest,
root=tmp_path,
environment_probe=_environment,
w0_loader=lambda: calls.append("w0"),
candidate_loader=lambda: calls.append("candidate"),
validate_only=True,
)
assert outcome.state == "valid"
assert outcome.fragment_count == 3
assert outcome.full_recording_count == 3
assert calls == []
def test_approved_fresh_basis_loads_models_after_preflight(tmp_path):
manifest = _manifest(tmp_path)
calls = []
outcome = experiment.preflight_and_load(
manifest,
root=tmp_path,
environment_probe=_environment,
w0_loader=lambda: calls.append("w0"),
candidate_loader=lambda: calls.append("candidate"),
)
assert outcome.state == "valid"
assert calls == ["w0", "candidate"]
def test_fragment_without_reference_loads_models_after_preflight(tmp_path):
manifest = _manifest(tmp_path)
fragment = manifest["inputs"][0]["fragment"]
(tmp_path / fragment["reference_path"]).unlink()
fragment["reference_path"] = None
fragment["reference_sha256"] = None
calls = []
outcome = experiment.preflight_and_load(
manifest,
root=tmp_path,
environment_probe=_environment,
w0_loader=lambda: calls.append("w0"),
candidate_loader=lambda: calls.append("candidate"),
)
assert outcome.state == "valid"
assert calls == ["w0", "candidate"]
@pytest.mark.parametrize("field", ["reference_path", "reference_sha256"])
def test_fragment_rejects_half_present_reference_before_loaders(tmp_path, field):
manifest = _manifest(tmp_path)
manifest["inputs"][0]["fragment"][field] = None
calls = []
with pytest.raises(experiment.PreflightError, match="reference-binding"):
experiment.preflight_and_load(
manifest,
root=tmp_path,
environment_probe=_environment,
w0_loader=lambda: calls.append("w0"),
candidate_loader=lambda: calls.append("candidate"),
)
assert calls == []
def test_missing_owner_approval_is_needs_user_and_never_loads(tmp_path):
manifest = _manifest(tmp_path)
manifest["fresh_basis"]["approval"] = None
calls = []
outcome = experiment.preflight_and_load(
manifest,
root=tmp_path,
environment_probe=_environment,
w0_loader=lambda: calls.append("w0"),
candidate_loader=lambda: calls.append("candidate"),
)
assert outcome.state == "needs-user"
assert outcome.reason == "owner-approval-required"
assert calls == []
@pytest.mark.parametrize(
"mutation",
[
lambda value: value.update(schema="3"),
lambda value: value.update(legacy_manifest="issue-41"),
lambda value: value["inputs"].__setitem__(
1,
{**value["inputs"][1], "source_sha256": value["inputs"][0]["source_sha256"]},
),
lambda value: value["inputs"][0]["fragment"].update(duration_seconds=299.9),
lambda value: value["inputs"][0]["full_recording"].update(
expected_cluster_count=5
),
lambda value: value["source"].update(commit="b" * 40),
],
)
def test_preflight_rejects_schema_basis_counts_and_source_before_load(
tmp_path, mutation
):
manifest = _manifest(tmp_path)
mutation(manifest)
calls = []
with pytest.raises(experiment.PreflightError):
experiment.preflight_and_load(
manifest,
root=tmp_path,
environment_probe=_environment,
w0_loader=lambda: calls.append("w0"),
candidate_loader=lambda: calls.append("candidate"),
)
assert calls == []
def test_preflight_rejects_hash_path_and_symlink_escape_without_leaking_path(tmp_path):
manifest = _manifest(tmp_path)
manifest["inputs"][0]["fragment"]["sha256"] = "f" * 64
calls = []
with pytest.raises(experiment.PreflightError) as raised:
experiment.preflight_and_load(
manifest,
root=tmp_path,
environment_probe=_environment,
w0_loader=lambda: calls.append("w0"),
candidate_loader=lambda: calls.append("candidate"),
)
assert str(tmp_path) not in str(raised.value)
assert calls == []
outside = tmp_path.parent / f"outside-{datetime.now(UTC).timestamp()}"
outside.mkdir()
(outside / "escape.wav").write_bytes(b"escape")
(tmp_path / "link").symlink_to(outside, target_is_directory=True)
manifest = _manifest(tmp_path)
manifest["inputs"][0]["fragment"]["path"] = "link/escape.wav"
with pytest.raises(experiment.PreflightError, match="path-escape"):
experiment.validate_manifest(manifest, root=tmp_path, environment=_environment())
def test_preflight_rejects_nul_path_without_leaking_or_loading(tmp_path):
manifest = _manifest(tmp_path)
manifest["inputs"][0]["source_path"] = "private\x00source.wav"
calls = []
with pytest.raises(experiment.PreflightError) as raised:
experiment.preflight_and_load(
manifest,
root=tmp_path,
environment_probe=_environment,
w0_loader=lambda: calls.append("w0"),
candidate_loader=lambda: calls.append("candidate"),
)
assert str(raised.value) == "path-escape"
assert "private" not in str(raised.value)
assert calls == []
def test_preflight_rejects_legacy_cell_names_roots_and_recipe_pin_mismatch(tmp_path):
manifest = _manifest(tmp_path)
manifest["schedule"][0]["semantic_id"] = "41-W0"
with pytest.raises(experiment.PreflightError, match="legacy-identity"):
experiment.validate_manifest(manifest, root=tmp_path, environment=_environment())
manifest = _manifest(tmp_path)
manifest["schedule"][0]["semantic_id"] = "W0-FRESH"
with pytest.raises(experiment.PreflightError, match="schedule-mismatch"):
experiment.validate_manifest(manifest, root=tmp_path, environment=_environment())
manifest = _manifest(tmp_path)
manifest["inputs"][0]["fragment"]["path"] = "issue-42-store/audio.wav"
with pytest.raises(experiment.PreflightError, match="legacy-root"):
experiment.validate_manifest(manifest, root=tmp_path, environment=_environment())
manifest = _manifest(tmp_path)
manifest["recipes"]["W0-FRESH"]["intra_op_threads"] = 4
with pytest.raises(experiment.PreflightError, match="w0-pin-mismatch"):
experiment.validate_manifest(manifest, root=tmp_path, environment=_environment())
manifest = _manifest(tmp_path)
manifest["recipes"]["SORTFORMER-V2-Q8"]["geometry"]["chunk_frames"] = 320
with pytest.raises(experiment.PreflightError, match="candidate-pin-mismatch"):
experiment.validate_manifest(manifest, root=tmp_path, environment=_environment())
manifest = _manifest(tmp_path)
manifest["recipes"]["SORTFORMER-V2-Q8"]["submodule_commits"]["extra"] = "9" * 40
with pytest.raises(experiment.PreflightError, match="candidate-build-provenance"):
experiment.validate_manifest(manifest, root=tmp_path, environment=_environment())
def test_schedule_has_exact_primary_five_final_and_three_gated_full_pairs(tmp_path):
manifest = _manifest(tmp_path)
schedule = manifest["schedule"]
assert [(cell["pair_id"], cell["recipe"]) for cell in schedule] == [
("primary", "W0-FRESH"),
("primary", "SORTFORMER-V2-Q8"),
("final-1", "SORTFORMER-V2-Q8"),
("final-1", "W0-FRESH"),
("final-2", "W0-FRESH"),
("final-2", "SORTFORMER-V2-Q8"),
("final-3", "SORTFORMER-V2-Q8"),
("final-3", "W0-FRESH"),
("final-4", "W0-FRESH"),
("final-4", "SORTFORMER-V2-Q8"),
("final-5", "SORTFORMER-V2-Q8"),
("final-5", "W0-FRESH"),
]
assert len({cell["semantic_id"] for cell in schedule}) == 12
assert all(cell["fresh_process"] for cell in schedule)
for left, right in zip(schedule[::2], schedule[1::2], strict=True):
assert left["input_sha256"] == right["input_sha256"]
assert left["sidecar_sha256"] == right["sidecar_sha256"]
full_hashes = [item["full_recording"]["sha256"] for item in manifest["inputs"]]
sidecars = [
item["full_recording"]["sidecar_sha256"] for item in manifest["inputs"]
]
assert experiment.build_full_recording_schedule(
full_hashes,
sidecars,
final_median_speedup_percent=29.999,
experiment_state=None,
) == []
full = experiment.build_full_recording_schedule(
full_hashes,
sidecars,
final_median_speedup_percent=30.0,
experiment_state=None,
)
assert len(full) == 6
assert [cell["pair_id"] for cell in full] == [
"full-1", "full-1", "full-2", "full-2", "full-3", "full-3"
]
def test_fragment_state_machine_applies_primary_stop_before_final_cells(tmp_path):
manifest = _manifest(tmp_path)
store = experiment.ExperimentState(tmp_path / "primary-stop.json")
calls = []
def runner(cell):
calls.append(cell["pair_id"])
walls = [10.0, 10.0, 10.0]
if cell["recipe"] == "SORTFORMER-V2-Q8":
walls = [9.7, 9.7, 9.7]
return {"mandatory_passed": True, "input_wall_seconds": walls}
result = experiment.run_fragment_state_machine(
manifest, runner=runner, store=store
)
assert result["state"] == "stop"
assert result["first_reason"] == "equal-speed"
assert calls == ["primary", "primary"]
with pytest.raises(experiment.PreflightError, match="already-stopped"):
experiment.build_full_recording_schedule(
[item["full_recording"]["sha256"] for item in manifest["inputs"]],
[
item["full_recording"]["sidecar_sha256"]
for item in manifest["inputs"]
],
final_median_speedup_percent=40.0,
experiment_state=store.current,
)
def test_fragment_state_machine_aggregates_three_walls_and_opens_full_stage(tmp_path):
manifest = _manifest(tmp_path)
store = experiment.ExperimentState(tmp_path / "full-open.json")
calls = []
def runner(cell):
calls.append((cell["pair_id"], cell["recipe"]))
wall = 10.0 if cell["recipe"] == "W0-FRESH" else 6.0
return {
"mandatory_passed": True,
"input_wall_seconds": [wall, wall, wall],
}
result = experiment.run_fragment_state_machine(
manifest, runner=runner, store=store
)
assert len(calls) == 12
assert result["state"] == "full-recordings"
assert result["median_speedup_percent"] == pytest.approx(40.0)
assert len(result["full_schedule"]) == 6
@pytest.mark.parametrize(
("speedup", "state", "reason"),
[
(-5.0, "stop", "equal-speed"),
(5.0, "stop", "equal-speed"),
(5.0001, "stop", "below-continuation"),
(9.9999, "stop", "below-continuation"),
(10.0, "continue", None),
],
)
def test_primary_decision_preserves_five_and_ten_percent_boundaries(
speedup, state, reason
):
assert experiment.primary_decision(speedup, mandatory_gates_passed=True) == {
"state": state,
"reason": reason,
}
def test_final_statistics_use_exactly_five_values_and_thirty_percent_boundary():
values = [28.0, 29.0, 30.0, 31.0, 80.0]
assert experiment.final_statistics(values) == {
"median_speedup_percent": 30.0,
"mad_percentage_points": 1.0,
}
assert experiment.final_decision([28.0, 29.0, 29.999, 31.0, 80.0]) == {
"state": "stop",
"reason": "below-final-threshold",
"median_speedup_percent": 29.999,
"mad_percentage_points": pytest.approx(1.001),
}
assert experiment.final_decision(values)["state"] == "full-recordings"
with pytest.raises(ValueError, match="five-final-speedups"):
experiment.final_statistics([30.0] * 4)
def test_first_stop_is_atomic_and_later_cells_are_not_scheduled(tmp_path):
store = experiment.ExperimentState(tmp_path / "public-state.json")
calls = []
cells = [
{"semantic_id": "cell-a", "recipe": "W0-FRESH"},
{"semantic_id": "cell-b", "recipe": "SORTFORMER-V2-Q8"},
{"semantic_id": "cell-c", "recipe": "W0-FRESH"},
]
state = experiment.execute_until_stop(
cells,
lambda cell: calls.append(cell["semantic_id"])
or ({"mandatory_passed": cell["semantic_id"] != "cell-b"}),
store=store,
)
assert calls == ["cell-a", "cell-b"]
assert state["state"] == "stop"
assert state["first_reason"] == "mandatory-gate-failed"
assert experiment.SHA256_RE.fullmatch(state["result_sha256"])
persisted = json.loads((tmp_path / "public-state.json").read_text())
assert persisted == state
assert store.stop("later-reason", {"ignored": True}) == state
reloaded = experiment.ExperimentState(tmp_path / "public-state.json")
assert reloaded.stop("process-restarted", {"ignored": "again"}) == state
def _word(start, end, text, speaker):
return {"start": start, "end": end, "text": text, "speaker": speaker}
def _sidecar_word(start, end, text):
return {"start": start, "end": end, "text": text}
def test_quality_gate_checks_text_order_intervals_counts_and_canonical_labels():
sidecar = [
_sidecar_word(0.0, 0.5, "один"),
_sidecar_word(0.5, 1.0, "два"),
]
output = [_word(0.0, 0.5, "один", 9), _word(0.5, 1.0, "два", 4)]
intervals = [
{"start": 0.0, "end": 0.5, "speaker": 9},
{"start": 0.5, "end": 1.0, "speaker": 4},
]
verdict = experiment.quality_gate(
expected_cluster_count=2,
observed_cluster_count=2,
sidecar_words=sidecar,
output_words=output,
intervals=intervals,
audio_duration_seconds=1.0,
permitted_padding_seconds=0.0,
)
assert verdict == {"passed": True, "reason": None, "canonical_labels": [1, 2]}
reordered = [output[1], output[0]]
assert experiment.quality_gate(
expected_cluster_count=2,
observed_cluster_count=2,
sidecar_words=sidecar,
output_words=reordered,
intervals=intervals,
audio_duration_seconds=1.0,
permitted_padding_seconds=0.0,
)["reason"] == "word-order-mismatch"
invalid = copy.deepcopy(intervals)
invalid[1]["start"] = float("nan")
assert experiment.quality_gate(
expected_cluster_count=2,
observed_cluster_count=2,
sidecar_words=sidecar,
output_words=output,
intervals=invalid,
audio_duration_seconds=1.0,
permitted_padding_seconds=0.0,
)["reason"] == "invalid-intervals"
def test_quality_gate_normalizes_text_and_canonicalizes_tied_intervals():
sidecar = [
_sidecar_word(0.0, 0.5, " один "),
_sidecar_word(0.5, 1.0, " два "),
]
output = [_word(0.0, 0.5, "один", "z"), _word(0.5, 1.0, "два", "a")]
intervals = [
{"start": 0.0, "end": 0.5, "speaker": "z"},
{"start": 0.0, "end": 1.0, "speaker": "a"},
]
first = experiment.quality_gate(
expected_cluster_count=2,
observed_cluster_count=2,
sidecar_words=sidecar,
output_words=output,
intervals=intervals,
audio_duration_seconds=1.0,
permitted_padding_seconds=0.0,
)
second = experiment.quality_gate(
expected_cluster_count=2,
observed_cluster_count=2,
sidecar_words=sidecar,
output_words=output,
intervals=list(reversed(intervals)),
audio_duration_seconds=1.0,
permitted_padding_seconds=0.0,
)
assert first == second == {
"passed": True,
"reason": None,
"canonical_labels": [1, 2],
}
duplicate_cluster = [
{"start": 0.0, "end": 0.5, "speaker": "z"},
{"start": 0.5, "end": 1.0, "speaker": "z"},
]
assert experiment.quality_gate(
expected_cluster_count=2,
observed_cluster_count=2,
sidecar_words=sidecar,
output_words=output,
intervals=duplicate_cluster,
audio_duration_seconds=1.0,
permitted_padding_seconds=0.0,
)["reason"] == "cluster-count-mismatch"
def test_diagnostics_only_add_safe_listening_locations_and_hearing_needs_user():
diagnostics = experiment.diagnostic_listening_locations(
baseline={
"purity": 0.8,
"unmatched_words": 2,
"channel_stability": 0.9,
"short_turns": 4,
"overlap": 0.1,
"residual_clusters": 0,
},
candidate={
"purity": 0.7,
"unmatched_words": 3,
"channel_stability": 0.8,
"short_turns": 5,
"overlap": 0.2,
"residual_clusters": 1,
},
location_sha256=["9" * 64],
)
assert diagnostics == {"mandatory_passed": True, "location_sha256": ["9" * 64]}
assert experiment.hearing_gate({}) == {
"state": "needs-user",
"reason": "hearing-decisions-required",
}
categories = {category: "pass" for category in experiment.HEARING_CATEGORIES}
assert experiment.hearing_gate(categories) == {"state": "go", "reason": None}
categories[experiment.HEARING_CATEGORIES[2]] = "fail"
assert experiment.hearing_gate(categories) == {
"state": "stop",
"reason": "hearing-gate-failed",
}
def test_blinded_review_material_is_seeded_categorized_and_procedural():
diagnostic = {
category: [hashlib.sha256(f"diagnostic-{category}".encode()).hexdigest()]
for category in experiment.HEARING_CATEGORIES
}
matching = {
category: [
hashlib.sha256(f"match-a-{category}".encode()).hexdigest(),
hashlib.sha256(f"match-b-{category}".encode()).hexdigest(),
]
for category in experiment.HEARING_CATEGORIES
}
blinded, reveal = experiment.build_blinded_review_material(
diagnostic_locations=diagnostic,
matching_locations=matching,
private_seed_sha256="a" * 64,
matching_sample_size=1,
)
repeated, repeated_reveal = experiment.build_blinded_review_material(
diagnostic_locations=diagnostic,
matching_locations=matching,
private_seed_sha256="a" * 64,
matching_sample_size=1,
)
assert blinded == repeated
assert reveal == repeated_reveal
assert set(blinded["categories"]) == set(experiment.HEARING_CATEGORIES)
assert all(len(items) == 2 for items in blinded["categories"].values())
assert set(reveal["pair_order"].values()) <= {
("W0-FRESH", "SORTFORMER-V2-Q8"),
("SORTFORMER-V2-Q8", "W0-FRESH"),
}
serialized = experiment.serialize_public(blinded)
assert "W0-FRESH" not in serialized
assert "SORTFORMER-V2-Q8" not in serialized
assert blinded["reveal_sha256"] == experiment.canonical_sha256(reveal)
assert "процедурного ослепления" in (
experiment.build_blinded_review_material.__doc__ or ""
)
assert "может восстановить порядок" in (
experiment.build_blinded_review_material.__doc__ or ""
)
def test_memory_gate_uses_frozen_plateau_and_linear_growth_policy_boundaries():
mib = 1024**2
passing = experiment.memory_gate(
stage="fragment",
oom=False,
swap_before_bytes=0,
swap_after_bytes=0,
peak_rss_bytes=300 * mib,
rss_after_inputs=[100 * mib, 170 * mib, 234 * mib],
machine_ram_bytes=16 * 1024**3,
)
assert passing == {
"state": "pass",
"reason": None,
"plateau_policy": "fragment-only:last-delta<=max(64MiB,5%-of-max-post-input-rss);linear=all-deltas>same-tolerance",
}
assert experiment.memory_gate(
stage="fragment",
oom=False,
swap_before_bytes=0,
swap_after_bytes=0,
peak_rss_bytes=300 * mib,
rss_after_inputs=[100 * mib, 170 * mib, 235 * mib],
machine_ram_bytes=16 * 1024**3,
)["reason"] == "rss-linear-growth"
assert experiment.memory_gate(
stage="fragment",
oom=False,
swap_before_bytes=0,
swap_after_bytes=1,
peak_rss_bytes=300 * mib,
rss_after_inputs=[100 * mib, 120 * mib, 121 * mib],
machine_ram_bytes=16 * 1024**3,
)["reason"] is None
assert experiment.memory_gate(
stage="fragment",
oom=True,
swap_before_bytes=0,
swap_after_bytes=0,
peak_rss_bytes=0,
rss_after_inputs=[0, 0, 0],
machine_ram_bytes=16 * 1024**3,
)["reason"] == "oom"
def test_memory_gate_allows_preoccupied_swap() -> None:
mib = 1024**2
result = experiment.memory_gate(
stage="fragment",
oom=False,
swap_before_bytes=1_730_000_000,
swap_after_bytes=1_730_000_000,
peak_rss_bytes=300 * mib,
rss_after_inputs=[100 * mib, 120 * mib, 121 * mib],
machine_ram_bytes=16 * 1024**3,
)
assert result["state"] == "pass"
assert result["reason"] is None
def test_memory_gate_marks_two_gibibyte_review_as_needs_user():
result = experiment.memory_gate(
stage="fragment",
oom=False,
swap_before_bytes=0,
swap_after_bytes=0,
peak_rss_bytes=2 * 1024**3 + 1,
rss_after_inputs=[2 * 1024**3, 2 * 1024**3, 2 * 1024**3],
machine_ram_bytes=16 * 1024**3,
)
assert result["state"] == "needs-user"
assert result["reason"] == "peak-rss-review"
reserved_ram = experiment.memory_gate(
stage="fragment",
oom=False,
swap_before_bytes=0,
swap_after_bytes=0,
peak_rss_bytes=3 * 1024**3,
rss_after_inputs=[1024**3, 1024**3, 1024**3],
machine_ram_bytes=16_601_477_120,
)
assert reserved_ram["state"] == "needs-user"
def test_memory_gate_skips_unsupported_plateau_in_single_full_recording_cell():
result = experiment.memory_gate(
stage="full",
oom=False,
swap_before_bytes=0,
swap_after_bytes=0,
peak_rss_bytes=1024**3,
rss_after_inputs=[900 * 1024**2],
machine_ram_bytes=16 * 1024**3,
)
assert result == {
"state": "pass",
"reason": None,
"plateau_policy": "fragment-only:last-delta<=max(64MiB,5%-of-max-post-input-rss);linear=all-deltas>same-tolerance",
}
gib = 1024**3
divergent = experiment.memory_gate(
stage="fragment",
oom=False,
swap_before_bytes=0,
swap_after_bytes=0,
peak_rss_bytes=4 * gib,
rss_after_inputs=[gib, int(1.1 * gib), int(1.18 * gib)],
machine_ram_bytes=32 * gib,
)
assert divergent["reason"] == "rss-linear-growth"
def _sortformer_gate(**changes):
recipe = _recipes()["SORTFORMER-V2-Q8"]
values = {
"effective_recipe": recipe,
"probabilities": [[0.0, 0.25, 0.5, 1.0], [0.1, 0.2, 0.3, 0.4]],
"labels": [1, 4],
"frame_count": 2,
"frame_probs_start": 0,
"segments": [
{"start": 0.0, "end": 0.08, "speaker": 1},
{"start": 0.08, "end": 0.16, "speaker": 4},
],
"channel_interval_counts": {1: 1, 2: 0, 3: 0, 4: 1},
"channel_durations_seconds": {1: 0.08, 2: 0.0, 3: 0.0, 4: 0.08},
"inference_wall_seconds": 1.0,
"post_processing_wall_seconds": 0.1,
}
values.update(changes)
return experiment.sortformer_gate(**values)
def test_sortformer_gate_checks_effective_config_shape_values_labels_and_metrics():
assert _sortformer_gate() == {"passed": True, "reason": None}
assert _sortformer_gate(probabilities=[[0.1, 0.2, 0.3]])["reason"] == (
"probability-shape"
)
assert _sortformer_gate(probabilities=[[0.1, 0.2, float("nan"), 0.4]])[
"reason"
] == "probability-value"
assert _sortformer_gate(labels=[0, 4])["reason"] == "speaker-label"
changed = _recipes()["SORTFORMER-V2-Q8"]
changed["seconds_per_frame"] = 0.1
assert _sortformer_gate(effective_recipe=changed)["reason"] == (
"effective-config-mismatch"
)
assert _sortformer_gate(inference_wall_seconds=-1)["reason"] == (
"invalid-stage-wall"
)
assert _sortformer_gate(
frame_count=5,
frame_probs_start=3,
) == {"passed": True, "reason": None}
assert _sortformer_gate(channel_interval_counts={1: 2, 2: 0, 3: 0, 4: 0})[
"reason"
] == "channel-counts"
class _Measurement:
def __init__(self, trace):
self.trace = trace
self.walls = {
"w0-process": 10.0,
"candidate-inference": 5.5,
"candidate-post-processing": 0.5,
}
def measure(self, label, operation):
self.trace.append(f"timer-start:{label}")
value = operation()
self.trace.append(f"timer-stop:{label}")
return value, {
"wall_seconds": self.walls[label],
"user_cpu_seconds": 1.0,
"system_cpu_seconds": 0.1,
"peak_rss_bytes": 100,
}
def test_measurement_boundary_times_only_exact_w0_and_candidate_operations():
trace = []
samples = np.asarray([0.0, 0.25], dtype=np.float32)
class W0:
def process(self, actual):
assert actual is samples
trace.append("w0-process")
return "w0-result"
class Runtime:
def prepare_samples(self, actual):
assert actual is samples
trace.append("prepare-samples")
return "prepared-buffer"
def stream_open(self):
trace.append("stream-open")
return "stream"
def stream_push(self, stream, actual):
assert stream == "stream" and actual == "prepared-buffer"
trace.append("stream-push")
def stream_finish(self, stream):
assert stream == "stream"
trace.append("stream-finish")
return "raw-segments"
def stream_close(self, stream):
assert stream == "stream"
trace.append("stream-close")
measurement = _Measurement(trace)
w0 = experiment.measure_w0(W0(), samples, measurement)
candidate = experiment.measure_candidate(
Runtime(),
samples,
lambda raw: trace.append("post-processing") or [raw],
measurement,
)
assert w0["result"] == "w0-result"
assert w0["metrics"]["wall_seconds"] == 10.0
assert candidate["result"] == ["raw-segments"]
assert candidate["metrics"]["wall_seconds"] == 6.0
assert candidate["inference_wall_seconds"] == 5.5
assert candidate["post_processing_wall_seconds"] == 0.5
assert trace == [
"timer-start:w0-process",
"w0-process",
"timer-stop:w0-process",
"prepare-samples",
"timer-start:candidate-inference",
"stream-open",
"stream-push",
"stream-finish",
"timer-stop:candidate-inference",
"timer-start:candidate-post-processing",
"post-processing",
"stream-close",
"timer-stop:candidate-post-processing",
]
def _guard(recipe="W0-FRESH", **changes):
result = {
"machine_sha256": "1" * 64,
"affinity": list(range(8)),
"logical_cores": 16,
"physical_cores": 8,
"cpuid_isa_sha256": "2" * 64,
"ram_bytes": 16 * 1024**3,
"os_sha256": "3" * 64,
"compiler_sha256": "4" * 64,
"runtime_versions_sha256": "5" * 64,
"recipe": recipe,
"effective_threads": 8 if recipe == "W0-FRESH" else 4,
"power_mode": "balanced",
"governor": "powersave",
"turbo": True,
"throttling": False,
"throttle_count": 10,
"swap_used_bytes": 0,
"swap_sin_bytes": 0,
"swap_sout_bytes": 0,
}
result.update(changes)
return result
def test_pair_guards_require_same_machine_affinity_and_frozen_thread_counts():
assert experiment.validate_pair_guards(
_guard(),
_guard(),
_guard("SORTFORMER-V2-Q8"),
_guard("SORTFORMER-V2-Q8"),
expected_machine=_machine(),
) == []
affinity_reasons = experiment.validate_pair_guards(
_guard(),
_guard(),
_guard("SORTFORMER-V2-Q8", affinity=list(range(1, 9))),
_guard("SORTFORMER-V2-Q8", affinity=list(range(1, 9))),
expected_machine=_machine(),
)
assert set(affinity_reasons) == {
"manifest-machine-mismatch",
"pair-affinity-mismatch",
}
assert "effective-threads-mismatch" in experiment.validate_pair_guards(
_guard(effective_threads=4),
_guard(effective_threads=4),
_guard("SORTFORMER-V2-Q8"),
_guard("SORTFORMER-V2-Q8"),
expected_machine=_machine(),
)
assert "pair-power-mismatch" in experiment.validate_pair_guards(
_guard(),
_guard(),
_guard("SORTFORMER-V2-Q8", governor="performance"),
_guard("SORTFORMER-V2-Q8", governor="performance"),
expected_machine=_machine(),
)
wrong = _guard(affinity=list(range(8, 16)))
assert "manifest-machine-mismatch" in experiment.validate_pair_guards(
wrong,
wrong,
_guard("SORTFORMER-V2-Q8", affinity=list(range(8, 16))),
_guard("SORTFORMER-V2-Q8", affinity=list(range(8, 16))),
expected_machine=_machine(),
)
def test_guarded_cell_excludes_invalid_wall_and_retries_once_after_stabilization():
snapshots = iter(
[
_guard(),
_guard(power_mode="performance"),
_guard(),
_guard(),
]
)
walls = iter([100.0, 50.0])
stabilizations = []
result = experiment.run_guarded_cell(
{"semantic_id": "safe-cell", "recipe": "W0-FRESH"},
runner=lambda _request: {"wall_seconds": next(walls)},
guard_reader=lambda: next(snapshots),
stabilize=lambda: stabilizations.append("stabilized"),
expected_machine=_machine(),
)
assert result["state"] == "accepted"
assert result["result"] == {"wall_seconds": 50.0}
assert stabilizations == ["stabilized"]
assert result["attempts"][0]["valid"] is False
assert "result" not in result["attempts"][0]
assert result["attempts"][1]["valid"] is True
def test_guarded_cell_accepts_stable_manifest_declared_performance_mode():
machine = _machine()
machine["power_mode"] = "performance"
result = experiment.run_guarded_cell(
{"semantic_id": "safe-cell", "recipe": "W0-FRESH"},
runner=lambda _request: {"wall_seconds": 1.0},
guard_reader=lambda: _guard(power_mode="performance"),
stabilize=lambda: pytest.fail("stable mode must not retry"),
expected_machine=machine,
)
assert result["state"] == "accepted"
assert len(result["attempts"]) == 1
def test_second_guard_invalidation_stops_with_first_reason():
throttled = iter(
[
_guard(),
_guard(throttling=True, throttle_count=11),
_guard(),
_guard(throttling=True, throttle_count=11),
]
)
second = experiment.run_guarded_cell(
{"semantic_id": "safe-cell", "recipe": "W0-FRESH"},
runner=lambda _request: {"wall_seconds": 99.0},
guard_reader=lambda: next(throttled),
stabilize=lambda: None,
expected_machine=_machine(),
)
assert second["state"] == "stop"
assert second["reason"] == "new-throttling"
assert len(second["attempts"]) == 2
def test_guarded_cell_allows_preoccupied_swap_when_counters_stay_stable():
snapshots = iter(
[
_guard(
swap_used_bytes=1_730_000_000,
swap_sin_bytes=100,
swap_sout_bytes=200,
),
_guard(
swap_used_bytes=1_730_000_000,
swap_sin_bytes=100,
swap_sout_bytes=200,
),
]
)
result = experiment.run_guarded_cell(
{"semantic_id": "safe-cell", "recipe": "W0-FRESH"},
runner=lambda _request: {"wall_seconds": 1.0},
guard_reader=lambda: next(snapshots),
stabilize=lambda: None,
expected_machine=_machine(),
)
assert result["state"] == "accepted"
assert len(result["attempts"]) == 1
def test_oom_stops_without_retry_and_private_result_is_written_atomically(tmp_path):
calls = []
result = experiment.run_guarded_cell(
{"semantic_id": "safe-cell", "recipe": "W0-FRESH"},
runner=lambda _request: (_ for _ in ()).throw(MemoryError("private")),
guard_reader=lambda: _guard(),
stabilize=lambda: calls.append("stabilize"),
expected_machine=_machine(),
)
assert result["state"] == "stop"
assert result["reason"] == "oom"
assert calls == []
assert "private" not in json.dumps(result)
private_path = tmp_path / "closed" / "raw-result.json"
digest = experiment.persist_private_result(
private_path, {"raw_rows": [1, 2, 3], "secret": "allowed-here"}
)
assert digest == experiment.file_sha256(private_path)
assert sorted(path.name for path in private_path.parent.iterdir()) == [
"raw-result.json"
]
failed_path = tmp_path / "failed" / "raw-result.json"
with pytest.raises(ValueError):
experiment.persist_private_result(
failed_path, {"probability": float("nan"), "words": ["private"]}
)
assert list(failed_path.parent.iterdir()) == []
def test_guarded_cell_does_not_retry_hard_mismatch_and_sanitizes_exceptions():
snapshots = iter([_guard(), _guard(governor="performance")])
stabilizations = []
hard = experiment.run_guarded_cell(
{"semantic_id": "safe-cell", "recipe": "W0-FRESH"},
runner=lambda _request: {"wall_seconds": 1.0},
guard_reader=lambda: next(snapshots),
stabilize=lambda: stabilizations.append("unexpected"),
expected_machine=_machine(),
)
assert hard["state"] == "stop"
assert hard["reason"] == "runtime-power-setting-changed"
assert len(hard["attempts"]) == 1
assert stabilizations == []
failed = experiment.run_guarded_cell(
{"semantic_id": "safe-cell", "recipe": "W0-FRESH"},
runner=lambda _request: (_ for _ in ()).throw(RuntimeError("/private/path")),
guard_reader=lambda: _guard(),
stabilize=lambda: None,
expected_machine=_machine(),
)
assert failed["state"] == "stop"
assert failed["reason"] == "runtime-error"
assert "/private/path" not in json.dumps(failed)
unreadable = experiment.run_guarded_cell(
{"semantic_id": "safe-cell", "recipe": "W0-FRESH"},
runner=lambda _request: {"wall_seconds": 1.0},
guard_reader=lambda: (_ for _ in ()).throw(OSError("/private/guard")),
stabilize=lambda: None,
expected_machine=_machine(),
)
assert unreadable["state"] == "stop"
assert unreadable["reason"] == "runtime-guard-unavailable"
assert "/private/guard" not in json.dumps(unreadable)
calls = []
wrong_recipe = experiment.run_guarded_cell(
{"semantic_id": "safe-cell", "recipe": "SORTFORMER-V2-Q8"},
runner=lambda _request: calls.append("run") or {"wall_seconds": 1.0},
guard_reader=lambda: _guard("W0-FRESH"),
stabilize=lambda: None,
expected_machine=_machine(),
)
assert wrong_recipe["reason"] == "runtime-recipe-mismatch"
assert calls == []
@pytest.mark.parametrize("counter", ["swap_sin_bytes", "swap_sout_bytes"])
def test_guarded_cell_hashes_result_rejected_by_after_run_swap(counter):
snapshots = iter([_guard(), _guard(**{counter: 1})])
raw_result = {"wall_seconds": 1.0, "private": "not-published"}
result = experiment.run_guarded_cell(
{"semantic_id": "safe-cell", "recipe": "W0-FRESH"},
runner=lambda _request: raw_result,
guard_reader=lambda: next(snapshots),
stabilize=lambda: None,
expected_machine=_machine(),
)
assert result["state"] == "stop"
assert result["reason"] == "active-swap"
assert result["attempts"][0]["invalid_result_sha256"] == (
experiment.canonical_sha256(raw_result)
)
assert "result" not in result["attempts"][0]
def test_non_finite_private_result_becomes_sanitized_first_stop(tmp_path):
guarded = experiment.run_guarded_cell(
{"semantic_id": "safe-cell", "recipe": "W0-FRESH"},
runner=lambda _request: {"wall_seconds": float("nan")},
guard_reader=lambda: _guard(),
stabilize=lambda: None,
expected_machine=_machine(),
)
assert guarded["state"] == "stop"
assert guarded["reason"] == "invalid-result-value"
assert "nan" not in json.dumps(guarded)
store = experiment.ExperimentState(tmp_path / "public-state.json")
state = store.stop("mandatory-gate-failed", {"probability": float("nan")})
assert state["first_reason"] == "invalid-result-value"
assert experiment.SHA256_RE.fullmatch(state["result_sha256"])
def test_public_plan_and_result_are_redacted_and_privacy_scanned(tmp_path):
manifest = _manifest(tmp_path)
outcome = experiment.validate_manifest(
manifest, root=tmp_path, environment=_environment()
)
plan = experiment.safe_public_plan(manifest, outcome)
serialized = experiment.serialize_public(plan)
assert plan["state"] == "valid"
assert plan["recipes"] == ["W0-FRESH", "SORTFORMER-V2-Q8"]
assert plan["fragment_count"] == 3
assert plan["scheduled_cell_count"] == 12
assert str(tmp_path) not in serialized
assert "fragment-0" not in serialized
assert "words-fragment" not in serialized
experiment.privacy_scan(plan)
report = experiment.safe_public_result(
source_commit="a" * 40,
result_sha256="b" * 64,
aggregate_timings={"w0_wall_seconds": 100.0, "candidate_wall_seconds": 60.0},
deltas={"speedup_percent": 40.0},
statistics_values={
"median_speedup_percent": 40.0,
"mad_percentage_points": 1.0,
},
gate_verdicts={"quality": "pass", "hearing": "needs-user"},
limitations=["four-speaker-output-limit", "non-english-domain-risk"],
first_stop_reason=None,
)
experiment.privacy_scan(report)
assert report["state"] == "needs-user"
assert report["limitations"][-1] == "procedural-blinding"
with pytest.raises(experiment.PrivacyError):
experiment.privacy_scan({"intervals": [{"start": 1.0}]})
with pytest.raises(experiment.PrivacyError):
experiment.privacy_scan({"note": str(tmp_path / "secret.wav")})
with pytest.raises(experiment.PrivacyError):
experiment.privacy_scan({"source_path": "relative-but-private.wav"})
with pytest.raises(experiment.PrivacyError):
experiment.privacy_scan({"note": "/opt/private/model.bin"})
with pytest.raises(experiment.PrivacyError):
experiment.privacy_scan({"note": "failure at /opt/private/model.bin"})
with pytest.raises(experiment.PreflightError, match="public-numeric-value"):
experiment.safe_public_result(
source_commit="a" * 40,
result_sha256="b" * 64,
aggregate_timings={"note": "private transcript"},
deltas={},
statistics_values={},
gate_verdicts={},
limitations=[],
first_stop_reason=None,
)
def test_public_result_requires_all_gates_and_final_speed_threshold_for_go():
common = {
"source_commit": "a" * 40,
"result_sha256": "b" * 64,
"aggregate_timings": {"candidate_wall_seconds": 60.0},
"deltas": {"speedup_percent": 40.0},
"limitations": ["four-speaker-output-limit"],
"first_stop_reason": None,
}
automatic = {
"quality": "pass",
"sortformer": "pass",
"memory": "pass",
"measurement": "pass",
"hearing": "pass",
}
below = experiment.safe_public_result(
**common,
statistics_values={"median_speedup_percent": 29.999},
gate_verdicts=automatic,
)
assert below["state"] == "stop"
assert below["first_stop_reason"] == "below-final-threshold"
incomplete = experiment.safe_public_result(
**common,
statistics_values={"median_speedup_percent": 40.0},
gate_verdicts={**automatic, "quality": "needs-user"},
)
assert incomplete["state"] == "needs-user"
go = experiment.safe_public_result(
**common,
statistics_values={"median_speedup_percent": 30.0},
gate_verdicts=automatic,
)
assert go["state"] == "go"
assert go["limitations"] == [
"four-speaker-output-limit",
"procedural-blinding",
]
deduplicated = experiment.safe_public_result(
**{**common, "limitations": ["procedural-blinding"]},
statistics_values={"median_speedup_percent": 30.0},
gate_verdicts=automatic,
)
assert deduplicated["limitations"] == ["procedural-blinding"]
def test_validate_only_cli_is_useful_redacted_and_never_calls_loader(
tmp_path, capsys
):
manifest = _manifest(tmp_path)
manifest_path = tmp_path / "manifest.json"
manifest_path.write_text(json.dumps(manifest), encoding="utf-8")
calls = []
exit_code = experiment.run_cli(
SimpleNamespace(manifest=manifest_path, validate_only=True),
environment_probe=_environment,
w0_loader=lambda: calls.append("w0"),
candidate_loader=lambda: calls.append("candidate"),
)
output = capsys.readouterr().out
assert exit_code == 0
assert calls == []
assert '"schema_status": "pass"' in output
assert '"pin_status": "pass"' in output
assert '"schedule_status": "pass"' in output
assert str(tmp_path) not in output
manifest["fresh_basis"]["approval"] = None
manifest_path.write_text(json.dumps(manifest), encoding="utf-8")
assert experiment.run_cli(
SimpleNamespace(manifest=manifest_path, validate_only=True),
environment_probe=_environment,
w0_loader=lambda: calls.append("w0"),
candidate_loader=lambda: calls.append("candidate"),
) == 0
assert json.loads(capsys.readouterr().out)["state"] == "needs-user"
def test_invalid_cli_output_has_one_trailing_newline(tmp_path, capsys):
manifest_path = tmp_path / "invalid.json"
manifest_path.write_text("{}", encoding="utf-8")
with pytest.raises(SystemExit) as raised:
experiment.main(["--manifest", str(manifest_path), "--validate-only"])
assert raised.value.code == 2
output = capsys.readouterr().out
assert output.endswith("\n")
assert not output.endswith("\n\n")
def test_child_process_request_result_seam_uses_atomic_private_files(tmp_path):
request = {
"semantic_id": "cell-safe",
"recipe": "W0-FRESH",
"input_sha256": ["1" * 64, "2" * 64, "3" * 64],
"sidecar_sha256": ["4" * 64, "5" * 64, "6" * 64],
}
commands = []
def fake_process(command):
commands.append(command)
request_path = Path(command[-1])
payload = json.loads(request_path.read_text())
response = {
"worker_schema": experiment.WORKER_SCHEMA,
"semantic_id": payload["semantic_id"],
"status": "complete",
"result": {"wall_seconds": 1.0},
"result_sha256": experiment.canonical_sha256({"wall_seconds": 1.0}),
}
Path(payload["response_path"]).write_text(json.dumps(response))
return SimpleNamespace(returncode=0, stdout="private log", stderr="")
result = experiment.run_child_process(
request, work_dir=tmp_path, process_runner=fake_process
)
assert result["result"] == {"wall_seconds": 1.0}
assert len(commands) == 1
assert (tmp_path / "logs" / "cell-safe.stdout.log").read_text() == "private log"
stale_runner = lambda _command: SimpleNamespace(
returncode=0, stdout="", stderr=""
)
with pytest.raises(RuntimeError, match="child-process-failed"):
experiment.run_child_process(
request, work_dir=tmp_path, process_runner=stale_runner
)
def test_child_process_requires_an_explicit_worker_runner(tmp_path):
request = {
"semantic_id": "cell-safe",
"recipe": "W0-FRESH",
"input_sha256": ["1" * 64],
"sidecar_sha256": ["2" * 64],
}
with pytest.raises(experiment.PreflightError, match="worker-runner-required"):
experiment.run_child_process(request, work_dir=tmp_path)
class _FakeFunction:
def __init__(self, function):
self.function = function
self.argtypes = None
self.restype = None
def __call__(self, *args):
return self.function(*args)
class _FakeCAbi:
def __init__(
self,
*,
speakers=4,
seconds_per_frame=0.08,
create_status=0,
grown_segment_count=False,
):
self.speakers = speakers
self.seconds = seconds_per_frame
self.create_status = create_status
self.grown_segment_count = grown_segment_count
self.seen = None
self.pushed = None
self.closed = False
for name in dir(self):
if name.startswith("nemo_speech_"):
setattr(self, name, _FakeFunction(getattr(self, name)))
def nemo_speech_diar_create(self, config, output):
self.seen = config.contents
output._obj.value = 101
return self.create_status
def nemo_speech_diar_destroy(self, _model):
return None
def nemo_speech_diar_num_speakers(self, _model):
return self.speakers
def nemo_speech_diar_seconds_per_frame(self, _model):
return self.seconds
def nemo_speech_diar_stream_open(self, _model, output):
self.closed = False
output._obj.value = 202
return 0
def nemo_speech_diar_stream_push_f32(self, _stream, samples, count, sample_rate):
self.pushed = ([samples[index] for index in range(count)], sample_rate)
return 0
def nemo_speech_diar_stream_finish(self, _stream):
return 0
def nemo_speech_diar_stream_close(self, _stream):
self.closed = True
def nemo_speech_diar_frame_count(self, _stream):
assert not self.closed
return 2
def nemo_speech_diar_frame_probs_start(self, _stream):
assert not self.closed
return 0
def nemo_speech_diar_frame_probs(self, _stream, output, capacity):
assert not self.closed
assert capacity == 8
for index, value in enumerate((0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8)):
output[index] = value
return 0
def nemo_speech_diar_segments(self, _stream, config, output, capacity, count):
assert not self.closed
assert config.contents.onset == pytest.approx(0.641)
if not output:
count._obj.value = 2
return 0
assert capacity == 2
if self.grown_segment_count:
count._obj.value = 3
return 0
output[0].start_time = 0.0
output[0].end_time = 0.8
output[0].speaker = 1
output[1].start_time = 0.8
output[1].end_time = 1.6
output[1].speaker = 4
return 0
def test_fake_c_abi_validates_layout_cpu_selection_and_effective_config():
library = _FakeCAbi()
adapter = experiment.SortformerCAbi(library)
result = adapter.synthetic_check()
model = adapter.create_model("synthetic-model.gguf")
with pytest.raises(experiment.CAbiError, match="model-already-created"):
adapter.create_model("second-model.gguf")
stream = adapter.stream_open(model)
samples = np.asarray([0.0, 0.25], dtype=np.float32)
prepared = adapter.prepare_samples(samples)
assert prepared.owner is samples
assert ctypes.addressof(prepared.pointer.contents) == samples.ctypes.data
adapter.stream_push(stream, prepared, sample_rate=16_000)
adapter.stream_finish(stream)
output = adapter.collect_result(stream, model)
adapter.stream_close(stream)
adapter.destroy_model(model)
assert result == {
"model_config_size": 56,
"segmentation_config_size": 48,
"segment_size": 24,
"gpu": -1,
"num_speakers": 4,
"seconds_per_frame": pytest.approx(0.08),
"chunk_frames": 340,
"right_context_frames": 40,
}
assert library.seen.gpu == -1
assert library.seen.preset == b"streaming"
assert library.seen.update_period_frames == 300
assert library.pushed[0] == pytest.approx([0.0, 0.25])
assert library.pushed[1] == 16_000
assert output["frame_count"] == 2
assert output["frame_probs_start"] == 0
assert output["probabilities"][1] == pytest.approx([0.5, 0.6, 0.7, 0.8])
assert output["segments"] == [
{"start": 0.0, "end": 0.8, "speaker": 1},
{"start": 0.8, "end": 1.6, "speaker": 4},
]
def test_fake_c_abi_rejects_layout_and_runtime_config_mismatches():
with pytest.raises(experiment.CAbiError, match="layout"):
experiment.SortformerCAbi(_FakeCAbi(), expected_layout={"size": 999})
with pytest.raises(experiment.CAbiError, match="num-speakers"):
experiment.SortformerCAbi(_FakeCAbi(speakers=3)).synthetic_check()
with pytest.raises(experiment.CAbiError, match="seconds-per-frame"):
experiment.SortformerCAbi(_FakeCAbi(seconds_per_frame=0.1)).synthetic_check()
with pytest.raises(experiment.CAbiError, match="effective-config"):
experiment.SortformerCAbi(_FakeCAbi(create_status=1)).synthetic_check()
def test_fake_c_abi_load_boundary_configures_and_returns_adapter():
calls = []
library = _FakeCAbi()
adapter = experiment.SortformerCAbi.load(
Path("closed/libnemo_speech.so"),
loader=lambda path: calls.append(path) or library,
)
assert calls == ["closed/libnemo_speech.so"]
assert library.nemo_speech_diar_seconds_per_frame.restype is ctypes.c_double
assert library.nemo_speech_diar_frame_count.restype is ctypes.c_int64
assert adapter.synthetic_check()["num_speakers"] == 4
def test_fake_c_abi_rejects_segment_count_growth_beyond_owned_buffer():
adapter = experiment.SortformerCAbi(_FakeCAbi(grown_segment_count=True))
model = adapter.create_model("synthetic-model.gguf")
stream = adapter.stream_open(model)
with pytest.raises(experiment.CAbiError, match="segment-count-changed"):
adapter.collect_result(stream, model)
def test_candidate_measurement_collects_c_abi_result_before_stream_close():
trace = []
library = _FakeCAbi()
adapter = experiment.SortformerCAbi(library)
model = adapter.create_model("synthetic-model.gguf")
measured = experiment.measure_candidate(
adapter,
np.asarray([0.0, 0.25], dtype=np.float32),
lambda stream: adapter.collect_result(stream, model),
_Measurement(trace),
)
assert measured["result"]["frame_count"] == 2
assert library.closed is True