@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 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
- const firstToken = tokens[0];
2471
- const lastToken = tokens[tokens.length - 1];
2472
- if (!firstToken || !lastToken) throw new Error("Cannot build segment from an empty token array");
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: tokens.map((t) => t.text).join(""),
2475
- start_ms: firstToken.start_ms,
2476
- end_ms: lastToken.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