No description
  • Python 97.7%
  • Dockerfile 2.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
brennoflavio 3c3a8ed970
All checks were successful
Publish container image / publish (push) Successful in 2m8s
first commit
2026-09-21 13:07:44 -03:00
.forgejo/workflows first commit 2026-09-21 13:07:44 -03:00
tests first commit 2026-09-21 13:07:44 -03:00
.dockerignore first commit 2026-09-21 13:07:44 -03:00
.gitignore first commit 2026-09-21 13:07:44 -03:00
app.py first commit 2026-09-21 13:07:44 -03:00
Dockerfile first commit 2026-09-21 13:07:44 -03:00
inference.py first commit 2026-09-21 13:07:44 -03:00
LICENSE.silero-vad first commit 2026-09-21 13:07:44 -03:00
pyproject.toml first commit 2026-09-21 13:07:44 -03:00
README.md first commit 2026-09-21 13:07:44 -03:00
segmentation.py first commit 2026-09-21 13:07:44 -03:00
uv.lock first commit 2026-09-21 13:07:44 -03:00

Home VAD

Unauthenticated, CPU-only HTTP API for Silero VAD. Accepts an audio file and returns speech intervals, not transcription, speaker identification, or wake-word detection. No streaming API or infrastructure manifests are included.

Model provisioning

The image contains the API and dependencies, not Silero model weights. Neither the Docker build nor runtime downloads models. Provision the model separately and mount it read-only, readable by UID/GID 10001.

MODEL_PATH is required: an absolute path to the mounted file, not a directory or remote URL. The supported artifact is the standard recurrent ONNX model:

Property Value
Upstream version v6.2.2
Commit 60b7ffa243625ebdc1070275a29f18c87843786a
Upstream path src/silero_vad/data/silero_vad.onnx
SHA-256 1a153a22f4509e292a94e67d6f9b85e8deb25b4988682b7e174c65279d8788e3

Download on the provisioning machine, outside the image build:

mkdir -p models
curl --fail --location \
  https://raw.githubusercontent.com/snakers4/silero-vad/60b7ffa243625ebdc1070275a29f18c87843786a/src/silero_vad/data/silero_vad.onnx \
  --output models/silero_vad.onnx
printf '%s  %s\n' \
  1a153a22f4509e292a94e67d6f9b85e8deb25b4988682b7e174c65279d8788e3 \
  models/silero_vad.onnx | sha256sum --check
chmod 644 models/silero_vad.onnx

Checksum verification belongs to provisioning; runtime checks the model interface and executes a warmup frame before becoming ready. Missing, unreadable, corrupt, or incompatible models fail startup. JIT, sequence, and alternate ONNX variants are not supported. No automatic fallback is used.

Build and run

docker build -t home-vad:local .
docker run --rm --name home-vad \
  --publish 127.0.0.1:8000:8000 \
  --read-only --tmpfs /tmp:rw,noexec,nosuid,size=256m \
  --env MODEL_PATH=/models/silero_vad.onnx \
  --mount type=bind,src="$(pwd)/models/silero_vad.onnx",dst=/models/silero_vad.onnx,readonly \
  home-vad:local

The image uses Python 3.12, locked uv dependencies, CPU ONNX Runtime, and one Uvicorn worker on port 8000. Inference uses one thread and processes one file at a time. No PyTorch, TorchAudio, Silero Python package, or GPU runtime is installed. The Silero package is deliberately avoided because it includes model assets and Torch dependencies.

API

curl --fail-with-body http://localhost:8000/vad \
  -F 'file=@mensagem.ogg'

Send exactly one multipart file named file, with no additional fields. PyAV decodes common formats such as WAV, Ogg/Opus, MP3, and FLAC and resamples/downmixes the first audio stream to mono 16 kHz. Files referring to external resources are not supported; decoding disables external file/network protocols.

Example response:

{
  "duration_seconds": 5.12,
  "segments": [{"start": 0.48, "end": 2.91}]
}

Timestamps are seconds on the decoded recording's timeline, before any speech removal. Endpoints are bounded by the decoded duration; the end is exclusive. No speech is a successful response with "segments": []. Results do not include frame probabilities or a segment-level confidence score.

Segmentation follows upstream defaults: entry threshold 0.5, exit threshold 0.35, minimum speech 250 ms (strictly greater, as upstream implements it), minimum silence 100 ms, and padding 30 ms. There is no maximum segment duration other than the file duration limit. Model frames are 512 samples (32 ms); the last frame is zero-padded, but output timestamps never extend into that padding. Recurrent state and context are new for every request, including after failures.

Endpoint Purpose
POST /vad Detect speech in an uploaded audio file
GET /healthz Process liveness; returns ok
GET /readyz Model loaded and service accepting work; returns ok or HTTP 503
GET /docs Interactive API documentation

Limits and errors

  • Maximum file: 25 MiB; total multipart body: 25 MiB + 64 KiB.
  • Maximum decoded audio: 600 seconds, checked before each frame is resampled and again against the resampled output size.
  • Upload timeout: 30 seconds.
  • At most nine outstanding requests, including uploads, active work, and queued work. One detection runs at a time; excess requests receive HTTP 503 with Retry-After: 5. Queue saturation does not fail readiness.
  • Disconnecting/cancelling skips queued work. Active native inference retains its slot until it finishes. Normal shutdown stops admission and drains admitted jobs; the container's Uvicorn graceful HTTP timeout is 30 seconds.
  • HTTP 400: malformed multipart/headers; 408: upload timeout; 413: size/duration limit; 415: incorrect content type; 422: empty/invalid audio or missing file; 500: internal inference failure; 503: unavailable capacity. Internal exception details are logged server-side, not returned to clients.

Uploads may spool temporarily to /tmp; they are closed after parsing. Audio is not stored persistently or logged. /docs may fetch UI assets from a CDN in the browser; inference and the API do not require internet access.

Forgejo publishing

.forgejo/workflows/docker-image.yml follows home-whisper/home-classifier:

  • Pushes to master and manual dispatch on master build and publish the image.
  • Image: git.brennoflavio.com.br/brennoflavio/home-vad:YYYYMMDDHHMMSS (UTC).
  • Registry cache tag: build-cache; no latest tag, Git tags, or releases.
  • Required secrets: REGISTRY_USERNAME and REGISTRY_PASSWORD, with image push access. Actions are loaded from git.brennoflavio.com.br/ci-actions.
  • The runner needs Docker/Buildx and access to the registry, base images, and dependency indexes. It does not need model weights.
  • Like the sibling workflows, this is a publishing job, not a separate test job. Run validation before pushing. It does not deploy infrastructure.

The workflow builds the runner's native architecture (validated locally on Linux amd64); it does not promise a multi-architecture image.

Validation

uv sync --frozen
uv run pytest
uv run ruff check .
uv run ruff format --check .

Default tests use fake inference sessions and real audio decoding; they never download models. To include a real-model silence/state-isolation test:

VAD_TEST_MODEL_PATH="$(pwd)/models/silero_vad.onnx" uv run pytest

An optional timestamp parity test compares 1,000 randomized cases with the pinned upstream implementation, without importing Torch. Download the reference source separately (the test verifies its SHA-256 before executing its pure timestamp function):

curl --fail --location \
  https://raw.githubusercontent.com/snakers4/silero-vad/60b7ffa243625ebdc1070275a29f18c87843786a/src/silero_vad/utils_vad.py \
  --output /tmp/silero-v6.2.2-utils.py
VAD_TEST_UPSTREAM_PATH=/tmp/silero-v6.2.2-utils.py uv run pytest tests/test_upstream.py

Before deployment, smoke-test a real speech recording and silence, measure CPU, peak memory, and latency on the target node, and keep the unauthenticated service internal. Future manifests should mount weights read-only, allow writable /tmp, set resource limits, and configure startup/readiness on /readyz and liveness on /healthz. Network policy should limit callers and deny unnecessary egress.

Attribution

segmentation.py and the ONNX frame handling in inference.py are adapted from Silero VAD v6.2.2 at the commit above. The timestamp adaptation removes options not used by this service and returns sample coordinates before conversion to seconds. Upstream's MIT notice is in LICENSE.silero-vad and is included in the image.