Vendor api/proto/voicestudio/runtime/v1/runtime_adapter.proto from vssaas byte-identically into backend/runtime_adapter/, generate the grpcio stubs into gen/ (committed, same policy and import fixup as backend/worker/protocol/gen/), and add the drift test that regenerates into a tmpdir and diffs.
3.8 KiB
VoiceStudio runtime adapter
A local gRPC server implementing the vssaas GPU-node runtime contract
voicestudio.runtime.v1.RuntimeAdapterService, so a vssaas GPU Gateway can
drive this VoiceStudio backend as its inference runtime.
Boundary (deliberate non-capabilities)
- Binds only a Unix-domain socket (default
/run/voicestudio/runtime.sock, override withVOICE_STUDIO_RUNTIME_SOCKET). No HTTP listener, no TCP. - Never reaches PostgreSQL, customer credentials, or arbitrary network URLs.
Executeaccepts local file handles only — absolute paths generated by the Gateway; any URL-shaped or relative handle is rejected as invalid input. - The Gateway owns leases, artifact transfer, retries, and billing. This adapter owns approved model loading and inference only.
Running
# serve (production socket):
VOICE_STUDIO_RUNTIME_SOCKET=/run/voicestudio/runtime.sock \
python -m backend.runtime_adapter
# self-check: starts the server on a private temp socket and validates the
# same expectations the Go preflight (cmd/runtime-adapter-preflight) enforces:
python -m backend.runtime_adapter --selfcheck
Environment:
| Variable | Default | Meaning |
|---|---|---|
VOICE_STUDIO_RUNTIME_SOCKET |
/run/voicestudio/runtime.sock |
Unix socket path (must be absolute; parent dir must exist and not be world-writable). |
VOICE_STUDIO_RUNTIME_SLOTS |
1 |
Concurrent execution slots per device. |
Wire contract and generated stubs
runtime_adapter.proto is a byte-identical vendored copy of the vssaas
contract api/proto/voicestudio/runtime/v1/runtime_adapter.proto. Do not edit
it here; re-vendor from vssaas when the contract changes, then regenerate.
The gen/ stubs are committed (same policy as backend/worker/protocol/gen/).
Regenerate with:
uv run python scripts/gen_runtime_adapter_protocol.py
tests/test_runtime_adapter_gen.py fails if the committed stubs drift from
the proto.
Preflight expectations honoured
The Go preflight (internal/gateway/preflight.go) fails closed unless:
- the socket path is absolute, a real Unix socket (not a symlink), and its
parent directory is not world-writable —
server.prepare_socketenforces the same rules at bind time; HealthreturnsSERVING_STATE_READYwith nonempty runtime + adapter versions, andGetCapabilitiesreturns identical versions — both handlers read the same constants, so they cannot disagree;- at least one device with nonempty id/hardware class, nonzero VRAM and
slots,
free_slots <= total_slots, unique ids; - at least one model explicitly READY with
catalog_model_id,model_version,model_digest, and ≥1 precision. A loading, installed, or failed model is reported with its true state and never as READY.
Model identity
catalog_model_id— the VoiceStudio TTS engine id (omnivoice,voxcpm2, …) fromservices.tts_backend's registry.model_version— the installed Hugging Face revision (40-char commit SHA) recorded byservices.hf_revisions(curated pin or thevoicestudio-revisionmarker).model_digest—sha256:<hex>computed over the installed snapshot files (sorted relative path + per-file SHA-256), cached next to the repo cache keyed by (revision, file list, sizes, mtimes) so multi-GB weights are hashed once. Seedigest.py.
Failure taxonomy
Stable codes (prefix RTA_) map onto the proto's RuntimeFailureClass:
invalid input (RTA_INPUT_*), model load (RTA_MODEL_LOAD_FAILED),
inference (RTA_INFERENCE_*), GPU resource (RTA_GPU_*), local storage
(RTA_STORAGE_*), cancellation (terminal ExecutionCanceled), and adapter
crash (RTA_RUNTIME_CRASH). See codes.py.
Tests
uv run pytest backend/tests/test_runtime_adapter_capabilities.py \
backend/tests/test_runtime_adapter_execute.py \
tests/test_runtime_adapter_gen.py