# openalice-voice-agent

> Rust AI voice participant — STT→A2A LLM→TTS pipeline that joins openalice-voice SFU rooms as an agent.

## At a glance
| field | value |
|---|---|
| kind | `service` |
| priority | `P3` |
| default branch | `mvp` |
| last activity | 2026-06-16T02:43:42+00:00 |
| features catalogued | **7** |
| runtime deps declared | **5** |

## Manifest
`.atlas-deps.yml` — parsed cleanly: **yes**

### Runtime deps
- **`openalice`** · `http` · via `A2A_URL` — External A2A endpoint — forward transcribed user utterances and receive LLM reply text
- **`openalice-agent-protocol-rs`** · `other` · via `cargo` — ConnectorEvent / ToolCall types and A2A message schema
- **`openalice-axum-common`** · `other` · via `cargo` — Shared Axum/reqwest/JWT plumbing used at compile time
- **`openalice-voice`** · `ws` · via `VOICE_SFU_WS_URL` — Join SFU voice rooms as a participant; receive Opus audio frames and send TTS audio back
- **`openalice-rt`** · `ws` · via `RT_URL` · _optional_ — Persona side-channel — emit emotion/expression events so the VRM avatar reacts to conversation

### Consumed env
- `VOICE_AGENT_PORT`
- `VOICE_AGENT_MODE`
- `VOICE_AGENT_AUTO_JOIN_ROOM`
- `VOICE_SFU_WS_URL`
- `VOICE_SFU_API_URL`
- `A2A_URL`
- `A2A_API_KEY`
- `RT_URL`
- `STT_PROVIDER`
- `STT_API_URL`
- `STT_API_KEY`
- `STT_MODEL`
- `TTS_PROVIDER`
- `TTS_API_URL`
- `TTS_API_KEY`
- `TTS_VOICE_ID`
- `TTS_MODEL`
- `VAD_THRESHOLD`
- `SILENCE_DURATION_MS`
- `MAX_AUDIO_DURATION_MS`

### Exposes
- `http_endpoint` — GET /health
- `docker_image` — openalice-voice-agent

## Features (7)

### voice-agent/llm
- **`voice-agent.llm.bridge`** — A2A JSON-RPC 2.0 client in src/a2a_client.rs. Calls tasks/send on the openalice A2A gateway (A2A_URL) with the transcript as a user message and channel metadata "voice". Extracts the response text from result.history[-1].parts[0].text.  
  *src/a2a_client.rs:1*

### voice-agent/metrics
- **`voice-agent.http.metrics`** — GET /metrics — Prometheus text exposition with `voice_agent_up`,  
  *src/main.rs:6*

### voice-agent/persona
- **`voice-agent.persona.rt-emitter`** — Real-time Persona side-channel in src/rt_emitter.rs. Sends WebSocket "mutate" events to openalice-rt for the agent persona: emotion state (thinking/speaking/neutral) and lipsync viseme weights (placeholder float array). Failure is non-fatal; the pipeline continues without RT if unreachable.  
  *src/rt_emitter.rs:1*

### voice-agent/service
- **`voice-agent.service.crate`** — Binary crate `openalice-voice-agent` (Tokio + Axum runtime). Exposes GET /health and GET /metrics; spawns AgentManager background task that joins SFU rooms and runs the voice pipeline on demand. Entry point: src/main.rs.  
  *src/main.rs:1*

### voice-agent/stt
- **`voice-agent.stt.pipeline`** — Speech-to-text stage. Trait SttProvider in src/stt.rs. Concrete backend: OpenAiStt — POSTs WAV to OpenAI Whisper (default model: whisper-1, configurable via STT_MODEL). PCM→WAV conversion is performed inline by OpusCodec::pcm_to_wav.  
  *src/stt.rs:1*

### voice-agent/tts
- **`voice-agent.tts.pipeline`** — Text-to-speech stage. Trait TtsProvider + enum TtsBackend in src/tts.rs. Two concrete backends selectable at runtime via TTS_PROVIDER: OpenAiTts (requests response_format "opus"/OGG Opus bytes) and ElevenLabsTts (v1 API with stability/similarity_boost tuning). Audio bytes are forwarded as binary WebSocket frames to the SFU.  
  *src/tts.rs:1*

### voice-agent/vad
- **`voice-agent.vad.energy`** — Energy-based voice activity detector in src/vad.rs. is_speech() computes RMS over i16 PCM frames against a configurable threshold (VAD_THRESHOLD, default 0.5 normalised). VadState tracks speech/silence transitions and fires speech-end events after SILENCE_DURATION_MS (default 1500ms) of sustained quiet.  
  *src/vad.rs:1*

---

*Generated by [openalice-atlas](https://atlas.blal.pro) — single source of truth for the openalicelabs ecosystem.*
