VSIP Python SDK
Real-time voice events for AI voice agents: semantic turn detection, identity-gated barge-in, and speaker verification over one WebSocket.
pip install vsip-sdk # once published; for now: pip install -e sdk/python
60-second integration
import asyncio
from vsip import VSIPClient, VoiceSession, Turn, int16_to_wav
client = VSIPClient(api_key="vsip_...", url="wss://api.vsip.online/v1/stream")
async def on_turn(turn: Turn):
if not turn.should_respond:
return # verified: not your enrolled user
text = await my_stt(int16_to_wav(turn.audio)) # STT-ready WAV slice
reply = await my_llm(text, interrupted=turn.interrupted_agent)
await session.speak_gate() # never talk over an open turn
await client.agent_speaking() # arm barge-in detection
await my_tts_play(reply) # stop this in on_barge_in
await client.agent_idle()
def on_barge_in(event):
my_tts_stop() # session already sent agent_idle
session = VoiceSession(client, on_turn=on_turn, on_barge_in=on_barge_in)
async def main():
async with client:
await session.start()
async for frame in my_mic_frames(): # 16kHz mono int16 PCM
await session.send_audio(frame)
asyncio.run(main())
What VoiceSession does for you
These behaviors are the recommended protocol usage (each one exists because skipping it caused a real failure in field testing):
| Behavior | Why |
|---|---|
| Turn audio includes onset lookback (1.0s / 1.5s for barge-ins) | turn_start arrives 1–2 ticks after real speech onset — naive slicing loses first syllables |
Post-lock turns are held for speaker_verification (12s cap) |
the live speaker_id on turn_end can be a whole turn stale |
| Pre-lock turns dispatch immediately | first-conversation UX — the first sentence enrolls AND gets answered |
on_barge_in + automatic set_agent_state idle |
TTS must stop within ~250ms of the event |
speak_gate() |
starting TTS over an open user turn makes the server treat the tail of that turn as a barge-in |
| Turn handlers are queued, never dropped | a barge-in mid-response must not discard the interrupting utterance |
| Rolling buffer capped outside turns | an hour of silence costs no memory |
Lower level
VSIPClient alone gives you: typed events (async for event in client.events()),
control commands (agent_speaking/idle, set_barge_in_mode, enroll_speaker,
toggle_lock, reset), tagged frames for aec="server"
(send_mic_frame / send_tts_reference), and transparent reconnect+resume
(sessions survive network blips ≤60s — enrollment and lock state included).
Unknown event types parse as GenericEvent and unknown fields are ignored:
the wire protocol is additive-only, so this SDK won't break as the API grows.
Pipecat adapter
pip install "vsip-sdk[pipecat]"
from vsip import VSIPClient
from vsip.adapters.pipecat import VSIPProcessor
vsip = VSIPProcessor(VSIPClient(api_key="vsip_...", url="wss://.../v1/stream"))
pipeline = Pipeline([
transport.input(),
stt,
vsip, # identity-gated interruption + verification gating
context_aggr,
llm,
tts,
transport.output(),
])
- Identity-gated interruption: VSIP's
barge_inbecomes a pipeline interruption — only your enrolled speaker can cut the bot off. Setallow_interruptions=Falseon the transport so VSIP is the sole authority. - Verification gating: once locked, TranscriptionFrames are held until
the
speaker_verificationverdict — impostor turns never reach your LLM. Each verdict is also emitted as aVSIPVerificationFrame. - Automatic TTS sync: Bot speaking-state frames drive
set_agent_state.
LiveKit Agents plugin
pip install "vsip-sdk[livekit]"
from livekit.agents import AgentSession
from vsip import VSIPClient
from vsip.adapters.livekit import VSIPVAD
vsip_vad = VSIPVAD(
VSIPClient(api_key="vsip_...", url="wss://.../v1/stream"),
on_verification=lambda v: None, # optional: gate the agent's response
)
session = AgentSession(vad=vsip_vad, stt=..., llm=..., tts=...)
VSIP plugs in as the session's VAD / turn detector: turn_start and
verified barge_in become START_OF_SPEECH, turn_end becomes
END_OF_SPEECH. So VSIP's semantic endpointing and identity-gated barge-in
drive the agent's turn-taking — a cough or bystander can't interrupt. Use the
on_verification callback to additionally gate the agent's response on
should_respond.
Twilio Media Streams bridge
Connect a phone call's audio to VSIP — VSIP ingests Twilio's μ-law natively.
The bridge is framework-agnostic (no extra dependency); wire it to your
FastAPI / Starlette / websockets endpoint:
from vsip import VSIPClient
from vsip.adapters.twilio import TwilioBridge
@app.websocket("/twilio-stream")
async def twilio_stream(ws):
await ws.accept()
client = VSIPClient(api_key="vsip_...", url="wss://.../v1/stream",
audio_format="mulaw8k")
bridge = TwilioBridge(client,
on_turn_end=lambda e: ...,
on_verification=lambda v: ...) # gate on should_respond
async with client:
await bridge.run(ws.iter_text(), send=ws.send_text)
On barge_in the bridge sends Twilio a clear message — flushing buffered
playback so the caller's interruption stops your TTS instantly.
Availability
| Target | Status |
|---|---|
Python SDK (VSIPClient, VoiceSession) |
✅ available |
Pipecat (vsip-sdk[pipecat]) |
✅ available |
LiveKit Agents (vsip-sdk[livekit]) |
✅ available |
Twilio Media Streams (vsip.adapters.twilio) |
✅ available |
JavaScript / TypeScript (@vsip/sdk) |
✅ available |
Examples
examples/echo_agent.py— minimal end-to-end agent loop against a local API with your microphone.
Release files for vsip-sdk 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| vsip_sdk-0.2.1.tar.gz | 31.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vsip_sdk-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 58.8 kB
Release files / vsip_sdk-0.2.1.tar.gz
| Download URL | vsip_sdk-0.2.1.tar.gz |
|---|---|
| Size | 31.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d9c68c0a9fad8ad152ccf0784910842cad5473eb66f7941ebc6b806d50b499fc
|
|
BLAKE2b-256 checksum How to use checksums |
2835d7c913031691ab17582fbf28a8f30e6f9e99df84de6513ee5269b1bac516
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.6
|
Release files / vsip_sdk-0.2.1-py3-none-any.whl
| Download URL | vsip_sdk-0.2.1-py3-none-any.whl |
|---|---|
| Size | 27.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
81b88667738ebbc3c655bb820e32718d0411f5b4524a3ce0873ac21558d3555d
|
|
BLAKE2b-256 checksum How to use checksums |
89a2224fa5e7544c5d29e17137bb94b77cf5bdfb14b7fea38e614cdb088d453f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.6
|