@soniox/node 2.0.2 → 2.1.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/dist/index.cjs +604 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +853 -7
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +853 -7
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +600 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1875,11 +1875,14 @@ var SonioxAuthAPI = class {
|
|
|
1875
1875
|
* const ttsKey = await client.auth.createTemporaryKey({
|
|
1876
1876
|
* usage_type: 'tts_rt',
|
|
1877
1877
|
* expires_in_seconds: 300,
|
|
1878
|
+
* single_use: true,
|
|
1879
|
+
* max_session_duration_seconds: 600,
|
|
1878
1880
|
* });
|
|
1879
1881
|
* ```
|
|
1880
1882
|
*/
|
|
1881
1883
|
async createTemporaryKey(request, signal) {
|
|
1882
1884
|
if (!Number.isFinite(request.expires_in_seconds) || request.expires_in_seconds < 1 || request.expires_in_seconds > 3600) throw new Error("expires_in_seconds must be a finite number between 1 and 3600");
|
|
1885
|
+
if (request.max_session_duration_seconds !== void 0 && (!Number.isFinite(request.max_session_duration_seconds) || request.max_session_duration_seconds < 1 || request.max_session_duration_seconds > 18e3)) throw new Error("max_session_duration_seconds must be a finite number between 1 and 18000");
|
|
1883
1886
|
return (await this.http.request({
|
|
1884
1887
|
method: "POST",
|
|
1885
1888
|
path: "/v1/auth/temporary-api-key",
|
|
@@ -1889,6 +1892,38 @@ var SonioxAuthAPI = class {
|
|
|
1889
1892
|
}
|
|
1890
1893
|
};
|
|
1891
1894
|
|
|
1895
|
+
//#endregion
|
|
1896
|
+
//#region src/async/concurrency-limits.ts
|
|
1897
|
+
var SonioxConcurrencyLimitsAPI = class {
|
|
1898
|
+
constructor(http) {
|
|
1899
|
+
this.http = http;
|
|
1900
|
+
}
|
|
1901
|
+
/**
|
|
1902
|
+
* Retrieves current concurrency counts and configured limits.
|
|
1903
|
+
*
|
|
1904
|
+
* Values are region-scoped according to the client's configured REST API
|
|
1905
|
+
* endpoint.
|
|
1906
|
+
*
|
|
1907
|
+
* @param signal - Optional AbortSignal for cancellation.
|
|
1908
|
+
* @returns Current counts and configured limits for project and organization scopes.
|
|
1909
|
+
* @throws {@link SonioxHttpError} On API errors.
|
|
1910
|
+
*
|
|
1911
|
+
* @example
|
|
1912
|
+
* ```typescript
|
|
1913
|
+
* const limits = await client.concurrencyLimits.get();
|
|
1914
|
+
* console.log(limits.project.current.transcribe_concurrent);
|
|
1915
|
+
* console.log(limits.project.limits.transcribe_concurrent);
|
|
1916
|
+
* ```
|
|
1917
|
+
*/
|
|
1918
|
+
async get(signal) {
|
|
1919
|
+
return (await this.http.request({
|
|
1920
|
+
method: "GET",
|
|
1921
|
+
path: "/v1/concurrency-limits",
|
|
1922
|
+
...signal && { signal }
|
|
1923
|
+
})).data;
|
|
1924
|
+
}
|
|
1925
|
+
};
|
|
1926
|
+
|
|
1892
1927
|
//#endregion
|
|
1893
1928
|
//#region src/async/files.ts
|
|
1894
1929
|
/**
|
|
@@ -2230,6 +2265,27 @@ var SonioxFilesAPI = class {
|
|
|
2230
2265
|
})).data, this.http, limit, signal);
|
|
2231
2266
|
}
|
|
2232
2267
|
/**
|
|
2268
|
+
* Returns the total number of files, split by source.
|
|
2269
|
+
*
|
|
2270
|
+
* @param options - Optional cancellation parameters.
|
|
2271
|
+
* @returns Total file counts for Playground, Public API, and all sources.
|
|
2272
|
+
* @throws {@link SonioxHttpError} On API errors.
|
|
2273
|
+
*
|
|
2274
|
+
* @example
|
|
2275
|
+
* ```typescript
|
|
2276
|
+
* const counts = await client.files.count();
|
|
2277
|
+
* console.log(counts.total);
|
|
2278
|
+
* ```
|
|
2279
|
+
*/
|
|
2280
|
+
async count(options = {}) {
|
|
2281
|
+
const { signal } = options;
|
|
2282
|
+
return (await this.http.request({
|
|
2283
|
+
method: "GET",
|
|
2284
|
+
path: "/v1/files/count",
|
|
2285
|
+
...signal && { signal }
|
|
2286
|
+
})).data;
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2233
2289
|
* Retrieve metadata for an uploaded file.
|
|
2234
2290
|
*
|
|
2235
2291
|
* @param file - The UUID of the file or a SonioxFile instance
|
|
@@ -2344,6 +2400,186 @@ var SonioxModelsAPI = class {
|
|
|
2344
2400
|
}
|
|
2345
2401
|
};
|
|
2346
2402
|
|
|
2403
|
+
//#endregion
|
|
2404
|
+
//#region src/async/translation.ts
|
|
2405
|
+
/**
|
|
2406
|
+
* Reshape a transcript produced by a translation-enabled transcription into a
|
|
2407
|
+
* structured {@link SonioxTranslation} result.
|
|
2408
|
+
*
|
|
2409
|
+
* This is the same logic `SonioxTranslationJob.getTranslation()` applies.
|
|
2410
|
+
* Use it directly in webhook handlers or anywhere else you already have a
|
|
2411
|
+
* transcript in hand.
|
|
2412
|
+
*
|
|
2413
|
+
* @param transcript - Transcript (or any object with a `tokens` array) emitted
|
|
2414
|
+
* for a translation-enabled transcription.
|
|
2415
|
+
* @param mode - Whether to reshape as one-way or two-way; the discriminator
|
|
2416
|
+
* tells the helper which result shape to produce.
|
|
2417
|
+
* @returns A {@link SonioxTranslation} keyed on `mode`.
|
|
2418
|
+
*
|
|
2419
|
+
* @example
|
|
2420
|
+
* ```typescript
|
|
2421
|
+
* import { translateFromTranscript } from '@soniox/node';
|
|
2422
|
+
*
|
|
2423
|
+
* // From a webhook handler that just received the transcript
|
|
2424
|
+
* const result = translateFromTranscript(transcript, { type: 'one_way', to: 'es' });
|
|
2425
|
+
* console.log(result.translation_text);
|
|
2426
|
+
* ```
|
|
2427
|
+
*/
|
|
2428
|
+
function translateFromTranscript(transcript, mode) {
|
|
2429
|
+
const segments = reshapeTokens(transcript.tokens);
|
|
2430
|
+
const duration_ms = computeDurationMs(transcript.tokens);
|
|
2431
|
+
if (mode.type === "two_way") return {
|
|
2432
|
+
mode: "two_way",
|
|
2433
|
+
language_a: mode.language_a,
|
|
2434
|
+
language_b: mode.language_b,
|
|
2435
|
+
duration_ms,
|
|
2436
|
+
segments
|
|
2437
|
+
};
|
|
2438
|
+
let original_text = "";
|
|
2439
|
+
let translation_text = "";
|
|
2440
|
+
for (const segment of segments) {
|
|
2441
|
+
for (const token of segment.original_tokens) original_text += token.text;
|
|
2442
|
+
for (const token of segment.translation_tokens ?? []) translation_text += token.text;
|
|
2443
|
+
}
|
|
2444
|
+
return {
|
|
2445
|
+
mode: "one_way",
|
|
2446
|
+
...mode.from !== void 0 && { from: mode.from },
|
|
2447
|
+
to: mode.to,
|
|
2448
|
+
duration_ms,
|
|
2449
|
+
segments,
|
|
2450
|
+
original_text,
|
|
2451
|
+
translation_text
|
|
2452
|
+
};
|
|
2453
|
+
}
|
|
2454
|
+
/**
|
|
2455
|
+
* Reshape a flat token stream into per-utterance translation segments.
|
|
2456
|
+
*
|
|
2457
|
+
* Internal step; exported only via {@link translateFromTranscript}.
|
|
2458
|
+
*/
|
|
2459
|
+
function reshapeTokens(tokens) {
|
|
2460
|
+
if (tokens.length === 0) return [];
|
|
2461
|
+
return mergeChunks(splitIntoChunks(tokens));
|
|
2462
|
+
}
|
|
2463
|
+
/**
|
|
2464
|
+
* Walk tokens in order and start a new chunk whenever the speaker, language
|
|
2465
|
+
* (target lang for translations, source lang otherwise), or "is this a
|
|
2466
|
+
* translation token" flag changes.
|
|
2467
|
+
*/
|
|
2468
|
+
function splitIntoChunks(tokens) {
|
|
2469
|
+
const chunks = [];
|
|
2470
|
+
let current = null;
|
|
2471
|
+
for (const token of tokens) {
|
|
2472
|
+
const speaker = token.speaker ?? void 0;
|
|
2473
|
+
const language = token.language ?? void 0;
|
|
2474
|
+
const isTranslation = token.translation_status === "translation";
|
|
2475
|
+
if (current === null || current.speaker !== speaker || current.language !== language || current.isTranslation !== isTranslation) {
|
|
2476
|
+
current = {
|
|
2477
|
+
speaker,
|
|
2478
|
+
language,
|
|
2479
|
+
isTranslation,
|
|
2480
|
+
tokens: []
|
|
2481
|
+
};
|
|
2482
|
+
chunks.push(current);
|
|
2483
|
+
}
|
|
2484
|
+
current.tokens.push(token);
|
|
2485
|
+
}
|
|
2486
|
+
return chunks;
|
|
2487
|
+
}
|
|
2488
|
+
/**
|
|
2489
|
+
* Pair each `'original'` chunk with the immediately following translation
|
|
2490
|
+
* chunk for the same speaker, and emit a {@link TranslationSegment} for
|
|
2491
|
+
* every logical utterance.
|
|
2492
|
+
*
|
|
2493
|
+
* Soniox emits tokens in strict order: original chunk → translation chunk →
|
|
2494
|
+
* next original chunk → next translation chunk. So a chunk of
|
|
2495
|
+
* `translation_status: 'original'` tokens is always followed by its
|
|
2496
|
+
* translation when the same-speaker next chunk is a translation chunk.
|
|
2497
|
+
*
|
|
2498
|
+
* Chunks of `translation_status: 'none'` (third-language pass-through under
|
|
2499
|
+
* `between`) are never followed by a translation and stay standalone.
|
|
2500
|
+
*/
|
|
2501
|
+
function mergeChunks(chunks) {
|
|
2502
|
+
const segments = [];
|
|
2503
|
+
let i = 0;
|
|
2504
|
+
while (i < chunks.length) {
|
|
2505
|
+
const chunk = chunks[i];
|
|
2506
|
+
if (!chunk) {
|
|
2507
|
+
i += 1;
|
|
2508
|
+
continue;
|
|
2509
|
+
}
|
|
2510
|
+
if (chunk.isTranslation) {
|
|
2511
|
+
segments.push(buildSegmentFromChunks(void 0, chunk));
|
|
2512
|
+
i += 1;
|
|
2513
|
+
continue;
|
|
2514
|
+
}
|
|
2515
|
+
const firstStatus = chunk.tokens[0]?.translation_status;
|
|
2516
|
+
const next = chunks[i + 1];
|
|
2517
|
+
if (firstStatus === "original" && next && next.isTranslation && next.speaker === chunk.speaker) {
|
|
2518
|
+
segments.push(buildSegmentFromChunks(chunk, next));
|
|
2519
|
+
i += 2;
|
|
2520
|
+
continue;
|
|
2521
|
+
}
|
|
2522
|
+
segments.push(buildSegmentFromChunks(chunk, void 0));
|
|
2523
|
+
i += 1;
|
|
2524
|
+
}
|
|
2525
|
+
return segments;
|
|
2526
|
+
}
|
|
2527
|
+
/**
|
|
2528
|
+
* Build a {@link TranslationSegment} from at most one original chunk and at
|
|
2529
|
+
* most one translation chunk. At least one must be provided.
|
|
2530
|
+
*/
|
|
2531
|
+
function buildSegmentFromChunks(original, translation) {
|
|
2532
|
+
const original_tokens = original?.tokens ?? [];
|
|
2533
|
+
const translation_tokens = translation?.tokens ?? [];
|
|
2534
|
+
const speaker = original?.speaker ?? translation?.speaker;
|
|
2535
|
+
const from = resolveFrom(original_tokens, translation_tokens);
|
|
2536
|
+
const to = translation_tokens[0]?.language ?? void 0;
|
|
2537
|
+
const firstOriginal = original_tokens[0];
|
|
2538
|
+
const lastOriginal = original_tokens[original_tokens.length - 1];
|
|
2539
|
+
const original_text = original_tokens.reduce((acc, t) => acc + t.text, "");
|
|
2540
|
+
const translation_text = translation_tokens.reduce((acc, t) => acc + t.text, "");
|
|
2541
|
+
const segment = {
|
|
2542
|
+
from,
|
|
2543
|
+
original_text,
|
|
2544
|
+
original_tokens
|
|
2545
|
+
};
|
|
2546
|
+
if (firstOriginal?.start_ms !== void 0) segment.start_ms = firstOriginal.start_ms;
|
|
2547
|
+
if (lastOriginal?.end_ms !== void 0) segment.end_ms = lastOriginal.end_ms;
|
|
2548
|
+
if (speaker !== void 0) segment.speaker = speaker;
|
|
2549
|
+
if (translation_tokens.length > 0) {
|
|
2550
|
+
if (to !== void 0) segment.to = to;
|
|
2551
|
+
segment.translation_text = translation_text;
|
|
2552
|
+
segment.translation_tokens = translation_tokens;
|
|
2553
|
+
}
|
|
2554
|
+
return segment;
|
|
2555
|
+
}
|
|
2556
|
+
/**
|
|
2557
|
+
* Resolve a segment's `from` (source language) field.
|
|
2558
|
+
*
|
|
2559
|
+
* Original tokens carry the source language in `language`; translation
|
|
2560
|
+
* tokens carry it in `source_language`. When neither is available we fall
|
|
2561
|
+
* back to an empty string so the field stays a `string`.
|
|
2562
|
+
*/
|
|
2563
|
+
function resolveFrom(originals, translations) {
|
|
2564
|
+
const fromOriginal = originals[0]?.language;
|
|
2565
|
+
if (fromOriginal !== void 0 && fromOriginal !== null) return fromOriginal;
|
|
2566
|
+
const fromTranslation = translations[0]?.source_language;
|
|
2567
|
+
if (fromTranslation !== void 0 && fromTranslation !== null) return fromTranslation;
|
|
2568
|
+
return "";
|
|
2569
|
+
}
|
|
2570
|
+
/**
|
|
2571
|
+
* Total audio duration in ms. Translation tokens carry no timestamps, so we
|
|
2572
|
+
* scan only original tokens (i.e. not `translation_status: 'translation'`).
|
|
2573
|
+
*/
|
|
2574
|
+
function computeDurationMs(tokens) {
|
|
2575
|
+
let max = 0;
|
|
2576
|
+
for (const token of tokens) {
|
|
2577
|
+
if (token.translation_status === "translation") continue;
|
|
2578
|
+
if (token.end_ms !== void 0 && token.end_ms > max) max = token.end_ms;
|
|
2579
|
+
}
|
|
2580
|
+
return max;
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2347
2583
|
//#endregion
|
|
2348
2584
|
//#region src/async/stt.ts
|
|
2349
2585
|
/**
|
|
@@ -2464,16 +2700,25 @@ function segmentTranscript(tokens, options = {}) {
|
|
|
2464
2700
|
return segmentTokens(tokens, options, buildSegment);
|
|
2465
2701
|
}
|
|
2466
2702
|
/**
|
|
2467
|
-
* Helper to build a segment from a list of tokens
|
|
2703
|
+
* Helper to build a segment from a list of tokens.
|
|
2704
|
+
*
|
|
2705
|
+
* `start_ms` / `end_ms` are populated from the first/last tokens that carry
|
|
2706
|
+
* timestamps. Translation tokens have no timing, so a translation-only
|
|
2707
|
+
* segment ends up without timestamps.
|
|
2468
2708
|
*/
|
|
2469
2709
|
function buildSegment(tokens, speaker, language) {
|
|
2470
|
-
|
|
2471
|
-
const
|
|
2472
|
-
|
|
2710
|
+
if (tokens.length === 0) throw new Error("Cannot build segment from an empty token array");
|
|
2711
|
+
const text = tokens.map((t) => t.text).join("");
|
|
2712
|
+
let start_ms;
|
|
2713
|
+
let end_ms;
|
|
2714
|
+
for (const token of tokens) {
|
|
2715
|
+
if (start_ms === void 0 && token.start_ms !== void 0) start_ms = token.start_ms;
|
|
2716
|
+
if (token.end_ms !== void 0) end_ms = token.end_ms;
|
|
2717
|
+
}
|
|
2473
2718
|
return {
|
|
2474
|
-
text
|
|
2475
|
-
start_ms
|
|
2476
|
-
end_ms
|
|
2719
|
+
text,
|
|
2720
|
+
...start_ms !== void 0 && { start_ms },
|
|
2721
|
+
...end_ms !== void 0 && { end_ms },
|
|
2477
2722
|
...speaker != null && { speaker },
|
|
2478
2723
|
...language != null && { language },
|
|
2479
2724
|
tokens
|
|
@@ -2827,6 +3072,66 @@ var SonioxTranscription = class SonioxTranscription {
|
|
|
2827
3072
|
}
|
|
2828
3073
|
};
|
|
2829
3074
|
/**
|
|
3075
|
+
* A translation job.
|
|
3076
|
+
*
|
|
3077
|
+
* Mirrors {@link SonioxTranscription}, with an additional translation mode and
|
|
3078
|
+
* helpers that reshape the completed transcript into a {@link SonioxTranslation}.
|
|
3079
|
+
*/
|
|
3080
|
+
var SonioxTranslationJob = class SonioxTranslationJob extends SonioxTranscription {
|
|
3081
|
+
/**
|
|
3082
|
+
* Pre-fetched translation result. Only available when using `translate()`
|
|
3083
|
+
* with `wait: true`, `fetch_translation !== false`, and the job completed
|
|
3084
|
+
* successfully.
|
|
3085
|
+
*/
|
|
3086
|
+
translation;
|
|
3087
|
+
constructor(data, http, _mode, transcript, translation) {
|
|
3088
|
+
super(data, http, transcript);
|
|
3089
|
+
this._mode = _mode;
|
|
3090
|
+
this.translation = translation;
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Retrieves and reshapes the completed transcript into a structured
|
|
3094
|
+
* translation result.
|
|
3095
|
+
*
|
|
3096
|
+
* Returns cached translation if available. Use `force: true` to bypass the
|
|
3097
|
+
* cached translation and fetch a fresh transcript from the API.
|
|
3098
|
+
*/
|
|
3099
|
+
async getTranslation(options) {
|
|
3100
|
+
const { force, signal } = options ?? {};
|
|
3101
|
+
if (!force && this.translation !== void 0) return this.translation;
|
|
3102
|
+
const transcript = await this.getTranscript({
|
|
3103
|
+
...force !== void 0 && { force },
|
|
3104
|
+
...signal !== void 0 && { signal }
|
|
3105
|
+
});
|
|
3106
|
+
if (!transcript) return null;
|
|
3107
|
+
return translateFromTranscript(transcript, this._mode);
|
|
3108
|
+
}
|
|
3109
|
+
/**
|
|
3110
|
+
* Alias for {@link SonioxTranslationJob.getTranslation}.
|
|
3111
|
+
*/
|
|
3112
|
+
async fetchTranslation(options) {
|
|
3113
|
+
return this.getTranslation(options);
|
|
3114
|
+
}
|
|
3115
|
+
/**
|
|
3116
|
+
* Re-fetches this translation job to get the latest status.
|
|
3117
|
+
*/
|
|
3118
|
+
async refresh(signal) {
|
|
3119
|
+
return new SonioxTranslationJob((await this._http.request({
|
|
3120
|
+
method: "GET",
|
|
3121
|
+
path: `/v1/transcriptions/${this.id}`,
|
|
3122
|
+
...signal && { signal }
|
|
3123
|
+
})).data, this._http, this._mode);
|
|
3124
|
+
}
|
|
3125
|
+
/**
|
|
3126
|
+
* Waits for the translation job to complete or fail.
|
|
3127
|
+
*/
|
|
3128
|
+
async wait(options = {}) {
|
|
3129
|
+
const completed = await super.wait(options);
|
|
3130
|
+
if (completed instanceof SonioxTranslationJob) return completed;
|
|
3131
|
+
return new SonioxTranslationJob(completed.toJSON(), this._http, this._mode);
|
|
3132
|
+
}
|
|
3133
|
+
};
|
|
3134
|
+
/**
|
|
2830
3135
|
* Result set for transcription listing.
|
|
2831
3136
|
*/
|
|
2832
3137
|
var TranscriptionListResult = class {
|
|
@@ -2967,6 +3272,26 @@ var SonioxSttApi = class {
|
|
|
2967
3272
|
})).data, this.http, options, signal);
|
|
2968
3273
|
}
|
|
2969
3274
|
/**
|
|
3275
|
+
* Returns the total number of transcriptions, split by request scope.
|
|
3276
|
+
*
|
|
3277
|
+
* @param signal - Optional AbortSignal for request cancellation.
|
|
3278
|
+
* @returns Total transcription counts for Playground, Public API, and all scopes.
|
|
3279
|
+
* @throws {@link SonioxHttpError} On API errors.
|
|
3280
|
+
*
|
|
3281
|
+
* @example
|
|
3282
|
+
* ```typescript
|
|
3283
|
+
* const counts = await client.stt.count();
|
|
3284
|
+
* console.log(counts.total);
|
|
3285
|
+
* ```
|
|
3286
|
+
*/
|
|
3287
|
+
async count(signal) {
|
|
3288
|
+
return (await this.http.request({
|
|
3289
|
+
method: "GET",
|
|
3290
|
+
path: "/v1/transcriptions/count",
|
|
3291
|
+
...signal && { signal }
|
|
3292
|
+
})).data;
|
|
3293
|
+
}
|
|
3294
|
+
/**
|
|
2970
3295
|
* Retrieves a transcription by ID
|
|
2971
3296
|
*
|
|
2972
3297
|
* @param id - The UUID of the transcription or a SonioxTranscription instance.
|
|
@@ -3304,6 +3629,78 @@ var SonioxSttApi = class {
|
|
|
3304
3629
|
}
|
|
3305
3630
|
}
|
|
3306
3631
|
/**
|
|
3632
|
+
* Starts a translation job.
|
|
3633
|
+
*
|
|
3634
|
+
* Sugar around {@link SonioxSttApi.transcribe}: configures translation from
|
|
3635
|
+
* the supplied {@link TranslateMode} (`{ to }`, `{ to, from }`, or
|
|
3636
|
+
* `{ between }`) and returns a {@link SonioxTranslationJob}. With
|
|
3637
|
+
* `wait: true`, waits for completion before returning, mirroring
|
|
3638
|
+
* {@link SonioxSttApi.transcribe}.
|
|
3639
|
+
*
|
|
3640
|
+
* Method-owned fields (cannot be set by the caller):
|
|
3641
|
+
* - `translation` — derived from `to` / `between`.
|
|
3642
|
+
* - `language_hints` + `language_hints_strict: true` — derived from
|
|
3643
|
+
* `from` / `between`.
|
|
3644
|
+
* - `enable_language_identification: true` — always. The reshape step
|
|
3645
|
+
* relies on every token carrying a `language` field to group originals
|
|
3646
|
+
* with their translations.
|
|
3647
|
+
* - `fetch_transcript` — derived from `fetch_translation` when `wait=true`.
|
|
3648
|
+
*
|
|
3649
|
+
* @param options - Mode, audio source, and pass-through options.
|
|
3650
|
+
* @returns The translation job.
|
|
3651
|
+
* @throws {@link SonioxHttpError} On API errors.
|
|
3652
|
+
* @throws `Error` On validation errors or wait timeout.
|
|
3653
|
+
*
|
|
3654
|
+
* @example
|
|
3655
|
+
* ```typescript
|
|
3656
|
+
* // One-way: detect any source language, translate to Spanish.
|
|
3657
|
+
* const job = await client.stt.translate({
|
|
3658
|
+
* audio_url: 'https://soniox.com/media/examples/coffee_shop.mp3',
|
|
3659
|
+
* to: 'es',
|
|
3660
|
+
* });
|
|
3661
|
+
*
|
|
3662
|
+
* const completed = await job.wait();
|
|
3663
|
+
* const result = await completed.getTranslation();
|
|
3664
|
+
* console.log(result?.translation_text);
|
|
3665
|
+
*
|
|
3666
|
+
* // One-way with explicit source language.
|
|
3667
|
+
* const explicitJob = await client.stt.translate({
|
|
3668
|
+
* file: buffer,
|
|
3669
|
+
* filename: 'meeting.mp3',
|
|
3670
|
+
* from: 'en',
|
|
3671
|
+
* to: 'es',
|
|
3672
|
+
* wait: true,
|
|
3673
|
+
* });
|
|
3674
|
+
* console.log(explicitJob.translation?.translation_text);
|
|
3675
|
+
*
|
|
3676
|
+
* // Two-way: bidirectional translation between two languages.
|
|
3677
|
+
* const twoWayJob = await client.stt.translate({
|
|
3678
|
+
* audio_url: 'https://soniox.com/media/examples/coffee_shop.mp3',
|
|
3679
|
+
* between: ['en', 'es'],
|
|
3680
|
+
* enable_speaker_diarization: true,
|
|
3681
|
+
* wait: true,
|
|
3682
|
+
* });
|
|
3683
|
+
* for (const seg of twoWayJob.translation?.segments ?? []) {
|
|
3684
|
+
* console.log(`[${seg.from}] ${seg.original_text}`);
|
|
3685
|
+
* if (seg.translation_text) {
|
|
3686
|
+
* console.log(` → [${seg.to}] ${seg.translation_text}`);
|
|
3687
|
+
* }
|
|
3688
|
+
* }
|
|
3689
|
+
* ```
|
|
3690
|
+
*/
|
|
3691
|
+
async translate(options) {
|
|
3692
|
+
const { translation, language_hints, mode } = resolveTranslateMode(options);
|
|
3693
|
+
const transcribeOptions = buildTranscribeOptionsFromTranslate(options, translation, language_hints);
|
|
3694
|
+
const transcription = await this.transcribe(transcribeOptions);
|
|
3695
|
+
const job = new SonioxTranslationJob(transcription.toJSON(), this.http, mode, transcription.transcript);
|
|
3696
|
+
if (!options.wait) return job;
|
|
3697
|
+
if (job.status === "completed" && options.fetch_translation !== false) {
|
|
3698
|
+
const translationResult = await job.getTranslation();
|
|
3699
|
+
return new SonioxTranslationJob(job.toJSON(), this.http, mode, job.transcript, translationResult);
|
|
3700
|
+
}
|
|
3701
|
+
return new SonioxTranslationJob(job.toJSON(), this.http, mode, job.transcript, job.status === "error" ? null : void 0);
|
|
3702
|
+
}
|
|
3703
|
+
/**
|
|
3307
3704
|
* Permanently deletes all transcriptions.
|
|
3308
3705
|
* Iterates through all pages of transcriptions and deletes each one.
|
|
3309
3706
|
*
|
|
@@ -3360,6 +3757,93 @@ var SonioxSttApi = class {
|
|
|
3360
3757
|
}
|
|
3361
3758
|
}
|
|
3362
3759
|
};
|
|
3760
|
+
const DEFAULT_TRANSLATE_MODEL = "stt-async-v4";
|
|
3761
|
+
/**
|
|
3762
|
+
* Resolve the caller-supplied {@link TranslateMode} into the underlying
|
|
3763
|
+
* `translation` config plus optional `language_hints`, and the reshape mode
|
|
3764
|
+
* descriptor passed to {@link translateFromTranscript}.
|
|
3765
|
+
*
|
|
3766
|
+
* The discriminated input type already enforces "exactly one of `to` /
|
|
3767
|
+
* `between`" at compile time. The runtime checks here are a defensive
|
|
3768
|
+
* backup for callers that bypass typings.
|
|
3769
|
+
*/
|
|
3770
|
+
function resolveTranslateMode(options) {
|
|
3771
|
+
const between = options.between;
|
|
3772
|
+
const to = options.to;
|
|
3773
|
+
const from = options.from;
|
|
3774
|
+
if (between !== void 0) {
|
|
3775
|
+
if (to !== void 0) throw new Error("translate: cannot specify both \"to\" and \"between\"");
|
|
3776
|
+
if (from !== void 0) throw new Error("translate: cannot specify \"from\" together with \"between\"");
|
|
3777
|
+
if (!Array.isArray(between) || between.length !== 2) throw new Error("translate: \"between\" must be a [language_a, language_b] tuple");
|
|
3778
|
+
const [language_a, language_b] = between;
|
|
3779
|
+
if (!language_a || !language_b) throw new Error("translate: \"between\" languages must be non-empty strings");
|
|
3780
|
+
return {
|
|
3781
|
+
translation: {
|
|
3782
|
+
type: "two_way",
|
|
3783
|
+
language_a,
|
|
3784
|
+
language_b
|
|
3785
|
+
},
|
|
3786
|
+
language_hints: [language_a, language_b],
|
|
3787
|
+
mode: {
|
|
3788
|
+
type: "two_way",
|
|
3789
|
+
language_a,
|
|
3790
|
+
language_b
|
|
3791
|
+
}
|
|
3792
|
+
};
|
|
3793
|
+
}
|
|
3794
|
+
if (to !== void 0) {
|
|
3795
|
+
if (!to) throw new Error("translate: \"to\" must be a non-empty string");
|
|
3796
|
+
if (from !== void 0 && !from) throw new Error("translate: \"from\" must be a non-empty string when provided");
|
|
3797
|
+
return {
|
|
3798
|
+
translation: {
|
|
3799
|
+
type: "one_way",
|
|
3800
|
+
target_language: to
|
|
3801
|
+
},
|
|
3802
|
+
language_hints: from !== void 0 ? [from] : void 0,
|
|
3803
|
+
mode: from !== void 0 ? {
|
|
3804
|
+
type: "one_way",
|
|
3805
|
+
to,
|
|
3806
|
+
from
|
|
3807
|
+
} : {
|
|
3808
|
+
type: "one_way",
|
|
3809
|
+
to
|
|
3810
|
+
}
|
|
3811
|
+
};
|
|
3812
|
+
}
|
|
3813
|
+
throw new Error("translate: requires either \"to\" or \"between\"");
|
|
3814
|
+
}
|
|
3815
|
+
/**
|
|
3816
|
+
* Build the {@link TranscribeOptions} payload for `translate()` by stripping
|
|
3817
|
+
* the mode-only fields (`to` / `from` / `between`), applying the model
|
|
3818
|
+
* default, and pinning method-owned fields (`translation`, `language_hints`,
|
|
3819
|
+
* `language_hints_strict`, `enable_language_identification`).
|
|
3820
|
+
*
|
|
3821
|
+
* `enable_language_identification` is forced to `true` because the reshape
|
|
3822
|
+
* step depends on language-tagged transcript tokens.
|
|
3823
|
+
*/
|
|
3824
|
+
function buildTranscribeOptionsFromTranslate(options, translation, language_hints) {
|
|
3825
|
+
const rest = { ...options };
|
|
3826
|
+
delete rest.to;
|
|
3827
|
+
delete rest.from;
|
|
3828
|
+
delete rest.between;
|
|
3829
|
+
delete rest.model;
|
|
3830
|
+
delete rest.fetch_translation;
|
|
3831
|
+
delete rest.fetch_transcript;
|
|
3832
|
+
delete rest.language_hints;
|
|
3833
|
+
delete rest.language_hints_strict;
|
|
3834
|
+
const base = {
|
|
3835
|
+
...rest,
|
|
3836
|
+
model: options.model ?? DEFAULT_TRANSLATE_MODEL,
|
|
3837
|
+
translation,
|
|
3838
|
+
enable_language_identification: true,
|
|
3839
|
+
fetch_transcript: options.wait && options.fetch_translation !== false ? true : false
|
|
3840
|
+
};
|
|
3841
|
+
if (language_hints !== void 0) {
|
|
3842
|
+
base.language_hints = language_hints;
|
|
3843
|
+
base.language_hints_strict = true;
|
|
3844
|
+
}
|
|
3845
|
+
return base;
|
|
3846
|
+
}
|
|
3363
3847
|
|
|
3364
3848
|
//#endregion
|
|
3365
3849
|
//#region src/async/tts.ts
|
|
@@ -3441,6 +3925,110 @@ var SonioxTtsApi = class extends TtsRestClient {
|
|
|
3441
3925
|
}
|
|
3442
3926
|
};
|
|
3443
3927
|
|
|
3928
|
+
//#endregion
|
|
3929
|
+
//#region src/async/usage-logs.ts
|
|
3930
|
+
/**
|
|
3931
|
+
* Result set for usage log listing.
|
|
3932
|
+
*/
|
|
3933
|
+
var UsageLogListResult = class {
|
|
3934
|
+
/**
|
|
3935
|
+
* Usage logs from the first page of results.
|
|
3936
|
+
*/
|
|
3937
|
+
usage_logs;
|
|
3938
|
+
/**
|
|
3939
|
+
* Pagination cursor for the next page. Null if no more pages.
|
|
3940
|
+
*/
|
|
3941
|
+
next_page_cursor;
|
|
3942
|
+
constructor(initialResponse, _http, _options) {
|
|
3943
|
+
this._http = _http;
|
|
3944
|
+
this._options = _options;
|
|
3945
|
+
this.usage_logs = initialResponse.usage_logs;
|
|
3946
|
+
this.next_page_cursor = initialResponse.next_page_cursor;
|
|
3947
|
+
}
|
|
3948
|
+
/**
|
|
3949
|
+
* Returns the raw data for this list result.
|
|
3950
|
+
*/
|
|
3951
|
+
toJSON() {
|
|
3952
|
+
return {
|
|
3953
|
+
usage_logs: this.usage_logs,
|
|
3954
|
+
next_page_cursor: this.next_page_cursor
|
|
3955
|
+
};
|
|
3956
|
+
}
|
|
3957
|
+
/**
|
|
3958
|
+
* Returns true if there are more pages of results beyond the first page.
|
|
3959
|
+
*/
|
|
3960
|
+
isPaged() {
|
|
3961
|
+
return this.next_page_cursor !== null;
|
|
3962
|
+
}
|
|
3963
|
+
/**
|
|
3964
|
+
* Async iterator that automatically fetches all pages.
|
|
3965
|
+
* Use with `for await...of` to iterate through all usage logs.
|
|
3966
|
+
*/
|
|
3967
|
+
async *[Symbol.asyncIterator]() {
|
|
3968
|
+
for (const usageLog of this.usage_logs) yield usageLog;
|
|
3969
|
+
let cursor = this.next_page_cursor;
|
|
3970
|
+
while (cursor !== null) {
|
|
3971
|
+
const response = await this._http.request({
|
|
3972
|
+
method: "GET",
|
|
3973
|
+
path: "/v1/usage-logs",
|
|
3974
|
+
query: {
|
|
3975
|
+
start_time: this._options.start_time,
|
|
3976
|
+
end_time: this._options.end_time,
|
|
3977
|
+
limit: this._options.limit,
|
|
3978
|
+
sort: this._options.sort,
|
|
3979
|
+
cursor
|
|
3980
|
+
},
|
|
3981
|
+
...this._options.signal && { signal: this._options.signal }
|
|
3982
|
+
});
|
|
3983
|
+
for (const usageLog of response.data.usage_logs) yield usageLog;
|
|
3984
|
+
cursor = response.data.next_page_cursor;
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
};
|
|
3988
|
+
var SonioxUsageLogsAPI = class {
|
|
3989
|
+
constructor(http) {
|
|
3990
|
+
this.http = http;
|
|
3991
|
+
}
|
|
3992
|
+
/**
|
|
3993
|
+
* Retrieves per-request usage log entries for the project.
|
|
3994
|
+
*
|
|
3995
|
+
* The returned result is async iterable. Use `for await...of` to iterate
|
|
3996
|
+
* through all pages.
|
|
3997
|
+
*
|
|
3998
|
+
* @param options - Required time window plus optional pagination, sorting, and cancellation.
|
|
3999
|
+
* @returns UsageLogListResult with async iteration support.
|
|
4000
|
+
* @throws {@link SonioxHttpError} On API errors.
|
|
4001
|
+
*
|
|
4002
|
+
* @example
|
|
4003
|
+
* ```typescript
|
|
4004
|
+
* const result = await client.usageLogs.list({
|
|
4005
|
+
* start_time: '2026-04-28T09:00:00Z',
|
|
4006
|
+
* end_time: '2026-04-29T09:00:00Z',
|
|
4007
|
+
* sort: 'end_time_desc',
|
|
4008
|
+
* });
|
|
4009
|
+
*
|
|
4010
|
+
* for await (const log of result) {
|
|
4011
|
+
* console.log(log.model, log.cost_usd);
|
|
4012
|
+
* }
|
|
4013
|
+
* ```
|
|
4014
|
+
*/
|
|
4015
|
+
async list(options) {
|
|
4016
|
+
const { start_time, end_time, limit, sort, cursor, signal } = options;
|
|
4017
|
+
return new UsageLogListResult((await this.http.request({
|
|
4018
|
+
method: "GET",
|
|
4019
|
+
path: "/v1/usage-logs",
|
|
4020
|
+
query: {
|
|
4021
|
+
start_time,
|
|
4022
|
+
end_time,
|
|
4023
|
+
limit,
|
|
4024
|
+
sort,
|
|
4025
|
+
cursor
|
|
4026
|
+
},
|
|
4027
|
+
...signal && { signal }
|
|
4028
|
+
})).data, this.http, options);
|
|
4029
|
+
}
|
|
4030
|
+
};
|
|
4031
|
+
|
|
3444
4032
|
//#endregion
|
|
3445
4033
|
//#region src/async/webhooks.ts
|
|
3446
4034
|
const VALID_STATUSES = ["completed", "error"];
|
|
@@ -4373,6 +4961,8 @@ var SonioxNodeClient = class {
|
|
|
4373
4961
|
models;
|
|
4374
4962
|
webhooks;
|
|
4375
4963
|
auth;
|
|
4964
|
+
concurrencyLimits;
|
|
4965
|
+
usageLogs;
|
|
4376
4966
|
realtime;
|
|
4377
4967
|
constructor(options = {}) {
|
|
4378
4968
|
const apiKey = options.api_key ?? process.env["SONIOX_API_KEY"];
|
|
@@ -4397,6 +4987,8 @@ var SonioxNodeClient = class {
|
|
|
4397
4987
|
this.models = new SonioxModelsAPI(http);
|
|
4398
4988
|
this.webhooks = new SonioxWebhooksAPI(this.stt);
|
|
4399
4989
|
this.auth = new SonioxAuthAPI(http);
|
|
4990
|
+
this.concurrencyLimits = new SonioxConcurrencyLimitsAPI(http);
|
|
4991
|
+
this.usageLogs = new SonioxUsageLogsAPI(http);
|
|
4400
4992
|
this.tts = new SonioxTtsApi(apiKey, options.tts_api_url ?? process.env["SONIOX_TTS_API_URL"] ?? regionDefaults.tts_api_url, http);
|
|
4401
4993
|
this.realtime = new SonioxRealtimeApi({
|
|
4402
4994
|
api_key: apiKey,
|
|
@@ -4434,6 +5026,7 @@ exports.SONIOX_TMP_API_KEY_DURATION_MIN = SONIOX_TMP_API_KEY_DURATION_MIN;
|
|
|
4434
5026
|
exports.SONIOX_TMP_API_KEY_USAGE_TYPE = SONIOX_TMP_API_KEY_USAGE_TYPE;
|
|
4435
5027
|
exports.SONIOX_TTS_API_BASE_URL = SONIOX_TTS_API_BASE_URL;
|
|
4436
5028
|
exports.SONIOX_TTS_WS_URL = SONIOX_TTS_WS_URL;
|
|
5029
|
+
exports.SonioxConcurrencyLimitsAPI = SonioxConcurrencyLimitsAPI;
|
|
4437
5030
|
exports.SonioxError = SonioxError;
|
|
4438
5031
|
exports.SonioxFile = SonioxFile;
|
|
4439
5032
|
exports.SonioxHttpError = SonioxHttpError;
|
|
@@ -4441,9 +5034,12 @@ exports.SonioxNodeClient = SonioxNodeClient;
|
|
|
4441
5034
|
exports.SonioxRealtimeApi = SonioxRealtimeApi;
|
|
4442
5035
|
exports.SonioxTranscript = SonioxTranscript;
|
|
4443
5036
|
exports.SonioxTranscription = SonioxTranscription;
|
|
5037
|
+
exports.SonioxTranslationJob = SonioxTranslationJob;
|
|
4444
5038
|
exports.SonioxTtsApi = SonioxTtsApi;
|
|
5039
|
+
exports.SonioxUsageLogsAPI = SonioxUsageLogsAPI;
|
|
4445
5040
|
exports.StateError = StateError;
|
|
4446
5041
|
exports.TranscriptionListResult = TranscriptionListResult;
|
|
5042
|
+
exports.UsageLogListResult = UsageLogListResult;
|
|
4447
5043
|
exports.buildUrl = buildUrl;
|
|
4448
5044
|
exports.createAbortError = createAbortError;
|
|
4449
5045
|
exports.createHttpError = createHttpError;
|
|
@@ -4458,4 +5054,5 @@ exports.normalizeHeaders = normalizeHeaders;
|
|
|
4458
5054
|
exports.resolveConnectionConfig = resolveConnectionConfig;
|
|
4459
5055
|
exports.segmentRealtimeTokens = segmentRealtimeTokens;
|
|
4460
5056
|
exports.segmentTranscript = segmentTranscript;
|
|
5057
|
+
exports.translateFromTranscript = translateFromTranscript;
|
|
4461
5058
|
//# sourceMappingURL=index.cjs.map
|