Files
VoiceStudio/backend/runtime_adapter/README.md
T
velixio b2f94d2bf8 feat(runtime-adapter): vendor the vssaas wire contract and committed stubs
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.
2026-08-13 13:37:56 +05:30

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 with VOICE_STUDIO_RUNTIME_SOCKET). No HTTP listener, no TCP.
  • Never reaches PostgreSQL, customer credentials, or arbitrary network URLs. Execute accepts 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_socket enforces the same rules at bind time;
  • Health returns SERVING_STATE_READY with nonempty runtime + adapter versions, and GetCapabilities returns 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, …) from services.tts_backend's registry.
  • model_version — the installed Hugging Face revision (40-char commit SHA) recorded by services.hf_revisions (curated pin or the voicestudio-revision marker).
  • model_digestsha256:<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. See digest.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