Skip to content

feat(M6b): legacy cleanup + CLI/FFI/Python migration for v1.0 - #16

Merged
ekhodzitsky merged 20 commits into
masterfrom
feat/m6b-legacy-cleanup
May 9, 2026
Merged

ekhodzitsky merged 20 commits into
masterfrom
feat/m6b-legacy-cleanup

Conversation

@ekhodzitsky

Copy link
Copy Markdown
Owner

Summary

Final v1.0 architecture milestone. Closes the M6 split (M6a additive Pipeline; M6b removes legacy + migrates consumers). 11 atomic commits inside one PR. Bumps 0.6.0-alpha.0 → 0.6.0-alpha.3.

Breaking change. Removes Pipeline::new(DiarizationConfig, VadConfig), OfflineDiarizer, DiarizationConfig, VadConfig, VoiceActivityDetector, EnergyVad, SileroVad, segment_speech, DummyExtractor, OnnxEmbeddingExtractor, EcapaTdnnExtractor, EcapaMelOnnxExtractor, RawAudioOnnxExtractor, ClusteringBackend, EmbeddingDim. compute_fbank privatized.

Highlights

  • Module rename: pipeline_v2 → pipeline. Cargo feature pipeline = ["download"] (default-on).
  • Public API: only polyvoice::Pipeline::builder() for diarization. Migration guide at docs/MIGRATING-FROM-0.5.md.
  • CLI rewrite: polyvoice diarize <wav> --profile mobile|balanced + polyvoice download-models + polyvoice models list/info.
  • Bench rewrite: polyvoice-bench <dataset> --profile balanced --output report.json emitting polyvoice-bench-v1 JSON schema.
  • C FFI ABI v2: polyvoice_pipeline_create/run/destroy/free_string. Old ABI removed; include/polyvoice.h rewritten.
  • Python pyo3 rewrite: polyvoice.Pipeline.mobile(), Pipeline.balanced(), Pipeline.run(samples, sample_rate=16000) returning a dict.
  • DER baseline schema at tests/der_baseline.json (numbers deferred to operational follow-up after M5 INT8 publish closes). scripts/run-der-baseline.sh helper.
  • OnlineDiarizer annotated #[deprecated(since = "0.6.0-alpha.3")] — streaming redesign in v1.1.

Test plan

  • 163 lib tests pass
  • 4 CLI smoke tests + 1 FFI smoke test (1 #[ignore]) + 2 DER baseline schema tests + 7 pipeline synthetic + 2 Python smoke
  • cargo clippy --all-targets --all-features -- -D warnings clean
  • cargo fmt --check clean
  • cargo check --target wasm32-unknown-unknown --no-default-features --features resegmentation --lib clean
  • scripts/release-gate.sh — 5 PASS, 0 FAIL, 9 PENDING (M8/M9 stubs)
  • maturin build --release succeeds; pytest smoke green
  • CI matrix on PR

Out of scope

  • Real DER baseline numbers — operational follow-up after M5 INT8 GitHub Releases publish.
  • iOS/Windows wheels — M8.
  • Android NNAPI — M8.
  • Streaming v1.1 — separate spec.
  • v1.0.0 GA polish (CHANGELOG voice, blog post, release-gate green) — M9.

References

🤖 Generated with Claude Code

ekhodzitsky and others added 20 commits May 8, 2026 11:34
….rs (OfflineDiarizer/VadConfig/EnergyVad/SileroVad)
…+ EcapaMelOnnxExtractor + RawAudioOnnxExtractor + DummyExtractor + OnnxEmbeddingExtractor

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…kend + EmbeddingDim

- Delete DiarizationConfig, ClusteringBackend, EmbeddingDim from types.rs
- Add ClusterConfig (threshold + max_speakers) to cluster.rs
- Add OnlineConfig (window/hop/sample_rate + cluster fields) to online.rs
- Add FfiConfig (window/hop/sample_rate) to ffi.rs
- Remove DiarizationConfig param from EmbeddingExtractor::extract trait
- Update FbankOnnxExtractor, FfiStubExtractor impls accordingly
- Update embedder.rs ResNet34Adapter + CamPlusPlusExtractor
- Remove ClusteringBackend, DiarizationConfig, EmbeddingDim from lib.rs re-exports
- Add ClusterConfig, OnlineConfig to lib.rs public API

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Replace legacy CLI (references deleted DiarizationConfig/VadConfig/SileroVad)
  with new implementation built on Pipeline::builder() + PipelineConfig
- Add models/download-models subcommands via ModelRegistry
- Extend cli feature to pull in pipeline + all required sub-features
- Stub polyvoice-bench.rs to compile (full rewrite is Task 7)
- Add tests/cli_smoke_test.rs with 4 smoke tests (help, diarize-help,
  invalid-profile, models-list)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…der() + JSON report

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace legacy FfiStubExtractor-based ABI with ABI v2: profile-based
Pipeline::builder() API, catch_unwind on all entry points, CString
ownership protocol for JSON output, and matching C header + smoke test.
Wire ffi feature to full pipeline stack in Cargo.toml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
….builder()

- Replace legacy DummyExtractor/OfflineDiarizer/DiarizationConfig bindings
  with new Pipeline.builder() API (mobile/balanced static constructors)
- Drop Turn pyclass; run() returns a plain dict with num_speakers + turns list
- Enable full feature set on polyvoice dep (pipeline, segmentation, embedder,
  clusterer, resegmentation) so Pipeline/PipelineConfig/ModelRegistry export
- Update polyvoice/__init__.py to remove Turn re-export
- Add python/tests/test_smoke.py; 2/2 smoke tests pass

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er deprecation + CHANGELOG + tag m6b-complete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tests integration.rs/property.rs/test_e2e_onnx.rs/test_pipeline.rs and benches
diarization/der + examples offline/online/onnx all referenced removed legacy
types (DiarizationConfig/OfflineDiarizer/DummyExtractor/EnergyVad/SileroVad).
Removed wholesale — replacement coverage in M6a/M6b smoke tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Path A — the M6b new pipeline (PowersetSegmenter + INT8/FP32 embedders +
AHC/NME-SC) was benchmarked and found non-functional:
  - M6b INT8 Balanced: DER ~64.4% (always 1 speaker)
  - M6b FP32 Custom:    DER ~52.4% (6–42 speakers, extreme over-segmentation)
  - v0.5.2 legacy:      DER ~13.8% (threshold 0.45, 232 VoxConverse-test files)

Changes:
- Restore legacy modules from cb764ff: pipeline.rs, vad.rs, silero_vad.rs,
  onnx.rs, embedding.rs (with DummyExtractor + extract(samples, config))
- Restore DiarizationConfig in types.rs
- Move non-functional M6b pipeline to pipeline_v1 (experimental)
- Rewrite CLI and bench to use legacy Pipeline::new() + SileroVad +
  FbankOnnxExtractor + AHC
- Point manifest profiles back to proven FP32 models (silero_vad +
  wespeaker_resnet34 / cam_pp_fp32)
- Fill tests/der_baseline.json with operational numbers from full run:
  DER 13.83% (miss 3.82%, FA 3.68%, confusion 6.34%)
- Update CHANGELOG, m5_manifest_smoke_test, der_baseline_test
- Preserve M0 ModelRegistry, M1–M5 components as optional/experimental
Production-readiness sprint — 4 parallel agents:

1. FFI (src/ffi.rs):
   - Rewrite from pipeline_v1 (non-functional) to legacy v0.5.2 pipeline
   - PolyvoicePipeline now owns Pipeline + FbankOnnxExtractor + SileroVad
   - polyvoice_pipeline_run validates sample_rate == 16000

2. Python bindings (python/src/lib.rs):
   - Rewrite from pipeline_v1 to legacy pipeline
   - Pipeline pyclass owns legacy Pipeline + extractor + vad
   - run() takes &mut self (required for SileroVad state)
   - API unchanged: Pipeline.mobile() / .balanced() / .run(samples, sr)

3. Chaos tests (tests/chaos_test.rs):
   - 11 tests covering empty samples, very short audio, all-silence,
     loud audio, truncated WAV, AHC edge cases, invalid VAD chunk size,
     mismatched sample rate, proptest randomized samples (0–32k)
   - All use DummyExtractor + EnergyVad (no ONNX required)

4. E2E + DER regression (tests/e2e_smoke_test.rs, tests/der_regression_test.rs):
   - E2E smoke: 1 file from VoxConverse-test, asserts DER < 50%
   - DER regression: 10-file fixed subset, asserts avg DER < 25%
   - Both #[ignore] — require cached ONNX models + dataset
   - Compile-checked in CI via --no-run
Production-readiness sprint — second wave (4 parallel agents):

1. CI E2E jobs (.github/workflows/ci.yml):
   - e2e-smoke: runs on every PR, 1 file, DER < 50%, needs: [test]
   - e2e-der-regression: nightly schedule, 10 files, DER < 25%, uploads artifact

2. DoS guard (src/types.rs, src/pipeline.rs, src/ffi.rs, tests/chaos_test.rs):
   - DiarizationConfig.max_duration_secs = 3600.0 (1 hour default)
   - Pipeline::run() rejects audio exceeding the limit with AudioTooLong error
   - FFI maps AudioTooLong to PolyvoiceStatus::AudioTooLong = 3
   - Chaos test verifies the guard with 2s audio vs 1s limit

3. FFI header sync (include/polyvoice.h):
   - Updated comments to legacy v0.5.2 architecture
   - Added Doxygen doc comments for all functions
   - Verified C syntax with cc -fsyntax-only

4. Dead-code cleanup (Cargo.toml, docs/strategy/dead-code-cleanup-plan.md):
   - default features slimmed from 6 to 1 (spectral only)
   - cli feature slimmed to [onnx, download, dep:clap]
   - ffi feature slimmed to [onnx, download]
   - M1–M5 experimental code preserved behind explicit flags
   - Full verification: cargo check/test/clippy all pass
Third wave (3 parallel agents + manual fixes):

1. Security audit (docs/security/audit-2026-05-08.md):
   - cargo audit: 0 vulnerabilities, 1 unmaintained warning (paste)
   - FFI-001: Added MAX_SAMPLES guard (1h @ 16kHz) in polyvoice_pipeline_run
   - SERIAL-001: Added finite/non-negative validation in RTTM parser
   - SECRETS-002: Added secret file patterns to .gitignore
   - DOS-001/DOS-004: Added max_duration_secs guard to pipeline_v1

2. Performance regression (tests/perf_regression_test.rs):
   - Tracks RTF and peak RSS on 5-file VoxConverse subset
   - Asserts avg RTF < 0.25 and peak RSS < 500 MB (Linux)
   - Prints JSON report for CI parsing
   - #[ignore] — requires ONNX models + dataset

3. Python wheel validation (docs/strategy/python-wheel-validation.md):
   - Verified maturin build --release succeeds
   - Removed stale .so shadowing artifact
   - Updated python/tests/test_pipeline.py to new balanced()/run() API
   - Added .gitignore for stale native extensions

4. Manual: Removed spectral from default features (now empty default)
   - cargo check --no-default-features compiles only working code
…pinning, DoS guards

Fifth hardening swarm. Closes all MEDIUM and HIGH security findings.

SUPPLY-002 (Model signing):
- Add minisign-verify (zero-dep pure Rust) under download feature
- Bake public key into binary via include_str!
- Inline per-model signatures in manifest.toml
- Stream-verify SHA-256 + Minisign simultaneously in 64 KiB loop
- Preserve download_with_checksum as backwards-compatible wrapper
- Add verify.rs with SignatureError + 5 unit tests
- Sign all 7 official models (FP32 + INT8)
- Add scripts/sign-models.sh release script
- Add sign-manifest-dry-run CI job

SUPPLY-003 (TLS pinning):
- Pin ureq to rustls only (default-features = false)
- native-tls eliminated from dependency tree

DOS-003 (ONNX validation):
- Add validate_onnx_header() in src/onnx.rs
- Check ONNX magic or protobuf header (0x08) in first 64 bytes
- Wired into all commit_from_file call paths
- 5 unit tests (valid ONNX, random bytes, empty, magic, protobuf)

FFI-003 + CACHE-001:
- Replace panic swallowing with eprintln! observability
- Mark ensure_in_cache_only #[doc(hidden)]

Cargo.toml:
- Add exclude[] for models/*.onnx, data/, fuzz/, python/
  Shrinks crate package from 254 MiB to 1.5 MiB

Docs:
- Update security audit (all MEDIUM/HIGH → FIXED)
- Add model-signing-plan.md
- Update CHANGELOG with security section

Refs: docs/security/audit-2026-05-08.md
- cargo fmt on all files (fixes CI fmt job)
- verify.rs tests now skip gracefully when minisign CLI is unavailable
- sign-models.sh --dry-run no longer requires signing.key

Refs: PR #16
Using /nonexistent/path fails on Windows because create_dir_all
may succeed on the current drive. Instead, create a temp file and
pass it as cache_dir — create_dir_all on a file path always fails
reliably cross-platform.
@ekhodzitsky
ekhodzitsky merged commit 75d92ca into master May 9, 2026
28 of 30 checks passed
@ekhodzitsky
ekhodzitsky deleted the feat/m6b-legacy-cleanup branch August 4, 2026 15:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant