assemblyai 4.2.2 → 4.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.3.0] - 2024-02-15
4
+
5
+ ### Added
6
+
7
+ - Add `RealtimeTranscriber.configureEndUtteranceSilenceThreshold` function
8
+ - Add `RealtimeTranscriber.forceEndUtterance` function
9
+ - Add `end_utterance_silence_threshold` property to `CreateRealtimeTranscriberParams` and `RealtimeTranscriberParams` types.
10
+
11
+ ## [4.2.3] - 2024-02-13
12
+
13
+ ### Added
14
+
15
+ - Add `speech_model` field to `TranscriptParams` and add `SpeechModel` type.
16
+
3
17
  ## [4.2.2] - 2024-01-29
4
18
 
5
19
  ### Changed
@@ -195,6 +195,8 @@
195
195
  }
196
196
 
197
197
  const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
198
+ const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
199
+ const terminateSessionMessage = `{"terminate_session":true}`;
198
200
  class RealtimeTranscriber {
199
201
  constructor(params) {
200
202
  var _a, _b;
@@ -203,6 +205,8 @@
203
205
  this.sampleRate = (_b = params.sampleRate) !== null && _b !== void 0 ? _b : 16000;
204
206
  this.wordBoost = params.wordBoost;
205
207
  this.encoding = params.encoding;
208
+ this.end_utterance_silence_threshold =
209
+ params.end_utterance_silence_threshold;
206
210
  if ("token" in params && params.token)
207
211
  this.token = params.token;
208
212
  if ("apiKey" in params && params.apiKey)
@@ -249,6 +253,13 @@
249
253
  });
250
254
  }
251
255
  this.socket.binaryType = "arraybuffer";
256
+ this.socket.onopen = () => {
257
+ if (this.end_utterance_silence_threshold === undefined ||
258
+ this.end_utterance_silence_threshold === null) {
259
+ return;
260
+ }
261
+ this.configureEndUtteranceSilenceThreshold(this.end_utterance_silence_threshold);
262
+ };
252
263
  this.socket.onclose = ({ code, reason }) => {
253
264
  var _a, _b;
254
265
  if (!reason) {
@@ -305,10 +316,7 @@
305
316
  });
306
317
  }
307
318
  sendAudio(audio) {
308
- if (!this.socket || this.socket.readyState !== WebSocket$1.OPEN) {
309
- throw new Error("Socket is not open for communication");
310
- }
311
- this.socket.send(audio);
319
+ this.send(audio);
312
320
  }
313
321
  stream() {
314
322
  return new WritableStream({
@@ -317,11 +325,30 @@
317
325
  },
318
326
  });
319
327
  }
328
+ /**
329
+ * Manually end an utterance
330
+ */
331
+ forceEndUtterance() {
332
+ this.send(forceEndOfUtteranceMessage);
333
+ }
334
+ /**
335
+ * Configure the threshold for how long to wait before ending an utterance. Default is 700ms.
336
+ * @param threshold The duration of the end utterance silence threshold in milliseconds
337
+ * @format integer
338
+ */
339
+ configureEndUtteranceSilenceThreshold(threshold) {
340
+ this.send(`{"end_utterance_silence_threshold":${threshold}}`);
341
+ }
342
+ send(data) {
343
+ if (!this.socket || this.socket.readyState !== WebSocket$1.OPEN) {
344
+ throw new Error("Socket is not open for communication");
345
+ }
346
+ this.socket.send(data);
347
+ }
320
348
  close(waitForSessionTermination = true) {
321
349
  return __awaiter(this, void 0, void 0, function* () {
322
350
  if (this.socket) {
323
351
  if (this.socket.readyState === WebSocket$1.OPEN) {
324
- const terminateSessionMessage = `{"terminate_session": true}`;
325
352
  if (waitForSessionTermination) {
326
353
  const sessionTerminatedPromise = new Promise((resolve) => {
327
354
  this.sessionTerminatedResolve = resolve;
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).assemblyai={})}(this,(function(e){"use strict";function t(e,t,s,i){return new(s||(s=Promise))((function(o,n){function r(e){try{c(i.next(e))}catch(e){n(e)}}function a(e){try{c(i.throw(e))}catch(e){n(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof s?t:new s((function(e){e(t)}))).then(r,a)}c((i=i.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;class s{constructor(e){this.params=e}fetch(e,s){var i;return t(this,void 0,void 0,(function*(){(s=null!=s?s:{}).headers=null!==(i=s.headers)&&void 0!==i?i:{},s.headers=Object.assign({Authorization:this.params.apiKey,"Content-Type":"application/json"},s.headers),e.startsWith("http")||(e=this.params.baseUrl+e);const t=yield fetch(e,s);if(t.status>=400){let e;const s=yield t.text();if(s){try{e=JSON.parse(s)}catch(e){}if(null==e?void 0:e.error)throw new Error(e.error);throw new Error(s)}throw new Error(`HTTP Error: ${t.status} ${t.statusText}`)}return t}))}fetchJson(e,s){return t(this,void 0,void 0,(function*(){return(yield this.fetch(e,s)).json()}))}}class i extends s{summary(e){return this.fetchJson("/lemur/v3/generate/summary",{method:"POST",body:JSON.stringify(e)})}questionAnswer(e){return this.fetchJson("/lemur/v3/generate/question-answer",{method:"POST",body:JSON.stringify(e)})}actionItems(e){return this.fetchJson("/lemur/v3/generate/action-items",{method:"POST",body:JSON.stringify(e)})}task(e){return this.fetchJson("/lemur/v3/generate/task",{method:"POST",body:JSON.stringify(e)})}purgeRequestData(e){return this.fetchJson(`/lemur/v3/${e}`,{method:"DELETE"})}}const{WritableStream:o}="undefined"!=typeof window?window:"undefined"!=typeof global?global:globalThis;var n=null;"undefined"!=typeof WebSocket?n=WebSocket:"undefined"!=typeof MozWebSocket?n=MozWebSocket:"undefined"!=typeof global?n=global.WebSocket||global.MozWebSocket:"undefined"!=typeof window?n=window.WebSocket||window.MozWebSocket:"undefined"!=typeof self&&(n=self.WebSocket||self.MozWebSocket);var r,a=n;!function(e){e[e.BadSampleRate=4e3]="BadSampleRate",e[e.AuthFailed=4001]="AuthFailed",e[e.InsufficientFundsOrFreeAccount=4002]="InsufficientFundsOrFreeAccount",e[e.NonexistentSessionId=4004]="NonexistentSessionId",e[e.SessionExpired=4008]="SessionExpired",e[e.ClosedSession=4010]="ClosedSession",e[e.RateLimited=4029]="RateLimited",e[e.UniqueSessionViolation=4030]="UniqueSessionViolation",e[e.SessionTimeout=4031]="SessionTimeout",e[e.AudioTooShort=4032]="AudioTooShort",e[e.AudioTooLong=4033]="AudioTooLong",e[e.BadJson=4100]="BadJson",e[e.BadSchema=4101]="BadSchema",e[e.TooManyStreams=4102]="TooManyStreams",e[e.Reconnected=4103]="Reconnected",e[e.ReconnectAttemptsExhausted=1013]="ReconnectAttemptsExhausted"}(r||(r={}));const c={[r.BadSampleRate]:"Sample rate must be a positive integer",[r.AuthFailed]:"Not Authorized",[r.InsufficientFundsOrFreeAccount]:"Insufficient funds or you are using a free account. This feature is paid-only and requires you to add a credit card. Please visit https://assemblyai.com/dashboard/ to add a credit card to your account.",[r.NonexistentSessionId]:"Session ID does not exist",[r.SessionExpired]:"Session has expired",[r.ClosedSession]:"Session is closed",[r.RateLimited]:"Rate limited",[r.UniqueSessionViolation]:"Unique session violation",[r.SessionTimeout]:"Session Timeout",[r.AudioTooShort]:"Audio too short",[r.AudioTooLong]:"Audio too long",[r.BadJson]:"Bad JSON",[r.BadSchema]:"Bad schema",[r.TooManyStreams]:"Too many streams",[r.Reconnected]:"Reconnected",[r.ReconnectAttemptsExhausted]:"Reconnect attempts exhausted"};class l extends Error{}class d{constructor(e){var t,s;if(this.listeners={},this.realtimeUrl=null!==(t=e.realtimeUrl)&&void 0!==t?t:"wss://api.assemblyai.com/v2/realtime/ws",this.sampleRate=null!==(s=e.sampleRate)&&void 0!==s?s:16e3,this.wordBoost=e.wordBoost,this.encoding=e.encoding,"token"in e&&e.token&&(this.token=e.token),"apiKey"in e&&e.apiKey&&(this.apiKey=e.apiKey),!this.token&&!this.apiKey)throw new Error("API key or temporary token is required.")}connectionUrl(){const e=new URL(this.realtimeUrl);if("wss:"!==e.protocol)throw new Error("Invalid protocol, must be wss");const t=new URLSearchParams;return this.token&&t.set("token",this.token),t.set("sample_rate",this.sampleRate.toString()),this.wordBoost&&this.wordBoost.length>0&&t.set("word_boost",JSON.stringify(this.wordBoost)),this.encoding&&t.set("encoding",this.encoding),e.search=t.toString(),e}on(e,t){this.listeners[e]=t}connect(){return new Promise((e=>{if(this.socket)throw new Error("Already connected");const t=this.connectionUrl();this.token?this.socket=new a(t.toString()):this.socket=new a(t.toString(),{headers:{Authorization:this.apiKey}}),this.socket.binaryType="arraybuffer",this.socket.onclose=({code:e,reason:t})=>{var s,i;t||e in r&&(t=c[e]),null===(i=(s=this.listeners).close)||void 0===i||i.call(s,e,t)},this.socket.onerror=e=>{var t,s,i,o;e.error?null===(s=(t=this.listeners).error)||void 0===s||s.call(t,e.error):null===(o=(i=this.listeners).error)||void 0===o||o.call(i,new Error(e.message))},this.socket.onmessage=({data:t})=>{var s,i,o,n,r,a,c,d,u,h,f,p,m;const y=JSON.parse(t.toString());if("error"in y)null===(i=(s=this.listeners).error)||void 0===i||i.call(s,new l(y.error));else switch(y.message_type){case"SessionBegins":{const t={sessionId:y.session_id,expiresAt:new Date(y.expires_at)};e(t),null===(n=(o=this.listeners).open)||void 0===n||n.call(o,t);break}case"PartialTranscript":y.created=new Date(y.created),null===(a=(r=this.listeners).transcript)||void 0===a||a.call(r,y),null===(d=(c=this.listeners)["transcript.partial"])||void 0===d||d.call(c,y);break;case"FinalTranscript":y.created=new Date(y.created),null===(h=(u=this.listeners).transcript)||void 0===h||h.call(u,y),null===(p=(f=this.listeners)["transcript.final"])||void 0===p||p.call(f,y);break;case"SessionTerminated":null===(m=this.sessionTerminatedResolve)||void 0===m||m.call(this)}}}))}sendAudio(e){if(!this.socket||this.socket.readyState!==a.OPEN)throw new Error("Socket is not open for communication");this.socket.send(e)}stream(){return new o({write:e=>{this.sendAudio(e)}})}close(e=!0){return t(this,void 0,void 0,(function*(){if(this.socket){if(this.socket.readyState===a.OPEN){const t='{"terminate_session": true}';if(e){const e=new Promise((e=>{this.sessionTerminatedResolve=e}));this.socket.send(t),yield e}else this.socket.send(t)}"removeAllListeners"in this.socket&&this.socket.removeAllListeners(),this.socket.close()}this.listeners={},this.socket=void 0}))}}class u extends s{constructor(e){super(e),this.rtFactoryParams=e}createService(e){return this.transcriber(e)}transcriber(e){const t=Object.assign({},e);return t.token||t.apiKey||(t.apiKey=this.rtFactoryParams.apiKey),new d(t)}createTemporaryToken(e){return t(this,void 0,void 0,(function*(){return(yield this.fetchJson("/v2/realtime/token",{method:"POST",body:JSON.stringify(e)})).token}))}}function h(e){return e.startsWith("http")||e.startsWith("https")?null:e.startsWith("file://")?e.substring(7):e.startsWith("file:")?e.substring(5):e}class f extends s{constructor(e,t){super(e),this.files=t}transcribe(e,s){return t(this,void 0,void 0,(function*(){const t=yield this.submit(e);return yield this.waitUntilReady(t.id,s)}))}submit(e){return t(this,void 0,void 0,(function*(){const{audio:t}=e,s=function(e,t){var s={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&t.indexOf(i)<0&&(s[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var o=0;for(i=Object.getOwnPropertySymbols(e);o<i.length;o++)t.indexOf(i[o])<0&&Object.prototype.propertyIsEnumerable.call(e,i[o])&&(s[i[o]]=e[i[o]])}return s}(e,["audio"]);let i;if("string"==typeof t){const e=h(t);i=null!==e?yield this.files.upload(e):t}else i=yield this.files.upload(t);return yield this.fetchJson("/v2/transcript",{method:"POST",body:JSON.stringify(Object.assign(Object.assign({},s),{audio_url:i}))})}))}create(e,s){var i;return t(this,void 0,void 0,(function*(){const t=h(e.audio_url);if(null!==t){const s=yield this.files.upload(t);e.audio_url=s}const o=yield this.fetchJson("/v2/transcript",{method:"POST",body:JSON.stringify(e)});return null===(i=null==s?void 0:s.poll)||void 0===i||i?yield this.waitUntilReady(o.id,s):o}))}waitUntilReady(e,s){var i,o;return t(this,void 0,void 0,(function*(){const t=null!==(i=null==s?void 0:s.pollingInterval)&&void 0!==i?i:3e3,n=null!==(o=null==s?void 0:s.pollingTimeout)&&void 0!==o?o:-1,r=Date.now();for(;;){const s=yield this.get(e);if("completed"===s.status||"error"===s.status)return s;if(n>0&&Date.now()-r>n)throw new Error("Polling timeout");yield new Promise((e=>setTimeout(e,t)))}}))}get(e){return this.fetchJson(`/v2/transcript/${e}`)}list(e){return t(this,void 0,void 0,(function*(){let t="/v2/transcript";"string"==typeof e?t=e:e&&(t=`${t}?${new URLSearchParams(Object.keys(e).map((t=>{var s;return[t,(null===(s=e[t])||void 0===s?void 0:s.toString())||""]})))}`);const s=yield this.fetchJson(t);for(const e of s.transcripts)e.created=new Date(e.created),e.completed&&(e.completed=new Date(e.completed));return s}))}delete(e){return this.fetchJson(`/v2/transcript/${e}`,{method:"DELETE"})}wordSearch(e,t){const s=new URLSearchParams({words:t.join(",")});return this.fetchJson(`/v2/transcript/${e}/word-search?${s.toString()}`)}sentences(e){return this.fetchJson(`/v2/transcript/${e}/sentences`)}paragraphs(e){return this.fetchJson(`/v2/transcript/${e}/paragraphs`)}subtitles(e,s="srt",i){return t(this,void 0,void 0,(function*(){let t=`/v2/transcript/${e}/${s}`;if(i){const e=new URLSearchParams;e.set("chars_per_caption",i.toString()),t+=`?${e.toString()}`}const o=yield this.fetch(t);return yield o.text()}))}redactions(e){return this.fetchJson(`/v2/transcript/${e}/redacted-audio`)}}class p extends s{upload(e){return t(this,void 0,void 0,(function*(){let s;s="string"==typeof e?yield function(e){return t(this,void 0,void 0,(function*(){throw new Error("Interacting with the file system is not supported in this environment.")}))}():e;return(yield this.fetchJson("/v2/upload",{method:"POST",body:s,headers:{"Content-Type":"application/octet-stream"},duplex:"half"})).upload_url}))}}e.AssemblyAI=class{constructor(e){e.baseUrl=e.baseUrl||"https://api.assemblyai.com",e.baseUrl&&e.baseUrl.endsWith("/")&&(e.baseUrl=e.baseUrl.slice(0,-1)),this.files=new p(e),this.transcripts=new f(e,this.files),this.lemur=new i(e),this.realtime=new u(e)}},e.FileService=p,e.LemurService=i,e.RealtimeService=class extends d{},e.RealtimeServiceFactory=class extends u{},e.RealtimeTranscriber=d,e.RealtimeTranscriberFactory=u,e.TranscriptService=f}));
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).assemblyai={})}(this,(function(e){"use strict";function t(e,t,s,i){return new(s||(s=Promise))((function(n,o){function r(e){try{c(i.next(e))}catch(e){o(e)}}function a(e){try{c(i.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?n(e.value):(t=e.value,t instanceof s?t:new s((function(e){e(t)}))).then(r,a)}c((i=i.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;class s{constructor(e){this.params=e}fetch(e,s){var i;return t(this,void 0,void 0,(function*(){(s=null!=s?s:{}).headers=null!==(i=s.headers)&&void 0!==i?i:{},s.headers=Object.assign({Authorization:this.params.apiKey,"Content-Type":"application/json"},s.headers),e.startsWith("http")||(e=this.params.baseUrl+e);const t=yield fetch(e,s);if(t.status>=400){let e;const s=yield t.text();if(s){try{e=JSON.parse(s)}catch(e){}if(null==e?void 0:e.error)throw new Error(e.error);throw new Error(s)}throw new Error(`HTTP Error: ${t.status} ${t.statusText}`)}return t}))}fetchJson(e,s){return t(this,void 0,void 0,(function*(){return(yield this.fetch(e,s)).json()}))}}class i extends s{summary(e){return this.fetchJson("/lemur/v3/generate/summary",{method:"POST",body:JSON.stringify(e)})}questionAnswer(e){return this.fetchJson("/lemur/v3/generate/question-answer",{method:"POST",body:JSON.stringify(e)})}actionItems(e){return this.fetchJson("/lemur/v3/generate/action-items",{method:"POST",body:JSON.stringify(e)})}task(e){return this.fetchJson("/lemur/v3/generate/task",{method:"POST",body:JSON.stringify(e)})}purgeRequestData(e){return this.fetchJson(`/lemur/v3/${e}`,{method:"DELETE"})}}const{WritableStream:n}="undefined"!=typeof window?window:"undefined"!=typeof global?global:globalThis;var o=null;"undefined"!=typeof WebSocket?o=WebSocket:"undefined"!=typeof MozWebSocket?o=MozWebSocket:"undefined"!=typeof global?o=global.WebSocket||global.MozWebSocket:"undefined"!=typeof window?o=window.WebSocket||window.MozWebSocket:"undefined"!=typeof self&&(o=self.WebSocket||self.MozWebSocket);var r,a=o;!function(e){e[e.BadSampleRate=4e3]="BadSampleRate",e[e.AuthFailed=4001]="AuthFailed",e[e.InsufficientFundsOrFreeAccount=4002]="InsufficientFundsOrFreeAccount",e[e.NonexistentSessionId=4004]="NonexistentSessionId",e[e.SessionExpired=4008]="SessionExpired",e[e.ClosedSession=4010]="ClosedSession",e[e.RateLimited=4029]="RateLimited",e[e.UniqueSessionViolation=4030]="UniqueSessionViolation",e[e.SessionTimeout=4031]="SessionTimeout",e[e.AudioTooShort=4032]="AudioTooShort",e[e.AudioTooLong=4033]="AudioTooLong",e[e.BadJson=4100]="BadJson",e[e.BadSchema=4101]="BadSchema",e[e.TooManyStreams=4102]="TooManyStreams",e[e.Reconnected=4103]="Reconnected",e[e.ReconnectAttemptsExhausted=1013]="ReconnectAttemptsExhausted"}(r||(r={}));const c={[r.BadSampleRate]:"Sample rate must be a positive integer",[r.AuthFailed]:"Not Authorized",[r.InsufficientFundsOrFreeAccount]:"Insufficient funds or you are using a free account. This feature is paid-only and requires you to add a credit card. Please visit https://assemblyai.com/dashboard/ to add a credit card to your account.",[r.NonexistentSessionId]:"Session ID does not exist",[r.SessionExpired]:"Session has expired",[r.ClosedSession]:"Session is closed",[r.RateLimited]:"Rate limited",[r.UniqueSessionViolation]:"Unique session violation",[r.SessionTimeout]:"Session Timeout",[r.AudioTooShort]:"Audio too short",[r.AudioTooLong]:"Audio too long",[r.BadJson]:"Bad JSON",[r.BadSchema]:"Bad schema",[r.TooManyStreams]:"Too many streams",[r.Reconnected]:"Reconnected",[r.ReconnectAttemptsExhausted]:"Reconnect attempts exhausted"};class l extends Error{}const d='{"terminate_session":true}';class h{constructor(e){var t,s;if(this.listeners={},this.realtimeUrl=null!==(t=e.realtimeUrl)&&void 0!==t?t:"wss://api.assemblyai.com/v2/realtime/ws",this.sampleRate=null!==(s=e.sampleRate)&&void 0!==s?s:16e3,this.wordBoost=e.wordBoost,this.encoding=e.encoding,this.end_utterance_silence_threshold=e.end_utterance_silence_threshold,"token"in e&&e.token&&(this.token=e.token),"apiKey"in e&&e.apiKey&&(this.apiKey=e.apiKey),!this.token&&!this.apiKey)throw new Error("API key or temporary token is required.")}connectionUrl(){const e=new URL(this.realtimeUrl);if("wss:"!==e.protocol)throw new Error("Invalid protocol, must be wss");const t=new URLSearchParams;return this.token&&t.set("token",this.token),t.set("sample_rate",this.sampleRate.toString()),this.wordBoost&&this.wordBoost.length>0&&t.set("word_boost",JSON.stringify(this.wordBoost)),this.encoding&&t.set("encoding",this.encoding),e.search=t.toString(),e}on(e,t){this.listeners[e]=t}connect(){return new Promise((e=>{if(this.socket)throw new Error("Already connected");const t=this.connectionUrl();this.token?this.socket=new a(t.toString()):this.socket=new a(t.toString(),{headers:{Authorization:this.apiKey}}),this.socket.binaryType="arraybuffer",this.socket.onopen=()=>{void 0!==this.end_utterance_silence_threshold&&null!==this.end_utterance_silence_threshold&&this.configureEndUtteranceSilenceThreshold(this.end_utterance_silence_threshold)},this.socket.onclose=({code:e,reason:t})=>{var s,i;t||e in r&&(t=c[e]),null===(i=(s=this.listeners).close)||void 0===i||i.call(s,e,t)},this.socket.onerror=e=>{var t,s,i,n;e.error?null===(s=(t=this.listeners).error)||void 0===s||s.call(t,e.error):null===(n=(i=this.listeners).error)||void 0===n||n.call(i,new Error(e.message))},this.socket.onmessage=({data:t})=>{var s,i,n,o,r,a,c,d,h,u,f,p,m;const y=JSON.parse(t.toString());if("error"in y)null===(i=(s=this.listeners).error)||void 0===i||i.call(s,new l(y.error));else switch(y.message_type){case"SessionBegins":{const t={sessionId:y.session_id,expiresAt:new Date(y.expires_at)};e(t),null===(o=(n=this.listeners).open)||void 0===o||o.call(n,t);break}case"PartialTranscript":y.created=new Date(y.created),null===(a=(r=this.listeners).transcript)||void 0===a||a.call(r,y),null===(d=(c=this.listeners)["transcript.partial"])||void 0===d||d.call(c,y);break;case"FinalTranscript":y.created=new Date(y.created),null===(u=(h=this.listeners).transcript)||void 0===u||u.call(h,y),null===(p=(f=this.listeners)["transcript.final"])||void 0===p||p.call(f,y);break;case"SessionTerminated":null===(m=this.sessionTerminatedResolve)||void 0===m||m.call(this)}}}))}sendAudio(e){this.send(e)}stream(){return new n({write:e=>{this.sendAudio(e)}})}forceEndUtterance(){this.send('{"force_end_utterance":true}')}configureEndUtteranceSilenceThreshold(e){this.send(`{"end_utterance_silence_threshold":${e}}`)}send(e){if(!this.socket||this.socket.readyState!==a.OPEN)throw new Error("Socket is not open for communication");this.socket.send(e)}close(e=!0){return t(this,void 0,void 0,(function*(){if(this.socket){if(this.socket.readyState===a.OPEN)if(e){const e=new Promise((e=>{this.sessionTerminatedResolve=e}));this.socket.send(d),yield e}else this.socket.send(d);"removeAllListeners"in this.socket&&this.socket.removeAllListeners(),this.socket.close()}this.listeners={},this.socket=void 0}))}}class u extends s{constructor(e){super(e),this.rtFactoryParams=e}createService(e){return this.transcriber(e)}transcriber(e){const t=Object.assign({},e);return t.token||t.apiKey||(t.apiKey=this.rtFactoryParams.apiKey),new h(t)}createTemporaryToken(e){return t(this,void 0,void 0,(function*(){return(yield this.fetchJson("/v2/realtime/token",{method:"POST",body:JSON.stringify(e)})).token}))}}function f(e){return e.startsWith("http")||e.startsWith("https")?null:e.startsWith("file://")?e.substring(7):e.startsWith("file:")?e.substring(5):e}class p extends s{constructor(e,t){super(e),this.files=t}transcribe(e,s){return t(this,void 0,void 0,(function*(){const t=yield this.submit(e);return yield this.waitUntilReady(t.id,s)}))}submit(e){return t(this,void 0,void 0,(function*(){const{audio:t}=e,s=function(e,t){var s={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&t.indexOf(i)<0&&(s[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var n=0;for(i=Object.getOwnPropertySymbols(e);n<i.length;n++)t.indexOf(i[n])<0&&Object.prototype.propertyIsEnumerable.call(e,i[n])&&(s[i[n]]=e[i[n]])}return s}(e,["audio"]);let i;if("string"==typeof t){const e=f(t);i=null!==e?yield this.files.upload(e):t}else i=yield this.files.upload(t);return yield this.fetchJson("/v2/transcript",{method:"POST",body:JSON.stringify(Object.assign(Object.assign({},s),{audio_url:i}))})}))}create(e,s){var i;return t(this,void 0,void 0,(function*(){const t=f(e.audio_url);if(null!==t){const s=yield this.files.upload(t);e.audio_url=s}const n=yield this.fetchJson("/v2/transcript",{method:"POST",body:JSON.stringify(e)});return null===(i=null==s?void 0:s.poll)||void 0===i||i?yield this.waitUntilReady(n.id,s):n}))}waitUntilReady(e,s){var i,n;return t(this,void 0,void 0,(function*(){const t=null!==(i=null==s?void 0:s.pollingInterval)&&void 0!==i?i:3e3,o=null!==(n=null==s?void 0:s.pollingTimeout)&&void 0!==n?n:-1,r=Date.now();for(;;){const s=yield this.get(e);if("completed"===s.status||"error"===s.status)return s;if(o>0&&Date.now()-r>o)throw new Error("Polling timeout");yield new Promise((e=>setTimeout(e,t)))}}))}get(e){return this.fetchJson(`/v2/transcript/${e}`)}list(e){return t(this,void 0,void 0,(function*(){let t="/v2/transcript";"string"==typeof e?t=e:e&&(t=`${t}?${new URLSearchParams(Object.keys(e).map((t=>{var s;return[t,(null===(s=e[t])||void 0===s?void 0:s.toString())||""]})))}`);const s=yield this.fetchJson(t);for(const e of s.transcripts)e.created=new Date(e.created),e.completed&&(e.completed=new Date(e.completed));return s}))}delete(e){return this.fetchJson(`/v2/transcript/${e}`,{method:"DELETE"})}wordSearch(e,t){const s=new URLSearchParams({words:t.join(",")});return this.fetchJson(`/v2/transcript/${e}/word-search?${s.toString()}`)}sentences(e){return this.fetchJson(`/v2/transcript/${e}/sentences`)}paragraphs(e){return this.fetchJson(`/v2/transcript/${e}/paragraphs`)}subtitles(e,s="srt",i){return t(this,void 0,void 0,(function*(){let t=`/v2/transcript/${e}/${s}`;if(i){const e=new URLSearchParams;e.set("chars_per_caption",i.toString()),t+=`?${e.toString()}`}const n=yield this.fetch(t);return yield n.text()}))}redactions(e){return this.fetchJson(`/v2/transcript/${e}/redacted-audio`)}}class m extends s{upload(e){return t(this,void 0,void 0,(function*(){let s;s="string"==typeof e?yield function(e){return t(this,void 0,void 0,(function*(){throw new Error("Interacting with the file system is not supported in this environment.")}))}():e;return(yield this.fetchJson("/v2/upload",{method:"POST",body:s,headers:{"Content-Type":"application/octet-stream"},duplex:"half"})).upload_url}))}}e.AssemblyAI=class{constructor(e){e.baseUrl=e.baseUrl||"https://api.assemblyai.com",e.baseUrl&&e.baseUrl.endsWith("/")&&(e.baseUrl=e.baseUrl.slice(0,-1)),this.files=new m(e),this.transcripts=new p(e,this.files),this.lemur=new i(e),this.realtime=new u(e)}},e.FileService=m,e.LemurService=i,e.RealtimeService=class extends h{},e.RealtimeServiceFactory=class extends u{},e.RealtimeTranscriber=h,e.RealtimeTranscriberFactory=u,e.TranscriptService=p}));
package/dist/bun.mjs CHANGED
@@ -130,6 +130,8 @@ class RealtimeError extends Error {
130
130
  }
131
131
 
132
132
  const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
133
+ const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
134
+ const terminateSessionMessage = `{"terminate_session":true}`;
133
135
  class RealtimeTranscriber {
134
136
  constructor(params) {
135
137
  this.listeners = {};
@@ -137,6 +139,8 @@ class RealtimeTranscriber {
137
139
  this.sampleRate = params.sampleRate ?? 16000;
138
140
  this.wordBoost = params.wordBoost;
139
141
  this.encoding = params.encoding;
142
+ this.end_utterance_silence_threshold =
143
+ params.end_utterance_silence_threshold;
140
144
  if ("token" in params && params.token)
141
145
  this.token = params.token;
142
146
  if ("apiKey" in params && params.apiKey)
@@ -183,6 +187,13 @@ class RealtimeTranscriber {
183
187
  });
184
188
  }
185
189
  this.socket.binaryType = "arraybuffer";
190
+ this.socket.onopen = () => {
191
+ if (this.end_utterance_silence_threshold === undefined ||
192
+ this.end_utterance_silence_threshold === null) {
193
+ return;
194
+ }
195
+ this.configureEndUtteranceSilenceThreshold(this.end_utterance_silence_threshold);
196
+ };
186
197
  this.socket.onclose = ({ code, reason }) => {
187
198
  if (!reason) {
188
199
  if (code in RealtimeErrorType) {
@@ -236,10 +247,7 @@ class RealtimeTranscriber {
236
247
  });
237
248
  }
238
249
  sendAudio(audio) {
239
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
240
- throw new Error("Socket is not open for communication");
241
- }
242
- this.socket.send(audio);
250
+ this.send(audio);
243
251
  }
244
252
  stream() {
245
253
  return new WritableStream({
@@ -248,10 +256,29 @@ class RealtimeTranscriber {
248
256
  },
249
257
  });
250
258
  }
259
+ /**
260
+ * Manually end an utterance
261
+ */
262
+ forceEndUtterance() {
263
+ this.send(forceEndOfUtteranceMessage);
264
+ }
265
+ /**
266
+ * Configure the threshold for how long to wait before ending an utterance. Default is 700ms.
267
+ * @param threshold The duration of the end utterance silence threshold in milliseconds
268
+ * @format integer
269
+ */
270
+ configureEndUtteranceSilenceThreshold(threshold) {
271
+ this.send(`{"end_utterance_silence_threshold":${threshold}}`);
272
+ }
273
+ send(data) {
274
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
275
+ throw new Error("Socket is not open for communication");
276
+ }
277
+ this.socket.send(data);
278
+ }
251
279
  async close(waitForSessionTermination = true) {
252
280
  if (this.socket) {
253
281
  if (this.socket.readyState === WebSocket.OPEN) {
254
- const terminateSessionMessage = `{"terminate_session": true}`;
255
282
  if (waitForSessionTermination) {
256
283
  const sessionTerminatedPromise = new Promise((resolve) => {
257
284
  this.sessionTerminatedResolve = resolve;
package/dist/deno.mjs CHANGED
@@ -130,6 +130,8 @@ class RealtimeError extends Error {
130
130
  }
131
131
 
132
132
  const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
133
+ const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
134
+ const terminateSessionMessage = `{"terminate_session":true}`;
133
135
  class RealtimeTranscriber {
134
136
  constructor(params) {
135
137
  this.listeners = {};
@@ -137,6 +139,8 @@ class RealtimeTranscriber {
137
139
  this.sampleRate = params.sampleRate ?? 16000;
138
140
  this.wordBoost = params.wordBoost;
139
141
  this.encoding = params.encoding;
142
+ this.end_utterance_silence_threshold =
143
+ params.end_utterance_silence_threshold;
140
144
  if ("token" in params && params.token)
141
145
  this.token = params.token;
142
146
  if ("apiKey" in params && params.apiKey)
@@ -183,6 +187,13 @@ class RealtimeTranscriber {
183
187
  });
184
188
  }
185
189
  this.socket.binaryType = "arraybuffer";
190
+ this.socket.onopen = () => {
191
+ if (this.end_utterance_silence_threshold === undefined ||
192
+ this.end_utterance_silence_threshold === null) {
193
+ return;
194
+ }
195
+ this.configureEndUtteranceSilenceThreshold(this.end_utterance_silence_threshold);
196
+ };
186
197
  this.socket.onclose = ({ code, reason }) => {
187
198
  if (!reason) {
188
199
  if (code in RealtimeErrorType) {
@@ -236,10 +247,7 @@ class RealtimeTranscriber {
236
247
  });
237
248
  }
238
249
  sendAudio(audio) {
239
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
240
- throw new Error("Socket is not open for communication");
241
- }
242
- this.socket.send(audio);
250
+ this.send(audio);
243
251
  }
244
252
  stream() {
245
253
  return new WritableStream({
@@ -248,10 +256,29 @@ class RealtimeTranscriber {
248
256
  },
249
257
  });
250
258
  }
259
+ /**
260
+ * Manually end an utterance
261
+ */
262
+ forceEndUtterance() {
263
+ this.send(forceEndOfUtteranceMessage);
264
+ }
265
+ /**
266
+ * Configure the threshold for how long to wait before ending an utterance. Default is 700ms.
267
+ * @param threshold The duration of the end utterance silence threshold in milliseconds
268
+ * @format integer
269
+ */
270
+ configureEndUtteranceSilenceThreshold(threshold) {
271
+ this.send(`{"end_utterance_silence_threshold":${threshold}}`);
272
+ }
273
+ send(data) {
274
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
275
+ throw new Error("Socket is not open for communication");
276
+ }
277
+ this.socket.send(data);
278
+ }
251
279
  async close(waitForSessionTermination = true) {
252
280
  if (this.socket) {
253
281
  if (this.socket.readyState === WebSocket.OPEN) {
254
- const terminateSessionMessage = `{"terminate_session": true}`;
255
282
  if (waitForSessionTermination) {
256
283
  const sessionTerminatedPromise = new Promise((resolve) => {
257
284
  this.sessionTerminatedResolve = resolve;
package/dist/index.cjs CHANGED
@@ -177,6 +177,8 @@ class RealtimeError extends Error {
177
177
  }
178
178
 
179
179
  const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
180
+ const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
181
+ const terminateSessionMessage = `{"terminate_session":true}`;
180
182
  class RealtimeTranscriber {
181
183
  constructor(params) {
182
184
  var _a, _b;
@@ -185,6 +187,8 @@ class RealtimeTranscriber {
185
187
  this.sampleRate = (_b = params.sampleRate) !== null && _b !== void 0 ? _b : 16000;
186
188
  this.wordBoost = params.wordBoost;
187
189
  this.encoding = params.encoding;
190
+ this.end_utterance_silence_threshold =
191
+ params.end_utterance_silence_threshold;
188
192
  if ("token" in params && params.token)
189
193
  this.token = params.token;
190
194
  if ("apiKey" in params && params.apiKey)
@@ -231,6 +235,13 @@ class RealtimeTranscriber {
231
235
  });
232
236
  }
233
237
  this.socket.binaryType = "arraybuffer";
238
+ this.socket.onopen = () => {
239
+ if (this.end_utterance_silence_threshold === undefined ||
240
+ this.end_utterance_silence_threshold === null) {
241
+ return;
242
+ }
243
+ this.configureEndUtteranceSilenceThreshold(this.end_utterance_silence_threshold);
244
+ };
234
245
  this.socket.onclose = ({ code, reason }) => {
235
246
  var _a, _b;
236
247
  if (!reason) {
@@ -287,10 +298,7 @@ class RealtimeTranscriber {
287
298
  });
288
299
  }
289
300
  sendAudio(audio) {
290
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
291
- throw new Error("Socket is not open for communication");
292
- }
293
- this.socket.send(audio);
301
+ this.send(audio);
294
302
  }
295
303
  stream() {
296
304
  return new WritableStream({
@@ -299,11 +307,30 @@ class RealtimeTranscriber {
299
307
  },
300
308
  });
301
309
  }
310
+ /**
311
+ * Manually end an utterance
312
+ */
313
+ forceEndUtterance() {
314
+ this.send(forceEndOfUtteranceMessage);
315
+ }
316
+ /**
317
+ * Configure the threshold for how long to wait before ending an utterance. Default is 700ms.
318
+ * @param threshold The duration of the end utterance silence threshold in milliseconds
319
+ * @format integer
320
+ */
321
+ configureEndUtteranceSilenceThreshold(threshold) {
322
+ this.send(`{"end_utterance_silence_threshold":${threshold}}`);
323
+ }
324
+ send(data) {
325
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
326
+ throw new Error("Socket is not open for communication");
327
+ }
328
+ this.socket.send(data);
329
+ }
302
330
  close(waitForSessionTermination = true) {
303
331
  return __awaiter(this, void 0, void 0, function* () {
304
332
  if (this.socket) {
305
333
  if (this.socket.readyState === WebSocket.OPEN) {
306
- const terminateSessionMessage = `{"terminate_session": true}`;
307
334
  if (waitForSessionTermination) {
308
335
  const sessionTerminatedPromise = new Promise((resolve) => {
309
336
  this.sessionTerminatedResolve = resolve;
package/dist/index.mjs CHANGED
@@ -175,6 +175,8 @@ class RealtimeError extends Error {
175
175
  }
176
176
 
177
177
  const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
178
+ const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
179
+ const terminateSessionMessage = `{"terminate_session":true}`;
178
180
  class RealtimeTranscriber {
179
181
  constructor(params) {
180
182
  var _a, _b;
@@ -183,6 +185,8 @@ class RealtimeTranscriber {
183
185
  this.sampleRate = (_b = params.sampleRate) !== null && _b !== void 0 ? _b : 16000;
184
186
  this.wordBoost = params.wordBoost;
185
187
  this.encoding = params.encoding;
188
+ this.end_utterance_silence_threshold =
189
+ params.end_utterance_silence_threshold;
186
190
  if ("token" in params && params.token)
187
191
  this.token = params.token;
188
192
  if ("apiKey" in params && params.apiKey)
@@ -229,6 +233,13 @@ class RealtimeTranscriber {
229
233
  });
230
234
  }
231
235
  this.socket.binaryType = "arraybuffer";
236
+ this.socket.onopen = () => {
237
+ if (this.end_utterance_silence_threshold === undefined ||
238
+ this.end_utterance_silence_threshold === null) {
239
+ return;
240
+ }
241
+ this.configureEndUtteranceSilenceThreshold(this.end_utterance_silence_threshold);
242
+ };
232
243
  this.socket.onclose = ({ code, reason }) => {
233
244
  var _a, _b;
234
245
  if (!reason) {
@@ -285,10 +296,7 @@ class RealtimeTranscriber {
285
296
  });
286
297
  }
287
298
  sendAudio(audio) {
288
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
289
- throw new Error("Socket is not open for communication");
290
- }
291
- this.socket.send(audio);
299
+ this.send(audio);
292
300
  }
293
301
  stream() {
294
302
  return new WritableStream({
@@ -297,11 +305,30 @@ class RealtimeTranscriber {
297
305
  },
298
306
  });
299
307
  }
308
+ /**
309
+ * Manually end an utterance
310
+ */
311
+ forceEndUtterance() {
312
+ this.send(forceEndOfUtteranceMessage);
313
+ }
314
+ /**
315
+ * Configure the threshold for how long to wait before ending an utterance. Default is 700ms.
316
+ * @param threshold The duration of the end utterance silence threshold in milliseconds
317
+ * @format integer
318
+ */
319
+ configureEndUtteranceSilenceThreshold(threshold) {
320
+ this.send(`{"end_utterance_silence_threshold":${threshold}}`);
321
+ }
322
+ send(data) {
323
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
324
+ throw new Error("Socket is not open for communication");
325
+ }
326
+ this.socket.send(data);
327
+ }
300
328
  close(waitForSessionTermination = true) {
301
329
  return __awaiter(this, void 0, void 0, function* () {
302
330
  if (this.socket) {
303
331
  if (this.socket.readyState === WebSocket.OPEN) {
304
- const terminateSessionMessage = `{"terminate_session": true}`;
305
332
  if (waitForSessionTermination) {
306
333
  const sessionTerminatedPromise = new Promise((resolve) => {
307
334
  this.sessionTerminatedResolve = resolve;
package/dist/node.cjs CHANGED
@@ -129,6 +129,8 @@ class RealtimeError extends Error {
129
129
  }
130
130
 
131
131
  const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
132
+ const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
133
+ const terminateSessionMessage = `{"terminate_session":true}`;
132
134
  class RealtimeTranscriber {
133
135
  constructor(params) {
134
136
  this.listeners = {};
@@ -136,6 +138,8 @@ class RealtimeTranscriber {
136
138
  this.sampleRate = params.sampleRate ?? 16000;
137
139
  this.wordBoost = params.wordBoost;
138
140
  this.encoding = params.encoding;
141
+ this.end_utterance_silence_threshold =
142
+ params.end_utterance_silence_threshold;
139
143
  if ("token" in params && params.token)
140
144
  this.token = params.token;
141
145
  if ("apiKey" in params && params.apiKey)
@@ -182,6 +186,13 @@ class RealtimeTranscriber {
182
186
  });
183
187
  }
184
188
  this.socket.binaryType = "arraybuffer";
189
+ this.socket.onopen = () => {
190
+ if (this.end_utterance_silence_threshold === undefined ||
191
+ this.end_utterance_silence_threshold === null) {
192
+ return;
193
+ }
194
+ this.configureEndUtteranceSilenceThreshold(this.end_utterance_silence_threshold);
195
+ };
185
196
  this.socket.onclose = ({ code, reason }) => {
186
197
  if (!reason) {
187
198
  if (code in RealtimeErrorType) {
@@ -235,10 +246,7 @@ class RealtimeTranscriber {
235
246
  });
236
247
  }
237
248
  sendAudio(audio) {
238
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
239
- throw new Error("Socket is not open for communication");
240
- }
241
- this.socket.send(audio);
249
+ this.send(audio);
242
250
  }
243
251
  stream() {
244
252
  return new web.WritableStream({
@@ -247,10 +255,29 @@ class RealtimeTranscriber {
247
255
  },
248
256
  });
249
257
  }
258
+ /**
259
+ * Manually end an utterance
260
+ */
261
+ forceEndUtterance() {
262
+ this.send(forceEndOfUtteranceMessage);
263
+ }
264
+ /**
265
+ * Configure the threshold for how long to wait before ending an utterance. Default is 700ms.
266
+ * @param threshold The duration of the end utterance silence threshold in milliseconds
267
+ * @format integer
268
+ */
269
+ configureEndUtteranceSilenceThreshold(threshold) {
270
+ this.send(`{"end_utterance_silence_threshold":${threshold}}`);
271
+ }
272
+ send(data) {
273
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
274
+ throw new Error("Socket is not open for communication");
275
+ }
276
+ this.socket.send(data);
277
+ }
250
278
  async close(waitForSessionTermination = true) {
251
279
  if (this.socket) {
252
280
  if (this.socket.readyState === WebSocket.OPEN) {
253
- const terminateSessionMessage = `{"terminate_session": true}`;
254
281
  if (waitForSessionTermination) {
255
282
  const sessionTerminatedPromise = new Promise((resolve) => {
256
283
  this.sessionTerminatedResolve = resolve;
package/dist/node.mjs CHANGED
@@ -127,6 +127,8 @@ class RealtimeError extends Error {
127
127
  }
128
128
 
129
129
  const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
130
+ const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
131
+ const terminateSessionMessage = `{"terminate_session":true}`;
130
132
  class RealtimeTranscriber {
131
133
  constructor(params) {
132
134
  this.listeners = {};
@@ -134,6 +136,8 @@ class RealtimeTranscriber {
134
136
  this.sampleRate = params.sampleRate ?? 16000;
135
137
  this.wordBoost = params.wordBoost;
136
138
  this.encoding = params.encoding;
139
+ this.end_utterance_silence_threshold =
140
+ params.end_utterance_silence_threshold;
137
141
  if ("token" in params && params.token)
138
142
  this.token = params.token;
139
143
  if ("apiKey" in params && params.apiKey)
@@ -180,6 +184,13 @@ class RealtimeTranscriber {
180
184
  });
181
185
  }
182
186
  this.socket.binaryType = "arraybuffer";
187
+ this.socket.onopen = () => {
188
+ if (this.end_utterance_silence_threshold === undefined ||
189
+ this.end_utterance_silence_threshold === null) {
190
+ return;
191
+ }
192
+ this.configureEndUtteranceSilenceThreshold(this.end_utterance_silence_threshold);
193
+ };
183
194
  this.socket.onclose = ({ code, reason }) => {
184
195
  if (!reason) {
185
196
  if (code in RealtimeErrorType) {
@@ -233,10 +244,7 @@ class RealtimeTranscriber {
233
244
  });
234
245
  }
235
246
  sendAudio(audio) {
236
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
237
- throw new Error("Socket is not open for communication");
238
- }
239
- this.socket.send(audio);
247
+ this.send(audio);
240
248
  }
241
249
  stream() {
242
250
  return new WritableStream({
@@ -245,10 +253,29 @@ class RealtimeTranscriber {
245
253
  },
246
254
  });
247
255
  }
256
+ /**
257
+ * Manually end an utterance
258
+ */
259
+ forceEndUtterance() {
260
+ this.send(forceEndOfUtteranceMessage);
261
+ }
262
+ /**
263
+ * Configure the threshold for how long to wait before ending an utterance. Default is 700ms.
264
+ * @param threshold The duration of the end utterance silence threshold in milliseconds
265
+ * @format integer
266
+ */
267
+ configureEndUtteranceSilenceThreshold(threshold) {
268
+ this.send(`{"end_utterance_silence_threshold":${threshold}}`);
269
+ }
270
+ send(data) {
271
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
272
+ throw new Error("Socket is not open for communication");
273
+ }
274
+ this.socket.send(data);
275
+ }
248
276
  async close(waitForSessionTermination = true) {
249
277
  if (this.socket) {
250
278
  if (this.socket.readyState === WebSocket.OPEN) {
251
- const terminateSessionMessage = `{"terminate_session": true}`;
252
279
  if (waitForSessionTermination) {
253
280
  const sessionTerminatedPromise = new Promise((resolve) => {
254
281
  this.sessionTerminatedResolve = resolve;
@@ -6,6 +6,7 @@ export declare class RealtimeTranscriber {
6
6
  private encoding?;
7
7
  private apiKey?;
8
8
  private token?;
9
+ private end_utterance_silence_threshold?;
9
10
  private socket?;
10
11
  private listeners;
11
12
  private sessionTerminatedResolve?;
@@ -20,6 +21,17 @@ export declare class RealtimeTranscriber {
20
21
  connect(): Promise<SessionBeginsEventData>;
21
22
  sendAudio(audio: AudioData): void;
22
23
  stream(): WritableStream<AudioData>;
24
+ /**
25
+ * Manually end an utterance
26
+ */
27
+ forceEndUtterance(): void;
28
+ /**
29
+ * Configure the threshold for how long to wait before ending an utterance. Default is 700ms.
30
+ * @param threshold The duration of the end utterance silence threshold in milliseconds
31
+ * @format integer
32
+ */
33
+ configureEndUtteranceSilenceThreshold(threshold: number): void;
34
+ private send;
23
35
  close(waitForSessionTermination?: boolean): Promise<void>;
24
36
  }
25
37
  /**
@@ -9,6 +9,11 @@ export type AudioData = ArrayBufferLike;
9
9
  * @enum {string}
10
10
  */
11
11
  export type AudioEncoding = "pcm_s16le" | "pcm_mulaw";
12
+ /** @description Configure the threshold for how long to wait before ending an utterance. Default is 700ms. */
13
+ export type ConfigureEndUtteranceSilenceThreshold = {
14
+ /** @description The duration threshold in milliseconds */
15
+ end_utterance_silence_threshold: number;
16
+ };
12
17
  export type FinalTranscript = RealtimeBaseTranscript & {
13
18
  /**
14
19
  * @description Describes the type of message
@@ -20,6 +25,11 @@ export type FinalTranscript = RealtimeBaseTranscript & {
20
25
  /** @description Whether the text is formatted, for example Dollar -> $ */
21
26
  text_formatted: boolean;
22
27
  };
28
+ /** @description Manually end an utterance */
29
+ export type ForceEndUtterance = {
30
+ /** @description A boolean value to communicate that you wish to force the end of the utterance */
31
+ force_end_utterance: boolean;
32
+ };
23
33
  /** @enum {string} */
24
34
  export type MessageType = "SessionBegins" | "PartialTranscript" | "FinalTranscript" | "SessionTerminated";
25
35
  export type PartialTranscript = RealtimeBaseTranscript & {
@@ -991,6 +991,11 @@ export type SeverityScoreSummary = {
991
991
  /** Format: double */
992
992
  medium: number;
993
993
  };
994
+ /**
995
+ * @description The speech model to use for the transcription.
996
+ * @enum {string}
997
+ */
998
+ export type SpeechModel = "nano";
994
999
  /**
995
1000
  * @description The replacement logic for detected PII, can be "entity_type" or "hash". See [PII redaction](https://www.assemblyai.com/docs/models/pii-redaction) for more details.
996
1001
  * @enum {string}
@@ -1187,6 +1192,7 @@ export type TopicDetectionResult = {
1187
1192
  * @description A transcript object
1188
1193
  * @example {
1189
1194
  * "id": "9ea68fd3-f953-42c1-9742-976c447fb463",
1195
+ * "speech_model": null,
1190
1196
  * "language_model": "assemblyai_default",
1191
1197
  * "acoustic_model": "assemblyai_default",
1192
1198
  * "language_code": "en_us",
@@ -1989,6 +1995,11 @@ export type Transcript = {
1989
1995
  speaker_labels?: boolean | null;
1990
1996
  /** @description Tell the speaker label model how many speakers it should attempt to identify, up to 10. See [Speaker diarization](https://www.assemblyai.com/docs/models/speaker-diarization) for more details. */
1991
1997
  speakers_expected?: number | null;
1998
+ /**
1999
+ * @description The speech model used for the transcription. When `null`, the default model is used.
2000
+ * @default null
2001
+ */
2002
+ speech_model: SpeechModel | null;
1992
2003
  /**
1993
2004
  * Format: float
1994
2005
  * @description Defaults to null. Reject audio files that contain less than this fraction of speech.
@@ -2123,6 +2134,7 @@ export type TranscriptListItem = {
2123
2134
  /**
2124
2135
  * @description The parameters for creating a transcript
2125
2136
  * @example {
2137
+ * "speech_model": null,
2126
2138
  * "language_code": "en_us",
2127
2139
  * "punctuate": true,
2128
2140
  * "format_text": true,
@@ -2228,6 +2240,11 @@ export type TranscriptOptionalParams = {
2228
2240
  * @default null
2229
2241
  */
2230
2242
  speakers_expected?: number | null;
2243
+ /**
2244
+ * @description The speech model to use for the transcription. When `null`, the default model is used.
2245
+ * @default null
2246
+ */
2247
+ speech_model?: SpeechModel | null;
2231
2248
  /**
2232
2249
  * Format: float
2233
2250
  * @description Reject audio files that contain less than this fraction of speech.
@@ -1,12 +1,35 @@
1
1
  import { AudioEncoding, FinalTranscript, PartialTranscript, RealtimeTranscript, RealtimeTranscriptType } from "../asyncapi.generated";
2
2
  type CreateRealtimeTranscriberParams = {
3
+ /**
4
+ * The WebSocket URL that the RealtimeTranscriber connects to
5
+ */
3
6
  realtimeUrl?: string;
7
+ /**
8
+ * The sample rate of the streamed audio
9
+ */
4
10
  sampleRate?: number;
11
+ /**
12
+ * Add up to 2500 characters of custom vocabulary
13
+ */
5
14
  wordBoost?: string[];
15
+ /**
16
+ * The encoding of the audio data
17
+ */
6
18
  encoding?: AudioEncoding;
19
+ /**
20
+ * The duration of the end utterance silence threshold in milliseconds
21
+ */
22
+ end_utterance_silence_threshold?: number;
7
23
  } & ({
24
+ /**
25
+ * The API key used to authenticate the RealtimeTranscriber
26
+ * Using an API key to authenticate the RealtimeTranscriber is not supported in the browser.
27
+ */
8
28
  apiKey?: string;
9
29
  } | {
30
+ /**
31
+ * The temporary token used to authenticate the RealtimeTranscriber
32
+ */
10
33
  token: string;
11
34
  });
12
35
  /**
@@ -14,13 +37,36 @@ type CreateRealtimeTranscriberParams = {
14
37
  */
15
38
  type CreateRealtimeServiceParams = CreateRealtimeTranscriberParams;
16
39
  type RealtimeTranscriberParams = {
40
+ /**
41
+ * The WebSocket URL that the RealtimeTranscriber connects to
42
+ */
17
43
  realtimeUrl?: string;
44
+ /**
45
+ * The sample rate of the streamed audio
46
+ */
18
47
  sampleRate?: number;
48
+ /**
49
+ * Add up to 2500 characters of custom vocabulary
50
+ */
19
51
  wordBoost?: string[];
52
+ /**
53
+ * The encoding of the audio data
54
+ */
20
55
  encoding?: AudioEncoding;
56
+ /**
57
+ * The duration of the end utterance silence threshold in milliseconds
58
+ */
59
+ end_utterance_silence_threshold?: number;
21
60
  } & ({
61
+ /**
62
+ * The API key used to authenticate the RealtimeTranscriber.
63
+ * Using an API key to authenticate the RealtimeTranscriber is not supported in the browser.
64
+ */
22
65
  apiKey: string;
23
66
  } | {
67
+ /**
68
+ * The temporary token used to authenticate the RealtimeTranscriber
69
+ */
24
70
  token: string;
25
71
  });
26
72
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assemblyai",
3
- "version": "4.2.2",
3
+ "version": "4.3.0",
4
4
  "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.",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -20,6 +20,26 @@ import {
20
20
  } from "../../utils/errors";
21
21
 
22
22
  const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
23
+ const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
24
+ const terminateSessionMessage = `{"terminate_session":true}`;
25
+
26
+ type BufferLike =
27
+ | string
28
+ | Buffer
29
+ | DataView
30
+ | number
31
+ | ArrayBufferView
32
+ | Uint8Array
33
+ | ArrayBuffer
34
+ | SharedArrayBuffer
35
+ | ReadonlyArray<unknown>
36
+ | ReadonlyArray<number>
37
+ | { valueOf(): ArrayBuffer }
38
+ | { valueOf(): SharedArrayBuffer }
39
+ | { valueOf(): Uint8Array }
40
+ | { valueOf(): ReadonlyArray<number> }
41
+ | { valueOf(): string }
42
+ | { [Symbol.toPrimitive](hint: string): string };
23
43
 
24
44
  export class RealtimeTranscriber {
25
45
  private realtimeUrl: string;
@@ -28,6 +48,7 @@ export class RealtimeTranscriber {
28
48
  private encoding?: AudioEncoding;
29
49
  private apiKey?: string;
30
50
  private token?: string;
51
+ private end_utterance_silence_threshold?: number;
31
52
  private socket?: WebSocket;
32
53
  private listeners: RealtimeListeners = {};
33
54
  private sessionTerminatedResolve?: () => void;
@@ -37,6 +58,8 @@ export class RealtimeTranscriber {
37
58
  this.sampleRate = params.sampleRate ?? 16_000;
38
59
  this.wordBoost = params.wordBoost;
39
60
  this.encoding = params.encoding;
61
+ this.end_utterance_silence_threshold =
62
+ params.end_utterance_silence_threshold;
40
63
  if ("token" in params && params.token) this.token = params.token;
41
64
  if ("apiKey" in params && params.apiKey) this.apiKey = params.apiKey;
42
65
 
@@ -105,6 +128,18 @@ export class RealtimeTranscriber {
105
128
  }
106
129
  this.socket.binaryType = "arraybuffer";
107
130
 
131
+ this.socket.onopen = () => {
132
+ if (
133
+ this.end_utterance_silence_threshold === undefined ||
134
+ this.end_utterance_silence_threshold === null
135
+ ) {
136
+ return;
137
+ }
138
+ this.configureEndUtteranceSilenceThreshold(
139
+ this.end_utterance_silence_threshold
140
+ );
141
+ };
142
+
108
143
  this.socket.onclose = ({ code, reason }: CloseEvent) => {
109
144
  if (!reason) {
110
145
  if (code in RealtimeErrorType) {
@@ -159,10 +194,7 @@ export class RealtimeTranscriber {
159
194
  }
160
195
 
161
196
  sendAudio(audio: AudioData) {
162
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
163
- throw new Error("Socket is not open for communication");
164
- }
165
- this.socket.send(audio);
197
+ this.send(audio);
166
198
  }
167
199
 
168
200
  stream(): WritableStream<AudioData> {
@@ -173,10 +205,32 @@ export class RealtimeTranscriber {
173
205
  });
174
206
  }
175
207
 
208
+ /**
209
+ * Manually end an utterance
210
+ */
211
+ forceEndUtterance() {
212
+ this.send(forceEndOfUtteranceMessage);
213
+ }
214
+
215
+ /**
216
+ * Configure the threshold for how long to wait before ending an utterance. Default is 700ms.
217
+ * @param threshold The duration of the end utterance silence threshold in milliseconds
218
+ * @format integer
219
+ */
220
+ configureEndUtteranceSilenceThreshold(threshold: number) {
221
+ this.send(`{"end_utterance_silence_threshold":${threshold}}`);
222
+ }
223
+
224
+ private send(data: BufferLike) {
225
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
226
+ throw new Error("Socket is not open for communication");
227
+ }
228
+ this.socket.send(data);
229
+ }
230
+
176
231
  async close(waitForSessionTermination = true) {
177
232
  if (this.socket) {
178
233
  if (this.socket.readyState === WebSocket.OPEN) {
179
- const terminateSessionMessage = `{"terminate_session": true}`;
180
234
  if (waitForSessionTermination) {
181
235
  const sessionTerminatedPromise = new Promise<void>((resolve) => {
182
236
  this.sessionTerminatedResolve = resolve;
@@ -28,6 +28,12 @@ export type AudioData = ArrayBufferLike;
28
28
  */
29
29
  export type AudioEncoding = "pcm_s16le" | "pcm_mulaw";
30
30
 
31
+ /** @description Configure the threshold for how long to wait before ending an utterance. Default is 700ms. */
32
+ export type ConfigureEndUtteranceSilenceThreshold = {
33
+ /** @description The duration threshold in milliseconds */
34
+ end_utterance_silence_threshold: number;
35
+ };
36
+
31
37
  export type FinalTranscript = RealtimeBaseTranscript & {
32
38
  /**
33
39
  * @description Describes the type of message
@@ -40,6 +46,12 @@ export type FinalTranscript = RealtimeBaseTranscript & {
40
46
  text_formatted: boolean;
41
47
  };
42
48
 
49
+ /** @description Manually end an utterance */
50
+ export type ForceEndUtterance = {
51
+ /** @description A boolean value to communicate that you wish to force the end of the utterance */
52
+ force_end_utterance: boolean;
53
+ };
54
+
43
55
  /** @enum {string} */
44
56
  export type MessageType =
45
57
  | "SessionBegins"
@@ -1102,6 +1102,12 @@ export type SeverityScoreSummary = {
1102
1102
  medium: number;
1103
1103
  };
1104
1104
 
1105
+ /**
1106
+ * @description The speech model to use for the transcription.
1107
+ * @enum {string}
1108
+ */
1109
+ export type SpeechModel = "nano";
1110
+
1105
1111
  /**
1106
1112
  * @description The replacement logic for detected PII, can be "entity_type" or "hash". See [PII redaction](https://www.assemblyai.com/docs/models/pii-redaction) for more details.
1107
1113
  * @enum {string}
@@ -1310,6 +1316,7 @@ export type TopicDetectionResult = {
1310
1316
  * @description A transcript object
1311
1317
  * @example {
1312
1318
  * "id": "9ea68fd3-f953-42c1-9742-976c447fb463",
1319
+ * "speech_model": null,
1313
1320
  * "language_model": "assemblyai_default",
1314
1321
  * "acoustic_model": "assemblyai_default",
1315
1322
  * "language_code": "en_us",
@@ -2112,6 +2119,11 @@ export type Transcript = {
2112
2119
  speaker_labels?: boolean | null;
2113
2120
  /** @description Tell the speaker label model how many speakers it should attempt to identify, up to 10. See [Speaker diarization](https://www.assemblyai.com/docs/models/speaker-diarization) for more details. */
2114
2121
  speakers_expected?: number | null;
2122
+ /**
2123
+ * @description The speech model used for the transcription. When `null`, the default model is used.
2124
+ * @default null
2125
+ */
2126
+ speech_model: SpeechModel | null;
2115
2127
  /**
2116
2128
  * Format: float
2117
2129
  * @description Defaults to null. Reject audio files that contain less than this fraction of speech.
@@ -2272,6 +2284,7 @@ export type TranscriptListItem = {
2272
2284
  /**
2273
2285
  * @description The parameters for creating a transcript
2274
2286
  * @example {
2287
+ * "speech_model": null,
2275
2288
  * "language_code": "en_us",
2276
2289
  * "punctuate": true,
2277
2290
  * "format_text": true,
@@ -2377,6 +2390,11 @@ export type TranscriptOptionalParams = {
2377
2390
  * @default null
2378
2391
  */
2379
2392
  speakers_expected?: number | null;
2393
+ /**
2394
+ * @description The speech model to use for the transcription. When `null`, the default model is used.
2395
+ * @default null
2396
+ */
2397
+ speech_model?: SpeechModel | null;
2380
2398
  /**
2381
2399
  * Format: float
2382
2400
  * @description Reject audio files that contain less than this fraction of speech.
@@ -7,15 +7,38 @@ import {
7
7
  } from "../asyncapi.generated";
8
8
 
9
9
  type CreateRealtimeTranscriberParams = {
10
+ /**
11
+ * The WebSocket URL that the RealtimeTranscriber connects to
12
+ */
10
13
  realtimeUrl?: string;
14
+ /**
15
+ * The sample rate of the streamed audio
16
+ */
11
17
  sampleRate?: number;
18
+ /**
19
+ * Add up to 2500 characters of custom vocabulary
20
+ */
12
21
  wordBoost?: string[];
22
+ /**
23
+ * The encoding of the audio data
24
+ */
13
25
  encoding?: AudioEncoding;
26
+ /**
27
+ * The duration of the end utterance silence threshold in milliseconds
28
+ */
29
+ end_utterance_silence_threshold?: number;
14
30
  } & (
15
31
  | {
32
+ /**
33
+ * The API key used to authenticate the RealtimeTranscriber
34
+ * Using an API key to authenticate the RealtimeTranscriber is not supported in the browser.
35
+ */
16
36
  apiKey?: string;
17
37
  }
18
38
  | {
39
+ /**
40
+ * The temporary token used to authenticate the RealtimeTranscriber
41
+ */
19
42
  token: string;
20
43
  }
21
44
  );
@@ -26,15 +49,38 @@ type CreateRealtimeTranscriberParams = {
26
49
  type CreateRealtimeServiceParams = CreateRealtimeTranscriberParams;
27
50
 
28
51
  type RealtimeTranscriberParams = {
52
+ /**
53
+ * The WebSocket URL that the RealtimeTranscriber connects to
54
+ */
29
55
  realtimeUrl?: string;
56
+ /**
57
+ * The sample rate of the streamed audio
58
+ */
30
59
  sampleRate?: number;
60
+ /**
61
+ * Add up to 2500 characters of custom vocabulary
62
+ */
31
63
  wordBoost?: string[];
64
+ /**
65
+ * The encoding of the audio data
66
+ */
32
67
  encoding?: AudioEncoding;
68
+ /**
69
+ * The duration of the end utterance silence threshold in milliseconds
70
+ */
71
+ end_utterance_silence_threshold?: number;
33
72
  } & (
34
73
  | {
74
+ /**
75
+ * The API key used to authenticate the RealtimeTranscriber.
76
+ * Using an API key to authenticate the RealtimeTranscriber is not supported in the browser.
77
+ */
35
78
  apiKey: string;
36
79
  }
37
80
  | {
81
+ /**
82
+ * The temporary token used to authenticate the RealtimeTranscriber
83
+ */
38
84
  token: string;
39
85
  }
40
86
  );