From a8000bf1a2dea912a576c95d0a18be179ff4e282 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza Date: Wed, 19 Aug 2026 11:08:40 -0300 Subject: [PATCH] fix: resolve audio provider short-alias prefix in parseAudioModel (#10586) (#10747) Co-authored-by: Markus Hartung --- .../fixes/10586-audio-alias-prefix-gap.md | 1 + open-sse/config/audioRegistry.ts | 12 +++ tests/unit/audio-alias-prefix-10586.test.ts | 78 +++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 changelog.d/fixes/10586-audio-alias-prefix-gap.md create mode 100644 tests/unit/audio-alias-prefix-10586.test.ts diff --git a/changelog.d/fixes/10586-audio-alias-prefix-gap.md b/changelog.d/fixes/10586-audio-alias-prefix-gap.md new file mode 100644 index 0000000000..845f00e7dd --- /dev/null +++ b/changelog.d/fixes/10586-audio-alias-prefix-gap.md @@ -0,0 +1 @@ +- fix(sse): resolve the short provider-alias prefix (e.g. `el/`) advertised by GET /v1/models for audio speech, transcription and translation model ids (#10586) diff --git a/open-sse/config/audioRegistry.ts b/open-sse/config/audioRegistry.ts index 00e35cad6a..aaa727fc46 100644 --- a/open-sse/config/audioRegistry.ts +++ b/open-sse/config/audioRegistry.ts @@ -7,6 +7,8 @@ * - /v1/audio/speech (TTS API) */ +import { getProviderAlias } from "@/shared/constants/providers"; + interface AudioModel { id: string; name: string; @@ -677,6 +679,16 @@ function parseAudioModel( } } + // Phase 1.5: prefix match against the short provider alias the catalog itself + // advertises (e.g. "el/eleven_multilingual_v2" for elevenlabs) when it differs + // from the canonical registry key already tried in Phase 1. + for (const [providerId] of Object.entries(registry)) { + const alias = getProviderAlias(providerId); + if (alias && alias !== providerId && modelStr.startsWith(alias + "/")) { + return { provider: providerId, model: modelStr.slice(alias.length + 1) }; + } + } + // Phase 2: bare model lookup in hardcoded registry for (const [providerId, config] of Object.entries(registry)) { if (config.models.some((m) => m.id === modelStr)) { diff --git a/tests/unit/audio-alias-prefix-10586.test.ts b/tests/unit/audio-alias-prefix-10586.test.ts new file mode 100644 index 0000000000..f5624ca613 --- /dev/null +++ b/tests/unit/audio-alias-prefix-10586.test.ts @@ -0,0 +1,78 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +test("parseSpeechModel resolves the elevenlabs short-alias prefix advertised by /v1/models", async () => { + const { parseSpeechModel } = await import("../../open-sse/config/audioRegistry.ts"); + + const canonical = parseSpeechModel("elevenlabs/eleven_multilingual_v2"); + assert.deepEqual(canonical, { provider: "elevenlabs", model: "eleven_multilingual_v2" }); + + // This is the id /v1/models actually advertises in default "dual" prefix mode + // (REGISTRY["elevenlabs"].alias === "el"). It currently fails to parse. + const aliased = parseSpeechModel("el/eleven_multilingual_v2"); + assert.deepEqual( + aliased, + { provider: "elevenlabs", model: "eleven_multilingual_v2" }, + `expected "el/eleven_multilingual_v2" to resolve to the elevenlabs provider like its canonical twin does, but got ${JSON.stringify(aliased)}` + ); +}); + +test("parseSpeechModel resolves every alias registered for an AUDIO_SPEECH_PROVIDERS entry", async () => { + const { parseSpeechModel, AUDIO_SPEECH_PROVIDERS } = await import( + "../../open-sse/config/audioRegistry.ts" + ); + const { getProviderAlias } = await import("../../src/shared/constants/providers.ts"); + + for (const providerId of Object.keys(AUDIO_SPEECH_PROVIDERS)) { + const alias = getProviderAlias(providerId); + if (!alias || alias === providerId) continue; + const canonical = parseSpeechModel(`${providerId}/sample-model`); + assert.deepEqual(canonical, { provider: providerId, model: "sample-model" }); + const aliased = parseSpeechModel(`${alias}/sample-model`); + assert.deepEqual( + aliased, + { provider: providerId, model: "sample-model" }, + `expected "${alias}/sample-model" to resolve to ${providerId} but got ${JSON.stringify(aliased)}` + ); + } +}); + +test("parseTranscriptionModel resolves every alias registered for an AUDIO_TRANSCRIPTION_PROVIDERS entry", async () => { + const { parseTranscriptionModel, AUDIO_TRANSCRIPTION_PROVIDERS } = await import( + "../../open-sse/config/audioRegistry.ts" + ); + const { getProviderAlias } = await import("../../src/shared/constants/providers.ts"); + + for (const providerId of Object.keys(AUDIO_TRANSCRIPTION_PROVIDERS)) { + const alias = getProviderAlias(providerId); + if (!alias || alias === providerId) continue; + const canonical = parseTranscriptionModel(`${providerId}/sample-model`); + assert.deepEqual(canonical, { provider: providerId, model: "sample-model" }); + const aliased = parseTranscriptionModel(`${alias}/sample-model`); + assert.deepEqual( + aliased, + { provider: providerId, model: "sample-model" }, + `expected "${alias}/sample-model" to resolve to ${providerId} but got ${JSON.stringify(aliased)}` + ); + } +}); + +test("parseTranslationModel resolves every alias registered for an AUDIO_TRANSLATION_PROVIDERS entry", async () => { + const { parseTranslationModel, AUDIO_TRANSLATION_PROVIDERS } = await import( + "../../open-sse/config/audioRegistry.ts" + ); + const { getProviderAlias } = await import("../../src/shared/constants/providers.ts"); + + for (const providerId of Object.keys(AUDIO_TRANSLATION_PROVIDERS)) { + const alias = getProviderAlias(providerId); + if (!alias || alias === providerId) continue; + const canonical = parseTranslationModel(`${providerId}/sample-model`); + assert.deepEqual(canonical, { provider: providerId, model: "sample-model" }); + const aliased = parseTranslationModel(`${alias}/sample-model`); + assert.deepEqual( + aliased, + { provider: providerId, model: "sample-model" }, + `expected "${alias}/sample-model" to resolve to ${providerId} but got ${JSON.stringify(aliased)}` + ); + } +});