Skip to content

Speech Engine SDK#771

Merged
PaulAsjes merged 19 commits into
mainfrom
pma/voice-engine
May 12, 2026
Merged

Speech Engine SDK#771
PaulAsjes merged 19 commits into
mainfrom
pma/voice-engine

Conversation

@PaulAsjes
Copy link
Copy Markdown
Collaborator

@PaulAsjes PaulAsjes commented Apr 14, 2026

Note

Medium Risk
Adds a new async WebSocket server/session layer plus JWT verification for inbound Speech Engine connections and bumps the websockets dependency, so runtime behavior and compatibility may change even though changes are largely additive.

Overview
Introduces a new speech_engine module enabling server-side voice agents: a SpeechEngineServer (standalone WebSocket server) and SpeechEngineSession (event-emitter style session that streams agent_response messages, supports interruption/cancellation, and auto-parses common LLM streaming formats).

Extends the generated Speech Engine clients with SpeechEngineResource wrappers returned from create/get/update, adds speech_engine accessors to ElevenLabs/AsyncElevenLabs, and documents usage in README.md.

Adds HS256 JWT request verification for inbound connections (used by SpeechEngineServer and exposed via SpeechEngineResource.verify_request), comprehensive new tests for auth/session/server behavior, updates .fernignore to protect the new module, and bumps websockets to >=13.0.

Reviewed by Cursor Bugbot for commit dc02e44. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread src/elevenlabs/client.py Outdated
Comment thread src/elevenlabs/speech_engine/server.py Outdated
Comment thread src/elevenlabs/speech_engine/session.py Outdated
Comment thread src/elevenlabs/speech_engine/session.py
Comment thread src/elevenlabs/speech_engine/session.py Outdated
Comment thread src/elevenlabs/speech_engine/server.py Outdated
Comment thread src/elevenlabs/speech_engine/session.py
Comment thread src/elevenlabs/speech_engine/session.py
@PaulAsjes PaulAsjes changed the title [WIP] Speech Engine SDK Speech Engine SDK Apr 27, 2026
@PaulAsjes PaulAsjes requested a review from kraenhansen April 27, 2026 15:56
Comment thread src/elevenlabs/speech_engine/resource.py Outdated
Comment thread src/elevenlabs/speech_engine/server.py
Comment thread src/elevenlabs/speech_engine/session.py
Comment thread src/elevenlabs/speech_engine/session.py Outdated
Comment thread src/elevenlabs/speech_engine/server.py
Comment thread src/elevenlabs/speech_engine/session.py
Copy link
Copy Markdown
Member

@kraenhansen kraenhansen left a comment

Choose a reason for hiding this comment

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

I have a few more comments 🙈 Good to merge as-is 👍

Comment thread src/elevenlabs/speech_engine/resource.py
Comment thread src/elevenlabs/speech_engine/session.py Outdated
Comment thread src/elevenlabs/speech_engine/session.py
Comment thread src/elevenlabs/speech_engine/session.py Outdated
Comment thread src/elevenlabs/speech_engine/session.py
Comment thread src/elevenlabs/speech_engine/session.py
Comment thread src/elevenlabs/speech_engine/session.py
Comment thread src/elevenlabs/speech_engine/session.py
Comment thread src/elevenlabs/speech_engine/session.py
Comment thread src/elevenlabs/speech_engine/session.py Outdated
PaulAsjes and others added 14 commits May 12, 2026 16:05
Rename client_options parameter to client_wrapper for consistency with
Fern SDK conventions. Use get_headers() instead of accessing private
_api_key attribute. Update tests to match.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use a dedicated _in_transcript_handler flag instead of checking
_current_event_id is None, which conflated "no handler running"
with "handler running but transcript had no event_id".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the log call after the try/except and use `or []` to handle
null transcript_data, preventing len(None) TypeError.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The server uses websockets 13.x API (websocket.request.path,
websocket.request.headers) which doesn't exist in 11.x-12.x.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Without this, the run() loop continues calling recv() after a
close message, processing stale messages and reporting is_open
as True.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use websockets process_request callback to verify JWT before the
WebSocket handshake completes. Unauthenticated requests now get a
plain HTTP 401 instead of completing the upgrade first.

Also include exception in logger.exception error handler message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
json.loads can return lists, strings, numbers, or null. These
would crash _handle_message which expects a dict. Now emits an
error event and continues instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The string response path called _send_agent_response twice without
an explicit event_id, reading self._current_event_id at each call.
An interruption between the two awaits could stamp the terminator
with the wrong event_id. Now captures event_id once and passes it
through, matching the stream path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the stub _AsyncSpeechEngineAccessor with custom
SpeechEngineClient/AsyncSpeechEngineClient classes that extend the
Fern-generated clients and add resource() for WebSocket server setup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread src/elevenlabs/speech_engine_custom.py Outdated
Comment thread src/elevenlabs/speech_engine_custom.py
Comment thread src/elevenlabs/speech_engine/__init__.py Outdated
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ebb5053. Configure here.

Comment thread src/elevenlabs/speech_engine/session.py
@PaulAsjes PaulAsjes merged commit 450f18d into main May 12, 2026
4 of 6 checks passed
@PaulAsjes PaulAsjes deleted the pma/voice-engine branch May 12, 2026 19:15
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.

2 participants