- Python 98.9%
- Dockerfile 0.7%
- Shell 0.4%
192.168.88.17 (self-hosted OmniVoice) drops out periodically; agent then loses voice entirely. Wrap primary OmniVoice TTS in LK's FallbackAdapter with EdgeTTS as backup using ru-RU-SvetlanaNeural so the call keeps speaking through Microsoft's cloud when local is down. Both producers emit 24kHz mono so the adapter's sample-rate selection is uniform. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .github/workflows | ||
| docker | ||
| plugins | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| agent.py | ||
| agent_evlog.py | ||
| AGENTS.md | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| dealer_voice.py | ||
| Dockerfile | ||
| persona.md | ||
| poker_dealer.py | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
Hermes Call Agent
LiveKit voice agent for openbomber video calls. Joins rooms as a participant, listens via STT, responds via TTS, and can see the user's camera when asked.
Stack
| Component | Default | Env var |
|---|---|---|
| LLM | Gemini Flash | LLM_PROVIDER=gemini |
| STT | Groq Whisper large-v3-turbo | STT_PROVIDER=groq |
| TTS | Edge TTS (DmitryNeural) | TTS_PROVIDER=edge |
| VAD | Silero | — |
| Turn Detection | Multilingual Model | — |
| Video Vision | Enabled | AGENT_VIDEO=true |
| Logs | evlog-style NDJSON | AGENT_JSON_LOGS=true |
Quick Start
# 1. Copy and fill env
cp .env.example .env
# Edit .env with your API keys
# 2. Install deps (optional, for local dev)
uv venv && source .venv/bin/activate
uv pip install -e ".[whisper-local]"
# 3. Run
python agent.py start
Docker Deployment
docker build -t hermes-call-agent .
docker run -d --env-file .env hermes-call-agent
Managed by Coolify on the home server. Container name: hermes-call-agent.
Video Vision
When AGENT_VIDEO=true, the agent subscribes to remote video tracks and keeps the latest frame in memory. When the user says trigger words like "посмотри", "что видишь", "опиши", "look", "describe" — the frame is injected into the LLM as a base64 JPEG image.
The video pipeline:
VideoWatchersubscribes to all remote video tracks viatrack_subscribedevents- Continuously reads frames into a BGR numpy array (only latest kept)
- On vision trigger →
snapshot()converts to JPEG base64 - Frame injected into LLM message as
image_urlcontent block
Browser Screen Share
When AGENT_BROWSER_ENABLED=true, Dasha can open a public URL with Playwright
and publish the browser viewport as a LiveKit screen-share track. The Nuxt
frontend already treats screen-share tracks as focusable tiles, so the browser
appears like a normal shared screen.
The browser is controllable through voice tools: open a URL, search web or
YouTube, click a visible result, type into the active/search field, press keys,
go back, and close the track. For video requests the agent should use browser
search directly (site="youtube") and click the first result instead of
waiting on external web search.
Set AGENT_BROWSER_ALLOWED_HOSTS to a comma-separated allowlist for production
lockdown, for example dashka.live,docs.livekit.io,*.wikipedia.org. Empty
allowlist permits public http/https hosts but blocks localhost, private IPs,
link-local, multicast, and reserved IPs. Browser audio is not captured in this
first version.
Chromium currently runs in the agent container. If it starts competing with
voice CPU/memory under real traffic, split it into a separate browser-worker
service that publishes the same LiveKit screen-share track and expose the same
control API (open, search, click, type, press, back, close).
Environment Variables
See .env.example for full list. Key ones:
LIVEKIT_URL— LiveKit server URLLLM_PROVIDER—gemini(fast, recommended),hermes(local brain, slow),groqSTT_PROVIDER—groq(fast),whisper-local(CPU),deepgram,openaiTTS_PROVIDER—edge(free),elevenlabs,openaiAGENT_VIDEO—trueto enable camera visionAGENT_BROWSER_ENABLED—trueto let the agent publish a browser as screen shareAGENT_BROWSER_ALLOWED_HOSTS— optional comma-separated browser URL allowlistAGENT_LANGUAGE— Language code (default:ru)AGENT_JSON_LOGS— structured JSON lines includingevlog; defaults totrue, setfalsefor plain logsEVLOG_SERVICE,EVLOG_ENV— service/env fields attached to every evlog eventAGENT_USAGE_LOG_INTERVAL_SEC— throttle for Gemini Live usage snapshots; defaults to15
Logging
The agent emits evlog-style wide events for LiveKit join/session startup, model builders, STT/TTS, tool calls, memory, web search, Telegram delivery, video capture, realtime wake-word filtering, usage totals, and shutdown summaries. Sensitive fields such as API keys, cookies, and secret string tokens are redacted; numeric usage token counters are kept.
Project Structure
agent.py # Main agent (entrypoint, LLM/STT/TTS builders, VideoWatcher)
persona.md # System prompt for the voice persona
plugins/
edge_tts_plugin.py # Microsoft Edge TTS (free, good Russian voices)
whisper_local_stt.py # Local faster-whisper STT (CPU fallback)
Dockerfile # Production Docker image
.env.example # Configuration template