Skip to content

Repository files navigation

Rula

Local-first realtime Russian voice avatar. Rula is an open-source demo runtime for a private digital human that listens to Russian speech, transcribes it locally, answers with a local Qwen LLM, speaks with local TTS, and drives a browser VRM avatar through audio-based facial animation.

License: MIT Live preview Architecture article Local-first

Rula browser demo

Status: demo / early runtime. Rula is not production-ready until the legal, latency, GPU, soak, and air-gap gates pass on the target host.

Live browser preview: ai.igor-ya.ru. The preview exposes the browser demo surface; the target voice runtime is designed for local / on-prem inference.

Architecture article: Голосовой агент on-prem: закрытый контур и полсекунды.

What It Is

Rula is a closed-contour digital human runtime for local AI avatar experiments on a Windows + WSL2 + NVIDIA GPU workstation.

It combines:

  • Russian STT, local LLM, local TTS, VAD, and facial animation.
  • LiveKit/WebRTC media transport.
  • FastAPI voice-agent backend.
  • React + Three.js + VRM browser demo client.
  • Realtime turn state, interruption handling, stale-generation dropping, metrics, and eval scripts.

The current visual layer is a 3D VRM avatar, not a photorealistic 2D talking-head generator. A future video-avatar renderer can replace the visual layer while keeping the same realtime voice contracts.

Why It Matters

Voice-avatar demos break when conversation becomes real: late answers, old audio after interruption, echo mistaken for barge-in, mouth drift, and one GPU carrying STT, LLM, TTS, and face animation at the same time.

Rula treats those as systems problems. The project is built around measurable realtime contracts: first audio latency, barge-in latency, speculative turn-taking, stale artifact rejection, audio-face sync, GPU readiness, and air-gap acceptance.

Closed-Contour Runtime

The browser is only the demo client. The product boundary is the local runtime.

microphone / local client
 -> local LiveKit media path
 -> local FastAPI voice runtime
 -> local STT / LLM / TTS / face-animation models
 -> local avatar or video output

Default runtime principles:

  • No OpenAI, cloud STT, cloud TTS, or hosted avatar inference in the default path.
  • Internet is required only to download model artifacts and upstream dependencies.
  • Model weights live under models/hf/ and are ignored by Git.
  • Runtime state, logs, SQLite databases, generated media, and deployment secrets stay local.
  • Browser rendering can be replaced by a kiosk app, native shell, Unreal/Unity scene, or video-avatar renderer.
  • Air-gap checks are part of acceptance, not a documentation afterthought.

Architecture

flowchart LR
  Browser["Browser UI<br/>React + Three.js + VRM"] <--> LiveKit["LiveKit<br/>WebRTC audio + data"]
  LiveKit <--> Agent["FastAPI Agent<br/>VoiceSessionWorker"]
  Agent --> VAD["Silero VAD"]
  Agent --> STT["GigaAM-v3 STT"]
  Agent --> Brain["ConversationBrain<br/>state + routing + prompt"]
  Brain --> LLM["Qwen3 via local vLLM"]
  LLM --> TTS["Qwen3-TTS"]
  TTS --> A2F["NVIDIA Audio2Face-3D"]
  A2F --> Face["ARKit / VRM blendshape frames"]
  Face --> LiveKit
  TTS --> LiveKit
Loading

Current visual path:

TTS audio -> Audio2Face-3D -> blendshape envelopes -> Three.js VRM avatar

Future realistic-video path:

TTS audio -> audio-to-video model -> video frames -> WebRTC video track

Engineering Highlights

Rula is intentionally shaped as a realtime systems project, not a thin wrapper around model calls.

  • Modular monolith core: domain state, API, media runtime, metrics, and model adapters stay in one deployable boundary until measured scale pressure appears.
  • Explicit turn state machine: SessionStateMachine owns turn_id, generation_id, branch_state, interrupts, speculative branches, and stale artifact rejection.
  • Shared wire protocol: Python emits StreamEnvelope; TypeScript consumes the same event shape from packages/avatar_protocol.
  • Speculative turn-taking: TurnPolicy can start early generation after a silence window and discard it if the user resumes speaking.
  • Two-phase barge-in: playback ducks quickly, then STT verifies whether the burst is a real interruption or echo/noise.
  • Audio-first hot path: AudioPacer owns the continuous 20 ms LiveKit audio stream; face frames and metrics stay off the critical path.
  • Clause-level streaming: Qwen text is chunked into speakable clauses so TTS can start before the full answer is complete.
  • Client-side face anchoring: the browser anchors pts_ms to the moment audio is actually heard, reducing mouth drift under jitter.
  • Fail-closed readiness: /ready reports voice-avatar readiness only when artifacts, local services, GPU headroom, LiveKit, and voice engines are healthy.

Agent Brain

The agent brain is a deterministic dialogue runtime around a local LLM. The LLM generates dialogue text; the runtime owns state, timing, cancellation, media, and safety boundaries.

transcribed user turn
 -> ConversationBrain
 -> IntentRouter
 -> StateReducer
 -> ResponsePlanner
 -> direct response or Qwen3 LLM stream
 -> clause chunker
 -> Qwen3-TTS
 -> Audio2Face / avatar output

The brain keeps session-local memory, routes latency-critical intents before the LLM, composes a compact Russian voice prompt, emits direct deterministic answers when useful, and drops stale generation_id data at every hop.

Model Stack

Layer Model / runtime Used for Source
LLM Qwen/Qwen3-14B-FP8 Local dialogue model served by vLLM Hugging Face
LLM fallback Qwen/Qwen3-14B Optional non-FP8 fallback Hugging Face
TTS Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice Russian speech synthesis / avatar voice Hugging Face
STT ai-sage/GigaAM-v3 Russian speech recognition Hugging Face, GitHub
Face animation nvidia/Audio2Face-3D-v3.0 Audio-driven 3D facial animation Hugging Face, NVIDIA NGC
VAD silero-vad Voice activity detection / turn-taking GitHub
Avatar Alicia Solid VRM 0.51 Default replaceable VRM avatar UniVRM asset
Realtime transport LiveKit WebRTC audio/data room LiveKit, Docs
LLM serving vLLM OpenAI-compatible server Local /v1 LLM API shape only vLLM docs
VRM rendering @pixiv/three-vrm + Three.js Browser-side avatar runtime three-vrm

The OpenAI-compatible API shape is local vLLM compatibility. The default runtime does not call OpenAI cloud inference.

Tech Stack

  • Backend: Python 3.11, FastAPI, Pydantic, Uvicorn.
  • Voice runtime: in-process STT/TTS/A2F workers, LiveKit Python SDK, ONNX Runtime GPU.
  • LLM serving: local vLLM server.
  • Frontend: React, TypeScript, Vite, Three.js, @pixiv/three-vrm, LiveKit client.
  • Host profile: Windows checkout, WSL2/Docker GPU runtime, NVIDIA CUDA.
  • Observability: Prometheus and Grafana in the WSL compose stack.

Quick Start

Recommended target: Windows + WSL2, NVIDIA CUDA GPU, Docker Desktop with GPU support, 64 GB RAM minimum, 128 GB recommended, and a Hugging Face token.

git clone https://github.com/ykshv/rula.git
cd rula
powershell -ExecutionPolicy Bypass -File .\scripts\secrets\set_hf_token.ps1
powershell -ExecutionPolicy Bypass -File .\scripts\models\download_all.ps1
powershell -ExecutionPolicy Bypass -File .\scripts\assets\download_default_avatar.ps1
powershell -ExecutionPolicy Bypass -File .\scripts\dev\start_local.ps1

Open http://127.0.0.1:46174/.

Stop:

powershell -ExecutionPolicy Bypass -File .\scripts\dev\stop_local.ps1

From WSL:

cd /mnt/<drive>/path/to/rula/infra/wsl
cp .env.example .env
docker compose up --build

Default local endpoints: Web UI :46174, Agent API :46181, vLLM :46111, LiveKit :46280, Prometheus :46909, Grafana :46300.

Runtime Contracts

Every hot-path event or chunk carries:

session_id, turn_id, generation_id, branch_state, seq, pts_ms?

When the user interrupts, generation_id advances. Any stale audio, text, face frame, or avatar event from an older generation must be dropped silently. This invariant keeps realtime playback, barge-in, and avatar animation coherent.

API And Evals

Core endpoints: GET /health, GET /ready, GET /api/runtime/status, GET /metrics, POST /api/sessions, POST /api/chat/text, admin cancellation and conversation/turn trace endpoints, and GET /api/acceptance.

Local evidence path:

  • /metrics exposes Prometheus-compatible latency and reliability metrics.
  • TurnTrace records EOT, speculation, first LLM token, first TTS chunk, first audio, and audio completion.
  • SQLiteConversationAuditStore persists local conversation events and state snapshots.
  • scripts/evals/e2e_probe.py measures first audio, barge-in, speculative hit rate, and data-channel envelopes.
  • voice_smoke.py, gpu_smoke.py, latency_report.py, soak_test.py, and legal_gate.py define release evidence.

Quality Targets

Gate Target
First audio p50 600-700 ms after user speech end
First audio p95 <= 1100 ms
Avatar visible reaction <= 250 ms
Barge-in p95 <= 300 ms
Russian ASR WER <= 8%
Speculative hit rate >= 70%
Audio-face PTS drift <= 50 ms p95
Reliability 60-minute or 120+ turn soak test

Run acceptance checks before calling any deployment production-ready:

python .\scripts\evals\legal_gate.py
python .\scripts\evals\gpu_smoke.py
python .\scripts\evals\latency_report.py
python .\scripts\evals\soak_test.py
powershell -ExecutionPolicy Bypass -File .\scripts\airgap\check_windows_firewall.ps1

From WSL:

bash scripts/airgap/check_wsl_network.sh

Repository Layout

apps/agent/                  FastAPI agent and realtime voice runtime
apps/web/                    React + Three.js + VRM browser UI
packages/avatar_protocol/    Typed event/data-channel protocol
profiles/                    Hardware/runtime profiles
models/manifests/            Versioned model/avatar manifests only
scripts/models/              Model download and verification scripts
scripts/evals/               GPU, latency, legal, and soak checks
infra/wsl/                   Local WSL/Docker runtime

What Is Still Missing

  • Current legal evidence for every model, asset, container image, and redistribution path.
  • Full 60-minute / 120+ turn soak evidence on the target machine.
  • Verified air-gap run where the full conversation works with outbound network blocked.
  • Release manifest for model and container checksums.
  • Stronger browser/client security headers if the demo UI is exposed outside localhost.
  • Photorealistic or video-avatar visual layer if the target is a realistic human clone.
  • Tool-calling layer with explicit schemas, permission policy, timeouts, and audit trail.
  • Durable long-term memory or local RAG with privacy boundaries and evals.
  • Offline dependency mirror and reproducible closed-contour install bundle.
  • Operational runbooks for GPU OOM, LiveKit failures, vLLM failures, TTS stalls, and corrupted model cache.
  • Third-party attribution bundle for redistributed assets and model artifacts.

Security, Legal, License

  • Do not commit .env.local, infra/wsl/.env, tokens, keys, certificates, SQLite databases, logs, generated audio/video, or model weights.
  • Hugging Face tokens are stored locally through scripts/secrets/set_hf_token.ps1.
  • Voice cloning, realistic avatars, and redistribution require explicit consent and license review.
  • Third-party models and assets keep their own licenses and terms.
  • See SECURITY.md for vulnerability reporting and supported scope.

MIT License. The MIT license covers this repository's source code and documentation. Model weights, third-party assets, and external runtimes are governed by their upstream licenses.

About

Local-first realtime Russian voice avatar with STT, Qwen LLM, TTS, Audio2Face, LiveKit, and VRM rendering.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages