# OpenAI Transcription and Resumable Jobs Design ## Goal and Scope Add production-quality transcription of preprocessed chunks through the current OpenAI Audio Transcriptions API. Jobs must survive crashes and network loss, resume without repeating successful work, remain cancellable, and expose useful progress and sanitized API failures in the desktop GUI. Each chunk is submitted independently. Completed chunk transcripts are concatenated in source order without overlap deduplication. Translation, diarization, overlap reconciliation, and word-level timestamp inference are out of scope. ## Verified OpenAI Interface The implementation targets the installed OpenAI Python SDK 3.6.0 interface verified on 2026-08-30: ```python client.audio.transcriptions.create( file=audio_file, model=model, language="pt", prompt=prompt, response_format="json", ) ``` The method accepts arbitrary model strings and returns a transcription object with a `text` value for JSON responses. The SDK exposes distinct `RateLimitError`, `APIConnectionError`, `APITimeoutError`, and `APIStatusError` classes. The application default is the explicitly requested `gpt-transcribe`. The editable selector is seeded with `gpt-transcribe`, `gpt-4o-transcribe`, and `gpt-4o-mini-transcribe`, while allowing future compatible identifiers. Current official OpenAI documentation lists the two GPT-4o transcription models but does not list `gpt-transcribe`; therefore rejection of that identifier is treated as a permanent, clearly displayed API error rather than silently substituting a model. The app uses the transcription endpoint, never the translation endpoint. It sends `language="pt"`, the ISO-639-1 code documented by the API, while the prompt explicitly requests Brazilian Portuguese conventions. ## Architecture - `models.py` extends application settings with an optional context/vocabulary string. - `services/settings.py` persists the non-secret model, language, output directory, preprocessing values, and context. - `services/job_manifest.py` owns schema validation, atomic manifest writes, resumable state transitions, and transcript assembly. - `services/transcription.py` adapts the OpenAI SDK, classifies failures, implements bounded exponential backoff, and orchestrates one request per unfinished chunk. - `services/preprocessing.py` creates durable job directories inside the configured output directory and produces chunk records suitable for transcription. - `ui/settings_dialog.py` provides an editable model selector and multiline Context / Vocabulary field. - `ui/main_window.py` runs the job service in a Qt worker and displays progress, cancellation, elapsed time, current chunk, and API errors. - `app.py` wires the credential, OpenAI client, preprocessing, manifest, and transcription services. Services do not import PySide6. Network calls, retries, waits, manifest I/O, and preprocessing run outside the GUI thread. ## Durable Job Layout and Manifest Every run has a stable directory below `/voice-transcriptor-jobs//`: ```text manifest.json chunks/chunk-00000.m4a chunks/chunk-00001.m4a transcript.txt ``` The manifest remains the source of truth and is replaced atomically after every state transition. It contains no API key, authorization header, or sensitive request metadata. Schema version 2 records job identity and overall state; canonical source metadata; effective preprocessing and transcription settings; timestamps; and each chunk's index, relative audio path, exact source timing, status, attempt count, transcript, sanitized last error, and completion timestamp. Chunk statuses are `pending`, `processing`, `completed`, and `failed`. The overall job may additionally be `preprocessing`, `transcribing`, `completed`, `cancelled`, or `failed`. A successfully encoded chunk is recorded immediately. A successfully transcribed chunk is changed to `completed`, its transcript is stored, counters are updated, and the manifest is atomically replaced before the next API call. `transcript.txt` is then atomically regenerated from all completed chunk texts in index order. This ordering ensures a crash cannot make an unrecorded success appear complete. ## Resume Semantics Starting a job for a source first looks for an existing non-complete manifest whose source identity and effective settings match. The GUI may also resume a retained job directly from its manifest. On resume: - `completed` chunks are never submitted again; - interrupted `processing` chunks return to `pending` because no durable successful response exists; - `failed` chunks return to `pending` for an explicit user resume action; - existing valid encoded chunk files are reused; - missing or invalid encoded files are regenerated without affecting completed transcript records; - an explicit retranscribe action is the only operation allowed to clear completed transcript state. Cancellation stops before the next request or retry, marks the overall job `cancelled`, and retains the durable directory. Cancellation during an active synchronous SDK request takes effect immediately after that request returns; the UI remains responsive because the call is on a worker thread. ## Prompting and Language Preservation The effective prompt combines a fixed Brazilian-Portuguese instruction with optional user context: > Conversa em português brasileiro. Preserve a língua falada; não traduza. Preserve ortografia e pontuação brasileiras, números, nomes próprios, termos técnicos e siglas com máxima fidelidade. When supplied, the Context / Vocabulary value follows this instruction. It is trimmed, user-editable, and persisted as non-secret configuration. The default example is: > Brazilian Portuguese conversation. Preserve Brazilian spelling and punctuation. Vocabulary may include AWS, Kubernetes, OpenAI, PostgreSQL, Brasília, Banco do Brasil. No previous chunk transcript is injected into a later request in this milestone, keeping chunks independent and avoiding accidental propagation of errors. ## Retry and Error Classification The OpenAI client is configured with SDK retries disabled so application behavior is deterministic and visible. The job service uses cancellation-aware exponential backoff with jitter and a finite maximum attempt count. Retryable failures include rate limits, connection failures, timeouts, HTTP 408, HTTP 409, HTTP 429, and HTTP 5xx responses. A server-provided `Retry-After` delay is honored when available, subject to a reasonable cap. The GUI reports rate limiting and the next retry without exposing headers. Permanent failures include authentication and permission errors, invalid requests, unsupported models, missing files, and other non-retryable 4xx responses. They immediately mark the current chunk `failed` and the job `failed`. Retries never continue indefinitely. All logged errors use exception type, status code when safe, request ID when available, chunk index, and a sanitized message. API keys, authorization values, request headers, and raw SDK request objects are never logged or included in manifests. ## Progress and GUI Behavior The main action prepares and transcribes the recording as one worker-owned job. Visible state includes chunks completed / total, elapsed wall-clock time, current chunk, phase, retry/rate-limit notices, and sanitized API errors. The progress bar reflects completed chunks during transcription and preprocessing percentage before API work begins. Conflicting file/settings actions are disabled while active. Cancel remains available. All worker terminal paths restore controls, and closing the window requests cancellation. The settings dialog uses an editable combo box for model selection and a multiline Context / Vocabulary editor. The language remains editable for future languages, defaults to `pt-BR` in presentation and persistence, and is normalized to `pt` for the API when Brazilian Portuguese is selected. ## Transcript Timing Metadata Every chunk transcript retains the preprocessing manifest's exact decimal `source_start_seconds`, `source_end_seconds`, and `duration_seconds`. These values identify the source interval covered by the raw text. The plain `transcript.txt` contains text only, separated by newlines in chunk order; timing-rich data remains in `manifest.json` for future structured export and overlap deduplication. ## Testing and Validation Development follows test-driven cycles. Unit tests use a fake transcription endpoint and deterministic sleeper/random sources; they never require a real API key or network access. Coverage includes exact SDK arguments and independent chunk calls; Brazilian language/prompt composition; arbitrary model identifiers; retry, rate-limit, exhaustion, and permanent failures; atomic persistence; crash recovery and resume skipping; cancellation; timing retention and ordered concatenation; error sanitization; GUI worker progress; and settings migration/UI behavior. Final validation runs the complete pytest suite, Python compilation, whitespace checks, an offscreen GUI startup probe, and an optional local FFmpeg preprocessing smoke test when tools are installed. No live OpenAI request is required for automated validation. ## Success Criteria The application preprocesses and independently transcribes every unfinished chunk through the verified OpenAI Python SDK interface; defaults to the requested model and Brazilian-Portuguese guidance; persists each success and exact source timing immediately; resumes without retranscribing completed chunks; uses finite, graceful retry behavior; remains responsive and cancellable; exposes complete job progress and safe errors; concatenates raw chunk text without deduplication; and passes all automated and startup validation.