sdk_expose decorator — and CI regenerates them and
fails on a non-empty diff, which is what keeps that true.
That also means the SDKs stop where the schema does. Read
What the SDKs do not cover before you go looking
for a realtime method.
Install
Neither package is on PyPI or npm yet. Install from a checkout of the
repository until they are.
dist/, so it has to be built before it is
installed.
The client
base_url is the origin without /v1 — the client appends it. Both arguments
fall back to EESI_API_URL and EESI_API_KEY, and the URL defaults to
https://api.eesi.ai. An unauthenticated client is legal and 401s on almost
everything.
Making a call
A request body is either its typed model or a plain mapping of the same fields. A mapping is validated against that model before anything is sent, so a misspelled field raisesInvalidRequestError naming it rather than failing on
the wire.
eesi_sdk.models in Python — that module is the
public home for them, so regenerating never moves your import.
In TypeScript, declare a body away from its call site as the method’s own
…Body type (CreateSpeechBody), not as the response model. The response
model types the fields the server defaults — response_format, speed — as
always present, which is true coming back and wrong going out.
What the SDKs cover
Uploads and audio responses are handled for you:
create_speech returns the
audio bytes, and create_voice and create_transcription take a file. In
Python a file is raw bytes, a binary stream, or a (filename, content) /
(filename, content, content_type) tuple — pass the tuple form when the server
keys off the extension or the media type, which it does for voice references.
In TypeScript it is a Blob.
What the SDKs do not cover
This is the part that saves a support ticket.Realtime, Story, Sessions and Memory have no method
Realtime, Story, Sessions and Memory have no method
/v1/realtime is a WebSocket, so it is not an OpenAPI operation and cannot
be generated. /v1/story/*, /v1/speech/sessions/* and
/v1/user/memory/* are ordinary REST routes that carry no sdk_expose
decorator today. Call all of them with raw HTTP or a socket — do not go
looking for a method name that does not exist.Streaming paths are batch-only here
Streaming paths are batch-only here
SSE transcription (
stream=true) and streamed chat completions come back
as a stream the generated client does not decode. Use an OpenAI client
against the base URL, or read the response yourself. See
OpenAI compatibility.@eesi/realtime is internal, not a package to install
@eesi/realtime is internal, not a package to install
packages/realtime holds the shared TypeScript decoder for the realtime
event stream — alias folding, the cumulative-user-delta rule, assistant
segment merging — and every EESI client uses it. It is marked
"private": true and is consumed source-direct inside this repository, so
it is not something to add to your package.json. The rules it implements
are documented in Realtime events; the conformance
corpus that pins them is spec/realtime/v1/.Two key actions are session-only by design
Two key actions are session-only by design
Creating a key and un-archiving one refuse an API key, so neither client
has a method for them. See Authentication.
Timeouts and retries
Identical in both clients — change one and you change both.
A
POST, PUT, PATCH or DELETE that failed on the wire may still have
reached the server, and a read timeout means the server took the request — so a
retry there is a second voice cloned or a second charge, not a second attempt.
Retry those yourself, when you know the operation is safe to repeat.
Synthesis of a long input can outrun the 60-second read timeout. Raise it for
that call, or stream the response yourself — see
Stream speech to a file.
Errors
Everything raised subclassesEESISdkError, so you can catch the categories
separately or together.
ApiError reads both envelopes the backend uses — FastAPI’s {"detail": …}
from the control-plane routes and OpenAI’s {"error": {…}} from the speech
routes — so message is always the server’s own sentence. code and type
are set only when the envelope carries them.
The field is
status_code in Python and statusCode in TypeScript. Both
also carry body — the parsed JSON, or the raw text of a non-JSON response.code you will meet is in Errors.
Regenerating
After changing a route or ansdk_expose decorator:
Next
Quickstart
A key, a synthesized line, and a live conversation.
OpenAI compatibility
Point an existing OpenAI client here instead.
Errors
The codes behind every
ApiError.Cookbook
Working recipes, end to end.