EdTech Triage: Speech-to-Text API Timeout for Large Audio Uploads
Short answer: treat a long recording as an ingestion workflow, not one giant API call. Gate file size locally, upload with a bounded deadline, preserve one recording ID across retries, and send only confirmed transcripts to the support-ticket classifier. For an edtech support queue, quality versus latency is the real decision. A transcript that arrives quickly but drops the sentence containing a student's account number can route a ticket to the wrong team. A perfect transcript that takes long enough to make the queue stale has its own cost. The design below keeps those two failures visible instead of hiding both behind fetch() and one generic error handler. This is the notebook-to-prod step that matters. Start with a small state machine and an eval harness before adding provider-specific behavior. How should a speech-to-text API treat large audio, file size, and retry backoff? Split the request into four decisions: can the application accept the file, did the upload finish, did transcription return a result, and is that result good enough for ticket triage? Each decision needs its own state and timestamp. A multipart timeout answers only the second question. That distinction changes the retry rule. A local file-size rejection should make no network request. A 429 may be retriable with a capped delay and Retry-After . A client-side deadline after an upload may be ambiguous: the server could have accepted the body even though the client never received confirmation. Retrying with a new job ID can create two transcripts for one support ticket. Keep the original recording until the workflow has a confirmed result or a deliberate human fallback. Short sentence. No mystery state. For Node.js, put an AbortController around the operation whose deadline you are measuring, and record whether the abort happened while constructing the body, sending bytes, waiting for a response, or processing the response. fetch does not turn a network deadline into proof that the remote inference did not run. The application needs an idempotency or deduplication policy of its own, keyed by a stable recording ID. I make 429 an explicit fixture in the eval harness and inspect the resulting state transition, rather than treating a green retry test as evidence that the whole ingestion path is safe. That small distinction catches a surprisingly large class of queue bugs: the transport test can pass while the classifier still sees duplicate work, the support agent still receives a stale route, and prompt tokens still get spent on a transcript that should never have entered the downstream pipeline. The size gate is an application policy, not a claim about an upstream limit. Account for the audio bytes and multipart overhead, then leave room for the proxy and deployment environment that sit between the browser or worker and the API. If the policy rejects a recording, retain it locally and ask for a shorter segment or route it through an approved asynchronous path. Do not silently compress speech until the quality impact has been measured. A runnable gate before constructing the multipart body The example uses Python because the surrounding runtime is Python-oriented, but the boundaries map directly to a Node.js worker: Path.stat() is the local file check, the returned state is the queue message, and the retry function is policy rather than an SDK feature. The limits are deliberately product settings. They are not service limits. import argparse import json import random from pathlib import Path MAX_AUDIO_BYTES = 25 * 1024 * 1024 MULTIPART_OVERHEAD_BYTES = 1 * 1024 * 1024 MAX_REQUEST_BYTES = MAX_AUDIO_BYTES + MULTIPART_OVERHEAD_BYTES def inspect_recording(path: Path, recording_id: str) -> dict[str, int | str | bool]: audio_bytes = path.stat().st_size request_bytes = audio_bytes + MULTIPART_OVERHEAD_BYTES accepted = request_bytes float | None: retryable = status_code == 429 or 500 = 3: return None if retry_after_seconds is not None: return max(0.0, retry_after_seconds) return min(8.0, (2 ** attempt) + random.uniform(0.0, 0.25)) def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("recording", type=Path) parser.add_argument("--recording-id", required=True) args = parser.parse_args() print(json.dumps( inspect_recording(args.recording, args.recording_id), indent=2, )) if name == "main": main() Run the gate before allocating or streaming a multipart body: python recording_gate.py ./student-call.m4a --recording-id ticket-1842 The important output is the state transition, not the exact threshold. A recording just under the gate should enter the same test path as one just over it. The test harness should also cover an interrupted connection, a deadline after bytes have started moving, a 429 with Retry-After , and a successful transcript whose quality score is below the routing threshold. Do not log raw audio, credentials, or full transcript text in routine failure records. Log the stable ID, byte count, stage, attempt number, elapsed time, response class, and final state. That is enough to tell a transport failure from a language-quality failure while keeping the support queue's sensitive content out of ordinary operational logs - and it gives the eval harness a compact event stream to replay when a routing decision looks wrong. What should the edtech triage pipeline measure before it retries? The first useful measurement is stage latency: gate decision, upload start, upload confirmation, transcription completion, and ticket classification. Put a quality score beside the last two stages. For a ticket about a failed lesson, a transcript can be technically complete yet still omit the phrase that determines whether the case belongs to billing, accessibility, or curriculum support. Quality needs a fixed evaluation set. Build it from representative recordings: clear speech, background classroom noise, multiple speakers, names, course codes, and the short phrases agents use to identify an account. Keep the set versioned. A prompt or model change should be evaluated against the same examples before it changes the routing policy. Latency needs its own budget. Define a point at which the queue should stop waiting and offer a text fallback, a shorter upload, or human review. The right value depends on recording length, network conditions, and the support team's service target; I'm not sure a universal timeout can exist here. Your mileage may vary, so measure the distribution rather than choosing a number because it looks familiar. A retry is useful only when the likely cause is transient and the operation can be deduplicated. Exponential backoff with jitter prevents a group of workers from repeating the same request at once. It does not repair an oversized multipart body, a rejected media format, or a capability boundary. Those states should move to a visible fallback immediately. One concrete failure chain is worth testing. A parent records a seven-minute explanation, the local gate accepts it, and the connection stops just after the final bytes leave the worker. The client sees a timeout and creates a new ID on retry. Both submissions later produce plausible text; the classifier sees two events and may open or escalate the ticket twice. The repair is a stable recording ID, an upload-confirmed state, and a single accepted-transcript rule. It is not a larger timeout. The quality-versus-latency decision belongs in the queue For support triage, use a two-stage policy. First obtain a transcript under a bounded transport deadline. Then decide whether the transcript is safe enough to classify automatically. If the quality check is weak, keep the audio and transcript together for review rather than routing with false confidence. | Situation | Queue action | Why | |---|---|---| | File exceeds the application gate | Ask for a shorter segment or use an approved asynchronous path | No upload has started, so retrying cannot help | | Upload deadline is ambiguous | Reconcile by recording ID before resubmitting | The remote side may have accepted the body | 429 with a delay hint | Back off within a fixed attempt cap | The service is asking the client to reduce pressure | | Media or capability boundary | Use the configured fallback | Repeating the same request will produce the same decision | | Transcript quality below the routing threshold | Send to human review | Fast automation is less valuable than correct ownership | This table is intentionally conservative. It is not suitable when the product cannot retain audio securely, when a human review queue does not exist, or when support tickets must be routed synchronously with no fallback. In those cases, use a shorter recording policy, a text-first intake, or a speech system whose documented workflow matches the requirement. Stick with the simplest path that meets the measured quality target. The prompt comes later. A transcript should carry its recording ID, language metadata when known, and a provenance state such as confirmed or needs_review . The classifier's eval should score both the route and the confidence policy. This keeps prompt-cost decisions grounded: do not spend another model call summarizing a transcript that already failed the transport or quality gate. A production checklist that stays readable Before shipping, make the upload state observable, preserve the source audio through reconciliation, and test the exact boundaries that users will hit. Test file sizes around the local gate, long recordings, slow connections, a 429 , a connection interruption, a duplicate callback, and an answer that is complete but unsafe for automatic routing. Then inspect the queue by stable recording ID. Every retry should explain its reason, delay, attempt count, and next state. Every transcript should have one acceptance decision. Every automatic route should be traceable to the transcript version and evaluation policy that produced it. Keep the implementation boring. That is a feature for an ingestio
Comments
No comments yet. Start the discussion.