TTS: share runtime backend definitions

This commit is contained in:
Gustavo Madeira Santana
2026-03-15 21:15:53 +00:00
parent c2d3188f99
commit 8ff9277656
5 changed files with 107 additions and 48 deletions

View File

@@ -36,8 +36,8 @@ vi.mock("./media-runtime-registry.js", () => ({
),
}));
vi.mock("./tts-runtime-registry.js", () => ({
listExtensionHostTtsRuntimeProviders: vi.fn(() => [
vi.mock("./tts-runtime-backends.js", () => ({
listExtensionHostTtsRuntimeBackends: vi.fn(() => [
{ id: "openai", supportsTelephony: true },
{ id: "elevenlabs", supportsTelephony: true },
{ id: "edge", supportsTelephony: false },

View File

@@ -13,7 +13,7 @@ import {
buildExtensionHostMediaUnderstandingRegistry,
normalizeExtensionHostMediaProviderId,
} from "./media-runtime-registry.js";
import { listExtensionHostTtsRuntimeProviders } from "./tts-runtime-registry.js";
import { listExtensionHostTtsRuntimeBackends } from "./tts-runtime-backends.js";
export const EXTENSION_HOST_RUNTIME_BACKEND_FAMILY = "capability.runtime-backend";
@@ -203,7 +203,7 @@ export function resolveExtensionHostMediaRuntimeDefaultModel(params: {
}
export function listExtensionHostTtsRuntimeBackendCatalogEntries(): readonly ExtensionHostRuntimeBackendCatalogEntry[] {
return listExtensionHostTtsRuntimeProviders().map((provider, defaultRank) => ({
return listExtensionHostTtsRuntimeBackends().map((provider, defaultRank) => ({
id: buildRuntimeBackendCatalogId("tts", provider.id),
family: EXTENSION_HOST_RUNTIME_BACKEND_FAMILY,
subsystemId: "tts",

View File

@@ -0,0 +1,37 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import {
EXTENSION_HOST_TTS_RUNTIME_BACKEND_IDS,
getExtensionHostTtsRuntimeBackend,
listExtensionHostTtsRuntimeBackends,
} from "./tts-runtime-backends.js";
describe("tts-runtime-backends", () => {
afterEach(() => {
vi.unstubAllEnvs();
});
it("keeps the built-in backend order stable", () => {
expect(EXTENSION_HOST_TTS_RUNTIME_BACKEND_IDS).toEqual(["openai", "elevenlabs", "edge"]);
expect(listExtensionHostTtsRuntimeBackends().map((backend) => backend.id)).toEqual([
"openai",
"elevenlabs",
"edge",
]);
});
it("resolves API keys and configuration through shared backend definitions", () => {
vi.stubEnv("OPENAI_API_KEY", "");
vi.stubEnv("ELEVENLABS_API_KEY", "");
vi.stubEnv("XI_API_KEY", "");
const config = {
openai: { apiKey: "openai-key" },
elevenlabs: { apiKey: "" },
edge: { enabled: true },
} as const;
expect(getExtensionHostTtsRuntimeBackend("openai")?.resolveApiKey(config)).toBe("openai-key");
expect(getExtensionHostTtsRuntimeBackend("elevenlabs")?.isConfigured(config)).toBe(false);
expect(getExtensionHostTtsRuntimeBackend("edge")?.supportsTelephony).toBe(false);
});
});

View File

@@ -0,0 +1,56 @@
import type { TtsProvider } from "../config/types.tts.js";
import type { ResolvedTtsConfig } from "./tts-config.js";
export type ExtensionHostTtsRuntimeBackend = {
id: TtsProvider;
supportsTelephony: boolean;
resolveApiKey: (config: ResolvedTtsConfig) => string | undefined;
isConfigured: (config: ResolvedTtsConfig) => boolean;
};
const EXTENSION_HOST_TTS_RUNTIME_BACKENDS: readonly ExtensionHostTtsRuntimeBackend[] = [
{
id: "openai",
supportsTelephony: true,
resolveApiKey(config) {
return config.openai.apiKey || process.env.OPENAI_API_KEY;
},
isConfigured(config) {
return Boolean(this.resolveApiKey(config));
},
},
{
id: "elevenlabs",
supportsTelephony: true,
resolveApiKey(config) {
return config.elevenlabs.apiKey || process.env.ELEVENLABS_API_KEY || process.env.XI_API_KEY;
},
isConfigured(config) {
return Boolean(this.resolveApiKey(config));
},
},
{
id: "edge",
supportsTelephony: false,
resolveApiKey() {
return undefined;
},
isConfigured(config) {
return config.edge.enabled;
},
},
] as const;
export const EXTENSION_HOST_TTS_RUNTIME_BACKEND_IDS = EXTENSION_HOST_TTS_RUNTIME_BACKENDS.map(
(backend) => backend.id,
) as readonly TtsProvider[];
export function listExtensionHostTtsRuntimeBackends(): readonly ExtensionHostTtsRuntimeBackend[] {
return EXTENSION_HOST_TTS_RUNTIME_BACKENDS;
}
export function getExtensionHostTtsRuntimeBackend(
id: TtsProvider,
): ExtensionHostTtsRuntimeBackend | undefined {
return EXTENSION_HOST_TTS_RUNTIME_BACKENDS.find((backend) => backend.id === id);
}

View File

@@ -1,58 +1,24 @@
import type { TtsProvider } from "../config/types.tts.js";
import type { ResolvedTtsConfig } from "./tts-config.js";
import {
EXTENSION_HOST_TTS_RUNTIME_BACKEND_IDS,
getExtensionHostTtsRuntimeBackend,
listExtensionHostTtsRuntimeBackends,
type ExtensionHostTtsRuntimeBackend,
} from "./tts-runtime-backends.js";
export type ExtensionHostTtsRuntimeProvider = {
id: TtsProvider;
supportsTelephony: boolean;
resolveApiKey: (config: ResolvedTtsConfig) => string | undefined;
isConfigured: (config: ResolvedTtsConfig) => boolean;
};
export type ExtensionHostTtsRuntimeProvider = ExtensionHostTtsRuntimeBackend;
const EXTENSION_HOST_TTS_RUNTIME_PROVIDERS: readonly ExtensionHostTtsRuntimeProvider[] = [
{
id: "openai",
supportsTelephony: true,
resolveApiKey(config) {
return config.openai.apiKey || process.env.OPENAI_API_KEY;
},
isConfigured(config) {
return Boolean(this.resolveApiKey(config));
},
},
{
id: "elevenlabs",
supportsTelephony: true,
resolveApiKey(config) {
return config.elevenlabs.apiKey || process.env.ELEVENLABS_API_KEY || process.env.XI_API_KEY;
},
isConfigured(config) {
return Boolean(this.resolveApiKey(config));
},
},
{
id: "edge",
supportsTelephony: false,
resolveApiKey() {
return undefined;
},
isConfigured(config) {
return config.edge.enabled;
},
},
] as const;
export const EXTENSION_HOST_TTS_PROVIDER_IDS = EXTENSION_HOST_TTS_RUNTIME_PROVIDERS.map(
(provider) => provider.id,
) as readonly TtsProvider[];
export const EXTENSION_HOST_TTS_PROVIDER_IDS = EXTENSION_HOST_TTS_RUNTIME_BACKEND_IDS;
export function listExtensionHostTtsRuntimeProviders(): readonly ExtensionHostTtsRuntimeProvider[] {
return EXTENSION_HOST_TTS_RUNTIME_PROVIDERS;
return listExtensionHostTtsRuntimeBackends();
}
export function getExtensionHostTtsRuntimeProvider(
id: TtsProvider,
): ExtensionHostTtsRuntimeProvider | undefined {
return EXTENSION_HOST_TTS_RUNTIME_PROVIDERS.find((provider) => provider.id === id);
return getExtensionHostTtsRuntimeBackend(id);
}
export function resolveExtensionHostTtsApiKey(