@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 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
- const firstToken = tokens[0];
2474
- const lastToken = tokens[tokens.length - 1];
2475
- 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
+ }
2476
2718
  return {
2477
- text: tokens.map((t) => t.text).join(""),
2478
- start_ms: firstToken.start_ms,
2479
- end_ms: lastToken.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