SIGN IN SIGN UP

Speech Engine SDK (#771)

* rename to Speech Engine

* veng -> seng

* initial commit

* Bug fixes and API surface updates

* bug fixes and QoL improvements

* Address comments

* Fix client_wrapper naming and use public API for key access

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>

* Fix send_response silently failing when event_id is absent

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>

* Fix crash when user_transcript is null in wire message

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>

* Bump websockets minimum to >=13.0

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>

* Set _closed on protocol close message to stop the receive loop

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>

* Move auth to process_request for pre-handshake rejection

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>

* Guard against non-dict JSON messages in the receive loop

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>

* Capture event_id upfront in send_response for string path

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>

* Wire speech engine to Fern-generated CRUD client

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>

* override get

* fixes

* Return resource instead of response

* fix bugs

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
P
Paul Asjes committed
450f18dc854bfa6e185872c857a9498abe2cb0b3
Parent: bb1f507
Committed by GitHub <noreply@github.com> on 5/12/2026, 7:15:03 PM