diff --git a/CHANGELOG.md b/CHANGELOG.md index 6106b84..b80dccd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [4.41.6] + +- Expose `language_code` in `SyncTranscriptResponse` — surfaces the ISO 639-1 language code detected or used by the model for synchronous transcription requests (e.g. `"en"`, `"es"`; `undefined` for responses from older servers) +- Expose `auth_time_ms` in `DictationResponse` — surfaces the authentication and token/API-key validation portion of `request_time_ms` for dictation requests (`undefined` when omitted by older servers) + ## [4.41.5] - Add `speakerLabelsRevisionIntervalMs` streaming param (sent as `speaker_labels_revision_interval_ms`) — the cadence, in ms of audio time, at which the server emits mid-stream `speakerRevision` events when `speakerLabels` is enabled. Unset or `0` sends only the end-of-stream revision; non-zero values are clamped server-side to 120 000–300 000 ms. The first mid-stream revision needs ~120 s of streamed speech, and one is sent only when some earlier turn's label changed diff --git a/CLAUDE.md b/CLAUDE.md index 7946799..58f3794 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -371,7 +371,8 @@ afterwards. Over-cap prompts throw before the request is sent. There is no `mode timestamps), `confidence`, `llm_response` (`string | null`, the rewrite when the LLM pass ran), `llm_error` (`string | null`, why it failed when it did), `audio_duration_ms`, `session_id` (record it to correlate with support), and optional `request_time_ms` / `sync_time_ms` -(transcription time excluding the LLM pass). **Show `final_text`** — derived by the SDK as +(transcription time excluding the LLM pass) / `auth_time_ms` (auth portion of +`request_time_ms`). **Show `final_text`** — derived by the SDK as `llm_response` when there is one, `text` otherwise, so it is safe to read either way. **Errors**: failures throw `DictationError` (a separate class from `SyncTranscriptError`) with diff --git a/package.json b/package.json index 8266741..944ab0a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "assemblyai", - "version": "4.41.5", + "version": "4.41.6", "description": "The AssemblyAI JavaScript SDK provides an easy-to-use interface for interacting with the AssemblyAI API, which supports async and real-time transcription.", "engines": { "node": ">=18" diff --git a/src/types/dictation/index.ts b/src/types/dictation/index.ts index ffe6ce2..8bc38c0 100644 --- a/src/types/dictation/index.ts +++ b/src/types/dictation/index.ts @@ -140,6 +140,11 @@ export type DictationResponse = { * The time in milliseconds spent transcribing, excluding the LLM pass. */ sync_time_ms?: number; + /** + * Authentication and key-validation time in milliseconds, as a portion of + * `request_time_ms`. `undefined` when the server predates the field. + */ + auth_time_ms?: number; /** * The text to show the user: `llm_response` when the LLM pass produced one, * `text` otherwise. Derived by the SDK from those two fields, so reading it diff --git a/src/types/sync/index.ts b/src/types/sync/index.ts index 4196494..72c99a1 100644 --- a/src/types/sync/index.ts +++ b/src/types/sync/index.ts @@ -176,6 +176,14 @@ export type SyncTranscriptResponse = { * request with support. */ session_id: string; + /** + * ISO 639-1 language code the model used for this transcript (e.g. `"en"`, + * `"es"`). Populated when the server detects or is told the language; + * `undefined` for responses from servers that predate the field. Useful + * when `language_codes` was not set and you need to know which language the + * model chose. + */ + language_code?: string; /** * The end-to-end server-side request time in milliseconds. `undefined` * when the server predates the field. diff --git a/tests/unit/dictation.test.ts b/tests/unit/dictation.test.ts index 4f79627..f0c75c8 100644 --- a/tests/unit/dictation.test.ts +++ b/tests/unit/dictation.test.ts @@ -535,6 +535,22 @@ describe("dictation response", () => { expect(result.sync_time_ms).toBe(180.2); }); + it("should parse auth_time_ms when present in the response", async () => { + mockOk({ ...okResponse, auth_time_ms: 24.6 }); + const result = await assembly.dictation.transcribeLive( + chunks(bytes("RIFF")), + ); + expect(result.auth_time_ms).toBe(24.6); + }); + + it("should leave auth_time_ms undefined when omitted by the server", async () => { + mockOk(); + const result = await assembly.dictation.transcribeLive( + chunks(bytes("RIFF")), + ); + expect(result.auth_time_ms).toBeUndefined(); + }); + it("should prefer the LLM rewrite for final_text", async () => { mockOk({ ...okResponse, llm_response: "Take 2 tablets daily." }); const result = await assembly.dictation.transcribeLive( diff --git a/tests/unit/sync.test.ts b/tests/unit/sync.test.ts index 491c331..2608d16 100644 --- a/tests/unit/sync.test.ts +++ b/tests/unit/sync.test.ts @@ -171,6 +171,22 @@ describe("sync", () => { expect(result.request_time_ms).toBeUndefined(); }); + it("should parse a response with language_code", async () => { + const response = { ...okResponse, language_code: "es" }; + fetchMock.doMockOnceIf( + requestMatches({ url: liveUrl, method: "POST" }), + JSON.stringify(response), + ); + const result = await assembly.sync.transcribe(fakeWavBytes); + expect(result.language_code).toBe("es"); + }); + + it("should parse a response without language_code", async () => { + mockOk(); + const result = await assembly.sync.transcribe(fakeWavBytes); + expect(result.language_code).toBeUndefined(); + }); + it("should stream a chunked multipart body with the config part first", async () => { mockOk(); await assembly.sync.transcribe(fakeWavBytes);