/** * Whisper transcription backends. Tries local whisper.cpp first (free, fast, * private), falls back to the OpenAI API if the binary isn't installed. * * Both backends normalize to the same shape: * { language, duration, segments: [{ start, end, text }] } * * Word-level timestamps are optional. When `wordTimestamps: true`: * - whisper.cpp uses `--max-len 1` to force one-word segments + `-ml 1` * - OpenAI verbose_json with `timestamp_granularities[]=word` returns words * Both populate `words?: TranscriptWord[]` per segment when supported. When * unsupported (older whisper.cpp build) the field is omitted; agents check. */ export interface TranscriptWord { start: number; end: number; text: string; } export interface TranscriptSegment { start: number; end: number; text: string; /** Word-level timing if requested + available. */ words?: TranscriptWord[]; /** * Speaker label, when present. Populated only when the transcript was * produced by a tool that does diarization (whisperx, AssemblyAI, manual). * gg-editor's bundled `transcribe` does not produce speaker labels. */ speaker?: string; } export interface Transcript { language: string; durationSec: number; segments: TranscriptSegment[]; } /** * OpenAI transcription model identifiers — see * https://platform.openai.com/docs/api-reference/audio/createTranscription. * * - `whisper-1` Legacy Whisper V2. Only model with `verbose_json` + * word/segment timestamp_granularities. Best when you * need word-level timing (caption burn-in). * - `gpt-4o-transcribe` GPT-4o speech model (~$0.006/min). Higher accuracy. * JSON-only response — text + duration only, NO segments. * - `gpt-4o-mini-transcribe` Cheaper sibling (~$0.003/min). JSON-only. * - `gpt-4o-transcribe-diarize` Built-in speaker diarization. Returns segments with * `speaker` labels via `diarized_json`. Replaces the * whisperx + HF_TOKEN + pyannote chain. Requires * `chunking_strategy: "auto"` for inputs > 30s. */ export type OpenAITranscriptionModel = "whisper-1" | "gpt-4o-transcribe" | "gpt-4o-mini-transcribe" | "gpt-4o-transcribe-diarize"; export interface TranscribeOptions { /** Force a backend; otherwise auto-pick (whisperx if diarize=true, else local first, then api). */ backend?: "local" | "api" | "whisperx"; /** Local whisper.cpp model file. Required if backend=local. */ modelPath?: string; /** OpenAI API key (env OPENAI_API_KEY otherwise). */ apiKey?: string; /** * OpenAI model id. Default `whisper-1` because it's the only model that * returns word/segment timestamps — required by burn_subtitles and the * caption pipeline. Pick `gpt-4o-transcribe-diarize` for built-in speaker * labels (no whisperx install needed). */ apiModel?: OpenAITranscriptionModel; /** ISO-639-1 language code; whisper auto-detects when omitted. */ language?: string; /** Request word-level timestamps. Required for word-by-word burned captions. */ wordTimestamps?: boolean; /** * Run speaker diarization. Two paths exist: * 1. Set `apiModel: "gpt-4o-transcribe-diarize"` — server-side diarization, * no extra dependencies. Recommended. * 2. Set `backend: "whisperx"` (or omit and let auto-detect kick in) — * local whisperx + pyannote, requires `whisperx` on PATH and HF_TOKEN. * The `diarize: true` flag alone routes to whisperx for backwards compat. */ diarize?: boolean; /** HF token override (otherwise reads HF_TOKEN env). Used by whisperx --hf_token. */ hfToken?: string; /** whisperx model size. Default "base". */ whisperxModel?: string; /** * Server-side audio chunking for the OpenAI gpt-4o-transcribe family. Set to * `"auto"` to have the server normalize loudness, run VAD, and chunk the file * (up to 1500s for transcribe, 1400s for transcribe-diarize, both within the * 25 MB upload cap). Required for `gpt-4o-transcribe-diarize` on inputs > 30s * — auto-enabled in that case if you don't set it. */ chunkingStrategy?: "auto"; /** Abort signal. */ signal?: AbortSignal; } export declare function detectLocalWhisper(): { cmd: string; argsPrefix: string[]; } | undefined; /** * Detect whisperx (the diarization-capable wrapper around whisper). It prints * its name in --help; we accept either way it might be exposed. */ export declare function detectWhisperx(): boolean; export declare function detectApiKey(override?: string): string | undefined; export declare function transcribe(inputPath: string, opts?: TranscribeOptions): Promise; /** * whisper.cpp with -ml=1 emits ONE entry per token. We regroup adjacent tokens * back into sentence-ish segments (split at sentence punctuation) and attach * the original tokens as the `words` array. */ export declare function regroupTokensIntoSegments(tokens: Array<{ offsets: { from: number; to: number; }; text: string; }>): TranscriptSegment[]; export interface WhisperxJson { language?: string; segments?: Array<{ start: number; end: number; text: string; speaker?: string; words?: Array<{ start: number; end: number; word: string; speaker?: string; }>; }>; } /** * Pure-data conversion: whisperx JSON shape → our Transcript shape. * Exposed for unit tests; production code reads the file from disk first. */ export declare function whisperxJsonToTranscript(parsed: WhisperxJson, fallbackLanguage?: string): Transcript; /** * Per-model request shape. The OpenAI transcription API rejects mismatched * combinations (e.g. response_format=verbose_json on gpt-4o-transcribe), so we * route per model. Pure function — exported for unit tests. */ export interface OpenAIRequestPlan { /** What to put in the `response_format` form field. */ responseFormat: "verbose_json" | "json" | "diarized_json"; /** True if the model+request supports word-level + segment-level timing. */ emitsTimestamps: boolean; /** True if the response will include speaker labels. */ emitsSpeakers: boolean; /** * Effective chunking_strategy to send (or undefined to omit). Auto-enables * "auto" for diarize when the caller didn't explicitly set one. */ chunkingStrategy: "auto" | undefined; } export declare function planOpenAIRequest(model: OpenAITranscriptionModel, opts: Pick): OpenAIRequestPlan; /** * Response shape covering all three OpenAI variants we route. Optional fields * are present on different models: * - whisper-1 verbose_json: text, language, duration, segments[], words[] * - gpt-4o-(mini-)transcribe json: text, language?, duration? * - gpt-4o-transcribe-diarize diarized_json: text, segments[{speaker,start,end,text}] */ interface OpenAITranscriptionResponse { text?: string; language?: string; duration?: number; segments?: Array<{ start: number; end: number; text: string; /** Only on diarized_json. */ speaker?: string; }>; /** verbose_json + timestamp_granularities=word. */ words?: Array<{ start: number; end: number; word: string; }>; } /** * Pure-data conversion: OpenAI transcription response → our Transcript shape. * Exported for unit tests; production code calls this after fetch. */ export declare function openAIResponseToTranscript(data: OpenAITranscriptionResponse, plan: OpenAIRequestPlan, opts: Pick): Transcript; /** * Parse and validate a transcript JSON string. * * Validates that the result has the shape written by `transcribe()`: * - `language` — string * - `durationSec` — finite number ≥ 0 * - `segments` — array (may be empty; each element must have start/end/text) * * Throws a descriptive Error on any mismatch so callers can surface it * as a tool error instead of a cryptic `Cannot read property of undefined` * deep inside filler-word or caption logic. * * Does NOT validate optional `words` inside segments — the tools that * need word-level timings do their own `hasWords` check after calling this. */ export declare function parseTranscript(raw: string): Transcript; export {}; //# sourceMappingURL=whisper.d.ts.map