voicesim
GitHub本地模拟 ElevenLabs 语音交互及 OpenAI 音频服务,提供固定脚本响应与调试控制台。用于在不依赖真实 API 的情况下测试语音代理、签名 URL 生成及通话流程,支持状态查看、调用清理及负载限制配置。
Trigger Scenarios
Install
npx skills add langwatch/langwatch --skill voicesim -g -y
SKILL.md
Frontmatter
{
"name": "voicesim",
"description": "Run ElevenLabs voice calls locally against voicesim, haven's voice-provider stand-in. Use when someone says 'haven voice', 'voicesim', 'haven up +voice', 'test a voice agent without ElevenLabs', 'signed URL', 'convai socket', 'fake ElevenLabs', 'voice call console', 'haven sim voice', or needs a deterministic voice call in a test.",
"user-invocable": true
}
voicesim
Fakes ElevenLabs Conversational AI (the signed-URL mint and the conversation socket) and
OpenAI's pcm speech and transcription. Every answer is canned: tones for audio, four
scripted agent lines, one fixed caller transcript. Keys are accepted and never checked: a dev
shim, never expose it. Code: services/sims/voicesim, console apps/sims/src/voice.
Run it
- Opt-in:
haven up +voice(sticky). Runs in thegolane by default (seesims). - Already on?
haven sim voice statusanswers with the base URLs; the seeded org then has an ElevenLabs provider at voicesim, no clean instance needed. - Console:
https://voice.<slug>.langwatch.localhost(calls, transcripts, events, Clear calls);haven statusshows the loopback port. - Unless
.envnamesELEVENLABS_BASE_URL, the overlay sets it tohttp://127.0.0.1:<port>, a dummyELEVENLABS_API_KEYwhen none is set, andVOICE_UNSAFE_ALLOW_LOOPBACK_PROVIDERS=1. - The seed then stores the ElevenLabs provider row at voicesim. Standalone:
make service svc=voicesim(:5591).
The dev switch
VOICE_UNSAFE_ALLOW_LOOPBACK_PROVIDERS (packages/config/src/deployment-facts.ts) is the only
thing that lets the product call a loopback voice host. One rule decides it,
isAllowedElevenLabsUrl in @langwatch/model-provider-contract, used by the seed's keys
schema, the gateway's credential read and the signed-URL check. On, it admits http/ws on
127.0.0.1 or localhost with an explicit port, nothing else. Off (every production
process), those three points refuse loopback. The registry schema never carries the switch, so
the settings form still refuses a loopback base URL.
OpenAI audio is not redirected: the product has no audio-only OpenAI base URL, and
OPENAI_BASE_URL would move every model call.
Inspect and assert
GET /_sim/api/calls # newest first: turns, frame counts, protocol events
DELETE /_sim/api/calls # forget every call (204); ids keep counting
GET /_sim/api/status # stack, call count, the base URLs to point a provider at
GET /v1/convai/conversation/get-signed-url?agent_id=<id>
POST /v1/audio/speech POST /v1/audio/transcriptions
A call id is conv_voicesim_0001, 0002, ... An unfaked provider path answers 404.
Seed and reset
VOICESIM_SEED=1(haven sets it) records one finished two-turn call for agentagent_voicesim_seed, so the console starts with content.- Nothing persists: a restart of the lane that hosts it (
haven restart go) empties it.
Tests and load
- Caps:
VOICESIM_MAX_CALLS(default 200, oldest dropped) andVOICESIM_MAX_EVENTS_PER_CALL(default 500, the rest counted asdroppedEvents). - Drive the loopback port directly;
go test ./services/sims/voicesimcovers the protocol. - The Twilio phone transport is not faked.
From a terminal or agent
--json on every read; non-zero exit on failure; --stack <slug> reads another worktree.
haven sim voice status | list | get <id> | clear
Version History
- c0c14dc Current 2026-10-19 19:35


