# Copyright (c) 2026 The Anuvani-Diarization authors # SPDX-License-Identifier: MIT OR Apache-2.0 """Write THIRD_PARTY_NOTICES.md and LICENSES/crates/: what this project contains or links that others wrote. source env/canonical.sh && python3 tools/third_party_notices.py The Rust crates are the ones `cargo metadata --locked` resolves for the two shipped binaries (the wheel's extension `anuvani_diar._core`, from crates/diar-py, and the gateway, crates/diar-gateway), development-only dependencies left out; each crate's own licence files (LICENSE*, COPYING*, NOTICE*, UNLICENSE*) are copied from its source in the local cargo registry into LICENSES/crates/-/. The other sections are fixed text kept below; their licence texts are in LICENSES/third_party/ (and, for the wheel's bundled libraries, in the wheel's anuvani_diar/_libs/LICENSES/). Run it again after changing Cargo.lock. """ import json import shutil import subprocess from pathlib import Path PROJ = Path(__file__).resolve().parents[1] FILES = ("LICENSE", "LICENCE", "COPYING", "NOTICE", "UNLICENSE", "COPYRIGHT") HEAD = """# Third-party notices Anuvani-Diarization's code is licensed under MIT OR Apache-2.0 (`LICENSE-MIT`, `LICENSE-APACHE`). It contains, ports or links work by others, listed here with their licences. The model weights have their own licences, set by the models they derive from (see the model card): the segmentation network is a fine-tuned derivative of `BUT-FIT/diarizen-wavlm-large-s80-md-v2` and is CC BY-NC 4.0 (non-commercial); the PLDA model (BUT Speech@FIT) and the speaker-embedding network (`pyannote/wespeaker-voxceleb-resnet34-LM`) are CC BY 4.0. ## Code copied or adapted into this project | Upstream | Licence | Where | Licence text | |---|---|---|---| | DiariZen (BUT Speech@FIT), commit 844f555 | MIT | the segmentation network's code, verbatim (`python/anuvani_diar/nn/seg/`; also the frozen release's `hf_port_poc/anuvani_diar_net/`); its VBx and clustering set-up, ported to Rust (`crates/diar-core`) | `LICENSES/third_party/DiariZen-MIT.txt` | | torchaudio 2.1.1 (Facebook, Inc.) | BSD-2-Clause | `kaldi.py` (fbank) and `resample.py`, verbatim; the wav2vec2 building blocks inside DiariZen's code | `LICENSES/third_party/torchaudio-BSD-2-Clause.txt` | | pyannote.audio 3.1.1, DiariZen's fork (CNRS) | MIT | the voiceprint network's fbank, forward and statistics pooling (`voiceprint.py`); window reconstruction and masks, ported to Rust | `LICENSES/third_party/pyannote-audio-MIT.txt` | | WeSpeaker (Shuai Wang), via pyannote.audio | Apache-2.0 | the voiceprint network's ResNet34 (`voiceprint.py`) | `LICENSES/third_party/Apache-2.0.txt` | | VBx (BUT Speech@FIT), via DiariZen's `diarizen/clustering/VBx.py` | Apache-2.0 | variational Bayes clustering, ported to Rust (`crates/diar-core/src/vbx.rs`) | `LICENSES/third_party/Apache-2.0.txt` | | SciPy 1.15.3 (SciPy Developers) | BSD-3-Clause | centroid-linkage AHC and pairwise distances, reimplemented in Rust to give SciPy's exact results | `LICENSES/third_party/scipy-LICENSE.txt` | | NumPy 1.26.4 (NumPy Developers) | BSD-3-Clause | numerical behaviour reproduced in Rust; its OpenBLAS build is bundled (below) | `LICENSES/third_party/numpy-LICENSE.txt` | | python-soundfile 0.14.0 (Bastian Bechtold) | BSD-3-Clause | the sequence of libsndfile calls `sf.read` makes, reproduced in Rust (`crates/diar-audio`) | `LICENSES/third_party/soundfile-LICENSE.txt` | ## Native libraries bundled in the wheel Copied unchanged from the numpy 1.26.4 and soundfile 0.14.0 wheels, each checked by sha256 when it loads; their licence texts are in the wheel (`anuvani_diar/_libs/LICENSES/`, index in its README.txt): | Library | Licence | |---|---| | OpenBLAS 0.3.23.dev (numpy 1.26.4's build) | BSD-3-Clause | | libgfortran 5 | GPL-3.0 with the GCC Runtime Library Exception 3.1 | | libquadmath 0 | LGPL-2.1-or-later | | libsndfile 1.2.2, with FLAC 1.4.3, Ogg 1.3, Vorbis 1.3.7, Opus 1.4, mpg123 1.32.3, LAME 3.100 | LGPL-2.1-or-later (libsndfile, mpg123, LAME); BSD-3-Clause (FLAC, Ogg, Vorbis, Opus) | libsndfile is LGPL: `ANUVANI_DIAR_SNDFILE` (with `ANUVANI_DIAR_SNDFILE_UNCHECKED=1`) runs the engine with another build of it (`docs/WHEEL.md`). The source of the LGPL libraries (libsndfile with its codecs and the script that built it; libquadmath, as the GCC source package it was built from) is in the model repository's `third_party_sources/`, whose README traces each binary to its source. ## Container images the serving kit builds `triton_inference/` builds two images on the user's machine; they are not distributed here. The Triton image starts from `nvcr.io/nvidia/tritonserver:24.10-py3` (NVIDIA Deep Learning Container License) and installs PyTorch and torchaudio 2.1.1 (BSD-3-Clause and BSD-2-Clause) and NVIDIA's cuBLAS and NVRTC wheels (NVIDIA proprietary licence); the service image starts from `ubuntu:22.04` and installs FastAPI and Pydantic (MIT), Starlette and Uvicorn (BSD-3-Clause) and python-multipart (Apache-2.0) (`triton_inference/service/requirements.txt`). ## Rust crates compiled into the wheel and the gateway """ def crates(): meta = json.loads(subprocess.run(["cargo", "metadata", "--format-version", "1", "--locked"], cwd=PROJ, capture_output=True, text=True, check=True).stdout) pk = {p["id"]: p for p in meta["packages"]} ws = set(meta["workspace_members"]) nodes = {n["id"]: n for n in meta["resolve"]["nodes"]} stack = [i for i in ws if pk[i]["name"] in ("diar-py", "diar-gateway")] seen = set() while stack: for d in nodes[stack.pop()]["deps"]: if {k["kind"] for k in d["dep_kinds"]} - {"dev"} and d["pkg"] not in seen: seen.add(d["pkg"]) stack.append(d["pkg"]) return sorted((pk[i] for i in seen if i not in ws), key=lambda p: (p["name"], p["version"])) def main(): out = PROJ / "LICENSES" / "crates" if out.exists(): shutil.rmtree(out) rows = [] for p in crates(): src = Path(p["manifest_path"]).parent dst = out / f"{p['name']}-{p['version']}" texts = [] for f in sorted(src.iterdir()): if f.is_file() and f.name.upper().startswith(FILES): dst.mkdir(parents=True, exist_ok=True) shutil.copyfile(f, dst / f.name) texts.append(f.name) where = f"`LICENSES/crates/{dst.name}/` ({', '.join(texts)})" if texts else "no licence file in the crate's source" rows.append(f"| {p['name']} | {p['version']} | {p['license'] or '(see crate)'} | {where} |") body = (HEAD + f"{len(rows)} crates, resolved by `cargo metadata --locked` (`tools/third_party_notices.py`):\n\n" "| Crate | Version | Licence | Licence files |\n|---|---|---|---|\n" + "\n".join(rows) + "\n") (PROJ / "THIRD_PARTY_NOTICES.md").write_text(body) print(f"THIRD_PARTY_NOTICES.md: {len(rows)} crates; {sum(1 for r in rows if 'no licence file' in r)} without a licence file") if __name__ == "__main__": main()