Skip to content

Commit 6efae06

Browse files
kraenhansenclaude
andcommitted
Add keyterms and no_verbatim to Scribe realtime API
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 1f19ff1 commit 6efae06

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
@@ -66,6 +66,8 @@ class RealtimeAudioOptions(typing.TypedDict, total=False):
6666
min_silence_duration_ms: int
6767
language_code: str
6868
include_timestamps: bool
69+
keyterms: typing.List[str]
70+
no_verbatim: bool
6971

7072

7173
class RealtimeUrlOptions(typing.TypedDict, total=False):
@@ -92,6 +94,8 @@ class RealtimeUrlOptions(typing.TypedDict, total=False):
9294
min_silence_duration_ms: int
9395
language_code: str
9496
include_timestamps: bool
97+
keyterms: typing.List[str]
98+
no_verbatim: bool
9599

96100

97101
class ScribeRealtime:
@@ -196,6 +200,8 @@ async def _connect_audio(self, options: RealtimeAudioOptions) -> RealtimeConnect
196200
min_silence_duration_ms = options.get("min_silence_duration_ms")
197201
language_code = options.get("language_code")
198202
include_timestamps = options.get("include_timestamps", False)
203+
keyterms = options.get("keyterms")
204+
no_verbatim = options.get("no_verbatim")
199205

200206
if not audio_format or not sample_rate:
201207
raise ValueError("audio_format and sample_rate are required for manual audio mode")
@@ -211,6 +217,8 @@ async def _connect_audio(self, options: RealtimeAudioOptions) -> RealtimeConnect
211217
min_silence_duration_ms=min_silence_duration_ms,
212218
language_code=language_code,
213219
include_timestamps=include_timestamps,
220+
keyterms=keyterms,
221+
no_verbatim=no_verbatim,
214222
)
215223

216224
# Connect to WebSocket
@@ -243,6 +251,8 @@ async def _connect_url(self, options: RealtimeUrlOptions) -> RealtimeConnection:
243251
min_silence_duration_ms = options.get("min_silence_duration_ms")
244252
language_code = options.get("language_code")
245253
include_timestamps = options.get("include_timestamps", False)
254+
keyterms = options.get("keyterms")
255+
no_verbatim = options.get("no_verbatim")
246256

247257
if not url:
248258
raise ValueError("url is required for URL mode")
@@ -262,6 +272,8 @@ async def _connect_url(self, options: RealtimeUrlOptions) -> RealtimeConnection:
262272
min_silence_duration_ms=min_silence_duration_ms,
263273
language_code=language_code,
264274
include_timestamps=include_timestamps,
275+
keyterms=keyterms,
276+
no_verbatim=no_verbatim,
265277
)
266278

267279
# Connect to WebSocket
@@ -364,7 +376,9 @@ def _build_websocket_url(
364376
min_speech_duration_ms: typing.Optional[int] = None,
365377
min_silence_duration_ms: typing.Optional[int] = None,
366378
language_code: typing.Optional[str] = None,
367-
include_timestamps: typing.Optional[bool] = None
379+
include_timestamps: typing.Optional[bool] = None,
380+
keyterms: typing.Optional[typing.List[str]] = None,
381+
no_verbatim: typing.Optional[bool] = None,
368382
) -> str:
369383
"""Build the WebSocket URL with query parameters"""
370384
# Extract base domain
@@ -390,6 +404,11 @@ def _build_websocket_url(
390404
params.append(f"language_code={language_code}")
391405
if include_timestamps is not None:
392406
params.append(f"include_timestamps={str(include_timestamps).lower()}")
407+
if keyterms is not None:
408+
for term in keyterms:
409+
params.append(f"keyterms={term}")
410+
if no_verbatim is not None:
411+
params.append(f"no_verbatim={str(no_verbatim).lower()}")
393412

394413
query_string = "&".join(params)
395414
return f"{base}/v1/speech-to-text/realtime?{query_string}"

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)