Skip to content

Commit f785d33

Browse files
kraenhansenclaude
andauthored
Add keyterms and no_verbatim to Scribe realtime API (#778)
Bias the Scribe model towards specific terms via repeated `keyterms` query params and remove filler words/disfluencies with `no_verbatim`. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 837172c commit f785d33

2 files changed

Lines changed: 65 additions & 1 deletion

File tree

src/elevenlabs/realtime/scribe.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ class RealtimeAudioOptions(typing.TypedDict, total=False):
6767
min_silence_duration_ms: int
6868
language_code: str
6969
include_timestamps: bool
70+
keyterms: typing.List[str]
71+
no_verbatim: bool
7072

7173

7274
class RealtimeUrlOptions(typing.TypedDict, total=False):
@@ -93,6 +95,8 @@ class RealtimeUrlOptions(typing.TypedDict, total=False):
9395
min_silence_duration_ms: int
9496
language_code: str
9597
include_timestamps: bool
98+
keyterms: typing.List[str]
99+
no_verbatim: bool
96100

97101

98102
class ScribeRealtime:
@@ -197,6 +201,8 @@ async def _connect_audio(self, options: RealtimeAudioOptions) -> RealtimeConnect
197201
min_silence_duration_ms = options.get("min_silence_duration_ms")
198202
language_code = options.get("language_code")
199203
include_timestamps = options.get("include_timestamps", False)
204+
keyterms = options.get("keyterms")
205+
no_verbatim = options.get("no_verbatim")
200206

201207
if not audio_format or not sample_rate:
202208
raise ValueError("audio_format and sample_rate are required for manual audio mode")
@@ -212,6 +218,8 @@ async def _connect_audio(self, options: RealtimeAudioOptions) -> RealtimeConnect
212218
min_silence_duration_ms=min_silence_duration_ms,
213219
language_code=language_code,
214220
include_timestamps=include_timestamps,
221+
keyterms=keyterms,
222+
no_verbatim=no_verbatim,
215223
)
216224

217225
# Connect to WebSocket
@@ -244,6 +252,8 @@ async def _connect_url(self, options: RealtimeUrlOptions) -> RealtimeConnection:
244252
min_silence_duration_ms = options.get("min_silence_duration_ms")
245253
language_code = options.get("language_code")
246254
include_timestamps = options.get("include_timestamps", False)
255+
keyterms = options.get("keyterms")
256+
no_verbatim = options.get("no_verbatim")
247257

248258
if not url:
249259
raise ValueError("url is required for URL mode")
@@ -263,6 +273,8 @@ async def _connect_url(self, options: RealtimeUrlOptions) -> RealtimeConnection:
263273
min_silence_duration_ms=min_silence_duration_ms,
264274
language_code=language_code,
265275
include_timestamps=include_timestamps,
276+
keyterms=keyterms,
277+
no_verbatim=no_verbatim,
266278
)
267279

268280
# Connect to WebSocket
@@ -365,7 +377,9 @@ def _build_websocket_url(
365377
min_speech_duration_ms: typing.Optional[int] = None,
366378
min_silence_duration_ms: typing.Optional[int] = None,
367379
language_code: typing.Optional[str] = None,
368-
include_timestamps: typing.Optional[bool] = None
380+
include_timestamps: typing.Optional[bool] = None,
381+
keyterms: typing.Optional[typing.List[str]] = None,
382+
no_verbatim: typing.Optional[bool] = None,
369383
) -> str:
370384
"""Build the WebSocket URL with query parameters"""
371385
params = [
@@ -384,6 +398,11 @@ def _build_websocket_url(
384398
params.append((key, str(value)))
385399
if include_timestamps is not None:
386400
params.append(("include_timestamps", str(include_timestamps).lower()))
401+
if keyterms is not None:
402+
for term in keyterms:
403+
params.append(("keyterms", term))
404+
if no_verbatim is not None:
405+
params.append(("no_verbatim", str(no_verbatim).lower()))
387406

388407
return build_ws_url(self.base_url, ["v1", "speech-to-text", "realtime"], params)
389408

tests/test_stt_realtime.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,51 @@ def test_url_converts_http_to_ws(self):
9595

9696
assert url.startswith("ws://localhost:8080")
9797

98+
def test_includes_keyterms_as_repeated_query_params(self):
99+
"""Test that keyterms are included as repeated query params"""
100+
url = self.scribe._build_websocket_url(
101+
model_id="scribe_v2_realtime",
102+
audio_format="pcm_16000",
103+
commit_strategy="manual",
104+
keyterms=["ElevenLabs", "Scribe"],
105+
)
106+
107+
assert "keyterms=ElevenLabs" in url
108+
assert "keyterms=Scribe" in url
109+
110+
def test_includes_no_verbatim_true(self):
111+
"""Test that no_verbatim=true is included when set to True"""
112+
url = self.scribe._build_websocket_url(
113+
model_id="scribe_v2_realtime",
114+
audio_format="pcm_16000",
115+
commit_strategy="manual",
116+
no_verbatim=True,
117+
)
118+
119+
assert "no_verbatim=true" in url
120+
121+
def test_includes_no_verbatim_false(self):
122+
"""Test that no_verbatim=false is included when set to False"""
123+
url = self.scribe._build_websocket_url(
124+
model_id="scribe_v2_realtime",
125+
audio_format="pcm_16000",
126+
commit_strategy="manual",
127+
no_verbatim=False,
128+
)
129+
130+
assert "no_verbatim=false" in url
131+
132+
def test_omits_keyterms_and_no_verbatim_when_not_specified(self):
133+
"""Test that keyterms and no_verbatim are omitted when not specified"""
134+
url = self.scribe._build_websocket_url(
135+
model_id="scribe_v2_realtime",
136+
audio_format="pcm_16000",
137+
commit_strategy="manual",
138+
)
139+
140+
assert "keyterms" not in url
141+
assert "no_verbatim" not in url
142+
98143

99144
class TestConnectValidation:
100145
"""Tests for connect method validation"""

0 commit comments

Comments
 (0)