Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions scripts/verify-transcription-client.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,28 @@ test("transcribeWithOpenRouter posts base64 JSON with attribution headers", asyn
assert.equal(result.durationSeconds, 3); // from usage.seconds
});

test("transcribeWithOpenRouter maps the macOS companion's m4a mime variants to the m4a format", async () => {
// supabase-swift stores the companion's AAC-in-MP4 upload under audio/x-m4a
// (see supabase/migrations/20260704120000_meeting_audio_m4a_mime.sql); every
// mime the storage layer admits for that same file must resolve to "m4a" here,
// or the provider 415s real companion recordings as unsupported.
const cases = [
["audio/mp4", "m4a"],
["audio/x-m4a", "m4a"],
["audio/m4a", "m4a"],
["audio/aac", "aac"],
["audio/x-m4a; codecs=mp4a.40.2", "m4a"],
["audio/webm", "webm"],
["audio/unknown", "webm"],
];
for (const [mimeType, expectedFormat] of cases) {
const calls = fakeFetch(() => okResponse({ text: "x" }));
await transcribeWithOpenRouter(audioBlob(64), { apiKey: "or-key", mimeType });
const body = JSON.parse(calls[0].options.body);
assert.equal(body.input_audio.format, expectedFormat, `${mimeType} -> ${expectedFormat}`);
}
});

// ---------------------------------------------------------------------------
// AssemblyAI client (diarizing primary)
// ---------------------------------------------------------------------------
Expand Down
9 changes: 8 additions & 1 deletion src/lib/transcription/openrouter-stt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,17 @@ import { sendTranscription, type TranscriptionResult } from "./shared";
export const OPENROUTER_STT_URL = "https://openrouter.ai/api/v1/audio/transcriptions";
export const OPENROUTER_STT_DEFAULT_MODEL = "openai/whisper-1";

// supabase-swift stores the macOS companion's AAC-in-MP4 upload under a mime
// derived from the ".m4a" extension, not the explicit FileOptions content
// type, so the same recording can arrive as audio/mp4, audio/x-m4a, or
// audio/m4a (see supabase/migrations/20260704120000_meeting_audio_m4a_mime.sql).
// All must map to "m4a" or the provider 415s a real companion upload.
const M4A_MIME_TYPES = new Set(["audio/mp4", "audio/x-m4a", "audio/m4a"]);

/** OpenRouter's `input_audio.format` from a MIME type (codecs suffix ignored). */
function audioFormatFromMime(mime: string): string {
const base = mime.split(";")[0].trim();
if (base === "audio/mp4") return "m4a";
if (M4A_MIME_TYPES.has(base)) return "m4a";
if (base === "audio/ogg") return "ogg";
if (base === "audio/mpeg") return "mp3";
if (base === "audio/wav") return "wav";
Expand Down
Loading