diff --git a/CHANGELOG.md b/CHANGELOG.md index f7794ac..9e21ef3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,12 @@ - Add streaming parameters to match the Python SDK: - `voiceFocus` and `voiceFocusThreshold` (replaces the unreleased `noiseSuppressionModel` / `noiseSuppressionThreshold`) - - `continuousPartials` - - `customerSupportAudioCapture` (logs a warning when enabled — records session audio for AssemblyAI support) + - `continuousPartials` (U3Pro speech models only) + - `customerSupportAudioCapture` — internal/unstable; sent on the wire as `_customer_support_audio_capture` to mirror the server's stability marker. Records session audio for AssemblyAI support; only enable when coordinating with support + - `includePartialTurns` — explicitly include or exclude partial (non-final) turns + - `redactPii`, `redactPiiPolicies`, `redactPiiSub` — server-side PII redaction on final turns - `webhookUrl`, `webhookAuthHeaderName`, `webhookAuthHeaderValue` +- Add `StreamingPiiPolicy` and `StreamingPiiSubstitution` exported types. `StreamingPiiPolicy` covers the full server-side `AAIEntities` enum (65 entities) - Add `speaker` field to `StreamingWord` ## [4.20.0] diff --git a/package.json b/package.json index e8331ff..fd9b59e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "assemblyai", - "version": "4.33.0", + "version": "4.33.1", "description": "The AssemblyAI JavaScript SDK provides an easy-to-use interface for interacting with the AssemblyAI API, which supports async and real-time transcription, as well as the latest LeMUR models.", "engines": { "node": ">=18" diff --git a/src/services/streaming/service.ts b/src/services/streaming/service.ts index 1f204be..bb18ffc 100644 --- a/src/services/streaming/service.ts +++ b/src/services/streaming/service.ts @@ -198,12 +198,24 @@ export class StreamingTranscriber { ); } + if (this.params.interruptionDelay !== undefined) { + searchParams.set( + "interruption_delay", + this.params.interruptionDelay.toString(), + ); + } + if (this.params.customerSupportAudioCapture) { console.warn( "`customerSupportAudioCapture=true` will record session audio. Only enable this when explicitly coordinating with AssemblyAI support.", ); + // The server's canonical wire name is `_customer_support_audio_capture` + // (leading underscore = "not officially supported / unstable"). The + // server also accepts `customer_support_audio_capture` via + // `populate_by_name=True`, but we send the underscore form to honor + // the server's stability marker. searchParams.set( - "customer_support_audio_capture", + "_customer_support_audio_capture", this.params.customerSupportAudioCapture.toString(), ); } diff --git a/src/types/streaming/index.ts b/src/types/streaming/index.ts index 7c0ec00..f25584c 100644 --- a/src/types/streaming/index.ts +++ b/src/types/streaming/index.ts @@ -39,6 +39,7 @@ export type StreamingTranscriberParams = { voiceFocus?: VoiceFocusModel; voiceFocusThreshold?: number; continuousPartials?: boolean; + interruptionDelay?: number; customerSupportAudioCapture?: boolean; includePartialTurns?: boolean; redactPii?: boolean; @@ -82,35 +83,54 @@ export type VoiceFocusModel = "near-field" | "far-field"; export type StreamingPiiSubstitution = "hash" | "entity_name"; +// Source of truth: assemblyai/engineering/projects/pii/enums.py (`AAIEntities`). +// Keep this union in sync when entities are added or removed server-side. export type StreamingPiiPolicy = | "account_number" | "banking_information" | "blood_type" - | "credit_card_number" - | "credit_card_expiration" + | "corporate_action" | "credit_card_cvv" + | "credit_card_expiration" + | "credit_card_number" | "date" | "date_interval" | "date_of_birth" + | "day" | "drivers_license" | "drug" | "duration" + | "effect" | "email_address" | "event" | "filename" - | "gender_sexuality" + | "financial_metric" | "gender" + | "gender_sexuality" | "healthcare_number" | "injury" | "ip_address" | "language" | "location" + | "location_address" + | "location_address_street" + | "location_city" + | "location_coordinate" + | "location_country" + | "location_state" + | "location_zip" | "marital_status" + | "medical_code" | "medical_condition" | "medical_process" | "money_amount" + | "month" | "nationality" | "number_sequence" + | "occupation" + | "organization" + | "organization_id" + | "organization_medical_facility" | "passport_number" | "password" | "person_age" @@ -118,17 +138,18 @@ export type StreamingPiiPolicy = | "phone_number" | "physical_attribute" | "political_affiliation" - | "occupation" - | "organization" - | "organization_medical_facility" + | "product" + | "project" | "religion" | "sexuality" | "statistics" | "time" + | "trend" | "url" | "us_social_security_number" | "username" | "vehicle_id" + | "year" | "zodiac_sign"; export type StreamingTokenParams = { @@ -199,6 +220,7 @@ export type StreamingUpdateConfiguration = { keyterms_prompt?: string[]; prompt?: string; filter_profanity?: boolean; + interruption_delay?: number; }; export type StreamingForceEndpoint = { diff --git a/tests/unit/streaming.test.ts b/tests/unit/streaming.test.ts index 29e87bb..b05a7c3 100644 --- a/tests/unit/streaming.test.ts +++ b/tests/unit/streaming.test.ts @@ -173,6 +173,37 @@ describe("streaming", () => { await connect(rt, server); }); + it("should include interruption_delay in connection URL", async () => { + await cleanup(); + WS.clean(); + + const wsUrl = + `${websocketBaseUrl}?token=123&sample_rate=16000` + + `&speech_model=u3-rt-pro` + + `&interruption_delay=500`; + server = new WS(wsUrl); + rt = new StreamingTranscriber({ + websocketBaseUrl, + token: "123", + sampleRate: 16_000, + speechModel: "u3-rt-pro", + interruptionDelay: 500, + }); + onOpen = jest.fn(); + rt.on("open", onOpen); + await connect(rt, server); + }); + + it("should include interruption_delay in updateConfiguration message", async () => { + rt.updateConfiguration({ interruption_delay: 250 }); + await expect(server).toReceiveMessage( + JSON.stringify({ + type: "UpdateConfiguration", + interruption_delay: 250, + }), + ); + }); + it("should include voice_focus and voice_focus_threshold in connection URL", async () => { await cleanup(); WS.clean();