Skip to content

Conversation

@bml1g12
Copy link
Contributor

@bml1g12 bml1g12 commented Jan 13, 2026

This PR allows users to select scribe_v2 for use as a non-realtime STT engine, it also allows selecting STT engine by model_id. It deprecates the use_realtime parameter in favour of automatically enabling this based on the model_id name.

Context: I wanted to run the new scribe_v2 model (https://elevenlabs.io/docs/overview/models#models-overview) but saw the STT plugin has no model parameter, and even seems to be hardcoded to the old scribe_v1 model. This PR fixes that by introducing a model_id in a similar way to many other plugins.

Disclaimer: I am not an expert on Elevenlabs code, I just spotted what looks like a missing feature or oversight, and made this PR accordingly

Note: When I tried use_realtime=True with latest livekit version, I found regular " ElevenLabs STT connection closed unexpectedly" when using manual turn detection and ending user turn. I see when searching livekit agents forum many people report similar issues. For this reason I wanted to disable use_realtime and work with the latest model.

Warning: I notice that when use_realtime is set, it used a hardcoded realtime_scribe_v2 - this PR tries to avoid a breaking change by enforcing the existing behaviour, that use_realtime=True will always use that model, if we want to be more future proof here we could make a breaking change and make it fully dynamic

Summary by CodeRabbit

  • New Features

    • Added configurable model selection for ElevenLabs speech-to-text engines. Choose between scribe_v1, scribe_v2, and scribe_v2_realtime via the new model_id parameter.
  • Deprecations

    • The use_realtime parameter is now deprecated; use model_id to select your preferred STT model instead.
    • Added warnings when requesting server-side voice activity detection with non-realtime models.

✏️ Tip: You can customize this high-level summary in your review settings.

Copy link
Member

@chenghao-mou chenghao-mou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor issues but looks good otherwise! Thanks for contributing!

@chenghao-mou
Copy link
Member

/test-stt

@github-actions
Copy link
Contributor

github-actions bot commented Jan 13, 2026

STT Test Results

Status: ✗ Some tests failed

Metric Count
✓ Passed 22
✗ Failed 1
× Errors 1
→ Skipped 15
▣ Total 39
⏱ Duration 182.8s
Failed Tests
  • tests.test_stt::test_stream[livekit.agents.inference]
    stt_factory = <function parameter_factory.<locals>.<lambda> at 0x7f1e6eb1d8a0>
    request = <FixtureRequest for <Coroutine test_stream[livekit.agents.inference]>>
    
        @pytest.mark.usefixtures("job_process")
        @pytest.mark.parametrize("stt_factory", STTs)
        async def test_stream(stt_factory: Callable[[], STT], request):
            sample_rate = SAMPLE_RATE
            plugin_id = request.node.callspec.id.split("-")[0]
            frames, transcript, _ = await make_test_speech(chunk_duration_ms=10, sample_rate=sample_rate)
      
            # TODO: differentiate missing key vs other errors
            try:
                stt_instance: STT = stt_factory()
            except ValueError as e:
                pytest.skip(f"{plugin_id}: {e}")
      
            async with stt_instance as stt:
                label = f"{stt.model}@{stt.provider}"
                if not stt.capabilities.streaming:
                    pytest.skip(f"{label} does not support streaming")
      
                for attempt in range(MAX_RETRIES):
                    try:
                        state = {"closing": False}
      
                        async def _stream_input(
                            frames: list[rtc.AudioFrame], stream: RecognizeStream, state: dict = state
                        ):
                            for frame in frames:
                                stream.push_frame(frame)
                                await asyncio.sleep(0.005)
      
                            stream.end_input()
                            state["closing"] = True
      
                        async def _stream_output(stream: RecognizeStream, state: dict = state):
                            text = ""
                            # make sure the events are sent in the right order
                            recv_start, recv_end = False, True
                            start_time = time.time()
                            got_final_transcript = False
      
                            async for event in stream:
                                if event.type == agents.stt.SpeechEventType.START_OF_SPEECH:
    
  • tests.test_stt::test_stream[livekit.plugins.aws]
    def finalizer() -> None:
            """Yield again, to finalize."""
      
            async def async_finalizer() -> None:
                try:
                    await gen_obj.__anext__()  # type: ignore[union-attr]
                except StopAsyncIteration:
                    pass
                else:
                    msg = "Async generator fixture didn't stop."
                    msg += "Yield only once."
                    raise ValueError(msg)
      
            task = _create_task_in_context(event_loop, async_finalizer(), context)
    >       event_loop.run_until_complete(task)
    
    .venv/lib/python3.12/site-packages/pytest_asyncio/plugin.py:347: 
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
    
    self = <_UnixSelectorEventLoop running=False closed=True debug=False>
    future = <Task finished name='Task-113' coro=<_wrap_asyncgen_fixture.<locals>._asyncgen_fixture_wrapper.<locals>.finalizer.<loc... File "/home/runner/work/agents/agents/.venv/lib/python3.12/site-packages/smithy_http/aio/crt.py", line 104, in chunks>
    
        def run_until_complete(self, future):
            """Run until the Future is done.
      
            If the argument is a coroutine, it is wrapped in a Task.
      
            WARNING: It would be disastrous to call run_until_complete()
            with the same coroutine twice -- it would wrap it in two
            different Tasks and that can't be good.
      
            Return the Future's result, or raise its exception.
            """
            self._check_closed()
            self._check_running()
      
            new_task = not futures.isfuture(future)
            future = tasks.ensure_future(future, loop=self)
            if new_task:
                # An exception is raised if the future didn't complete, so there
                # is no need to log the "destroy pending task" message
                future._log_destroy_pending = False
      
            future.add_done_callback(_run_until_complete_cb)
            try:
                self.run_forever()
            except:
                if new_task and future.done() and not future.canc
    
Skipped Tests
Test Reason
tests.test_stt::test_recognize[livekit.plugins.assemblyai] universal-streaming-english@AssemblyAI does not support batch recognition
tests.test_stt::test_recognize[livekit.plugins.speechmatics] unknown@Speechmatics does not support batch recognition
tests.test_stt::test_recognize[livekit.plugins.fireworksai] unknown@FireworksAI does not support batch recognition
tests.test_stt::test_recognize[livekit.plugins.nvidia] unknown@unknown does not support batch recognition
tests.test_stt::test_recognize[livekit.plugins.aws] unknown@Amazon Transcribe does not support batch recognition
tests.test_stt::test_recognize[livekit.plugins.cartesia] ink-whisper@Cartesia does not support batch recognition
tests.test_stt::test_recognize[livekit.plugins.soniox] stt-rt-v3@Soniox does not support batch recognition
tests.test_stt::test_recognize[livekit.plugins.deepgram.STTv2] flux-general-en@Deepgram does not support batch recognition
tests.test_stt::test_recognize[livekit.plugins.gradium.STT] unknown@Gradium does not support batch recognition
tests.test_stt::test_recognize[livekit.agents.inference] unknown@livekit does not support batch recognition
tests.test_stt::test_recognize[livekit.plugins.azure] unknown@Azure STT does not support batch recognition
tests.test_stt::test_stream[livekit.plugins.elevenlabs] scribe_v1@ElevenLabs does not support streaming
tests.test_stt::test_stream[livekit.plugins.mistralai] voxtral-mini-latest@MistralAI does not support streaming
tests.test_stt::test_stream[livekit.plugins.openai] gpt-4o-mini-transcribe@api.openai.com does not support streaming
tests.test_stt::test_stream[livekit.plugins.fal] Wizper@Fal does not support streaming

Triggered by workflow run #233

@bml1g12
Copy link
Contributor Author

bml1g12 commented Jan 14, 2026

/test-stt

@github-actions
Copy link
Contributor

/test-stt command is only available for organization members.

@bml1g12
Copy link
Contributor Author

bml1g12 commented Jan 14, 2026

I have made the suggested changes ⚡

@bml1g12 bml1g12 requested a review from davidzhao January 15, 2026 10:34
Comment on lines 110 to 136
if is_given(use_realtime):
if use_realtime is True:
logger.warning(
"`use_realtime` parameter is deprecated. "
"Specify a realtime model_id to enable streaming. "
"Defaulting model_id to 'scribe_v2_realtime' "
)
model_id = "scribe_v2_realtime"
else:
logger.warning(
"`use_realtime` parameter is deprecated. Instead set model_id to determine if streaming is enabled."
)
if is_given(model_id) and "realtime" in model_id:
raise ValueError(
"The currently selected model is a realtime model but use_realtime is False"
)
else:
use_realtime = True if (is_given(model_id) and "realtime" in model_id) else False

# Handle model_id defaults
if not is_given(model_id):
if use_realtime:
logger.warning("model_id is not provided. Defaulting to 'scribe_v2_realtime'.")
model_id = "scribe_v2_realtime"
else:
logger.warning("model_id is not provided. Defaulting to 'scribe_v1'.")
model_id = "scribe_v1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe simplify the validation

Suggested change
if is_given(use_realtime):
if use_realtime is True:
logger.warning(
"`use_realtime` parameter is deprecated. "
"Specify a realtime model_id to enable streaming. "
"Defaulting model_id to 'scribe_v2_realtime' "
)
model_id = "scribe_v2_realtime"
else:
logger.warning(
"`use_realtime` parameter is deprecated. Instead set model_id to determine if streaming is enabled."
)
if is_given(model_id) and "realtime" in model_id:
raise ValueError(
"The currently selected model is a realtime model but use_realtime is False"
)
else:
use_realtime = True if (is_given(model_id) and "realtime" in model_id) else False
# Handle model_id defaults
if not is_given(model_id):
if use_realtime:
logger.warning("model_id is not provided. Defaulting to 'scribe_v2_realtime'.")
model_id = "scribe_v2_realtime"
else:
logger.warning("model_id is not provided. Defaulting to 'scribe_v1'.")
model_id = "scribe_v1"
if is_given(realtime_model):
if is_given(model_id):
logger.warning(
"both `use_realtime` and `model_id` parameters are provided. `use_realtime` will be ignored."
)
else:
logger.warning(
"`use_realtime` parameter is deprecated. "
"Specify a realtime model_id to enable streaming. "
"Defaulting model_id to 'scribe_v2_realtime' "
)
model_id = "scribe_v2_realtime" if realtime_model else "scribe_v1"
model_id = model_id if is_given(model_id) else "scribe_v1"
realtime_model = model_id == "scribe_v2_realtime"

then use realtime_model for streaming and

        if not realtime_model and is_given(server_vad):
            logger.warning("Server-side VAD is only supported for Scribe v2 realtime model")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I have made that change

@bml1g12 bml1g12 requested a review from longcw January 16, 2026 14:32
@davidzhao
Copy link
Member

/test-stt

@davidzhao
Copy link
Member

@bml1g12 just need to fix CI and we'll get this merged.

@bml1g12
Copy link
Contributor Author

bml1g12 commented Jan 17, 2026

@bml1g12 just need to fix CI and we'll get this merged.

@davidzhao Sorry but I do not understand this CI failure - as when I run

❯ make fix                                                                                     ─╯
Formatting code...
1 file reformatted, 589 files left unchanged
✓ Code formatted
Running linter with auto-fix...
All checks passed!
✓ Linting complete

All looks healthy, and @chenghao-mou suggests the failure is unrelated to this PR here

@chenghao-mou
Copy link
Member

chenghao-mou commented Jan 17, 2026

@bml1g12 just need to fix CI and we'll get this merged.

@davidzhao Sorry but I do not understand this CI failure - as when I run

❯ make fix                                                                                     ─╯
Formatting code...
1 file reformatted, 589 files left unchanged
✓ Code formatted
Running linter with auto-fix...
All checks passed!
✓ Linting complete

All looks healthy, and @chenghao-mou suggests the failure is unrelated to this PR here

The latest failure came from ruff in 3.9:

  uv run ruff format --check .
  shell: /usr/bin/bash -e {0}
  env:
    UV_CACHE_DIR: /home/runner/work/_temp/setup-uv-cache
    pythonLocation: /opt/hostedtoolcache/Python/3.9.25/x64
    PKG_CONFIG_PATH: /opt/hostedtoolcache/Python/3.9.25/x64/lib/pkgconfig
    Python_ROOT_DIR: /opt/hostedtoolcache/Python/3.9.25/x64
    Python2_ROOT_DIR: /opt/hostedtoolcache/Python/3.9.25/x64
    Python3_ROOT_DIR: /opt/hostedtoolcache/Python/3.9.25/x64
    LD_LIBRARY_PATH: /opt/hostedtoolcache/Python/3.9.25/x64/lib
Would reformat: livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py
1 file would be reformatted, 589 files already formatted

You can run uv run --python 3.10 ruff format --check .. There is an extra line on line 127.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 17, 2026

📝 Walkthrough

Walkthrough

Type annotations added to an ElevenLabs example script. The ElevenLabs STT component now supports configurable model selection through a new model_id parameter, replacing hardcoded model identifiers and deprecating the use_realtime flag with appropriate warnings.

Changes

Cohort / File(s) Summary
Type Annotations
examples/other/elevenlab_scribe_v2.py
Added explicit return type hints (-> None) to prewarm() and entrypoint() functions; added type annotation for session variable initialization
Model Configuration
livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py
Introduced ElevenLabsSTTModels type alias for selectable models (scribe_v1, scribe_v2, scribe_v2_realtime); added model_id field to STTOptions; extended STT.__init__ with model_id parameter; replaced hardcoded model identifiers with configurable selection; deprecated use_realtime parameter with conditional warnings; added validation warning for server_vad usage with non-realtime models

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Models now dance at your command,
No more hardcoded strings so bland!
Select your whisper, choose your way,
Scribe v1, v2—flexibility holds sway.
The realtime flag bids its farewell goodbye,
As model_id soars through the digital sky! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding support for specifying the scribe_v2 non-realtime model through a new model_id parameter to the ElevenLabs STT plugin.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@bml1g12
Copy link
Contributor Author

bml1g12 commented Jan 17, 2026

@bml1g12 just need to fix CI and we'll get this merged.

@davidzhao Sorry but I do not understand this CI failure - as when I run

❯ make fix                                                                                     ─╯
Formatting code...
1 file reformatted, 589 files left unchanged
✓ Code formatted
Running linter with auto-fix...
All checks passed!
✓ Linting complete

All looks healthy, and @chenghao-mou suggests the failure is unrelated to this PR here

The latest failure came from ruff in 3.9:

  uv run ruff format --check .
  shell: /usr/bin/bash -e {0}
  env:
    UV_CACHE_DIR: /home/runner/work/_temp/setup-uv-cache
    pythonLocation: /opt/hostedtoolcache/Python/3.9.25/x64
    PKG_CONFIG_PATH: /opt/hostedtoolcache/Python/3.9.25/x64/lib/pkgconfig
    Python_ROOT_DIR: /opt/hostedtoolcache/Python/3.9.25/x64
    Python2_ROOT_DIR: /opt/hostedtoolcache/Python/3.9.25/x64
    Python3_ROOT_DIR: /opt/hostedtoolcache/Python/3.9.25/x64
    LD_LIBRARY_PATH: /opt/hostedtoolcache/Python/3.9.25/x64/lib
Would reformat: livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py
1 file would be reformatted, 589 files already formatted

You can run uv run --python 3.10 ruff format --check .. There is an extra line on line 127.

Oh my apologies for missing that, I have pushed the ruff formatted changes

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py`:
- Around line 109-125: The logger.warning call in the branch that handles both
use_realtime and model_id ("both `use_realtime` and `model_id` parameters are
provided. `use_realtime` will be ignored.") exceeds the 100-character limit;
update the warning in the if is_given(use_realtime) and is_given(model_id)
branch by splitting the long string across multiple shorter literal parts (e.g.,
implicit string concatenation with parentheses or separate +-joined strings) in
the logger.warning call so it stays under 100 chars per line, keeping the same
message and leaving the surrounding logic (use_realtime, model_id, and
subsequent model_id defaulting) unchanged.
🧹 Nitpick comments (1)
examples/other/elevenlab_scribe_v2.py (1)

27-36: Remove deprecated use_realtime parameter from example.

The example uses both use_realtime=True (deprecated) and model_id="scribe_v2_realtime" (new). Per the implementation in stt.py, this combination logs a warning: "both use_realtime and model_id parameters are provided. use_realtime will be ignored." Examples should demonstrate the recommended usage pattern.

♻️ Suggested fix
     stt = elevenlabs.STT(
-        use_realtime=True,
         server_vad={
             "vad_silence_threshold_secs": 0.5,
             "vad_threshold": 0.5,
             "min_speech_duration_ms": 100,
             "min_silence_duration_ms": 300,
         },
         model_id="scribe_v2_realtime",
     )
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 988b395 and 94d8920.

📒 Files selected for processing (2)
  • examples/other/elevenlab_scribe_v2.py
  • livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings

Files:

  • examples/other/elevenlab_scribe_v2.py
  • livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: livekit/agents PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T07:44:56.353Z
Learning: Implement Model Interface Pattern for STT, TTS, LLM, and Realtime models with provider-agnostic interfaces, fallback adapters for resilience, and stream adapters for different streaming patterns
📚 Learning: 2026-01-16T07:44:56.353Z
Learnt from: CR
Repo: livekit/agents PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T07:44:56.353Z
Learning: Run all checks (format-check, lint, type-check) using 'make check' before committing

Applied to files:

  • livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py
📚 Learning: 2026-01-16T07:44:56.353Z
Learnt from: CR
Repo: livekit/agents PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-16T07:44:56.353Z
Learning: Applies to **/*.py : Run ruff linter and auto-fix issues

Applied to files:

  • livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py
🧬 Code graph analysis (2)
examples/other/elevenlab_scribe_v2.py (7)
examples/bank-ivr/ivr_navigator_agent.py (1)
  • prewarm (79-80)
examples/bank-ivr/ivr_system_agent.py (1)
  • prewarm (625-626)
examples/other/kokoro_tts.py (1)
  • prewarm (30-31)
examples/voice_agents/basic_agent.py (1)
  • prewarm (71-72)
examples/other/transcription/multi-user-transcriber.py (1)
  • prewarm (139-140)
examples/voice_agents/nvidia_test.py (1)
  • prewarm (21-22)
examples/voice_agents/zapier_mcp_integration.py (1)
  • prewarm (48-49)
livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py (2)
livekit-agents/livekit/agents/utils/misc.py (1)
  • is_given (25-26)
livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/tts.py (1)
  • model (193-194)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: unit-tests
  • GitHub Check: type-check (3.9)
  • GitHub Check: type-check (3.13)
🔇 Additional comments (9)
examples/other/elevenlab_scribe_v2.py (2)

17-17: LGTM on type annotations!

The added return type annotations (-> None) improve code clarity and align with stricter typing patterns in the codebase (e.g., examples/bank-ivr/ivr_navigator_agent.py).

Also applies to: 25-25


38-38: Explicit type annotation is acceptable but optional.

The type annotation on session is valid. However, since AgentSession(...) already returns AgentSession, the annotation is redundant. This is a style preference—keeping it is fine for explicitness.

livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py (7)

23-23: LGTM on import addition.

Literal import is correctly added to support the ElevenLabsSTTModels type alias.


60-66: LGTM on type definitions.

The ElevenLabsSTTModels type alias with documentation link and the model_id field in STTOptions provide good type safety while allowing custom model strings for flexibility. This aligns with the Model Interface Pattern.


84-89: LGTM on parameter additions.

The deprecation annotation for use_realtime and addition of model_id with NOT_GIVEN default follows the pattern established in past review discussions. Documentation in the docstring properly explains the deprecation.


155-157: LGTM on model property.

Returning self._opts.model_id aligns with the TTS pattern (livekit-plugins-elevenlabs/tts.py returns self._opts.model) and addresses the past review comment.


182-182: LGTM on API integration.

The model_id is correctly passed to the ElevenLabs API form data, replacing previously hardcoded values.


432-439: LGTM on WebSocket integration.

The model_id is correctly added to the WebSocket URL query parameters for the realtime streaming endpoint.


142-151: LGTM on options initialization.

The model_id is correctly passed to STTOptions, ensuring the resolved model is propagated through the options dataclass.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +109 to +125
if is_given(use_realtime):
if is_given(model_id):
logger.warning(
"both `use_realtime` and `model_id` parameters are provided. `use_realtime` will be ignored."
)
else:
logger.warning(
"`use_realtime` parameter is deprecated. "
"Specify a realtime model_id to enable streaming. "
"Defaulting model_id to one based on use_realtime parameter. "
)
model_id = "scribe_v2_realtime" if use_realtime else "scribe_v1"
model_id = model_id if is_given(model_id) else "scribe_v1"
use_realtime = model_id == "scribe_v2_realtime"

if not use_realtime and is_given(server_vad):
logger.warning("Server-side VAD is only supported for Scribe v2 realtime model")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the actual file
fd -t f "stt.py" | grep elevenlabs

Repository: livekit/agents

Length of output: 134


🏁 Script executed:

# Check the exact code at lines 109-125
if [ -f "livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py" ]; then
  sed -n '100,130p' "livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py"
fi

Repository: livekit/agents

Length of output: 1797


🌐 Web query:

ElevenLabs Scribe realtime models API 2025 streaming STT

💡 Result:

Short summary — ElevenLabs Scribe (2025/2026 Realtime):

  • Model: Scribe v2 Realtime — low‑latency live STT (≈150 ms median latency), 90+ languages, word-level timestamps, speaker diarization, entity detection and advanced vocab handling. [1][2][5]
  • Streaming API: WebSocket-based streaming (server- and client-side guides). Returns partial (interim) transcripts and committed segments; supports PCM 8–48 kHz and μ‑law. [3][4]
  • Client vs Server: Client connections use a single-use temporary token (for mic/browser use); server implementations use a standard API key and can stream from URLs or files. [4][3]
  • Developer controls: Voice Activity Detection (VAD), manual commit control, ability to send prior text context, and support for multi-language / auto language detection. [3][1]
  • Where to start / docs: ElevenLabs realtime STT streaming docs and cookbooks (includes quickstarts and WebSocket examples). [3][4]

If you want, I can: (A) paste the WebSocket quickstart example for client or server, or (B) walk through obtaining temporary tokens and a minimal streaming demo in Node/Python. Which do you prefer?


🏁 Script executed:

# Check file structure and look for model constants/definitions
head -50 "livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py"

Repository: livekit/agents

Length of output: 1506


🏁 Script executed:

# Check for any other realtime model references
rg "realtime|scribe" "livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/" -i

Repository: livekit/agents

Length of output: 4141


🏁 Script executed:

# Check line lengths in the problematic section
sed -n '109,125p' "livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py" | while IFS= read -r line; do
  len=${`#line`}
  if [ $len -gt 100 ]; then
    echo "Line exceeds 100 chars (length: $len): $line"
  fi
done

Repository: livekit/agents

Length of output: 209


🏁 Script executed:

# Look at the full method/context including docstrings
sed -n '70,130p' "livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py"

Repository: livekit/agents

Length of output: 3107


Fix line length violation on line 113.

Line 113 exceeds the 100-character maximum:

"both `use_realtime` and `model_id` parameters are provided. `use_realtime` will be ignored."

Split this warning message across multiple lines to comply with the Python style requirement.

The deprecation logic is sound. The exact match on line 122 (model_id == "scribe_v2_realtime") is appropriate—ElevenLabs currently provides only one realtime model, and the Literal type restricts the model_id to known values ("scribe_v1", "scribe_v2", "scribe_v2_realtime").

🤖 Prompt for AI Agents
In `@livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py`
around lines 109 - 125, The logger.warning call in the branch that handles both
use_realtime and model_id ("both `use_realtime` and `model_id` parameters are
provided. `use_realtime` will be ignored.") exceeds the 100-character limit;
update the warning in the if is_given(use_realtime) and is_given(model_id)
branch by splitting the long string across multiple shorter literal parts (e.g.,
implicit string concatenation with parentheses or separate +-joined strings) in
the logger.warning call so it stays under 100 chars per line, keeping the same
message and leaving the surrounding logic (use_realtime, model_id, and
subsequent model_id defaulting) unchanged.

@davidzhao davidzhao merged commit 853bc41 into livekit:main Jan 17, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants