Repository navigation
perf(transcription): replace the openai SDK with an in-house client - #366
Conversation
Replace the openai SDK with createTranscription, one multipart POST to /v1/audio/transcriptions through fetch with the caller's abort signal and the SDK's 10-minute timeout. It sends exactly once, so PodNotes' three attempts no longer stack on the SDK's own two retries (up to nine sends per chunk before). Error messages match the SDK's for OpenAI error bodies, plain-text and empty bodies, network failures and timeouts. TranscriptionService and the OpenAI diarization provider now pass the API key instead of a cached SDK client. The credential-cache hook in main.ts is left empty here and removed with its callers in the next commit.
The cache only held the SDK client so a changed OpenAI key reference could evict it. TranscriptionService now reads the key per run, so the settings tab no longer needs to invalidate anything on import or key selection.
Setting resolve.conditions replaces Vite's defaults, which include the "development|production" condition. Without it esm-env fell back to reading process.env.NODE_ENV at runtime, so Svelte's dev-only error branches shipped in the production main.js and dev builds depended on the shell that launched Obsidian. Production main.js: 313,095 -> 303,042 bytes raw, 98,849 -> 95,630 bytes gzip -9.
Chromium and WebKit reject fetch with a bare TypeError and never attach a cause, so the cause lookup could not trigger in Obsidian. Name what errorDetail mirrors so its branches are not simplified away.
Deploying podnotes with
|
| Latest commit: |
4bc910c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://24692d10.podnotes.pages.dev |
| Branch Preview URL: | https://refactor-drop-openai-sdk.podnotes.pages.dev |
EvidenceFailure notices, before and after"Before" is the real openai 7.25.0 SDK driven with a stubbed Whisper shows the same notice for every failure, before and after, because the API error never reached it: OpenAI diarization shows
Cancellation: before and after, unloading PodNotes mid-upload aborts the Runtime check in ObsidianObsidian 1.13.7 on headless Linux (Xvfb), isolated vault and private HOME. The vault holds
Both builds sent Bundle size
The client commit removes 364,847 bytes, the size of openai 7.25.0 in the bundle. The resolve condition drops Svelte's dev-only error branches. The production bundle is byte-identical whether the condition is spelled CommandsRun on the merge with master after |
The openai SDK waited for the server-directed delay before retrying, so a transient 429 with Retry-After: 4 recovered. createTranscription sends once and the retry loops waited a fixed 1 s then 2 s, so the same 429 exhausted all three attempts and the transcription failed. A non-2xx response now carries the delay from retry-after-ms or retry-after (seconds or HTTP date), bounded to 0-60 s as openai-node does. Both the Whisper and OpenAI diarization loops wait that delay through their existing abortable waits and fall back to the fixed backoff otherwise.
…i-sdk # Conflicts: # src/services/TranscriptionService.test.ts
Unit tests run in Node since #367, where the platform FormData encodes, so the request test can check the bytes that would go on the wire instead of the FormData object.
Fix round 1: server-directed retry delaysThe independent verifier found that a transient 429 with Change. A non-2xx response now throws an error that carries the server's retry delay. The delay is computed the way openai-node 7.25.0's Runtime, verifier scenarioThe verifier's
The earlier seven scenarios were re-run on master and this head: Whisper and diarization success, 401, persistent 500, and unload mid-upload. All seven match, apart from 9 → 3 uploads on persistent 500 and the dropped telemetry headers. Tests that fail on the previous head
Since #367 moved unit tests to Node, the client's request test now encodes the body with Commands on this headThe branch is merged with master |



Summary
PodNotes shipped the whole openai SDK, about half of
main.js, to make one kind of request. It now uses a small in-house client, so the plugin is 55% smaller and transcribes as before.createTranscriptionposts the same multipart request to/v1/audio/transcriptionsthroughfetch, with the caller's abort signal and the SDK's 10-minute timeout. Whisper and OpenAI diarization call it. Error text follows the SDK's rules, so failure notices read the same.retry-after-msorRetry-After, up to 60 s, as openai-node did) and otherwise 1 s, then 2 s.fetch, notrequestUrl, becauserequestUrlcannot abort an upload in flight, and a cancelled chunk would keep uploading and be billed. One scoped lint exception covers that call.openaipackage and the credential cache that held its client are removed.development|productionresolve condition again, so Svelte's dev flag is fixed at build time instead of read fromprocess.envin Obsidian. Dev builds keep the flag off, as before, unless Vite itself runs withNODE_ENV=development.Not included: Deepgram cancellation (still
requestUrl) and the doubled "Transcription failed:" prefix on Whisper notices.Compatibility: removing the SDK also drops its implicit environment overrides (
OPENAI_BASE_URL,OPENAI_ORG_ID,OPENAI_PROJECT_ID,OPENAI_CUSTOM_HEADERS,OPENAI_LOG), none of which is a PodNotes setting, and itsacceptandx-stainless-*telemetry headers. The client expects a JSON response, which both call sites request; it does not supportresponse_format: text.Verification
npm ci, thennpm run lint,format:check,typecheck,build,test(1201 tests) anddocs:buildpass on the merge with master.window.fetchreplaced by a recorder, Transcribe on a local mp3 sends the same request and shows the same notice as the master build in 7 scenarios, including unload mid-upload. A 429 withRetry-After: 4recovers on the second upload, as on master (sends at 0 and about 4,000 ms, note written). The comments have the tables and screenshots.main.js: 677,800 → 302,862 bytes raw, 177,286 → 95,570 bytes gzip.Release impact
None for settings, storage, the API or URIs. Every OpenAI transcription request now goes through new code. Persistent failures give up after 3 uploads instead of up to 9.
Note
Replace OpenAI SDK with in-house HTTP client for transcription and diarization
openaipackage and addscreateTranscriptionin openaiTranscription.ts, a raw fetch helper that builds multipart requests, sends the bearer credential, and has a ten-minute timeout plus caller-abort propagationRetry-Afterheaders (capped at one minute); invalid or excessive values fall back to the existing incremental delayPodNotesandPodNotesSettingsTabis removeddiarizeWithOpenAInow takes an API key instead of a client factory and posts through the shared helper; Whisper responses without atextfield are treated as failed attemptsfetch, with new suites covering multipart encoding, error messages, retry timing, timeout, and cancellationTranscriptionService.getApiKeyis now synchronous; disposal no longer clears any SDK client cache. Also addsdevelopment|productiontoresolve.conditionsin vite.config.tsMacroscope summarized 4bc910c.