SIGN IN SIGN UP

Add missing realtime speech-to-text options (#830)

* Add missing realtime speech-to-text options

Ports the TypeScript SDK fix (elevenlabs-js#436) to Python. The
hand-written realtime wrapper only exposed a subset of the parameters the
websocket endpoint accepts. asyncapi.yml is the contract used throughout.

New options, accepted by both connection modes:
- secondary_languages, sent as repeated secondary_languages params
- include_language_detection
- entity_detection, accepting a single category/type or a list
- filter_background_audio
- enable_logging
- token, a single-use token that authenticates the session on its own, so
  the xi-api-key header is omitted when no key is configured

The two option TypedDicts duplicated every shared field, so they now
inherit from a shared base instead of restating it, and the connect
methods build their query kwargs through one helper rather than
unpacking each option twice.

New server messages, previously dropped by the dispatcher:
- final_transcript and final_transcript_with_timestamps
- committed_transcript_entities

Also fixes unaccepted_terms: the enum only carried
"unaccepted_terms_error", which the server never sends, so
RealtimeEvents(message_type) raised and the message was swallowed. The
correct literal is added and both event names are emitted so existing
subscribers keep firing.

Behaviour change: connecting with neither an api_key nor a token now
raises ValueError instead of sending an empty xi-api-key header and
waiting for the server to reject it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Test behaviour rather than parameter presence

The URL tests asserted one substring each, which only proved a value
passed in came back out. Replaced with assertions on the exhaustive
parameter set, so a renamed, dropped or duplicated parameter fails, plus
cases for the behaviour that can actually regress: explicitly false
booleans surviving serialization, list parameters repeating rather than
joining, and both keyterm limits being inclusive at the boundary.

Also drops the "new params" framing, which described the diff rather than
the endpoint.

Verified by mutation: comma-joining a list, switching enable_logging to a
truthiness check, appending audio_format twice, and always sending the
api key header each fail at least two tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Address review: token precedence, and stop editing generated exports

Two fixes from bugbot review.

The api key was still sent whenever one was configured, even when the
caller passed a token. The server tries the single-use token first and
closes the session if it fails rather than falling back, so the key could
never authenticate that connection - it was just a long-lived credential
on the wire for no reason. A token now suppresses the header outright,
which is also what the option's docstring already claimed.

RealtimeEntityDetection was hand-added to src/elevenlabs/__init__.py,
which Fern generates. The realtime root exports come from
additional_init_exports in the API definition's generators.yml, so the
next regeneration would have dropped this one while leaving the others,
breaking `from elevenlabs import RealtimeEntityDetection`. Reverted that
file to generated state; the type is exported from elevenlabs.realtime.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Leave parameter constraints to the API

Per review: replicating server-side invariants client-side gives no type
safety, and the server reports these anyway. The real cost is drift - if
the constraint is relaxed server-side, callers cannot adopt the change
until the SDK is updated and shipped, which is a failure mode nobody is
watching for.

Drops the filter_background_audio/include_timestamps conflict check and
the keyterms count and length checks, along with their tests. The
constraints stay in the docstrings, mirroring the API reference, since a
stale doc misleads far less than a hard failure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Handle the invalid_request error message

Now declared in asyncapi.yml (elevenlabs/xi#44830), so the SDK can pick it
up from the spec.

The server sends this when it rejects the connection parameters: it
accepts the websocket, sends the error, then closes. The dispatcher
resolves events through RealtimeEvents(message_type) and ignores unknown
types, so the message was dropped and a bad parameter surfaced as a
connection close with no error event at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Drop tests that only exercised language primitives

Two of the tests added on this branch asserted nothing about the SDK.
test_committed_transcript_entities_carries_entities checked that a payload
the SDK never transforms survived an emitter, and the list-parameter test
duplicated the exhaustive serialization assertion, which already fails on
a joined list.

Strengthens the connect() option test instead: it is the only cover for
the _shared_url_kwargs mapping, where a typo drops an option silently, so
it now asserts the full parameter set rather than a few substrings.

Verified by mutation: comma-joining a list, enable_logging truthiness, a
mapping typo, sending the api key alongside a token, and removing either
the INVALID_REQUEST or FINAL_TRANSCRIPT enum member each still fail.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Bump version to 2.63.0

Minor: adds realtime speech-to-text options and message types. Matches the
JS SDK, which the two are released in lockstep with.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
P
Paul Asjes committed
e1cf8db99cfb5dc06861fc6b0a93453db06f4068
Parent: 80f2f2e
Committed by GitHub <noreply@github.com> on 8/11/2026, 9:52:20 AM