@pico-brief/speech-services-parallel 1.0.0
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/README.md +372 -0
- package/dist/KeyManager.d.ts +41 -0
- package/dist/KeyManager.d.ts.map +1 -0
- package/dist/KeyManager.js +61 -0
- package/dist/KeyManager.js.map +1 -0
- package/dist/audioFormat.d.ts +24 -0
- package/dist/audioFormat.d.ts.map +1 -0
- package/dist/audioFormat.js +52 -0
- package/dist/audioFormat.js.map +1 -0
- package/dist/clientFactory.d.ts +26 -0
- package/dist/clientFactory.d.ts.map +1 -0
- package/dist/clientFactory.js +28 -0
- package/dist/clientFactory.js.map +1 -0
- package/dist/concurrency.d.ts +25 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +84 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +48 -0
- package/dist/errors.js.map +1 -0
- package/dist/helpers.d.ts +48 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +73 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/merge.d.ts +35 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +37 -0
- package/dist/merge.js.map +1 -0
- package/dist/retry.d.ts +38 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +68 -0
- package/dist/retry.js.map +1 -0
- package/dist/synthesizeParallel.d.ts +32 -0
- package/dist/synthesizeParallel.d.ts.map +1 -0
- package/dist/synthesizeParallel.js +144 -0
- package/dist/synthesizeParallel.js.map +1 -0
- package/dist/transcribeParallel.d.ts +35 -0
- package/dist/transcribeParallel.d.ts.map +1 -0
- package/dist/transcribeParallel.js +131 -0
- package/dist/transcribeParallel.js.map +1 -0
- package/dist/types.d.ts +169 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semaphore-based concurrency control for parallel operations.
|
|
3
|
+
*
|
|
4
|
+
* Provides {@link mapWithConcurrency}, a `Promise.all`-like helper that caps the
|
|
5
|
+
* number of in-flight promises at any given time. Result order always matches
|
|
6
|
+
* the input order regardless of completion order.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A counting semaphore that limits the number of concurrent operations.
|
|
10
|
+
*
|
|
11
|
+
* When the maximum number of permits is reached, further {@link acquire} calls
|
|
12
|
+
* will wait in a FIFO queue until a permit is freed via {@link release}.
|
|
13
|
+
*/
|
|
14
|
+
class Semaphore {
|
|
15
|
+
max;
|
|
16
|
+
/** Queue of callers waiting for a permit. */
|
|
17
|
+
queue = [];
|
|
18
|
+
/** Number of permits currently held. */
|
|
19
|
+
active = 0;
|
|
20
|
+
/**
|
|
21
|
+
* @param max - Maximum number of concurrent permits.
|
|
22
|
+
*/
|
|
23
|
+
constructor(max) {
|
|
24
|
+
this.max = max;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Acquires a permit. Resolves immediately if one is available,
|
|
28
|
+
* otherwise waits until a permit is released.
|
|
29
|
+
*/
|
|
30
|
+
acquire() {
|
|
31
|
+
if (this.active < this.max) {
|
|
32
|
+
this.active++;
|
|
33
|
+
return Promise.resolve();
|
|
34
|
+
}
|
|
35
|
+
return new Promise(resolve => this.queue.push(resolve));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Releases a permit. If callers are waiting in the queue, the next one
|
|
39
|
+
* is resolved immediately (the active count stays the same).
|
|
40
|
+
*/
|
|
41
|
+
release() {
|
|
42
|
+
const next = this.queue.shift();
|
|
43
|
+
if (next) {
|
|
44
|
+
// Pass the permit directly to the next waiter (active count unchanged)
|
|
45
|
+
next();
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this.active--;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Maps items through an async function with optional concurrency limiting.
|
|
54
|
+
*
|
|
55
|
+
* Behaves like `Promise.all(items.map(fn))` but ensures at most `maxConcurrency`
|
|
56
|
+
* calls to `fn` are in flight at any time. If `maxConcurrency` is `undefined`
|
|
57
|
+
* or greater than or equal to `items.length`, falls back to plain `Promise.all`.
|
|
58
|
+
*
|
|
59
|
+
* Results are always returned in the same order as the input items.
|
|
60
|
+
*
|
|
61
|
+
* @typeParam T - Input item type.
|
|
62
|
+
* @typeParam R - Return type of the async mapper function.
|
|
63
|
+
* @param items - The items to process.
|
|
64
|
+
* @param fn - Async function called for each item with its index.
|
|
65
|
+
* @param maxConcurrency - Optional cap on the number of concurrent `fn` calls.
|
|
66
|
+
* @returns Array of results in the same order as `items`.
|
|
67
|
+
*/
|
|
68
|
+
export async function mapWithConcurrency(items, fn, maxConcurrency) {
|
|
69
|
+
// No limit needed — use plain Promise.all for maximum throughput
|
|
70
|
+
if (maxConcurrency === undefined || maxConcurrency >= items.length) {
|
|
71
|
+
return Promise.all(items.map((item, i) => fn(item, i)));
|
|
72
|
+
}
|
|
73
|
+
const semaphore = new Semaphore(maxConcurrency);
|
|
74
|
+
return Promise.all(items.map(async (item, i) => {
|
|
75
|
+
await semaphore.acquire();
|
|
76
|
+
try {
|
|
77
|
+
return await fn(item, i);
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
semaphore.release();
|
|
81
|
+
}
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=concurrency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.js","sourceRoot":"","sources":["../src/concurrency.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;GAKG;AACH,MAAM,SAAS;IASkB;IAR7B,6CAA6C;IACrC,KAAK,GAAmB,EAAE,CAAC;IACnC,wCAAwC;IAChC,MAAM,GAAG,CAAC,CAAC;IAEnB;;OAEG;IACH,YAA6B,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;IAAG,CAAC;IAE5C;;;OAGG;IACH,OAAO;QACH,IAAI,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC7B,CAAC;QACD,OAAO,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAClE,CAAC;IAED;;;OAGG;IACH,OAAO;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAChC,IAAI,IAAI,EAAE,CAAC;YACP,uEAAuE;YACvE,IAAI,EAAE,CAAC;QACX,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,MAAM,EAAE,CAAC;QAClB,CAAC;IACL,CAAC;CACJ;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACpC,KAAU,EACV,EAA0C,EAC1C,cAAuB;IAEvB,iEAAiE;IACjE,IAAI,cAAc,KAAK,SAAS,IAAI,cAAc,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjE,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,cAAc,CAAC,CAAC;IAChD,OAAO,OAAO,CAAC,GAAG,CACd,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE;QACxB,MAAM,SAAS,CAAC,OAAO,EAAE,CAAC;QAC1B,IAAI,CAAC;YACD,OAAO,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC7B,CAAC;gBAAS,CAAC;YACP,SAAS,CAAC,OAAO,EAAE,CAAC;QACxB,CAAC;IACL,CAAC,CAAC,CACL,CAAC;AACN,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error classification for deciding whether to retry a failed request.
|
|
3
|
+
*
|
|
4
|
+
* Errors are classified as either **terminal** (do not retry — the request
|
|
5
|
+
* is fundamentally invalid) or **retryable** (transient failure that may
|
|
6
|
+
* succeed on a subsequent attempt, possibly with a different credential).
|
|
7
|
+
*
|
|
8
|
+
* Classification rules:
|
|
9
|
+
* - Known `SpeechServiceError` codes like `INVALID_INPUT` or `VOICE_NOT_FOUND` → terminal
|
|
10
|
+
* - HTTP 400, 401, 403, 404, 422 → terminal (bad request or bad credentials)
|
|
11
|
+
* - HTTP 429, 500, 502, 503, 504 → retryable (rate limit or server error)
|
|
12
|
+
* - Unknown errors (not a `SpeechServiceError`) → retryable (safer to retry)
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Classifies an error as `"terminal"` (do not retry) or `"retryable"`.
|
|
16
|
+
*
|
|
17
|
+
* Non-`SpeechServiceError` exceptions default to `"retryable"` since they may
|
|
18
|
+
* be caused by transient issues like network timeouts.
|
|
19
|
+
*
|
|
20
|
+
* @param error - The caught error to classify.
|
|
21
|
+
* @returns `"terminal"` if the error should not be retried, `"retryable"` otherwise.
|
|
22
|
+
*/
|
|
23
|
+
export declare function classifyError(error: unknown): "terminal" | "retryable";
|
|
24
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAkBH;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,WAAW,CAWtE"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error classification for deciding whether to retry a failed request.
|
|
3
|
+
*
|
|
4
|
+
* Errors are classified as either **terminal** (do not retry — the request
|
|
5
|
+
* is fundamentally invalid) or **retryable** (transient failure that may
|
|
6
|
+
* succeed on a subsequent attempt, possibly with a different credential).
|
|
7
|
+
*
|
|
8
|
+
* Classification rules:
|
|
9
|
+
* - Known `SpeechServiceError` codes like `INVALID_INPUT` or `VOICE_NOT_FOUND` → terminal
|
|
10
|
+
* - HTTP 400, 401, 403, 404, 422 → terminal (bad request or bad credentials)
|
|
11
|
+
* - HTTP 429, 500, 502, 503, 504 → retryable (rate limit or server error)
|
|
12
|
+
* - Unknown errors (not a `SpeechServiceError`) → retryable (safer to retry)
|
|
13
|
+
*/
|
|
14
|
+
import { SpeechServiceError } from "@pico-brief/speech-services";
|
|
15
|
+
/** Error codes that indicate a permanent, non-retryable problem. */
|
|
16
|
+
const TERMINAL_CODES = new Set([
|
|
17
|
+
"INVALID_INPUT",
|
|
18
|
+
"UNKNOWN_PROVIDER",
|
|
19
|
+
"NOT_CONFIGURED",
|
|
20
|
+
"VOICE_NOT_FOUND",
|
|
21
|
+
]);
|
|
22
|
+
/** HTTP status codes that indicate a permanent client error. */
|
|
23
|
+
const TERMINAL_STATUS_CODES = new Set([400, 401, 403, 404, 422]);
|
|
24
|
+
/** HTTP status codes that indicate a transient server-side problem. */
|
|
25
|
+
const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);
|
|
26
|
+
/**
|
|
27
|
+
* Classifies an error as `"terminal"` (do not retry) or `"retryable"`.
|
|
28
|
+
*
|
|
29
|
+
* Non-`SpeechServiceError` exceptions default to `"retryable"` since they may
|
|
30
|
+
* be caused by transient issues like network timeouts.
|
|
31
|
+
*
|
|
32
|
+
* @param error - The caught error to classify.
|
|
33
|
+
* @returns `"terminal"` if the error should not be retried, `"retryable"` otherwise.
|
|
34
|
+
*/
|
|
35
|
+
export function classifyError(error) {
|
|
36
|
+
if (!(error instanceof SpeechServiceError))
|
|
37
|
+
return "retryable";
|
|
38
|
+
if (TERMINAL_CODES.has(error.code))
|
|
39
|
+
return "terminal";
|
|
40
|
+
if (error.statusCode !== undefined) {
|
|
41
|
+
if (TERMINAL_STATUS_CODES.has(error.statusCode))
|
|
42
|
+
return "terminal";
|
|
43
|
+
if (RETRYABLE_STATUS_CODES.has(error.statusCode))
|
|
44
|
+
return "retryable";
|
|
45
|
+
}
|
|
46
|
+
return "retryable";
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,oEAAoE;AACpE,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC3B,eAAe;IACf,kBAAkB;IAClB,gBAAgB;IAChB,iBAAiB;CACpB,CAAC,CAAC;AAEH,gEAAgE;AAChE,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAEjE,uEAAuE;AACvE,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAElE;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IACxC,IAAI,CAAC,CAAC,KAAK,YAAY,kBAAkB,CAAC;QAAE,OAAO,WAAW,CAAC;IAE/D,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,UAAU,CAAC;IAEtD,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACjC,IAAI,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC;YAAE,OAAO,UAAU,CAAC;QACnE,IAAI,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC;YAAE,OAAO,WAAW,CAAC;IACzE,CAAC;IAED,OAAO,WAAW,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small utility functions shared across the library.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Returns a promise that resolves after `ms` milliseconds.
|
|
6
|
+
* Used for backoff delays between retry attempts.
|
|
7
|
+
*
|
|
8
|
+
* @param ms - Number of milliseconds to sleep.
|
|
9
|
+
*/
|
|
10
|
+
export declare function sleepAsync(ms: number): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Generates a random alphanumeric string, used for creating unique temporary file prefixes.
|
|
13
|
+
*
|
|
14
|
+
* @param length - Length of the string to generate. @default 12
|
|
15
|
+
* @returns A lowercase alphanumeric string of the given length.
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateRandomString(length?: number): string;
|
|
18
|
+
/**
|
|
19
|
+
* Converts seconds to `HH:MM:SS.mmm` format for ffmpeg `-ss` / `-to` arguments.
|
|
20
|
+
*
|
|
21
|
+
* @param seconds - Time value in seconds (can include fractions).
|
|
22
|
+
* @returns A zero-padded timestamp string like `"01:23:45.678"`.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* formatTimestamp(3661.5); // "01:01:01.500"
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatTimestamp(seconds: number): string;
|
|
30
|
+
/**
|
|
31
|
+
* Normalizes an unknown thrown value to a human-readable string message.
|
|
32
|
+
*
|
|
33
|
+
* @param error - The caught value (may be an `Error`, a string, or anything else).
|
|
34
|
+
* @returns The error's message, the string itself, or `String(error)`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractErrorMessage(error: unknown): string;
|
|
37
|
+
/**
|
|
38
|
+
* Safely extracts an `ArrayBuffer` from a Node.js `Buffer`.
|
|
39
|
+
*
|
|
40
|
+
* Node.js `Buffer` objects may share an underlying `ArrayBuffer` (from the
|
|
41
|
+
* memory pool), so a naive `.buffer` access can return data from other buffers.
|
|
42
|
+
* This function slices to the exact byte range owned by `buf`.
|
|
43
|
+
*
|
|
44
|
+
* @param buf - The Node.js Buffer to convert.
|
|
45
|
+
* @returns A standalone `ArrayBuffer` containing only `buf`'s data.
|
|
46
|
+
*/
|
|
47
|
+
export declare function bufferToArrayBuffer(buf: Buffer): ArrayBuffer;
|
|
48
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,SAAK,GAAG,MAAM,CAOxD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQvD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAI1D;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAE5D"}
|
package/dist/helpers.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small utility functions shared across the library.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Returns a promise that resolves after `ms` milliseconds.
|
|
6
|
+
* Used for backoff delays between retry attempts.
|
|
7
|
+
*
|
|
8
|
+
* @param ms - Number of milliseconds to sleep.
|
|
9
|
+
*/
|
|
10
|
+
export function sleepAsync(ms) {
|
|
11
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generates a random alphanumeric string, used for creating unique temporary file prefixes.
|
|
15
|
+
*
|
|
16
|
+
* @param length - Length of the string to generate. @default 12
|
|
17
|
+
* @returns A lowercase alphanumeric string of the given length.
|
|
18
|
+
*/
|
|
19
|
+
export function generateRandomString(length = 12) {
|
|
20
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
21
|
+
let result = "";
|
|
22
|
+
for (let i = 0; i < length; i++) {
|
|
23
|
+
result += chars[Math.floor(Math.random() * chars.length)];
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Converts seconds to `HH:MM:SS.mmm` format for ffmpeg `-ss` / `-to` arguments.
|
|
29
|
+
*
|
|
30
|
+
* @param seconds - Time value in seconds (can include fractions).
|
|
31
|
+
* @returns A zero-padded timestamp string like `"01:23:45.678"`.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* formatTimestamp(3661.5); // "01:01:01.500"
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export function formatTimestamp(seconds) {
|
|
39
|
+
const h = Math.floor(seconds / 3600);
|
|
40
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
41
|
+
const s = seconds % 60;
|
|
42
|
+
const hh = String(h).padStart(2, "0");
|
|
43
|
+
const mm = String(m).padStart(2, "0");
|
|
44
|
+
const ss = s.toFixed(3).padStart(6, "0");
|
|
45
|
+
return `${hh}:${mm}:${ss}`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Normalizes an unknown thrown value to a human-readable string message.
|
|
49
|
+
*
|
|
50
|
+
* @param error - The caught value (may be an `Error`, a string, or anything else).
|
|
51
|
+
* @returns The error's message, the string itself, or `String(error)`.
|
|
52
|
+
*/
|
|
53
|
+
export function extractErrorMessage(error) {
|
|
54
|
+
if (error instanceof Error)
|
|
55
|
+
return error.message;
|
|
56
|
+
if (typeof error === "string")
|
|
57
|
+
return error;
|
|
58
|
+
return String(error);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Safely extracts an `ArrayBuffer` from a Node.js `Buffer`.
|
|
62
|
+
*
|
|
63
|
+
* Node.js `Buffer` objects may share an underlying `ArrayBuffer` (from the
|
|
64
|
+
* memory pool), so a naive `.buffer` access can return data from other buffers.
|
|
65
|
+
* This function slices to the exact byte range owned by `buf`.
|
|
66
|
+
*
|
|
67
|
+
* @param buf - The Node.js Buffer to convert.
|
|
68
|
+
* @returns A standalone `ArrayBuffer` containing only `buf`'s data.
|
|
69
|
+
*/
|
|
70
|
+
export function bufferToArrayBuffer(buf) {
|
|
71
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,EAAU;IACjC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAM,GAAG,EAAE;IAC5C,MAAM,KAAK,GAAG,sCAAsC,CAAC;IACrD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC3C,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACrC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC5C,MAAM,CAAC,GAAG,OAAO,GAAG,EAAE,CAAC;IACvB,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACtC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACtC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACzC,OAAO,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;AAC/B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAC9C,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC;IACjD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC3C,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC,UAAU,CAAgB,CAAC;AAC5F,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @pico-brief/speech-services-parallel
|
|
3
|
+
*
|
|
4
|
+
* Parallel orchestration for Speech-to-Text and Text-to-Speech operations.
|
|
5
|
+
*
|
|
6
|
+
* This module re-exports the two main entry points — {@link transcribeParallel}
|
|
7
|
+
* and {@link synthesizeParallel} — along with the {@link KeyManager} class for
|
|
8
|
+
* standalone credential rotation, and every public type used by the API.
|
|
9
|
+
*/
|
|
10
|
+
export { transcribeParallel } from "./transcribeParallel.js";
|
|
11
|
+
export { synthesizeParallel } from "./synthesizeParallel.js";
|
|
12
|
+
export { KeyManager } from "./KeyManager.js";
|
|
13
|
+
export type { TranscribeParallelParams, SynthesizeParallelParams, SynthesizeParallelResult, SynthesizeChunkInput, SynthesizeChunkResult, } from "./types.js";
|
|
14
|
+
export type { ChunkBoundary } from "./merge.js";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,YAAY,EACR,wBAAwB,EACxB,wBAAwB,EACxB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,GACxB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @pico-brief/speech-services-parallel
|
|
3
|
+
*
|
|
4
|
+
* Parallel orchestration for Speech-to-Text and Text-to-Speech operations.
|
|
5
|
+
*
|
|
6
|
+
* This module re-exports the two main entry points — {@link transcribeParallel}
|
|
7
|
+
* and {@link synthesizeParallel} — along with the {@link KeyManager} class for
|
|
8
|
+
* standalone credential rotation, and every public type used by the API.
|
|
9
|
+
*/
|
|
10
|
+
export { transcribeParallel } from "./transcribeParallel.js";
|
|
11
|
+
export { synthesizeParallel } from "./synthesizeParallel.js";
|
|
12
|
+
export { KeyManager } from "./KeyManager.js";
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/merge.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merging logic for transcription results from overlapping audio chunks.
|
|
3
|
+
*
|
|
4
|
+
* When audio is split into overlapping chunks, each chunk may transcribe words
|
|
5
|
+
* that fall in the overlap region. This module filters words by ownership
|
|
6
|
+
* boundaries so that each word appears exactly once in the merged output.
|
|
7
|
+
*/
|
|
8
|
+
import type { TranscribeResult } from "@pico-brief/speech-services";
|
|
9
|
+
/**
|
|
10
|
+
* Defines the ownership range for a single chunk's transcription result.
|
|
11
|
+
*
|
|
12
|
+
* Words whose `startTime` falls within `[ownedStart, ownedEnd)` are kept;
|
|
13
|
+
* words outside that range (i.e. in the overlap zone) are discarded to
|
|
14
|
+
* prevent duplicates.
|
|
15
|
+
*/
|
|
16
|
+
export interface ChunkBoundary {
|
|
17
|
+
/** The transcription result for this chunk. */
|
|
18
|
+
result: TranscribeResult;
|
|
19
|
+
/** Inclusive lower bound of the time range this chunk "owns" (seconds). */
|
|
20
|
+
ownedStart: number;
|
|
21
|
+
/** Exclusive upper bound; set to `Infinity` for the last chunk. */
|
|
22
|
+
ownedEnd: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Merges multiple chunk transcription results into a single unified result.
|
|
26
|
+
*
|
|
27
|
+
* For each chunk, only words whose `startTime` falls within the chunk's owned
|
|
28
|
+
* range are included. The combined words are sorted by timestamp, and the
|
|
29
|
+
* full text is reconstructed by joining word texts with spaces.
|
|
30
|
+
*
|
|
31
|
+
* @param chunks - Array of chunk results with their ownership boundaries.
|
|
32
|
+
* @returns A single merged {@link TranscribeResult} with deduplicated, sorted words.
|
|
33
|
+
*/
|
|
34
|
+
export declare function mergeTranscribeResults(chunks: ChunkBoundary[]): TranscribeResult;
|
|
35
|
+
//# sourceMappingURL=merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAmB,MAAM,6BAA6B,CAAC;AAErF;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC1B,+CAA+C;IAC/C,MAAM,EAAE,gBAAgB,CAAC;IACzB,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,gBAAgB,CAsBhF"}
|
package/dist/merge.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merging logic for transcription results from overlapping audio chunks.
|
|
3
|
+
*
|
|
4
|
+
* When audio is split into overlapping chunks, each chunk may transcribe words
|
|
5
|
+
* that fall in the overlap region. This module filters words by ownership
|
|
6
|
+
* boundaries so that each word appears exactly once in the merged output.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Merges multiple chunk transcription results into a single unified result.
|
|
10
|
+
*
|
|
11
|
+
* For each chunk, only words whose `startTime` falls within the chunk's owned
|
|
12
|
+
* range are included. The combined words are sorted by timestamp, and the
|
|
13
|
+
* full text is reconstructed by joining word texts with spaces.
|
|
14
|
+
*
|
|
15
|
+
* @param chunks - Array of chunk results with their ownership boundaries.
|
|
16
|
+
* @returns A single merged {@link TranscribeResult} with deduplicated, sorted words.
|
|
17
|
+
*/
|
|
18
|
+
export function mergeTranscribeResults(chunks) {
|
|
19
|
+
const allWords = [];
|
|
20
|
+
for (const { result, ownedStart, ownedEnd } of chunks) {
|
|
21
|
+
for (const word of result.words) {
|
|
22
|
+
// Only keep words that fall within this chunk's owned time range
|
|
23
|
+
if (word.startTime >= ownedStart && word.startTime < ownedEnd) {
|
|
24
|
+
allWords.push(word);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Sort all words by their start time for correct ordering
|
|
29
|
+
allWords.sort((a, b) => a.startTime - b.startTime);
|
|
30
|
+
const text = allWords.map(w => w.text).join(" ");
|
|
31
|
+
// Duration is the end time of the last word (safe against Infinity from ownedEnd)
|
|
32
|
+
const duration = allWords.length > 0 ? allWords[allWords.length - 1].endTime : 0;
|
|
33
|
+
// Use the language detected by the first chunk
|
|
34
|
+
const language = chunks.length > 0 ? chunks[0].result.language : "";
|
|
35
|
+
return { text, words: allWords, language, duration };
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoBH;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAuB;IAC1D,MAAM,QAAQ,GAAsB,EAAE,CAAC;IAEvC,KAAK,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,MAAM,EAAE,CAAC;QACpD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAC9B,iEAAiE;YACjE,IAAI,IAAI,CAAC,SAAS,IAAI,UAAU,IAAI,IAAI,CAAC,SAAS,GAAG,QAAQ,EAAE,CAAC;gBAC5D,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxB,CAAC;QACL,CAAC;IACL,CAAC;IAED,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;IAEnD,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjD,kFAAkF;IAClF,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACjF,+CAA+C;IAC/C,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAEpE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AACzD,CAAC"}
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry loop with exponential backoff, credential rotation, and error classification.
|
|
3
|
+
*
|
|
4
|
+
* On each transient failure the current credential is placed on cool-down
|
|
5
|
+
* (via {@link KeyManager.reportError}) and a fresh credential is selected for
|
|
6
|
+
* the next attempt. Terminal errors (bad input, invalid keys) are thrown
|
|
7
|
+
* immediately without retry.
|
|
8
|
+
*/
|
|
9
|
+
import type { KeyManager } from "./KeyManager.js";
|
|
10
|
+
/** Options that control retry behavior. */
|
|
11
|
+
export interface WithRetryOptions<TCredential> {
|
|
12
|
+
/** The key manager used for credential rotation between attempts. */
|
|
13
|
+
keyManager: KeyManager<TCredential>;
|
|
14
|
+
/** Maximum wall-clock time (ms) to keep retrying before giving up. */
|
|
15
|
+
retryTimeoutMs: number;
|
|
16
|
+
/** Optional abort signal — checked before each attempt and after each sleep. */
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
/** Human-readable label for error messages (e.g. `"Transcription"`). */
|
|
19
|
+
operationName: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Executes `fn` with automatic retry on transient errors.
|
|
23
|
+
*
|
|
24
|
+
* - **Credential rotation**: a fresh credential is obtained from the key manager on every attempt.
|
|
25
|
+
* - **Exponential backoff**: delay doubles each attempt (1 s → 2 s → 4 s → …) capped at 60 s, plus 30 % jitter.
|
|
26
|
+
* - **Deadline enforcement**: retries stop once `retryTimeoutMs` has elapsed.
|
|
27
|
+
* - **Abort support**: the loop checks `signal.aborted` before each attempt and after each sleep.
|
|
28
|
+
* - **Terminal errors**: errors classified as terminal (e.g. 401, 403) are thrown immediately.
|
|
29
|
+
*
|
|
30
|
+
* @typeParam TCredential - Credential type managed by the key manager.
|
|
31
|
+
* @typeParam TResult - Return type of the wrapped function.
|
|
32
|
+
* @param options - Retry configuration (key manager, timeout, signal, operation name).
|
|
33
|
+
* @param fn - The async function to execute. Receives a credential and should return a result.
|
|
34
|
+
* @returns The result of `fn` on the first successful attempt.
|
|
35
|
+
* @throws On terminal error, timeout, or abort.
|
|
36
|
+
*/
|
|
37
|
+
export declare function withRetry<TCredential, TResult>(options: WithRetryOptions<TCredential>, fn: (credential: TCredential) => Promise<TResult>): Promise<TResult>;
|
|
38
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,2CAA2C;AAC3C,MAAM,WAAW,gBAAgB,CAAC,WAAW;IACzC,qEAAqE;IACrE,UAAU,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;IACpC,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAC;IACvB,gFAAgF;IAChF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,wEAAwE;IACxE,aAAa,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,SAAS,CAAC,WAAW,EAAE,OAAO,EAChD,OAAO,EAAE,gBAAgB,CAAC,WAAW,CAAC,EACtC,EAAE,EAAE,CAAC,UAAU,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,GAClD,OAAO,CAAC,OAAO,CAAC,CA+ClB"}
|
package/dist/retry.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry loop with exponential backoff, credential rotation, and error classification.
|
|
3
|
+
*
|
|
4
|
+
* On each transient failure the current credential is placed on cool-down
|
|
5
|
+
* (via {@link KeyManager.reportError}) and a fresh credential is selected for
|
|
6
|
+
* the next attempt. Terminal errors (bad input, invalid keys) are thrown
|
|
7
|
+
* immediately without retry.
|
|
8
|
+
*/
|
|
9
|
+
import { classifyError } from "./errors.js";
|
|
10
|
+
import { sleepAsync, extractErrorMessage } from "./helpers.js";
|
|
11
|
+
/**
|
|
12
|
+
* Executes `fn` with automatic retry on transient errors.
|
|
13
|
+
*
|
|
14
|
+
* - **Credential rotation**: a fresh credential is obtained from the key manager on every attempt.
|
|
15
|
+
* - **Exponential backoff**: delay doubles each attempt (1 s → 2 s → 4 s → …) capped at 60 s, plus 30 % jitter.
|
|
16
|
+
* - **Deadline enforcement**: retries stop once `retryTimeoutMs` has elapsed.
|
|
17
|
+
* - **Abort support**: the loop checks `signal.aborted` before each attempt and after each sleep.
|
|
18
|
+
* - **Terminal errors**: errors classified as terminal (e.g. 401, 403) are thrown immediately.
|
|
19
|
+
*
|
|
20
|
+
* @typeParam TCredential - Credential type managed by the key manager.
|
|
21
|
+
* @typeParam TResult - Return type of the wrapped function.
|
|
22
|
+
* @param options - Retry configuration (key manager, timeout, signal, operation name).
|
|
23
|
+
* @param fn - The async function to execute. Receives a credential and should return a result.
|
|
24
|
+
* @returns The result of `fn` on the first successful attempt.
|
|
25
|
+
* @throws On terminal error, timeout, or abort.
|
|
26
|
+
*/
|
|
27
|
+
export async function withRetry(options, fn) {
|
|
28
|
+
const { keyManager, retryTimeoutMs, signal, operationName } = options;
|
|
29
|
+
const deadline = Date.now() + retryTimeoutMs;
|
|
30
|
+
let attempt = 0;
|
|
31
|
+
while (true) {
|
|
32
|
+
// Check for cancellation before each attempt
|
|
33
|
+
if (signal?.aborted) {
|
|
34
|
+
throw new Error(`${operationName} aborted`);
|
|
35
|
+
}
|
|
36
|
+
const credential = keyManager.getKey();
|
|
37
|
+
try {
|
|
38
|
+
return await fn(credential);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
// Terminal errors (bad input, invalid key, etc.) are not retryable
|
|
42
|
+
if (classifyError(e) === "terminal")
|
|
43
|
+
throw e;
|
|
44
|
+
// Mark the credential as failed so the key manager avoids it temporarily
|
|
45
|
+
keyManager.reportError(credential);
|
|
46
|
+
// Check if we've exceeded the retry deadline
|
|
47
|
+
if (Date.now() >= deadline) {
|
|
48
|
+
throw new Error(`${operationName} timed out after ${retryTimeoutMs}ms: ${extractErrorMessage(e)}`);
|
|
49
|
+
}
|
|
50
|
+
// Exponential backoff: min(1000 * 2^attempt, 60000) + 30% jitter
|
|
51
|
+
const base = Math.min(1000 * Math.pow(2, attempt), 60000);
|
|
52
|
+
const jitter = base * 0.3 * Math.random();
|
|
53
|
+
const delay = base + jitter;
|
|
54
|
+
// Don't sleep past the deadline
|
|
55
|
+
const remaining = deadline - Date.now();
|
|
56
|
+
if (remaining <= 0) {
|
|
57
|
+
throw new Error(`${operationName} timed out after ${retryTimeoutMs}ms: ${extractErrorMessage(e)}`);
|
|
58
|
+
}
|
|
59
|
+
await sleepAsync(Math.min(delay, remaining));
|
|
60
|
+
// Check for cancellation after sleeping
|
|
61
|
+
if (signal?.aborted) {
|
|
62
|
+
throw new Error(`${operationName} aborted`);
|
|
63
|
+
}
|
|
64
|
+
attempt++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAe/D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC3B,OAAsC,EACtC,EAAiD;IAEjD,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC;IACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc,CAAC;IAC7C,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,OAAO,IAAI,EAAE,CAAC;QACV,6CAA6C;QAC7C,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,GAAG,aAAa,UAAU,CAAC,CAAC;QAChD,CAAC;QAED,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;QACvC,IAAI,CAAC;YACD,OAAO,MAAM,EAAE,CAAC,UAAU,CAAC,CAAC;QAChC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,mEAAmE;YACnE,IAAI,aAAa,CAAC,CAAC,CAAC,KAAK,UAAU;gBAAE,MAAM,CAAC,CAAC;YAE7C,yEAAyE;YACzE,UAAU,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;YAEnC,6CAA6C;YAC7C,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;gBACzB,MAAM,IAAI,KAAK,CAAC,GAAG,aAAa,oBAAoB,cAAc,OAAO,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACvG,CAAC;YAED,iEAAiE;YACjE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;YAC1D,MAAM,MAAM,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC;YAE5B,gCAAgC;YAChC,MAAM,SAAS,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACxC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,GAAG,aAAa,oBAAoB,cAAc,OAAO,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACvG,CAAC;YAED,MAAM,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;YAE7C,wCAAwC;YACxC,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBAClB,MAAM,IAAI,KAAK,CAAC,GAAG,aAAa,UAAU,CAAC,CAAC;YAChD,CAAC;YAED,OAAO,EAAE,CAAC;QACd,CAAC;IACL,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parallel text-to-speech synthesis with audio concatenation.
|
|
3
|
+
*
|
|
4
|
+
* Each text chunk is synthesized independently (with retry and credential
|
|
5
|
+
* rotation), written to a temporary file to keep memory usage low, and then
|
|
6
|
+
* concatenated into a single audio buffer via ffmpeg (or `Buffer.concat` as
|
|
7
|
+
* a fallback). The result includes per-chunk metadata with timing offsets.
|
|
8
|
+
*/
|
|
9
|
+
import type { SynthesizeParallelParams, SynthesizeParallelResult } from "./types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Synthesizes multiple text chunks into a single audio buffer in parallel.
|
|
12
|
+
*
|
|
13
|
+
* Chunks are processed concurrently (optionally limited by `maxConcurrency`),
|
|
14
|
+
* each with automatic retry and credential rotation. The resulting audio
|
|
15
|
+
* segments are concatenated via ffmpeg into one continuous file.
|
|
16
|
+
*
|
|
17
|
+
* @param params - Configuration including provider, credentials, text chunks, and options.
|
|
18
|
+
* @returns Combined audio buffer, format string, and per-chunk metadata with timing offsets.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const result = await synthesizeParallel({
|
|
23
|
+
* provider: "openai",
|
|
24
|
+
* credentials: [{ apiKey: "sk-..." }],
|
|
25
|
+
* chunks: [{ text: "Hello" }, { text: "World" }],
|
|
26
|
+
* ffmpegPath: "ffmpeg",
|
|
27
|
+
* });
|
|
28
|
+
* fs.writeFileSync("output.mp3", result.audio);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function synthesizeParallel(params: SynthesizeParallelParams): Promise<SynthesizeParallelResult>;
|
|
32
|
+
//# sourceMappingURL=synthesizeParallel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synthesizeParallel.d.ts","sourceRoot":"","sources":["../src/synthesizeParallel.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH,OAAO,KAAK,EACR,wBAAwB,EACxB,wBAAwB,EAI3B,MAAM,YAAY,CAAC;AAOpB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAwH5G"}
|