unspeech 0.1.13 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +201 -5
- package/dist/index.mjs +205 -4
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as _xsai_ext_providers_utils0 from "@xsai-ext/providers/utils";
|
|
2
2
|
import { SpeechProviderWithExtraOptions } from "@xsai-ext/providers/utils";
|
|
3
|
-
import { CommonRequestOptions } from "@xsai/shared";
|
|
3
|
+
import { CommonRequestOptions, WithUnknown } from "@xsai/shared";
|
|
4
4
|
|
|
5
5
|
//#region src/types/voice.d.ts
|
|
6
6
|
interface Voice {
|
|
@@ -88,6 +88,92 @@ interface UnMicrosoftOptionCustomSSML {
|
|
|
88
88
|
}
|
|
89
89
|
/** @see {@link https://elevenlabs.io/docs/api-reference/text-to-speech/convert#request} */
|
|
90
90
|
type UnMicrosoftOptions = (UnMicrosoftOptionAutoSSML | UnMicrosoftOptionCustomSSML) & UnMicrosoftOptionCommon;
|
|
91
|
+
/**
|
|
92
|
+
* Default Microsoft output format used when callers only ask for OpenAI-style
|
|
93
|
+
* `mp3`. This keeps the common path on a compact 24kHz MP3 instead of the
|
|
94
|
+
* backend's maximum-quality 48kHz default.
|
|
95
|
+
*/
|
|
96
|
+
declare const DEFAULT_MICROSOFT_OUTPUT_FORMAT = "audio-24khz-48kbitrate-mono-mp3";
|
|
97
|
+
/**
|
|
98
|
+
* Checks whether a Microsoft voice id is safe to embed in SSML attributes.
|
|
99
|
+
*
|
|
100
|
+
* Use when:
|
|
101
|
+
* - A caller builds SSML locally before sending it to unspeech.
|
|
102
|
+
*
|
|
103
|
+
* Expects:
|
|
104
|
+
* - Microsoft neural voice ids such as `en-US-AvaMultilingualNeural`.
|
|
105
|
+
*
|
|
106
|
+
* Returns:
|
|
107
|
+
* - `true` for the canonical letters/digits/hyphen shape.
|
|
108
|
+
*/
|
|
109
|
+
declare function isMicrosoftVoiceId(voice: string): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Resolves OpenAI-style short audio format names to Microsoft output formats.
|
|
112
|
+
*
|
|
113
|
+
* Before:
|
|
114
|
+
* - `"mp3"`
|
|
115
|
+
* - `"wav"`
|
|
116
|
+
* - `"audio-24khz-48kbitrate-mono-mp3"`
|
|
117
|
+
*
|
|
118
|
+
* After:
|
|
119
|
+
* - `"audio-24khz-48kbitrate-mono-mp3"`
|
|
120
|
+
* - `"riff-24khz-16bit-mono-pcm"`
|
|
121
|
+
* - `"audio-24khz-48kbitrate-mono-mp3"`
|
|
122
|
+
*/
|
|
123
|
+
declare function resolveMicrosoftOutputFormat(responseFormat: string | undefined): string;
|
|
124
|
+
/**
|
|
125
|
+
* Infers the MIME type for a Microsoft output format.
|
|
126
|
+
*
|
|
127
|
+
* Before:
|
|
128
|
+
* - `"audio-24khz-48kbitrate-mono-mp3"`
|
|
129
|
+
* - `"ogg-24khz-16bit-mono-opus"`
|
|
130
|
+
* - `"riff-24khz-16bit-mono-pcm"`
|
|
131
|
+
*
|
|
132
|
+
* After:
|
|
133
|
+
* - `"audio/mpeg"`
|
|
134
|
+
* - `"audio/ogg"`
|
|
135
|
+
* - `"audio/wav"`
|
|
136
|
+
*/
|
|
137
|
+
declare function inferMicrosoftContentType(format: string): string;
|
|
138
|
+
/**
|
|
139
|
+
* Normalizes a speech speed multiplier into Microsoft SSML prosody rate.
|
|
140
|
+
*
|
|
141
|
+
* Before:
|
|
142
|
+
* - `1.0`
|
|
143
|
+
* - `1.2`
|
|
144
|
+
* - `0.8`
|
|
145
|
+
*
|
|
146
|
+
* After:
|
|
147
|
+
* - `""`
|
|
148
|
+
* - `"+20%"`
|
|
149
|
+
* - `"-20%"`
|
|
150
|
+
*/
|
|
151
|
+
declare function microsoftSpeedToProsodyRate(speed: number | undefined): string;
|
|
152
|
+
/**
|
|
153
|
+
* Escapes plain text before inserting it into Microsoft SSML.
|
|
154
|
+
*
|
|
155
|
+
* Before:
|
|
156
|
+
* - `"Tom & <Jerry>"`
|
|
157
|
+
*
|
|
158
|
+
* After:
|
|
159
|
+
* - `"Tom & <Jerry>"`
|
|
160
|
+
*/
|
|
161
|
+
declare function escapeMicrosoftSsmlText(text: string): string;
|
|
162
|
+
/**
|
|
163
|
+
* Builds a Microsoft-compatible SSML envelope.
|
|
164
|
+
*
|
|
165
|
+
* Use when:
|
|
166
|
+
* - Sending plain text through unspeech's Microsoft backend while preserving
|
|
167
|
+
* speed/prosody control.
|
|
168
|
+
*
|
|
169
|
+
* Expects:
|
|
170
|
+
* - `text` is plain text. Already-built SSML should be passed through with
|
|
171
|
+
* `disableSsml`.
|
|
172
|
+
*
|
|
173
|
+
* Returns:
|
|
174
|
+
* - A `<speak>` document that Microsoft accepts as `application/ssml+xml`.
|
|
175
|
+
*/
|
|
176
|
+
declare function buildMicrosoftSsml(text: string, voice: string, speed: number | undefined): string;
|
|
91
177
|
/**
|
|
92
178
|
* [Microsoft / Azure AI](https://speech.microsoft.com/portal) provider for [UnSpeech](https://github.com/moeru-ai/unspeech)
|
|
93
179
|
* only.
|
|
@@ -267,6 +353,50 @@ interface UnElevenLabsOptions {
|
|
|
267
353
|
*/
|
|
268
354
|
declare function createUnElevenLabs(apiKey: string, baseURL?: string): SpeechProviderWithExtraOptions<"eleven_english_sts_v2" | "eleven_flash_v2" | "eleven_flash_v2_5" | "eleven_multilingual_sts_v2" | "eleven_multilingual_v2", UnElevenLabsOptions> & VoiceProviderWithExtraOptions<UnElevenLabsOptions>;
|
|
269
355
|
//#endregion
|
|
356
|
+
//#region src/backend/stepfun.d.ts
|
|
357
|
+
interface UnStepfunOptions {
|
|
358
|
+
/**
|
|
359
|
+
* Audio volume. Range: 0.1 to 2.0.
|
|
360
|
+
*
|
|
361
|
+
* @default 1
|
|
362
|
+
*/
|
|
363
|
+
volume?: number;
|
|
364
|
+
/**
|
|
365
|
+
* Voice labels for `step-tts-2` and `step-tts-mini`.
|
|
366
|
+
*
|
|
367
|
+
* Not supported by `stepaudio-2.5-tts`; use `instruction` instead.
|
|
368
|
+
*/
|
|
369
|
+
voiceLabel?: {
|
|
370
|
+
language?: '粤语' | '四川话' | '日语' | string;
|
|
371
|
+
emotion?: string;
|
|
372
|
+
style?: string;
|
|
373
|
+
};
|
|
374
|
+
/**
|
|
375
|
+
* Global natural-language guidance for `stepaudio-2.5-tts`.
|
|
376
|
+
*/
|
|
377
|
+
instruction?: string;
|
|
378
|
+
/**
|
|
379
|
+
* Output sample rate.
|
|
380
|
+
*
|
|
381
|
+
* @default 24000
|
|
382
|
+
*/
|
|
383
|
+
sampleRate?: 8000 | 16000 | 22050 | 24000 | 48000 | number;
|
|
384
|
+
pronunciationMap?: {
|
|
385
|
+
tone: string[];
|
|
386
|
+
};
|
|
387
|
+
streamFormat?: 'audio' | 'sse';
|
|
388
|
+
markdownFilter?: boolean;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* [StepFun / 阶跃星辰](https://platform.stepfun.com/) provider for
|
|
392
|
+
* [UnSpeech](https://github.com/moeru-ai/unspeech).
|
|
393
|
+
*
|
|
394
|
+
* @param apiKey - StepFun API key
|
|
395
|
+
* @param baseURL - UnSpeech Instance URL
|
|
396
|
+
* @returns SpeechProviderWithExtraOptions
|
|
397
|
+
*/
|
|
398
|
+
declare function createUnStepfun(apiKey: string, baseURL?: string): SpeechProviderWithExtraOptions<`stepfun/${string}`, UnStepfunOptions> & VoiceProviderWithExtraOptions<UnStepfunOptions>;
|
|
399
|
+
//#endregion
|
|
270
400
|
//#region src/backend/volcengine.d.ts
|
|
271
401
|
interface UnVolcengineOptions {
|
|
272
402
|
app?: {
|
|
@@ -370,7 +500,7 @@ declare function createUnVolcengine(apiKey: string, baseURL?: string): SpeechPro
|
|
|
370
500
|
//#endregion
|
|
371
501
|
//#region src/backend/index.d.ts
|
|
372
502
|
/** @see {@link https://github.com/moeru-ai/unspeech} */
|
|
373
|
-
declare function createUnSpeech(apiKey: string, baseURL?: string): _xsai_ext_providers_utils0.SpeechProviderWithExtraOptions<`alibaba/${string}` | `aliyun/${string}` | `deepgram/${string}` | `elevenlabs/${string}` | `koemotion/${string}` | `openai/${string}` | `volcano/${string}` | `volcengine/${string}`, UnSpeechOptions> & VoiceProviderWithExtraOptions<{
|
|
503
|
+
declare function createUnSpeech(apiKey: string, baseURL?: string): _xsai_ext_providers_utils0.SpeechProviderWithExtraOptions<`alibaba/${string}` | `aliyun/${string}` | `deepgram/${string}` | `elevenlabs/${string}` | `koemotion/${string}` | `openai/${string}` | `stepfun/${string}` | `volcano/${string}` | `volcengine/${string}`, UnSpeechOptions> & VoiceProviderWithExtraOptions<{
|
|
374
504
|
appId: string;
|
|
375
505
|
backend: "volcano";
|
|
376
506
|
} | {
|
|
@@ -380,11 +510,77 @@ declare function createUnSpeech(apiKey: string, baseURL?: string): _xsai_ext_pro
|
|
|
380
510
|
backend: "azure" | "microsoft";
|
|
381
511
|
region: MicrosoftRegions | string;
|
|
382
512
|
} | {
|
|
383
|
-
backend: "ali" | "alibaba" | "alibaba-model-studio" | "aliyun" | "bailian" | "deepgram" | "elevenlabs" | "koemotion" | "openai";
|
|
513
|
+
backend: "ali" | "alibaba" | "alibaba-model-studio" | "aliyun" | "bailian" | "deepgram" | "elevenlabs" | "koemotion" | "openai" | "step" | "stepfun";
|
|
384
514
|
}>;
|
|
385
515
|
//#endregion
|
|
516
|
+
//#region src/utils/generate-speech-response.d.ts
|
|
517
|
+
type StringFetch$1 = (input: string, init: RequestInit) => Promise<Response>;
|
|
518
|
+
interface GenerateSpeechResponseOptions extends Omit<CommonRequestOptions, 'fetch'> {
|
|
519
|
+
fetch?: StringFetch$1 | typeof globalThis.fetch;
|
|
520
|
+
input: string;
|
|
521
|
+
/** @default `mp3` */
|
|
522
|
+
responseFormat?: 'aac' | 'flac' | 'mp3' | 'opus' | 'pcm' | 'wav' | string;
|
|
523
|
+
/** @default `1.0` */
|
|
524
|
+
speed?: number;
|
|
525
|
+
voice: string;
|
|
526
|
+
}
|
|
527
|
+
interface GenerateSpeechResponseResult {
|
|
528
|
+
body: ArrayBuffer;
|
|
529
|
+
contentType: null | string;
|
|
530
|
+
response: Response;
|
|
531
|
+
}
|
|
532
|
+
interface UnSpeechAPIErrorOptions {
|
|
533
|
+
requestBody: string;
|
|
534
|
+
response: Response;
|
|
535
|
+
responseBody: string;
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Error thrown when unspeech returns a non-2xx response.
|
|
539
|
+
*
|
|
540
|
+
* Use when:
|
|
541
|
+
* - Callers need to preserve unspeech/upstream status codes for fallback,
|
|
542
|
+
* retries, or diagnostics.
|
|
543
|
+
*
|
|
544
|
+
* Expects:
|
|
545
|
+
* - The caller has already read the response body text.
|
|
546
|
+
*
|
|
547
|
+
* Returns:
|
|
548
|
+
* - An Error carrying status, response body, headers, and URL metadata.
|
|
549
|
+
*/
|
|
550
|
+
declare class UnSpeechAPIError extends Error {
|
|
551
|
+
requestBody: string;
|
|
552
|
+
response: Response;
|
|
553
|
+
responseBody: string;
|
|
554
|
+
responseHeaders: Record<string, string>;
|
|
555
|
+
status: number;
|
|
556
|
+
statusCode: number;
|
|
557
|
+
statusText: string;
|
|
558
|
+
url: string;
|
|
559
|
+
constructor(message: string, options: UnSpeechAPIErrorOptions);
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Generates speech through unspeech and preserves response metadata.
|
|
563
|
+
*
|
|
564
|
+
* Use when:
|
|
565
|
+
* - A server proxy needs the binary audio bytes plus headers such as
|
|
566
|
+
* `content-type`.
|
|
567
|
+
* - A router needs non-2xx status/body metadata for fallback decisions.
|
|
568
|
+
*
|
|
569
|
+
* Expects:
|
|
570
|
+
* - `baseURL` points at an unspeech v1 endpoint.
|
|
571
|
+
* - Provider-specific options are passed through as xsAI-compatible fields
|
|
572
|
+
* such as `extraBody`, which are serialized into OpenAI-style snake_case.
|
|
573
|
+
*
|
|
574
|
+
* Returns:
|
|
575
|
+
* - The generated audio bytes, the `content-type` header when present, and
|
|
576
|
+
* the original `Response`.
|
|
577
|
+
*/
|
|
578
|
+
declare function generateSpeechResponse(options: WithUnknown<GenerateSpeechResponseOptions>): Promise<GenerateSpeechResponseResult>;
|
|
579
|
+
//#endregion
|
|
386
580
|
//#region src/utils/list-voices.d.ts
|
|
387
|
-
|
|
581
|
+
type StringFetch = (input: string, init: RequestInit) => Promise<Response>;
|
|
582
|
+
interface ListVoicesOptions extends Omit<CommonRequestOptions, 'fetch' | 'model'> {
|
|
583
|
+
fetch?: StringFetch | typeof globalThis.fetch;
|
|
388
584
|
query?: string;
|
|
389
585
|
}
|
|
390
586
|
interface ListVoicesResponse {
|
|
@@ -392,4 +588,4 @@ interface ListVoicesResponse {
|
|
|
392
588
|
}
|
|
393
589
|
declare function listVoices(options: ListVoicesOptions): Promise<Voice[]>;
|
|
394
590
|
//#endregion
|
|
395
|
-
export { ListVoicesOptions, ListVoicesResponse, MicrosoftRegions, UnAlibabaCloudOptions, UnDeepgramOptions, UnElevenLabsOptions, UnMicrosoftOptionAutoSSML, UnMicrosoftOptionCommon, UnMicrosoftOptionCustomSSML, UnMicrosoftOptions, UnSpeechOptions, UnVolcengineOptions, Voice, VoiceFormat, VoiceLanguage, VoiceProvider, VoiceProviderWithExtraOptions, createUnAlibabaCloud, createUnDeepgram, createUnElevenLabs, createUnMicrosoft, createUnSpeech, createUnVolcengine, listVoices };
|
|
591
|
+
export { DEFAULT_MICROSOFT_OUTPUT_FORMAT, GenerateSpeechResponseOptions, GenerateSpeechResponseResult, ListVoicesOptions, ListVoicesResponse, MicrosoftRegions, UnAlibabaCloudOptions, UnDeepgramOptions, UnElevenLabsOptions, UnMicrosoftOptionAutoSSML, UnMicrosoftOptionCommon, UnMicrosoftOptionCustomSSML, UnMicrosoftOptions, UnSpeechAPIError, UnSpeechAPIErrorOptions, UnSpeechOptions, UnStepfunOptions, UnVolcengineOptions, Voice, VoiceFormat, VoiceLanguage, VoiceProvider, VoiceProviderWithExtraOptions, buildMicrosoftSsml, createUnAlibabaCloud, createUnDeepgram, createUnElevenLabs, createUnMicrosoft, createUnSpeech, createUnStepfun, createUnVolcengine, escapeMicrosoftSsmlText, generateSpeechResponse, inferMicrosoftContentType, isMicrosoftVoiceId, listVoices, microsoftSpeedToProsodyRate, resolveMicrosoftOutputFormat };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSpeechProviderWithExtraOptions, merge } from "@xsai-ext/providers/utils";
|
|
2
|
-
import { objCamelToSnake, requestHeaders, requestURL
|
|
2
|
+
import { objCamelToSnake, requestBody, requestHeaders, requestURL } from "@xsai/shared";
|
|
3
3
|
|
|
4
4
|
//#region src/backend/alibabacloud.ts
|
|
5
5
|
/**
|
|
@@ -123,6 +123,118 @@ function createUnElevenLabs(apiKey, baseURL = "http://localhost:5933/v1/") {
|
|
|
123
123
|
//#endregion
|
|
124
124
|
//#region src/backend/microsoft.ts
|
|
125
125
|
/**
|
|
126
|
+
* Default Microsoft output format used when callers only ask for OpenAI-style
|
|
127
|
+
* `mp3`. This keeps the common path on a compact 24kHz MP3 instead of the
|
|
128
|
+
* backend's maximum-quality 48kHz default.
|
|
129
|
+
*/
|
|
130
|
+
const DEFAULT_MICROSOFT_OUTPUT_FORMAT = "audio-24khz-48kbitrate-mono-mp3";
|
|
131
|
+
const MICROSOFT_VOICE_ID = /^[a-z0-9-]+$/i;
|
|
132
|
+
/**
|
|
133
|
+
* Checks whether a Microsoft voice id is safe to embed in SSML attributes.
|
|
134
|
+
*
|
|
135
|
+
* Use when:
|
|
136
|
+
* - A caller builds SSML locally before sending it to unspeech.
|
|
137
|
+
*
|
|
138
|
+
* Expects:
|
|
139
|
+
* - Microsoft neural voice ids such as `en-US-AvaMultilingualNeural`.
|
|
140
|
+
*
|
|
141
|
+
* Returns:
|
|
142
|
+
* - `true` for the canonical letters/digits/hyphen shape.
|
|
143
|
+
*/
|
|
144
|
+
function isMicrosoftVoiceId(voice) {
|
|
145
|
+
return MICROSOFT_VOICE_ID.test(voice);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Resolves OpenAI-style short audio format names to Microsoft output formats.
|
|
149
|
+
*
|
|
150
|
+
* Before:
|
|
151
|
+
* - `"mp3"`
|
|
152
|
+
* - `"wav"`
|
|
153
|
+
* - `"audio-24khz-48kbitrate-mono-mp3"`
|
|
154
|
+
*
|
|
155
|
+
* After:
|
|
156
|
+
* - `"audio-24khz-48kbitrate-mono-mp3"`
|
|
157
|
+
* - `"riff-24khz-16bit-mono-pcm"`
|
|
158
|
+
* - `"audio-24khz-48kbitrate-mono-mp3"`
|
|
159
|
+
*/
|
|
160
|
+
function resolveMicrosoftOutputFormat(responseFormat) {
|
|
161
|
+
if (!responseFormat) return DEFAULT_MICROSOFT_OUTPUT_FORMAT;
|
|
162
|
+
if (responseFormat.includes("-")) return responseFormat;
|
|
163
|
+
if (responseFormat === "mp3") return DEFAULT_MICROSOFT_OUTPUT_FORMAT;
|
|
164
|
+
if (responseFormat === "wav") return "riff-24khz-16bit-mono-pcm";
|
|
165
|
+
if (responseFormat === "opus") return "ogg-24khz-16bit-mono-opus";
|
|
166
|
+
return responseFormat;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Infers the MIME type for a Microsoft output format.
|
|
170
|
+
*
|
|
171
|
+
* Before:
|
|
172
|
+
* - `"audio-24khz-48kbitrate-mono-mp3"`
|
|
173
|
+
* - `"ogg-24khz-16bit-mono-opus"`
|
|
174
|
+
* - `"riff-24khz-16bit-mono-pcm"`
|
|
175
|
+
*
|
|
176
|
+
* After:
|
|
177
|
+
* - `"audio/mpeg"`
|
|
178
|
+
* - `"audio/ogg"`
|
|
179
|
+
* - `"audio/wav"`
|
|
180
|
+
*/
|
|
181
|
+
function inferMicrosoftContentType(format) {
|
|
182
|
+
if (format.includes("mp3")) return "audio/mpeg";
|
|
183
|
+
if (format.includes("opus")) return "audio/ogg";
|
|
184
|
+
if (format.includes("pcm") || format.startsWith("riff")) return "audio/wav";
|
|
185
|
+
return "application/octet-stream";
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Normalizes a speech speed multiplier into Microsoft SSML prosody rate.
|
|
189
|
+
*
|
|
190
|
+
* Before:
|
|
191
|
+
* - `1.0`
|
|
192
|
+
* - `1.2`
|
|
193
|
+
* - `0.8`
|
|
194
|
+
*
|
|
195
|
+
* After:
|
|
196
|
+
* - `""`
|
|
197
|
+
* - `"+20%"`
|
|
198
|
+
* - `"-20%"`
|
|
199
|
+
*/
|
|
200
|
+
function microsoftSpeedToProsodyRate(speed) {
|
|
201
|
+
if (speed == null || speed === 1) return "";
|
|
202
|
+
const delta = Math.round((speed - 1) * 100);
|
|
203
|
+
if (delta === 0) return "";
|
|
204
|
+
return delta > 0 ? `+${delta}%` : `${delta}%`;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Escapes plain text before inserting it into Microsoft SSML.
|
|
208
|
+
*
|
|
209
|
+
* Before:
|
|
210
|
+
* - `"Tom & <Jerry>"`
|
|
211
|
+
*
|
|
212
|
+
* After:
|
|
213
|
+
* - `"Tom & <Jerry>"`
|
|
214
|
+
*/
|
|
215
|
+
function escapeMicrosoftSsmlText(text) {
|
|
216
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Builds a Microsoft-compatible SSML envelope.
|
|
220
|
+
*
|
|
221
|
+
* Use when:
|
|
222
|
+
* - Sending plain text through unspeech's Microsoft backend while preserving
|
|
223
|
+
* speed/prosody control.
|
|
224
|
+
*
|
|
225
|
+
* Expects:
|
|
226
|
+
* - `text` is plain text. Already-built SSML should be passed through with
|
|
227
|
+
* `disableSsml`.
|
|
228
|
+
*
|
|
229
|
+
* Returns:
|
|
230
|
+
* - A `<speak>` document that Microsoft accepts as `application/ssml+xml`.
|
|
231
|
+
*/
|
|
232
|
+
function buildMicrosoftSsml(text, voice, speed) {
|
|
233
|
+
const safe = escapeMicrosoftSsmlText(text);
|
|
234
|
+
const rate = microsoftSpeedToProsodyRate(speed);
|
|
235
|
+
return `<speak version='1.0' xml:lang='en-US'><voice name='${voice}'>${rate ? `<prosody rate='${rate}'>${safe}</prosody>` : safe}</voice></speak>`;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
126
238
|
* [Microsoft / Azure AI](https://speech.microsoft.com/portal) provider for [UnSpeech](https://github.com/moeru-ai/unspeech)
|
|
127
239
|
* only.
|
|
128
240
|
*
|
|
@@ -236,15 +348,104 @@ function createUnSpeech(apiKey, baseURL = "http://localhost:5933/v1/") {
|
|
|
236
348
|
} });
|
|
237
349
|
}
|
|
238
350
|
|
|
351
|
+
//#endregion
|
|
352
|
+
//#region src/utils/generate-speech-response.ts
|
|
353
|
+
/**
|
|
354
|
+
* Error thrown when unspeech returns a non-2xx response.
|
|
355
|
+
*
|
|
356
|
+
* Use when:
|
|
357
|
+
* - Callers need to preserve unspeech/upstream status codes for fallback,
|
|
358
|
+
* retries, or diagnostics.
|
|
359
|
+
*
|
|
360
|
+
* Expects:
|
|
361
|
+
* - The caller has already read the response body text.
|
|
362
|
+
*
|
|
363
|
+
* Returns:
|
|
364
|
+
* - An Error carrying status, response body, headers, and URL metadata.
|
|
365
|
+
*/
|
|
366
|
+
var UnSpeechAPIError = class extends Error {
|
|
367
|
+
requestBody;
|
|
368
|
+
response;
|
|
369
|
+
responseBody;
|
|
370
|
+
responseHeaders;
|
|
371
|
+
status;
|
|
372
|
+
statusCode;
|
|
373
|
+
statusText;
|
|
374
|
+
url;
|
|
375
|
+
constructor(message, options) {
|
|
376
|
+
super(message);
|
|
377
|
+
this.name = "UnSpeechAPIError";
|
|
378
|
+
this.requestBody = options.requestBody;
|
|
379
|
+
this.response = options.response;
|
|
380
|
+
this.responseBody = options.responseBody;
|
|
381
|
+
this.responseHeaders = Object.fromEntries(options.response.headers.entries());
|
|
382
|
+
this.status = options.response.status;
|
|
383
|
+
this.statusCode = options.response.status;
|
|
384
|
+
this.statusText = options.response.statusText;
|
|
385
|
+
this.url = options.response.url;
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
/**
|
|
389
|
+
* Generates speech through unspeech and preserves response metadata.
|
|
390
|
+
*
|
|
391
|
+
* Use when:
|
|
392
|
+
* - A server proxy needs the binary audio bytes plus headers such as
|
|
393
|
+
* `content-type`.
|
|
394
|
+
* - A router needs non-2xx status/body metadata for fallback decisions.
|
|
395
|
+
*
|
|
396
|
+
* Expects:
|
|
397
|
+
* - `baseURL` points at an unspeech v1 endpoint.
|
|
398
|
+
* - Provider-specific options are passed through as xsAI-compatible fields
|
|
399
|
+
* such as `extraBody`, which are serialized into OpenAI-style snake_case.
|
|
400
|
+
*
|
|
401
|
+
* Returns:
|
|
402
|
+
* - The generated audio bytes, the `content-type` header when present, and
|
|
403
|
+
* the original `Response`.
|
|
404
|
+
*/
|
|
405
|
+
async function generateSpeechResponse(options) {
|
|
406
|
+
const body = requestBody(options);
|
|
407
|
+
const response = await (options.fetch ?? globalThis.fetch)(requestURL("audio/speech", options.baseURL).toString(), {
|
|
408
|
+
body,
|
|
409
|
+
headers: requestHeaders({
|
|
410
|
+
"Content-Type": "application/json",
|
|
411
|
+
...options.headers
|
|
412
|
+
}, options.apiKey),
|
|
413
|
+
method: "POST",
|
|
414
|
+
signal: options.abortSignal
|
|
415
|
+
});
|
|
416
|
+
if (!response.ok) {
|
|
417
|
+
const responseBody = await response.text().catch(() => "");
|
|
418
|
+
throw new UnSpeechAPIError(`unspeech audio speech ${response.status}: ${responseBody.slice(0, 256)}`, {
|
|
419
|
+
requestBody: body,
|
|
420
|
+
response,
|
|
421
|
+
responseBody
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
body: await response.arrayBuffer(),
|
|
426
|
+
contentType: response.headers.get("content-type"),
|
|
427
|
+
response
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
239
431
|
//#endregion
|
|
240
432
|
//#region src/utils/list-voices.ts
|
|
241
433
|
async function listVoices(options) {
|
|
242
|
-
|
|
434
|
+
const response = await (options.fetch ?? globalThis.fetch)(requestURL(options.query ? `api/voices?${options.query}` : "api/voices", options.baseURL).toString(), {
|
|
243
435
|
headers: requestHeaders({ ...options.headers }, options.apiKey),
|
|
244
436
|
method: "GET",
|
|
245
437
|
signal: options.abortSignal
|
|
246
|
-
})
|
|
438
|
+
});
|
|
439
|
+
if (!response.ok) {
|
|
440
|
+
const responseBody = await response.text().catch(() => "");
|
|
441
|
+
throw new UnSpeechAPIError(`unspeech voices ${response.status}: ${responseBody.slice(0, 256)}`, {
|
|
442
|
+
requestBody: "",
|
|
443
|
+
response,
|
|
444
|
+
responseBody
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
return (await response.json()).voices;
|
|
247
448
|
}
|
|
248
449
|
|
|
249
450
|
//#endregion
|
|
250
|
-
export { createUnAlibabaCloud, createUnDeepgram, createUnElevenLabs, createUnMicrosoft, createUnSpeech, createUnVolcengine, listVoices };
|
|
451
|
+
export { UnSpeechAPIError, buildMicrosoftSsml, createUnAlibabaCloud, createUnDeepgram, createUnElevenLabs, createUnMicrosoft, createUnSpeech, createUnVolcengine, escapeMicrosoftSsmlText, generateSpeechResponse, inferMicrosoftContentType, isMicrosoftVoiceId, listVoices, microsoftSpeedToProsodyRate, resolveMicrosoftOutputFormat };
|