Skip to content

feat(sync): add AsyncSyncTranscriber for asyncio callers - #229

Merged
ccampbell-aai merged 3 commits into
masterfrom
ccampbell/sync-async-transcriber
Aug 12, 2026
Merged

feat(sync): add AsyncSyncTranscriber for asyncio callers#229
ccampbell-aai merged 3 commits into
masterfrom
ccampbell/sync-async-transcriber

Conversation

@ccampbell-aai

Copy link
Copy Markdown
Contributor

Summary

Adds aai.AsyncSyncTranscriber, the asyncio counterpart of SyncTranscriber, closing the gap where the sync product was the only wrapper without an asyncio implementation (prerecorded has Transcriber/AsyncTranscriber, streaming has StreamingClient/AsyncStreamingClient).

  • sync/v1/async_api.pyhttpx.AsyncClient twin of api.py's transcribe(); reuses the endpoint constants and _error_from_response, so error mapping (problem-details envelope, retry_after, legacy envelopes) stays shared.
  • sync/v1/async_client.pyAsyncSyncTranscriber with coroutine transcribe() / warm(). Same input types (path/bytes/file object — no URLs), same SyncTranscriptionConfig, SyncTranscriptResponse, and SyncTranscriptError. Reuses the existing _base.py helpers; path and file-object reads run off the event loop.
  • Lifecycle follows AsyncTranscriber: owns an HTTP pool by default (async with / aclose()), or shares a passed-in aai.AsyncClient, which stays the caller's to close. No transcribe_async() — fan-out is plain asyncio.gather.
  • Exports wired through sync/v1 and the top-level package; the sync/__init__.py shim stays backwards-compat-only.
  • 16 unit tests mirroring test_sync.py plus async-lifecycle cases; README and CLAUDE.md updated (both previously documented this class as a planned follow-up).
  • Version bump 0.65.00 → 0.66.00, mirroring the minor bump feat(prerecorded): add AsyncTranscriber for asyncio callers #227 used for AsyncTranscriber.

Test plan

  • pytest tests/unit — full suite green (only pre-existing pyaudio-missing extras failures)
  • ruff check + ruff format --check clean on changed files with CI-pinned 0.3.5

🤖 Generated with Claude Code

@ccampbell-aai

Copy link
Copy Markdown
Contributor Author

Live parity test: threading vs asyncio

Ran a side-by-side comparison of SyncTranscriber (threading) and AsyncSyncTranscriber (asyncio) against a live staging deployment of the sync API — same audio clip and identical configs through both wrappers, comparing the full SyncTranscriptResponse field by field (excluding session_id and request_time_ms, which differ per request by design).

Scenarios (threading side also covers the transcribe_async() Future path; asyncio side fans out with asyncio.gather and both sides exercise warm()):

Scenario Result
default config, path input ✅ identical
timestamps=True, path input ✅ identical
timestamps=True, bytes input ✅ identical

Transcript text, word texts, and word timestamps were identical between the two wrappers in every run (3 consecutive full passes).

One non-SDK observation surfaced while testing: word/overall confidence values can jitter in the 4th–5th decimal place (observed deltas 4.6e-05 to 7.3e-04) when requests are in flight concurrently. This was isolated to the server, not the wrapper: two identical concurrent requests through the same threading wrapper showed the same drift, while sequential requests were bit-for-bit deterministic every time — consistent with dynamic batching in the inference backend. The comparison treats confidences as equal within a 0.01 tolerance for that reason; text, words, and timestamps compare exactly.

🤖 Generated with Claude Code

@ccampbell-aai
ccampbell-aai marked this pull request as ready for review August 12, 2026 21:41
Comment thread assemblyai/__init__.py Outdated
ccampbell-aai and others added 3 commits August 12, 2026 17:45
The asyncio counterpart of SyncTranscriber: same input types, config,
result, and errors, with transcribe() and warm() as coroutines. Reuses
the sync/v1 base (_resolve_audio, _config_to_json, error mapping) and
follows the AsyncClient ownership pattern from prerecorded/v2 —
owns its HTTP pool by default, leaves a passed-in AsyncClient alone.
Path and file-object reads run off the event loop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both sync-product exports now come from the canonical sync/v1 module;
the sync/__init__.py shim stays for backwards-compatible imports only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 0.66.00 slot was taken by the LeMUR removal (#228) this branch is
now rebased onto.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ccampbell-aai
ccampbell-aai force-pushed the ccampbell/sync-async-transcriber branch from 780e197 to 2ad7961 Compare August 12, 2026 23:47
@ccampbell-aai
ccampbell-aai merged commit 3733ca3 into master Aug 12, 2026
7 checks passed
@ccampbell-aai
ccampbell-aai deleted the ccampbell/sync-async-transcriber branch August 12, 2026 23:52
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