From 2bc42ee10275d7b4c84b6c5ffdebcafd8efd9e55 Mon Sep 17 00:00:00 2001 From: Jer Miller Date: Fri, 31 Jul 2026 13:45:13 -0600 Subject: [PATCH] fix(speakers): align analyze wire literals statement_embeddings.payload_format and statement_embeddings.dtype were validated against legacy Python literals. The Rust helper emits raw-f32le-row-major-v1 and float32-le, while the adapter wrongly required f32le and float32. core/crates/solstone-core-speakers-analyze/src/lib.rs remains the source of truth through const PAYLOAD_FORMAT and const DTYPE_F32LE. The adapter and scripts/release_install_smoke.py now share one constant pair in solstone/apps/speakers/encoder_config.py, pinned to that Rust source by tests/test_speakers_analyze_wire_constants.py. Carrier: vpe-98. --- scripts/release_install_smoke.py | 10 +- solstone/apps/speakers/encoder_config.py | 10 +- .../transcribe/speakers_analyze_adapter.py | 10 +- tests/test_speakers_analyze_adapter.py | 32 ++++++- tests/test_speakers_analyze_wire_constants.py | 91 +++++++++++++++++++ 5 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 tests/test_speakers_analyze_wire_constants.py diff --git a/scripts/release_install_smoke.py b/scripts/release_install_smoke.py index 35ac572e8..9cb6d7d49 100644 --- a/scripts/release_install_smoke.py +++ b/scripts/release_install_smoke.py @@ -38,7 +38,11 @@ from scripts.check_wheel_contents import ( ) from scripts.release_digest import file_sha256_size from scripts.release_public_evidence import validate_public_evidence_tree -from solstone.apps.speakers.encoder_config import WESPEAKER_EMBEDDING_WIDTH +from solstone.apps.speakers.encoder_config import ( + SPEAKERS_ANALYZE_DTYPE, + SPEAKERS_ANALYZE_PAYLOAD_FORMAT, + WESPEAKER_EMBEDDING_WIDTH, +) from solstone.think.model_assets import ( PYANNOTE_SEGMENTATION_MODEL_FILENAME, WESPEAKER_MODEL_FILENAME, @@ -1035,10 +1039,10 @@ def _speakers_analyze_stdout_payload( ("statement_embeddings", "byte_count"), _expected_speakers_analyze_byte_count(), ), - (("statement_embeddings", "dtype"), "float32-le"), + (("statement_embeddings", "dtype"), SPEAKERS_ANALYZE_DTYPE), ( ("statement_embeddings", "payload_format"), - "raw-f32le-row-major-v1", + SPEAKERS_ANALYZE_PAYLOAD_FORMAT, ), (("statement_embeddings", "payload_path"), expected_payload_path), ) diff --git a/solstone/apps/speakers/encoder_config.py b/solstone/apps/speakers/encoder_config.py index 6af263c65..982fa0073 100644 --- a/solstone/apps/speakers/encoder_config.py +++ b/solstone/apps/speakers/encoder_config.py @@ -1,11 +1,19 @@ # SPDX-License-Identifier: AGPL-3.0-only # Copyright (c) 2026 sol pbc -"""Encoder-specific calibration constants governed by operator spec signoff.""" +"""Encoder calibration constants and native speakers-analyze wire literals. + +Calibration thresholds are governed by operator spec signoff. The wire +literals mirror the values declared by +``core/crates/solstone-core-speakers-analyze/src/lib.rs`` and are pinned to that +source by ``tests/test_speakers_analyze_wire_constants.py``. +""" import math ENCODER_ID: str = "wespeaker-resnet34-256" +SPEAKERS_ANALYZE_PAYLOAD_FORMAT: str = "raw-f32le-row-major-v1" +SPEAKERS_ANALYZE_DTYPE: str = "float32-le" WESPEAKER_EMBEDDING_WIDTH: int = 256 WESPEAKER_MODEL_SHA256: str = ( "5ef208a9da1453335308a6b6f4e6dfbd7e183a38b604de0a57664f45d257fe94" diff --git a/solstone/observe/transcribe/speakers_analyze_adapter.py b/solstone/observe/transcribe/speakers_analyze_adapter.py index 8d9c0c10c..36018ba3c 100644 --- a/solstone/observe/transcribe/speakers_analyze_adapter.py +++ b/solstone/observe/transcribe/speakers_analyze_adapter.py @@ -22,6 +22,8 @@ import numpy as np from solstone.apps.speakers.encoder_config import ( ENCODER_ID, + SPEAKERS_ANALYZE_DTYPE, + SPEAKERS_ANALYZE_PAYLOAD_FORMAT, WESPEAKER_EMBEDDING_WIDTH, ) from solstone.apps.speakers.evidence import ( @@ -480,9 +482,13 @@ def _accepted_result_from_response( statement_embeddings = _required_object(response, "statement_embeddings") _require_equal(statement_embeddings, "audio_buffer", {"full", "reduced"}) _require_value(statement_embeddings, "encoder", ENCODER_ID) - _require_value(statement_embeddings, "payload_format", "f32le") + _require_value( + statement_embeddings, + "payload_format", + SPEAKERS_ANALYZE_PAYLOAD_FORMAT, + ) _require_value(statement_embeddings, "payload_path", str(payload_path)) - _require_value(statement_embeddings, "dtype", "float32") + _require_value(statement_embeddings, "dtype", SPEAKERS_ANALYZE_DTYPE) statement_ids = _required_int_list(statement_embeddings, "statement_ids") if len(statement_ids) != len(set(statement_ids)): raise NativePayloadError("payload", "duplicate-statement-id") diff --git a/tests/test_speakers_analyze_adapter.py b/tests/test_speakers_analyze_adapter.py index 77cd92c65..e06e861c6 100644 --- a/tests/test_speakers_analyze_adapter.py +++ b/tests/test_speakers_analyze_adapter.py @@ -13,7 +13,12 @@ from typing import Any import numpy as np import pytest -from solstone.apps.speakers.encoder_config import ENCODER_ID, WESPEAKER_EMBEDDING_WIDTH +from solstone.apps.speakers.encoder_config import ( + ENCODER_ID, + SPEAKERS_ANALYZE_DTYPE, + SPEAKERS_ANALYZE_PAYLOAD_FORMAT, + WESPEAKER_EMBEDDING_WIDTH, +) from solstone.observe.transcribe.speakers_analyze_adapter import ( RESPONSE_SCHEMA, TEMP_PREFIX, @@ -65,9 +70,9 @@ def _response( "statement_embeddings": { "audio_buffer": "full", "encoder": ENCODER_ID, - "payload_format": "f32le", + "payload_format": SPEAKERS_ANALYZE_PAYLOAD_FORMAT, "payload_path": "__filled_by_test__", - "dtype": "float32", + "dtype": SPEAKERS_ANALYZE_DTYPE, "statement_ids": statement_ids, "durations_s": [0.5 for _ in statement_ids] if durations_s is None @@ -190,6 +195,19 @@ def test_success_maps_request_response_payload_and_cleans_temp_dir(tmp_path: Pat assert result.overlap_fraction == 0.25 +def test_helper_shaped_wire_literals_are_accepted(tmp_path: Path): + response = _response() + assert ( + response["statement_embeddings"]["payload_format"] + == SPEAKERS_ANALYZE_PAYLOAD_FORMAT + ) + assert response["statement_embeddings"]["dtype"] == SPEAKERS_ANALYZE_DTYPE + + result, _request, _temp_dir = _run_adapter(tmp_path, response=response) + + assert result.embeddings_data is not None + + @pytest.mark.parametrize( ("mutate", "reason"), [ @@ -249,6 +267,14 @@ def test_success_maps_request_response_payload_and_cleans_temp_dir(tmp_path: Pat lambda r: r["diarization"].update(statement_labels=[1]), "statement-label-count-mismatch", ), + ( + lambda r: r["statement_embeddings"].update(payload_format="f32le"), + "invalid-payload-format", + ), + ( + lambda r: r["statement_embeddings"].update(dtype="float32"), + "invalid-dtype", + ), ], ) def test_response_validation_rejects_invalid_payload_shapes( diff --git a/tests/test_speakers_analyze_wire_constants.py b/tests/test_speakers_analyze_wire_constants.py new file mode 100644 index 000000000..9baab96c8 --- /dev/null +++ b/tests/test_speakers_analyze_wire_constants.py @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: AGPL-3.0-only +# Copyright (c) 2026 sol pbc + +"""Tests for native speakers-analyze wire constants.""" + +from __future__ import annotations + +import re +from pathlib import Path + +import pytest + +from solstone.apps.speakers.discovery import ( + DISCOVERY_CLUSTER_DTYPE, + DISCOVERY_CLUSTER_PAYLOAD_FORMAT, +) +from solstone.apps.speakers.encoder_config import ( + SPEAKERS_ANALYZE_DTYPE, + SPEAKERS_ANALYZE_PAYLOAD_FORMAT, +) + +REPO_ROOT = Path(__file__).resolve().parents[1] +RUST_SOURCE = REPO_ROOT / "core/crates/solstone-core-speakers-analyze/src/lib.rs" + + +def _rust_str_const(source_path: Path, source: str, name: str) -> str: + match = re.search( + rf"^\s*(?:pub\s+)?const\s+{re.escape(name)}\s*:\s*&str\s*=\s*\"([^\"]+)\"\s*;", + source, + re.MULTILINE, + ) + assert match is not None, f"{source_path} does not declare Rust string const {name}" + return match.group(1) + + +def _extract_rust_speakers_analyze_wire_constants( + source_path: Path, +) -> tuple[str, str]: + try: + source = source_path.read_text(encoding="utf-8") + except OSError as exc: + raise AssertionError( + f"unable to read Rust speakers-analyze source: {source_path}" + ) from exc + + return ( + _rust_str_const(source_path, source, "PAYLOAD_FORMAT"), + _rust_str_const(source_path, source, "DTYPE_F32LE"), + ) + + +def _assert_speakers_analyze_wire_constants_match( + source_path: Path, +) -> tuple[str, str]: + payload_format, dtype = _extract_rust_speakers_analyze_wire_constants(source_path) + + assert payload_format == SPEAKERS_ANALYZE_PAYLOAD_FORMAT, ( + f"{source_path} PAYLOAD_FORMAT={payload_format!r} does not match " + "SPEAKERS_ANALYZE_PAYLOAD_FORMAT" + ) + assert dtype == SPEAKERS_ANALYZE_DTYPE, ( + f"{source_path} DTYPE_F32LE={dtype!r} does not match SPEAKERS_ANALYZE_DTYPE" + ) + + return payload_format, dtype + + +def test_rust_python_and_discovery_speakers_analyze_wire_constants_match() -> None: + payload_format, dtype = _assert_speakers_analyze_wire_constants_match(RUST_SOURCE) + + assert DISCOVERY_CLUSTER_PAYLOAD_FORMAT == payload_format + assert DISCOVERY_CLUSTER_DTYPE == dtype + + +def test_speakers_analyze_wire_constant_pin_rejects_mismatched_source( + tmp_path: Path, +) -> None: + source_path = tmp_path / "lib.rs" + source_path.write_text( + "\n".join( + [ + 'const PAYLOAD_FORMAT: &str = "deliberately-wrong-payload-format";', + 'const DTYPE_F32LE: &str = "deliberately-wrong-dtype";', + "", + ] + ), + encoding="utf-8", + ) + + with pytest.raises(AssertionError, match=re.escape(str(source_path))): + _assert_speakers_analyze_wire_constants_match(source_path) -- 2.51.2