CHANGELOG.md.
API — streaming finals: faster, and without the jitter
- Finals arrive faster, at every utterance length, with no outliers. Decode input shapes are now drawn from a small pre-warmed set and an endpoint decode takes priority over pending partial work on the GPU, so no live request ever pays first-use kernel planning. Measured from Almaty against production at a 200 ms end-of-utterance threshold: finals went from a 539 ms median with a 1,034 ms tail to ~420 ms with a 511 ms worst case across 32 consecutive finals. Transcription accuracy re-measured through the full streaming path: unchanged (0.5 pp better on FLEURS Kazakh).
- Repeat connections shake hands faster. Successful key authorization is reused for five minutes and no longer blocks other sockets on the same container; a warm reconnect measures ~0.6–0.7 s.
- Cold starts restore from a GPU memory snapshot instead of booting from scratch: the first connection after an idle period now waits ~15 s (from ~20–25 s), and every boot — fresh or restored — re-warms its decode shapes before serving, so a just-started container has no first-request penalty.
API — live streaming transcription
- New WebSocket endpoint
wss://geko--seta-stream-streamer-api.modal.run/v1/streamtranscribes speech as it is spoken, emittingpartialresults while someone is still talking andfinalones that are never revised. See streaming. - Runs as a separate service from file transcription, so the two scale independently and a streaming incident cannot take
/v1/transcribedown with it. Same API key, same credit balance. - Audio is PCM16-LE, mono, 16 kHz. Nothing is resampled server-side.
- **Priced at 6 credits/audio-second (0.36/audio-hour) for files. Keeping a partial transcript current re-decodes uncommitted audio on every update, so a second of speech passes through the model roughly six times. If you have a file,
POST /v1/transcribeis more accurate and a third of the price. - Billed on audio accepted, once, when the socket closes — including on an abrupt disconnect.
SDK 0.6.0
geko.stt.stream()opens a session:send()audio, iterate it for events,stop()when the audio ends. NewSttStreamexport andSttStream*event types.geko.stt.estimateStreamCredits(seconds)prices a stream before you spend it.- Still zero-dependency: uses the platform
WebSocket. Node 20 has none (stable in Node 22) — upgrade, or pass{ webSocket }. openTimeoutdefaults to 60s, because the service scales to zero and a first connection after idle waits ~15–25s for a cold start.
API — word-level timestamps
POST /v1/transcribe?timestamps=truenow also returnswords({ word, start, end }) andsegments({ id, text, start, end }), in seconds from the start of the audio. See word-level timestamps.- Free. The alignment is a by-product of the same CTC decode pass; billing is unchanged at
ceil(audio_seconds × 2.5). - Opt-in. Both fields are absent unless requested, so no existing caller’s response shape changes.
- Long audio is split at pauses before decoding, and each piece’s times are shifted back into the timeline of the whole file — so
start/endare always measured from the beginning of your audio. - OpenAI-compatible endpoint:
response_format=srtandvttnow return real subtitles instead of400, andverbose_jsonreturns populatedsegmentsandwords.
SDK 0.5.0
transcribe({ timestamps: true })surfaceswordsandsegments. New exported typesWordandTranscriptSegment.toSubtitles(transcription, "srt" | "vtt")— pure formatter, exported standalone and asgeko.stt.toSubtitles(). Usessegments, because one cue per word is unwatchable.- CLI:
transcribe --wordsprintsstart<TAB>end<TAB>word;--srt/--vttprint subtitles.
SDK 0.4.0
- Speech-to-text:
geko.stt.transcribe({ audio })or{ url }→ the transcript plus timing and the credits actually charged. Backed byseta-kk-ru-v2, which handles Kazakh/Russian code-switching at 8.71% WER on the KSC2 official test set, where Whisper large-v3 scores 44.95% on the same audio. See accuracy. - There is deliberately no
languageparameter — both alphabets share one 70-character output vocabulary, so the model is never told which language to expect. Transcripts are lowercase and unpunctuated because that vocabulary contains neither. geko.stt.models()lists what the endpoint serves.geko.stt.estimateCredits(seconds)prices a file with no request —ceil(seconds × 2.5), i.e. $0.36/audio-hour.- One API key, one balance, both services. TTS and STT are separately scaled GPU services on two hosts; the client hides that. New
sttBaseUrloption (plusGEKO_STT_BASE_URLand--stt-base-url) if you need to point it elsewhere. - CLI:
gekoai transcribe <file>andgekoai transcribe --url <url>. The transcript goes to stdout and the cost line to stderr, so> transcript.txtcaptures only text. - Audio uploads as an octet-stream rather than multipart, which keeps the body replayable — so transcriptions keep the existing retry-on-5xx behavior.
SDK 0.3.0
- Streaming:
geko.tts.stream()— an async iterable yielding one WAV chunk per sentence for lower time-to-first-audio on long text. Consumes the framedwav-frames-v1protocol. - No retries on streams (a consumed stream can’t be replayed);
AbortSignaland per-calltimeoutstill apply.
SDK 0.2.0
- CLI:
npx @gekoai/sdk say | voices | models. See CLI. - Clarified browser usage (proxy through your server; keep your key secret).
SDK 0.1.0
- Initial release:
Gekoclient withtts.create(),tts.voices(),models(),health(). Zero runtime dependencies, dual ESM + CommonJS, typed errors, timeouts, and automatic retries.
API
- Added speech-to-text:
POST /v1/transcribeand the OpenAI-compatiblePOST /v1/audio/transcriptions, on a separately scaled host. Same bearer key and same credit balance as synthesis. - Added
POST /v1/tts/stream(sentence-level streaming) and the OpenAI-compatiblePOST /v1/audio/speech(wav/pcm). - Enabled CORS for browser callers.
- The core
POST /v1/ttscontract is unchanged: WAV, 24 kHz, PCM16, mono.