Repository navigation
feat(sync,dictation): match the Python SDK's live-only sync path and raised caps - #181
Merged
Merged
Conversation
… config caps
client.sync opens one kind of connection: the chunked-multipart live upload
to /v1/transcribe/live. transcribe() resolves the audio as before and sends
it as a single chunk over that upload, with the config part first ({} when
empty); nothing posts to the buffered /v1/transcribe. The live endpoint is
named /v1/transcribe/live (the service also serves /v1/transcribe/stream).
SyncTranscribeOptions is { timeout, signal }: a 180 000 ms total deadline
from the start of the request, the same basis as the live methods.
Config caps match the service: prompt 6000 chars, keyterms_prompt 100 terms
and 8000 chars, conversation_context 500 turns and 16000 chars.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
stt_prompt 6000 chars; keyterms_prompt 100 terms and 8000 chars in total, with the term count checked ahead of the character total. llm_instruction stays at 2048. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…lopes
escapeFormParam follows the HTML5 form-encoding rules: a double quote becomes
%22, a backslash is doubled, and control characters other than ESC are
percent-encoded. errorFromResponse takes the message from a bare
{"error": "..."} body when detail and message are absent.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…error envelopes Every sync mock targets /v1/transcribe/live and one test drives transcribe(), transcribeLive() and openLive() to assert the buffered endpoint is never requested. Streamed request bodies are drained once per request and cached so several assertions can read one body. Adds the single-chunk framing on a rejected transcribe(), the raised sync and dictation caps including the 100-term count cap, message precedence detail > message > error, and the quote/TAB/ESC escaping cases. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ccampbell-aai
approved these changes
Sep 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings
client.syncandclient.dictationlevel with the Python SDK's sync + dictation changes merged this week (assemblyai-python-sdk #241, #244, #245, #247, #249, #250 → 1.5.2). Stacked on #179 (Carter'sclient.dictation), which it treats as the baseline. Bumps the package to 4.41.0.Audit method: a full read of both SDKs' sync + dictation surfaces at the #179 head vs Python master. Everything below is a behaviour Python has and Node lacked or contradicted; behaviours Node has that Python does not (late
write()dropped rather than thrown,signalon every live call,session.stream(), total-deadline timeouts) are left as they are.What changes
One connection for sync (Python #245).
client.sync.transcribe()no longer builds aFormDataand posts it to the buffered/v1/transcribe. It resolves the audio as before, then sends it as a single chunk over the same chunked-multipart live uploadtranscribeLive()/openLive()use. Theconfigpart is always present and precedesaudio({}when empty). Nothing in the client requests/v1/transcribeany more; a test drives every entry point and asserts that.Endpoint rename. The live endpoint is
/v1/transcribe/live(the sync service also serves/v1/transcribe/stream, the path it shipped under, so older deployments still answer).transcribe()options.SyncTranscribeOptionsis now{ timeout, signal }.timeoutdefaults to 180 000 ms and is a total deadline from the start of the request, the same basis as the live methods; the 60 s default is gone.warm()is unchanged.Caps raised to match the services (Python #250). Sync:
prompt4096 → 6000 chars,keyterms_prompt2048 → 8000 chars total plus a 100-term cap the SDK did not enforce,conversation_context100 → 500 turns / 4096 → 16 000 chars (still trimmed oldest-first). Dictation:stt_prompt4096 → 6000,keyterms_prompt2048 → 8000 chars + 100 terms. JSDoc on the config types updated to match. This supersedes #180, which raised the sync caps only and did not touch the type docs — #180 can be closed once this merges.Shared live plumbing.
errorFromResponsereads a bare{"error": "...", "error_code": "..."}body (precedencedetail>message>error), so that envelope no longer degrades to "failed with status N". Multipart file-name escaping follows the HTML5 form-encoding rules in full:"→%22, backslash doubled, C0 controls other than ESC percent-encoded.Docs
README and CLAUDE.md sync sections rewritten for one connection and one timeout; dictation cap comments updated; CHANGELOG
4.41.0entry.Testing
tests/unit/sync.test.ts: every mock repointed to/v1/transcribe/live; asserts the config part is present and first on the complete-audio path, file name and content type survive,signalaborts.tests/unit/sync-live.test.ts:liveUrl→/v1/transcribe/live; "never requests the buffered endpoint" acrosstranscribe()/transcribeLive()/openLive(); backslash + TAB escaping.tests/unit/dictation.test.ts: cap tests restated at the new numbers; over-the-count and at-the-countkeyterms_prompt;{error, error_code}envelope.pnpm lint:eslint,lint:tsc,lint:formatpass. Unit suite: 333/336 pass (the 3utils › user-agentfailures are pre-existing on Node 22).Notes for reviewers
ccampbell/node-dictation(feat(dictation): add client.dictation for the Dictation API #179). GitHub will retarget tomainwhen feat(dictation): add client.dictation for the Dictation API #179 merges; nothing here depends on feat(dictation): add client.dictation for the Dictation API #179's unmerged state beyond sharingsrc/utils/live.ts.transcribe()now handsfetchaReadableStreamrequest body withduplex: "half", like the live methods and likeclient.dictationalready do for whole audio. Streaming request bodies are unsupported in Firefox and Safari and need HTTP/2 in Chromium, soclient.sync.transcribe(blob)in a browser no longer works where the oldFormDatapost did. Python made the same live-only call without a browser to worry about. If browsers are in scope forclient.sync, the options are aFormDatafallback when streaming bodies are unavailable, or a CHANGELOG note thatclient.syncis Node/Bun/Deno/Chromium-over-HTTP/2. I have not added either; this needs an owner's call.session.write()after close; Node drops the chunk (rationale in feat(dictation): add client.dictation for the Dictation API #179). Python'sDictationConfigrejects unknown fields at runtime; TypeScript's excess-property checks cover that here.🤖 Generated with Claude Code