|
6 | 6 | publicKeyRawBase64UrlFromPem, |
7 | 7 | signDevicePayload, |
8 | 8 | } from "../infra/device-identity.js"; |
| 9 | +import { createEmptyPluginRegistry } from "../plugins/registry-empty.js"; |
| 10 | +import { getActivePluginRegistry, setActivePluginRegistry } from "../plugins/runtime.js"; |
9 | 11 | import { withEnvAsync } from "../test-utils/env.js"; |
10 | 12 | import { buildDeviceAuthPayload } from "./device-auth.js"; |
11 | 13 | import { validateTalkConfigResult } from "./protocol/index.js"; |
@@ -348,4 +350,54 @@ describe("gateway talk.config", () => { |
348 | 350 | globalThis.fetch = originalFetch; |
349 | 351 | } |
350 | 352 | }); |
| 353 | + |
| 354 | + it("allows extension speech providers through talk.speak", async () => { |
| 355 | + const { writeConfigFile } = await import("../config/config.js"); |
| 356 | + await writeConfigFile({ |
| 357 | + talk: { |
| 358 | + provider: "acme", |
| 359 | + providers: { |
| 360 | + acme: { |
| 361 | + voiceId: "plugin-voice", |
| 362 | + }, |
| 363 | + }, |
| 364 | + }, |
| 365 | + }); |
| 366 | + |
| 367 | + const previousRegistry = getActivePluginRegistry() ?? createEmptyPluginRegistry(); |
| 368 | + setActivePluginRegistry({ |
| 369 | + ...createEmptyPluginRegistry(), |
| 370 | + speechProviders: [ |
| 371 | + { |
| 372 | + pluginId: "acme-plugin", |
| 373 | + source: "test", |
| 374 | + provider: { |
| 375 | + id: "acme", |
| 376 | + label: "Acme Speech", |
| 377 | + isConfigured: () => true, |
| 378 | + synthesize: async () => ({ |
| 379 | + audioBuffer: Buffer.from([7, 8, 9]), |
| 380 | + outputFormat: "mp3", |
| 381 | + fileExtension: ".mp3", |
| 382 | + voiceCompatible: false, |
| 383 | + }), |
| 384 | + }, |
| 385 | + }, |
| 386 | + ], |
| 387 | + }); |
| 388 | + |
| 389 | + try { |
| 390 | + await withServer(async (ws) => { |
| 391 | + await connectOperator(ws, ["operator.read", "operator.write"]); |
| 392 | + const res = await fetchTalkSpeak(ws, { |
| 393 | + text: "Hello from plugin talk mode.", |
| 394 | + }); |
| 395 | + expect(res.ok).toBe(true); |
| 396 | + expect(res.payload?.provider).toBe("acme"); |
| 397 | + expect(res.payload?.audioBase64).toBe(Buffer.from([7, 8, 9]).toString("base64")); |
| 398 | + }); |
| 399 | + } finally { |
| 400 | + setActivePluginRegistry(previousRegistry); |
| 401 | + } |
| 402 | + }); |
351 | 403 | }); |
0 commit comments