Skip to main content

livekit-plugins-duplexjev

Audio end-of-turn detection for LiveKit Agents, powered by DuplexJev. It listens to how the user speaks, not only what they say, and tells your agent whether the user is finished, still talking, just backchanneling ("mm-hmm") or asking you to wait. The same request can also return emotion, laughter / breathing / coughs / sighs and barge-in.

  • Reads the audio, not the transcript. Every answer is one constrained token of a speech LLM: nothing is decoded, so the decision does not wait for STT.
  • Fast. Ten decisions about one utterance take 92 ms on one GPU, against 1,978 ms for ASR → LLM with the same LLM.
  • Accurate. On the CoDeTT turn-taking benchmark (English, zero-shot), DuplexJev-32B-Turn scores 70.0, against 51.4 for Smart Turn v3. See the paper.
  • More than turns, same request. Ask for emotion, non-verbal sounds or your own multiple-choice questions.
  • Chinese and English. CPU only on the agent side: the plugin calls an HTTP API.

Tested with livekit-agents 1.8.6. Maintained by Adventists.ai (AI降临派).

Install

pip install livekit-plugins-duplexjev

Use

from livekit.agents import AgentSession
from livekit.plugins import duplexjev, silero

session = AgentSession(
    stt=..., llm=..., tts=...,
    vad=silero.VAD.load(),
    turn_handling={"turn_detection": duplexjev.TurnDetector(
        api_url="http://localhost:8420",       # your own gateway; omit for the free hosted trial API
        extra_questions=["emotion", "sound"],
        on_answers=lambda a: print({k: v["answer"] for k, v in a.items()}),
    )},
)

Give it the agent's last message (new in 0.2.0). Turn-taking is clearly better when the model knows what the agent had just said. Keep a reference to the detector and attach it to the session once:

detector = duplexjev.TurnDetector(api_url="http://localhost:8420")
session = AgentSession(..., turn_handling={"turn_detection": detector})
detector.attach(session)        # follows conversation_item_added; or detector.set_assistant_said(text)

It needs a gateway from duplexjev>=0.6. On LiveKit eot-bench (English, DuplexJev-32B-Para v1.2) this cuts false cut-offs at a 300 ms budget from 23.5% to 19.7%.

AgentSession(turn_detection=duplexjev.TurnDetector(), ...) works too (LiveKit marks that argument as deprecated). A complete agent is in examples/agent.py.

A VAD is required. LiveKit ignores a streaming turn detector when the session has no VAD. The plugin buffers the user's audio, and after about 200 ms of VAD silence LiveKit asks it for a prediction: the last 8 s of the current user turn go to DuplexJev, and P(finished) becomes LiveKit's end_of_turn_probability. Below unlikely_threshold the session waits max_delay instead of min_delay before replying. The VAD's min_silence_duration must be at least 0.25 s (Silero's default is 0.55 s).

STT is optional for the decision. If the session has an STT, LiveKit still waits for its transcript before it commits the turn, as with any turn detector, because the LLM needs the text.

The default URL is our free trial API (rate limited to 30 requests per minute; the round trip from outside mainland China adds network latency). For a live agent, serve the model next to it (DuplexJev-4B-Para needs about 10 GB of GPU memory; for the best turn-taking use DuplexJev-32B-Para v1.2 on one 80 GB GPU):

pip install "vllm[audio]>=0.29" duplexjev-vllm "duplexjev[server]>=0.6"
vllm serve adventists-ai/DuplexJev-4B-Para --max-model-len 4096 --port 8000 &
duplexjev gateway --vllm http://localhost:8000/v1 --port 8420

Then pass api_url="http://localhost:8420" (or set DUPLEXJEV_API_URL).

Options

argument default
api_url hosted trial API base URL of a duplexjev gateway (or DUPLEXJEV_API_URL)
api_key None bearer key, if your gateway requires one (or DUPLEXJEV_API_KEY)
lang "en" "en" or "zh": wording of the questions (the model hears both languages either way)
unlikely_threshold 0.5 below this P(finished), wait max_delay instead of min_delay
backchannel_threshold None if set, also asks "What should the assistant do now?" and reports P(short acknowledgement) as LiveKit's backchannel probability
extra_questions [] "emotion", "sound", "barge_in", or dicts {"id", "text", "options"}
on_answers None callback with every answer dict; the latest is also on detector.last_answers
window_s 8.0 seconds of the current user turn sent with each request
trim_silence True drop the silence before the user started talking (keeps 0.3 s)
prediction_timeout 4.0 hosted, 1.0 own gateway how long LiveKit waits for a prediction
fallback_probability 1.0 P(finished) reported if the gateway fails, so the conversation never blocks
readout "turn" "act" asks "What should the assistant do now?" (P(reply) + P(short acknowledgement)); slightly better on eot-bench with context

After each prediction detector.last_turn_class is finished, not_finished, backchannel or hesitating (reply, keep_listening, acknowledge with readout="act"), for your own routing.

License

The plugin code is BSD-2-Clause. DuplexJev model weights are CC BY-NC 4.0 (non-commercial); see the model cards. For commercial use, contact jiejin@adventists.ai.

Metadata

Release files for livekit-plugins-duplexjev 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for livekit-plugins-duplexjev 0.3.0
File Size Uploaded
livekit_plugins_duplexjev-0.3.0.tar.gz 16.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for livekit-plugins-duplexjev 0.3.0
File Interpreter ABI Platform
livekit_plugins_duplexjev-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 30.4 kB

Release files / livekit_plugins_duplexjev-0.3.0.tar.gz

Download URL livekit_plugins_duplexjev-0.3.0.tar.gz
Size 16.6 kB
Tags Source
SHA-256 checksum
How to use checksums
2101be8c51b88d556798e42e9b1547805e668114ec0f41109c91cc1bea0d23cd
BLAKE2b-256 checksum
How to use checksums
d7ca13466c576b66e8dc3df70f1502b8bd8dc71deaea63007f944a84742b6782
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Release files / livekit_plugins_duplexjev-0.3.0-py3-none-any.whl

Download URL livekit_plugins_duplexjev-0.3.0-py3-none-any.whl
Size 13.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fdc2e7003820e0cc4a1974004c0be763c3c2f79bf02eb8a21d0cd8b1d7b1fdac
BLAKE2b-256 checksum
How to use checksums
08b755b89bb0d9909291d4c736d6e5cb5f1ff0fcca0a77a40cc849f3167e1cdc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Release history Release notifications | RSS feed

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page