Anuvani-Diarization / src /tools /third_party_notices.py
StephennFernandes's picture
Anuvani-Diarization 1.0.0
63dd946
Raw History Blame Contribute Delete
7.07 kB
# 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/<name>-<version>/. 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()