mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 12:22:14 +03:00
Move chat pipeline validation, circuit breaker execution, proxy resolution, logging, and session header handling into dedicated helpers to keep the SSE handler smaller and easier to verify. Also fix shared API option precedence, rebuild skill version caches after deletions, ignore api.trycloudflare.com false positives, and add rate-limit manager test flush/reset hooks for deterministic coverage. Expand integration and unit coverage across chat routing, auth, cloud sync, skills, executors, streaming, DB helpers, proxy handling, and provider/model utilities.
490 lines
14 KiB
JavaScript
490 lines
14 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
const { handleAudioSpeech } = await import("../../open-sse/handlers/audioSpeech.ts");
|
|
|
|
test("handleAudioSpeech requires model", async () => {
|
|
const response = await handleAudioSpeech({
|
|
body: { input: "hello" },
|
|
credentials: { apiKey: "x" },
|
|
});
|
|
const payload = await response.json();
|
|
|
|
assert.equal(response.status, 400);
|
|
assert.equal(payload.error.message, "model is required");
|
|
});
|
|
|
|
test("handleAudioSpeech requires input text", async () => {
|
|
const response = await handleAudioSpeech({
|
|
body: { model: "openai/tts-1" },
|
|
credentials: { apiKey: "x" },
|
|
});
|
|
const payload = await response.json();
|
|
|
|
assert.equal(response.status, 400);
|
|
assert.equal(payload.error.message, "input is required");
|
|
});
|
|
|
|
test("handleAudioSpeech proxies OpenAI-compatible providers with defaults", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let captured;
|
|
|
|
globalThis.fetch = async (url, options = {}) => {
|
|
captured = {
|
|
url: String(url),
|
|
headers: options.headers,
|
|
body: JSON.parse(String(options.body || "{}")),
|
|
};
|
|
|
|
return new Response(new Uint8Array([1, 2, 3]), {
|
|
status: 200,
|
|
headers: { "content-type": "audio/opus" },
|
|
});
|
|
};
|
|
|
|
try {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "openai/tts-1",
|
|
input: "hello world",
|
|
},
|
|
credentials: { apiKey: "openai-key" },
|
|
});
|
|
|
|
assert.equal(captured.url, "https://api.openai.com/v1/audio/speech");
|
|
assert.equal(captured.headers.Authorization, "Bearer openai-key");
|
|
assert.deepEqual(captured.body, {
|
|
model: "tts-1",
|
|
input: "hello world",
|
|
voice: "alloy",
|
|
response_format: "mp3",
|
|
speed: 1,
|
|
});
|
|
assert.equal(response.status, 200);
|
|
assert.equal(response.headers.get("content-type"), "audio/opus");
|
|
assert.ok(response.headers.get("access-control-allow-origin"));
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("handleAudioSpeech routes Deepgram with Token auth and model query parameter", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let capturedUrl;
|
|
let capturedHeaders;
|
|
|
|
globalThis.fetch = async (url, options = {}) => {
|
|
capturedUrl = String(url);
|
|
capturedHeaders = options.headers;
|
|
const body = JSON.parse(String(options.body || "{}"));
|
|
assert.deepEqual(body, { text: "deepgram text" });
|
|
|
|
return new Response(new Uint8Array([9, 8, 7]), {
|
|
status: 200,
|
|
headers: { "content-type": "audio/mpeg" },
|
|
});
|
|
};
|
|
|
|
try {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "deepgram/aura-asteria-en",
|
|
input: "deepgram text",
|
|
},
|
|
credentials: { apiKey: "dg-key" },
|
|
});
|
|
|
|
const url = new URL(capturedUrl);
|
|
assert.equal(url.origin + url.pathname, "https://api.deepgram.com/v1/speak");
|
|
assert.equal(url.searchParams.get("model"), "aura-asteria-en");
|
|
assert.equal(capturedHeaders.Authorization, "Token dg-key");
|
|
assert.equal(response.status, 200);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("handleAudioSpeech rejects invalid ElevenLabs voice identifiers", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let called = false;
|
|
globalThis.fetch = async () => {
|
|
called = true;
|
|
throw new Error("should not fetch");
|
|
};
|
|
|
|
try {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "elevenlabs/eleven_turbo_v2_5",
|
|
input: "bad voice",
|
|
voice: "../secret",
|
|
},
|
|
credentials: { apiKey: "xi-key" },
|
|
});
|
|
const payload = await response.json();
|
|
|
|
assert.equal(response.status, 400);
|
|
assert.equal(payload.error.message, "Invalid voice ID");
|
|
assert.equal(called, false);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("handleAudioSpeech maps Cartesia voice and wav output settings", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let captured;
|
|
|
|
globalThis.fetch = async (_url, options = {}) => {
|
|
captured = {
|
|
headers: options.headers,
|
|
body: JSON.parse(String(options.body || "{}")),
|
|
};
|
|
return new Response(new Uint8Array([4, 5, 6]), {
|
|
status: 200,
|
|
headers: { "content-type": "audio/wav" },
|
|
});
|
|
};
|
|
|
|
try {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "cartesia/sonic-2",
|
|
input: "cartesia text",
|
|
voice: "voice-123",
|
|
response_format: "wav",
|
|
},
|
|
credentials: { apiKey: "cartesia-key" },
|
|
});
|
|
|
|
assert.equal(captured.headers["X-API-Key"], "cartesia-key");
|
|
assert.equal(captured.headers["Cartesia-Version"], "2024-06-10");
|
|
assert.deepEqual(captured.body, {
|
|
model_id: "sonic-2",
|
|
transcript: "cartesia text",
|
|
voice: { mode: "id", id: "voice-123" },
|
|
output_format: { container: "wav", sample_rate: 44100 },
|
|
});
|
|
assert.equal(response.headers.get("content-type"), "audio/wav");
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("handleAudioSpeech maps PlayHT credentials, output format, and speed", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let captured;
|
|
|
|
globalThis.fetch = async (_url, options = {}) => {
|
|
captured = {
|
|
headers: options.headers,
|
|
body: JSON.parse(String(options.body || "{}")),
|
|
};
|
|
return new Response(new Uint8Array([7, 7, 7]), {
|
|
status: 200,
|
|
headers: { "content-type": "audio/mpeg" },
|
|
});
|
|
};
|
|
|
|
try {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "playht/Play3.0-mini",
|
|
input: "playht text",
|
|
response_format: "aac",
|
|
speed: 1.25,
|
|
},
|
|
credentials: { apiKey: "user-1:api-key-1" },
|
|
});
|
|
|
|
assert.equal(captured.headers["X-USER-ID"], "user-1");
|
|
assert.equal(captured.headers.Authorization, "Bearer api-key-1");
|
|
assert.equal(captured.body.voice_engine, "Play3.0-mini");
|
|
assert.equal(captured.body.output_format, "aac");
|
|
assert.equal(captured.body.speed, 1.25);
|
|
assert.equal(response.status, 200);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("handleAudioSpeech requires credentials for authenticated providers", async () => {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "openai/tts-1",
|
|
input: "hello world",
|
|
},
|
|
credentials: null,
|
|
});
|
|
const payload = await response.json();
|
|
|
|
assert.equal(response.status, 401);
|
|
assert.equal(payload.error.message, "No credentials for speech provider: openai");
|
|
});
|
|
|
|
test("handleAudioSpeech decodes Hyperbolic base64 audio responses", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let capturedBody;
|
|
|
|
globalThis.fetch = async (_url, options = {}) => {
|
|
capturedBody = JSON.parse(String(options.body || "{}"));
|
|
|
|
return new Response(JSON.stringify({ audio: "AQID" }), {
|
|
status: 200,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
};
|
|
|
|
try {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "hyperbolic/melo-tts",
|
|
input: "hyperbolic text",
|
|
},
|
|
credentials: { apiKey: "hyper-key" },
|
|
});
|
|
|
|
assert.deepEqual(capturedBody, { text: "hyperbolic text" });
|
|
assert.equal(response.status, 200);
|
|
assert.equal(response.headers.get("content-type"), "audio/mpeg");
|
|
assert.deepEqual(Array.from(new Uint8Array(await response.arrayBuffer())), [1, 2, 3]);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("handleAudioSpeech routes Nvidia TTS providers with default voice", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let captured;
|
|
|
|
globalThis.fetch = async (_url, options = {}) => {
|
|
captured = {
|
|
headers: options.headers,
|
|
body: JSON.parse(String(options.body || "{}")),
|
|
};
|
|
|
|
return new Response(new Uint8Array([3, 2, 1]), {
|
|
status: 200,
|
|
headers: { "content-type": "audio/wav" },
|
|
});
|
|
};
|
|
|
|
try {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "nvidia/nvidia/fastpitch",
|
|
input: "nvidia text",
|
|
},
|
|
credentials: { apiKey: "nvidia-key" },
|
|
});
|
|
|
|
assert.equal(captured.headers.Authorization, "Bearer nvidia-key");
|
|
assert.deepEqual(captured.body, {
|
|
input: { text: "nvidia text" },
|
|
voice: "default",
|
|
model: "nvidia/fastpitch",
|
|
});
|
|
assert.equal(response.status, 200);
|
|
assert.equal(response.headers.get("content-type"), "audio/wav");
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("handleAudioSpeech validates HuggingFace model identifiers", async () => {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "huggingface/../escape",
|
|
input: "bad model",
|
|
},
|
|
credentials: { apiKey: "hf-key" },
|
|
});
|
|
const payload = await response.json();
|
|
|
|
assert.equal(response.status, 400);
|
|
assert.equal(payload.error.message, "Invalid model ID");
|
|
});
|
|
|
|
test("handleAudioSpeech routes HuggingFace TTS providers to model-specific endpoints", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let captured;
|
|
|
|
globalThis.fetch = async (url, options = {}) => {
|
|
captured = {
|
|
url: String(url),
|
|
headers: options.headers,
|
|
body: JSON.parse(String(options.body || "{}")),
|
|
};
|
|
|
|
return new Response(new Uint8Array([6, 6, 6]), {
|
|
status: 200,
|
|
headers: { "content-type": "audio/wav" },
|
|
});
|
|
};
|
|
|
|
try {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "huggingface/facebook/mms-tts-eng",
|
|
input: "hf text",
|
|
},
|
|
credentials: { apiKey: "hf-key" },
|
|
});
|
|
|
|
assert.equal(captured.url, "https://api-inference.huggingface.co/models/facebook/mms-tts-eng");
|
|
assert.equal(captured.headers.Authorization, "Bearer hf-key");
|
|
assert.deepEqual(captured.body, { inputs: "hf text" });
|
|
assert.equal(response.status, 200);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("handleAudioSpeech maps Inworld requests to basic auth and wav output", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let captured;
|
|
|
|
globalThis.fetch = async (_url, options = {}) => {
|
|
captured = {
|
|
headers: options.headers,
|
|
body: JSON.parse(String(options.body || "{}")),
|
|
};
|
|
|
|
return new Response(JSON.stringify({ audioContent: "AQIDBA==" }), {
|
|
status: 200,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
};
|
|
|
|
try {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "inworld/inworld-tts-1.5-max",
|
|
input: "inworld text",
|
|
voice: "voice-9",
|
|
response_format: "wav",
|
|
},
|
|
credentials: { apiKey: "encoded-basic-token" },
|
|
});
|
|
|
|
assert.equal(captured.headers.Authorization, "Basic encoded-basic-token");
|
|
assert.deepEqual(captured.body, {
|
|
text: "inworld text",
|
|
voiceId: "voice-9",
|
|
modelId: "inworld-tts-1.5-max",
|
|
audioConfig: { audioEncoding: "LINEAR16" },
|
|
});
|
|
assert.equal(response.headers.get("content-type"), "audio/wav");
|
|
assert.deepEqual(Array.from(new Uint8Array(await response.arrayBuffer())), [1, 2, 3, 4]);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("handleAudioSpeech supports local Coqui providers without credentials", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let captured;
|
|
|
|
globalThis.fetch = async (_url, options = {}) => {
|
|
captured = JSON.parse(String(options.body || "{}"));
|
|
return new Response(new Uint8Array([1, 1, 1]), {
|
|
status: 200,
|
|
headers: { "content-type": "audio/wav" },
|
|
});
|
|
};
|
|
|
|
try {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "coqui/tts_models/en/ljspeech/tacotron2-DDC",
|
|
input: "coqui text",
|
|
voice: "speaker-a",
|
|
},
|
|
credentials: null,
|
|
});
|
|
|
|
assert.deepEqual(captured, { text: "coqui text", speaker_id: "speaker-a" });
|
|
assert.equal(response.status, 200);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("handleAudioSpeech supports local Tortoise providers with the default voice", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let captured;
|
|
|
|
globalThis.fetch = async (_url, options = {}) => {
|
|
captured = JSON.parse(String(options.body || "{}"));
|
|
return new Response(new Uint8Array([2, 2, 2]), {
|
|
status: 200,
|
|
headers: { "content-type": "audio/wav" },
|
|
});
|
|
};
|
|
|
|
try {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "tortoise/tortoise-v2",
|
|
input: "tortoise text",
|
|
},
|
|
credentials: null,
|
|
});
|
|
|
|
assert.deepEqual(captured, { text: "tortoise text", voice: "random" });
|
|
assert.equal(response.status, 200);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("handleAudioSpeech surfaces parsed upstream error messages", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
globalThis.fetch = async () =>
|
|
new Response(JSON.stringify({ error: { message: "quota exceeded" } }), {
|
|
status: 429,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
|
|
try {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "openai/tts-1",
|
|
input: "limited text",
|
|
},
|
|
credentials: { apiKey: "openai-key" },
|
|
});
|
|
const payload = await response.json();
|
|
|
|
assert.equal(response.status, 429);
|
|
assert.equal(payload.error.message, "quota exceeded");
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("handleAudioSpeech returns a 500 when the provider request throws", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
globalThis.fetch = async () => {
|
|
throw new Error("socket hang up");
|
|
};
|
|
|
|
try {
|
|
const response = await handleAudioSpeech({
|
|
body: {
|
|
model: "openai/tts-1",
|
|
input: "broken request",
|
|
},
|
|
credentials: { apiKey: "openai-key" },
|
|
});
|
|
const payload = await response.json();
|
|
|
|
assert.equal(response.status, 500);
|
|
assert.equal(payload.error.message, "Speech request failed: socket hang up");
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|