Skip to content

feat(sync,dictation): match the Python SDK's live-only sync path and raised caps - #181

Merged
aurpsis-aai merged 5 commits into
mainfrom
aurpsis/sdk-parity-dictation-sync
Sep 11, 2026
Merged

aurpsis-aai merged 5 commits into
mainfrom
aurpsis/sdk-parity-dictation-sync

Conversation

@aurpsis-aai

Copy link
Copy Markdown
Contributor

Summary

Brings client.sync and client.dictation level 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's client.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, signal on 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 a FormData and 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 upload transcribeLive() / openLive() use. The config part is always present and precedes audio ({} when empty). Nothing in the client requests /v1/transcribe any 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. SyncTranscribeOptions is now { timeout, signal }. timeout defaults 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: prompt 4096 → 6000 chars, keyterms_prompt 2048 → 8000 chars total plus a 100-term cap the SDK did not enforce, conversation_context 100 → 500 turns / 4096 → 16 000 chars (still trimmed oldest-first). Dictation: stt_prompt 4096 → 6000, keyterms_prompt 2048 → 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. errorFromResponse reads a bare {"error": "...", "error_code": "..."} body (precedence detail > 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.0 entry.

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, signal aborts.
  • tests/unit/sync-live.test.ts: liveUrl → /v1/transcribe/live; "never requests the buffered endpoint" across transcribe() / transcribeLive() / openLive(); backslash + TAB escaping.
  • tests/unit/dictation.test.ts: cap tests restated at the new numbers; over-the-count and at-the-count keyterms_prompt; {error, error_code} envelope.
  • pnpm lint:eslint, lint:tsc, lint:format pass. Unit suite: 333/336 pass (the 3 utils › user-agent failures are pre-existing on Node 22).

Notes for reviewers

  • Stacked on ccampbell/node-dictation (feat(dictation): add client.dictation for the Dictation API #179). GitHub will retarget to main when 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 sharing src/utils/live.ts.
  • Product point to confirm: transcribe() now hands fetch a ReadableStream request body with duplex: "half", like the live methods and like client.dictation already do for whole audio. Streaming request bodies are unsupported in Firefox and Safari and need HTTP/2 in Chromium, so client.sync.transcribe(blob) in a browser no longer works where the old FormData post did. Python made the same live-only call without a browser to worry about. If browsers are in scope for client.sync, the options are a FormData fallback when streaming bodies are unavailable, or a CHANGELOG note that client.sync is Node/Bun/Deno/Chromium-over-HTTP/2. I have not added either; this needs an owner's call.
  • Not mirrored on purpose: Python raises on a late session.write() after close; Node drops the chunk (rationale in feat(dictation): add client.dictation for the Dictation API #179). Python's DictationConfig rejects unknown fields at runtime; TypeScript's excess-property checks cover that here.

🤖 Generated with Claude Code

aurpsis-aai and others added 5 commits September 11, 2026 13:26
… 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>
Base automatically changed from ccampbell/node-dictation to main September 11, 2026 19:29
@aurpsis-aai
aurpsis-aai marked this pull request as ready for review September 11, 2026 19:47
@aurpsis-aai
aurpsis-aai merged commit abb620e into main Sep 11, 2026
1 check passed
@aurpsis-aai
aurpsis-aai deleted the aurpsis/sdk-parity-dictation-sync branch September 11, 2026 20:04
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