@soniox/node 2.0.3 → 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 +601 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +840 -7
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +840 -7
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +597 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1892,6 +1892,38 @@ var SonioxAuthAPI = class {
|
|
|
1892
1892
|
}
|
|
1893
1893
|
};
|
|
1894
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
|
+
|
|
1895
1927
|
//#endregion
|
|
1896
1928
|
//#region src/async/files.ts
|
|
1897
1929
|
/**
|
|
@@ -2233,6 +2265,27 @@ var SonioxFilesAPI = class {
|
|
|
2233
2265
|
})).data, this.http, limit, signal);
|
|
2234
2266
|
}
|
|
2235
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
|
+
/**
|
|
2236
2289
|
* Retrieve metadata for an uploaded file.
|
|
2237
2290
|
*
|
|
2238
2291
|
* @param file - The UUID of the file or a SonioxFile instance
|
|
@@ -2347,6 +2400,186 @@ var SonioxModelsAPI = class {
|
|
|
2347
2400
|
}
|
|
2348
2401
|
};
|
|
2349
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
|
+
|
|
2350
2583
|
//#endregion
|
|
2351
2584
|
//#region src/async/stt.ts
|
|
2352
2585
|
/**
|
|
@@ -2467,16 +2700,25 @@ function segmentTranscript(tokens, options = {}) {
|
|
|
2467
2700
|
return segmentTokens(tokens, options, buildSegment);
|
|
2468
2701
|
}
|
|
2469
2702
|
/**
|
|
2470
|
-
* 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.
|
|
2471
2708
|
*/
|
|
2472
2709
|
function buildSegment(tokens, speaker, language) {
|
|
2473
|
-
|
|
2474
|
-
const
|
|
2475
|
-
|
|
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
|
+
}
|
|
2476
2718
|
return {
|
|
2477
|
-
text
|
|
2478
|
-
start_ms
|
|
2479
|
-
end_ms
|
|
2719
|
+
text,
|
|
2720
|
+
...start_ms !== void 0 && { start_ms },
|
|
2721
|
+
...end_ms !== void 0 && { end_ms },
|
|
2480
2722
|
...speaker != null && { speaker },
|
|
2481
2723
|
...language != null && { language },
|
|
2482
2724
|
tokens
|
|
@@ -2830,6 +3072,66 @@ var SonioxTranscription = class SonioxTranscription {
|
|
|
2830
3072
|
}
|
|
2831
3073
|
};
|
|
2832
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
|
+
/**
|
|
2833
3135
|
* Result set for transcription listing.
|
|
2834
3136
|
*/
|
|
2835
3137
|
var TranscriptionListResult = class {
|
|
@@ -2970,6 +3272,26 @@ var SonioxSttApi = class {
|
|
|
2970
3272
|
})).data, this.http, options, signal);
|
|
2971
3273
|
}
|
|
2972
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
|
+
/**
|
|
2973
3295
|
* Retrieves a transcription by ID
|
|
2974
3296
|
*
|
|
2975
3297
|
* @param id - The UUID of the transcription or a SonioxTranscription instance.
|
|
@@ -3307,6 +3629,78 @@ var SonioxSttApi = class {
|
|
|
3307
3629
|
}
|
|
3308
3630
|
}
|
|
3309
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
|
+
/**
|
|
3310
3704
|
* Permanently deletes all transcriptions.
|
|
3311
3705
|
* Iterates through all pages of transcriptions and deletes each one.
|
|
3312
3706
|
*
|
|
@@ -3363,6 +3757,93 @@ var SonioxSttApi = class {
|
|
|
3363
3757
|
}
|
|
3364
3758
|
}
|
|
3365
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
|
+
}
|
|
3366
3847
|
|
|
3367
3848
|
//#endregion
|
|
3368
3849
|
//#region src/async/tts.ts
|
|
@@ -3444,6 +3925,110 @@ var SonioxTtsApi = class extends TtsRestClient {
|
|
|
3444
3925
|
}
|
|
3445
3926
|
};
|
|
3446
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
|
+
|
|
3447
4032
|
//#endregion
|
|
3448
4033
|
//#region src/async/webhooks.ts
|
|
3449
4034
|
const VALID_STATUSES = ["completed", "error"];
|
|
@@ -4376,6 +4961,8 @@ var SonioxNodeClient = class {
|
|
|
4376
4961
|
models;
|
|
4377
4962
|
webhooks;
|
|
4378
4963
|
auth;
|
|
4964
|
+
concurrencyLimits;
|
|
4965
|
+
usageLogs;
|
|
4379
4966
|
realtime;
|
|
4380
4967
|
constructor(options = {}) {
|
|
4381
4968
|
const apiKey = options.api_key ?? process.env["SONIOX_API_KEY"];
|
|
@@ -4400,6 +4987,8 @@ var SonioxNodeClient = class {
|
|
|
4400
4987
|
this.models = new SonioxModelsAPI(http);
|
|
4401
4988
|
this.webhooks = new SonioxWebhooksAPI(this.stt);
|
|
4402
4989
|
this.auth = new SonioxAuthAPI(http);
|
|
4990
|
+
this.concurrencyLimits = new SonioxConcurrencyLimitsAPI(http);
|
|
4991
|
+
this.usageLogs = new SonioxUsageLogsAPI(http);
|
|
4403
4992
|
this.tts = new SonioxTtsApi(apiKey, options.tts_api_url ?? process.env["SONIOX_TTS_API_URL"] ?? regionDefaults.tts_api_url, http);
|
|
4404
4993
|
this.realtime = new SonioxRealtimeApi({
|
|
4405
4994
|
api_key: apiKey,
|
|
@@ -4437,6 +5026,7 @@ exports.SONIOX_TMP_API_KEY_DURATION_MIN = SONIOX_TMP_API_KEY_DURATION_MIN;
|
|
|
4437
5026
|
exports.SONIOX_TMP_API_KEY_USAGE_TYPE = SONIOX_TMP_API_KEY_USAGE_TYPE;
|
|
4438
5027
|
exports.SONIOX_TTS_API_BASE_URL = SONIOX_TTS_API_BASE_URL;
|
|
4439
5028
|
exports.SONIOX_TTS_WS_URL = SONIOX_TTS_WS_URL;
|
|
5029
|
+
exports.SonioxConcurrencyLimitsAPI = SonioxConcurrencyLimitsAPI;
|
|
4440
5030
|
exports.SonioxError = SonioxError;
|
|
4441
5031
|
exports.SonioxFile = SonioxFile;
|
|
4442
5032
|
exports.SonioxHttpError = SonioxHttpError;
|
|
@@ -4444,9 +5034,12 @@ exports.SonioxNodeClient = SonioxNodeClient;
|
|
|
4444
5034
|
exports.SonioxRealtimeApi = SonioxRealtimeApi;
|
|
4445
5035
|
exports.SonioxTranscript = SonioxTranscript;
|
|
4446
5036
|
exports.SonioxTranscription = SonioxTranscription;
|
|
5037
|
+
exports.SonioxTranslationJob = SonioxTranslationJob;
|
|
4447
5038
|
exports.SonioxTtsApi = SonioxTtsApi;
|
|
5039
|
+
exports.SonioxUsageLogsAPI = SonioxUsageLogsAPI;
|
|
4448
5040
|
exports.StateError = StateError;
|
|
4449
5041
|
exports.TranscriptionListResult = TranscriptionListResult;
|
|
5042
|
+
exports.UsageLogListResult = UsageLogListResult;
|
|
4450
5043
|
exports.buildUrl = buildUrl;
|
|
4451
5044
|
exports.createAbortError = createAbortError;
|
|
4452
5045
|
exports.createHttpError = createHttpError;
|
|
@@ -4461,4 +5054,5 @@ exports.normalizeHeaders = normalizeHeaders;
|
|
|
4461
5054
|
exports.resolveConnectionConfig = resolveConnectionConfig;
|
|
4462
5055
|
exports.segmentRealtimeTokens = segmentRealtimeTokens;
|
|
4463
5056
|
exports.segmentTranscript = segmentTranscript;
|
|
5057
|
+
exports.translateFromTranscript = translateFromTranscript;
|
|
4464
5058
|
//# sourceMappingURL=index.cjs.map
|