fix: repair Audio Bridge runtime multipart self-loop (#10229)

* fix(guardrails): serialize audio bridge multipart safely

* docs(changelog): record Audio Bridge multipart fix

---------

Co-authored-by: backryun <bakryun0718@proton.me>
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-08-13 03:43:44 -03:00
committed by GitHub
parent 7acddd91a9
commit 32da2a1afe
3 changed files with 71 additions and 21 deletions

View File

@@ -0,0 +1 @@
- **Audio Bridge:** fix production transcription self-loop uploads so real audio reaches the configured STT provider instead of falling back to an unavailable-provider stub ([#10229](https://github.com/diegosouzapw/OmniRoute/pull/10229)).

View File

@@ -1,3 +1,5 @@
import { randomUUID } from "node:crypto";
import { AUDIO_TRANSCRIPTION_PROVIDERS } from "@omniroute/open-sse/config/audioRegistry.ts";
import { detectMediaParts } from "@omniroute/open-sse/utils/mediaParts";
@@ -201,10 +203,25 @@ export async function callAudioTranscription(
(detectedMime?.startsWith("audio/") ? detectedMime : undefined) ??
AUDIO_FORMAT_MIME[format] ??
"application/octet-stream";
const file = new Blob([Uint8Array.from(bytes)], { type: mime });
const form = new FormData();
form.set("file", file, `audio.${format.replace(/[^a-z0-9]/g, "") || "wav"}`);
form.set("model", config.model);
const safeMime = /^[a-z0-9][a-z0-9.+-]*\/[a-z0-9][a-z0-9.+-]*$/i.test(mime)
? mime
: "application/octet-stream";
const fileName = `audio.${format.replace(/[^a-z0-9]/g, "") || "wav"}`;
const boundary = `----OmniRouteAudioBridge${randomUUID().replace(/-/g, "")}`;
const CRLF = "\r\n";
const multipartBody = Buffer.concat([
Buffer.from(
`--${boundary}${CRLF}` +
`Content-Disposition: form-data; name="file"; filename="${fileName}"${CRLF}` +
`Content-Type: ${safeMime}${CRLF}${CRLF}`
),
bytes,
Buffer.from(
`${CRLF}--${boundary}${CRLF}` +
`Content-Disposition: form-data; name="model"${CRLF}${CRLF}` +
`${config.model}${CRLF}--${boundary}--${CRLF}`
),
]);
const port = (deps.getPort ?? (() => getRuntimePorts().port))();
const bearer = (deps.getBearer ?? resolveSelfLoopBearer)();
@@ -216,8 +233,9 @@ export async function callAudioTranscription(
headers: {
Accept: "application/json",
Authorization: `Bearer ${bearer}`,
"Content-Type": `multipart/form-data; boundary=${boundary}`,
},
body: form,
body: multipartBody,
}
);
if (!response.ok) {

View File

@@ -9,6 +9,33 @@ import {
type AudioPart,
} from "../../../src/lib/guardrails/audioBridgeHelpers.ts";
function assertMultipartFile(
init: RequestInit | undefined,
expectedFileName: string,
expectedMime: string,
expectedBytes: Buffer
): void {
const contentType = new Headers(init?.headers).get("content-type");
assert.match(contentType ?? "", /^multipart\/form-data; boundary=/);
const boundary = contentType?.split("boundary=", 2)[1];
assert.ok(boundary);
assert.ok(Buffer.isBuffer(init?.body));
const body = init?.body as Buffer;
assert.ok(
body.includes(
Buffer.concat([
Buffer.from(
`--${boundary}\r\n` +
`Content-Disposition: form-data; name="file"; filename="${expectedFileName}"\r\n` +
`Content-Type: ${expectedMime}\r\n\r\n`
),
expectedBytes,
Buffer.from("\r\n"),
])
)
);
}
test("fixed STT model is honored when its credential is usable", async () => {
const checked: string[] = [];
const selected = await selectAudioBridgeModel("deepgram/nova-2", async (model) => {
@@ -62,13 +89,20 @@ test("input_audio is posted as multipart to the authenticated transcription self
assert.equal(capturedUrl, "http://localhost:3210/v1/audio/transcriptions");
assert.equal(capturedInit?.method, "POST");
assert.equal(new Headers(capturedInit?.headers).get("authorization"), "Bearer internal-test-key");
const form = capturedInit?.body as FormData;
assert.equal(form.get("model"), "deepgram/nova-3");
const file = form.get("file") as File;
assert.equal(file.name, "audio.wav");
assert.equal(file.type, "audio/wav");
assert.equal(Buffer.from(await file.arrayBuffer()).toString(), "RIFF test audio");
const contentType = new Headers(capturedInit?.headers).get("content-type");
const boundary = contentType?.split("boundary=", 2)[1];
assert.ok(boundary);
assertMultipartFile(capturedInit, "audio.wav", "audio/wav", Buffer.from("RIFF test audio"));
const body = capturedInit?.body as Buffer;
assert.ok(
body.includes(
Buffer.from(
`--${boundary}\r\n` +
'Content-Disposition: form-data; name="model"\r\n\r\n' +
`deepgram/nova-3\r\n--${boundary}--\r\n`
)
)
);
});
test("audio extraction and replacement cover the full history without dropping failed clips", () => {
@@ -127,7 +161,7 @@ test("audio extraction and replacement cover the full history without dropping f
});
test("audio_url data URIs are decoded before multipart upload", async () => {
let uploaded: File | null = null;
let uploaded: RequestInit | undefined;
await callAudioTranscription(
{
messageIndex: 0,
@@ -139,7 +173,7 @@ test("audio_url data URIs are decoded before multipart upload", async () => {
{ model: "deepgram/nova-3", timeoutMs: 1_000 },
{
fetchImpl: async (_input, init) => {
uploaded = (init?.body as FormData).get("file") as File;
uploaded = init;
return Response.json({ text: "ok" });
},
getPort: () => 3210,
@@ -147,14 +181,12 @@ test("audio_url data URIs are decoded before multipart upload", async () => {
}
);
assert.ok(uploaded);
assert.equal(uploaded.type, "audio/mpeg");
assert.equal(Buffer.from(await uploaded.arrayBuffer()).toString(), "ID3");
assertMultipartFile(uploaded, "audio.mp3", "audio/mpeg", Buffer.from("ID3"));
});
test("remote audio_url uses the guarded remote fetch before self-loop upload", async () => {
let fetchedUrl = "";
let uploaded: File | null = null;
let uploaded: RequestInit | undefined;
await callAudioTranscription(
{
messageIndex: 0,
@@ -174,7 +206,7 @@ test("remote audio_url uses the guarded remote fetch before self-loop upload", a
};
},
fetchImpl: async (_input, init) => {
uploaded = (init?.body as FormData).get("file") as File;
uploaded = init;
return Response.json({ text: "ok" });
},
getPort: () => 3210,
@@ -183,6 +215,5 @@ test("remote audio_url uses the guarded remote fetch before self-loop upload", a
);
assert.equal(fetchedUrl, "https://media.example.test/clip.ogg");
assert.ok(uploaded);
assert.equal(Buffer.from(await uploaded.arrayBuffer()).toString(), "OggS remote audio");
assertMultipartFile(uploaded, "audio.ogg", "audio/ogg", Buffer.from("OggS remote audio"));
});