assemblyai 4.12.1 → 4.13.0-beta.1
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/assemblyai.umd.js +210 -4
- package/dist/assemblyai.umd.min.js +1 -1
- package/dist/browser.mjs +207 -5
- package/dist/bun.mjs +207 -5
- package/dist/deno.mjs +207 -5
- package/dist/index.cjs +210 -4
- package/dist/index.mjs +210 -5
- package/dist/node.cjs +207 -4
- package/dist/node.mjs +207 -5
- package/dist/services/index.d.ts +6 -1
- package/dist/services/streaming/factory.d.ts +10 -0
- package/dist/services/streaming/index.d.ts +2 -0
- package/dist/services/streaming/service.d.ts +20 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/openapi.generated.d.ts +1 -1
- package/dist/types/services/index.d.ts +1 -0
- package/dist/types/streaming/index.d.ts +71 -0
- package/dist/utils/errors/index.d.ts +1 -0
- package/dist/utils/errors/streaming.d.ts +23 -0
- package/dist/workerd.mjs +207 -5
- package/package.json +1 -1
- package/src/services/index.ts +14 -0
- package/src/services/streaming/factory.ts +39 -0
- package/src/services/streaming/index.ts +2 -0
- package/src/services/streaming/service.ts +219 -0
- package/src/types/index.ts +2 -1
- package/src/types/openapi.generated.ts +1 -1
- package/src/types/services/index.ts +1 -0
- package/src/types/streaming/index.ts +94 -0
- package/src/utils/errors/index.ts +6 -0
- package/src/utils/errors/streaming.ts +51 -0
package/dist/node.cjs
CHANGED
|
@@ -22,7 +22,7 @@ if (typeof navigator !== "undefined" && navigator.userAgent) {
|
|
|
22
22
|
defaultUserAgentString += navigator.userAgent;
|
|
23
23
|
}
|
|
24
24
|
const defaultUserAgent = {
|
|
25
|
-
sdk: { name: "JavaScript", version: "4.
|
|
25
|
+
sdk: { name: "JavaScript", version: "4.13.0-beta.1" },
|
|
26
26
|
};
|
|
27
27
|
if (typeof process !== "undefined") {
|
|
28
28
|
if (process.versions.node && defaultUserAgentString.indexOf("Node") === -1) {
|
|
@@ -198,9 +198,48 @@ const RealtimeErrorMessages = {
|
|
|
198
198
|
class RealtimeError extends Error {
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
const StreamingErrorType = {
|
|
202
|
+
BadSampleRate: 4000,
|
|
203
|
+
AuthFailed: 4001,
|
|
204
|
+
InsufficientFunds: 4002,
|
|
205
|
+
FreeTierUser: 4003,
|
|
206
|
+
NonexistentSessionId: 4004,
|
|
207
|
+
SessionExpired: 4008,
|
|
208
|
+
ClosedSession: 4010,
|
|
209
|
+
RateLimited: 4029,
|
|
210
|
+
UniqueSessionViolation: 4030,
|
|
211
|
+
SessionTimeout: 4031,
|
|
212
|
+
AudioTooShort: 4032,
|
|
213
|
+
AudioTooLong: 4033,
|
|
214
|
+
AudioTooSmallToTranscode: 4034,
|
|
215
|
+
BadSchema: 4101,
|
|
216
|
+
TooManyStreams: 4102,
|
|
217
|
+
Reconnected: 4103,
|
|
218
|
+
};
|
|
219
|
+
const StreamingErrorMessages = {
|
|
220
|
+
[StreamingErrorType.BadSampleRate]: "Sample rate must be a positive integer",
|
|
221
|
+
[StreamingErrorType.AuthFailed]: "Not Authorized",
|
|
222
|
+
[StreamingErrorType.InsufficientFunds]: "Insufficient funds",
|
|
223
|
+
[StreamingErrorType.FreeTierUser]: "This feature is paid-only and requires you to add a credit card. Please visit https://app.assemblyai.com/ to add a credit card to your account.",
|
|
224
|
+
[StreamingErrorType.NonexistentSessionId]: "Session ID does not exist",
|
|
225
|
+
[StreamingErrorType.SessionExpired]: "Session has expired",
|
|
226
|
+
[StreamingErrorType.ClosedSession]: "Session is closed",
|
|
227
|
+
[StreamingErrorType.RateLimited]: "Rate limited",
|
|
228
|
+
[StreamingErrorType.UniqueSessionViolation]: "Unique session violation",
|
|
229
|
+
[StreamingErrorType.SessionTimeout]: "Session Timeout",
|
|
230
|
+
[StreamingErrorType.AudioTooShort]: "Audio too short",
|
|
231
|
+
[StreamingErrorType.AudioTooLong]: "Audio too long",
|
|
232
|
+
[StreamingErrorType.AudioTooSmallToTranscode]: "Audio too small to transcode",
|
|
233
|
+
[StreamingErrorType.BadSchema]: "Bad schema",
|
|
234
|
+
[StreamingErrorType.TooManyStreams]: "Too many streams",
|
|
235
|
+
[StreamingErrorType.Reconnected]: "This session has been reconnected. This WebSocket is no longer valid.",
|
|
236
|
+
};
|
|
237
|
+
class StreamingError extends Error {
|
|
238
|
+
}
|
|
239
|
+
|
|
201
240
|
const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
|
|
202
241
|
const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
|
|
203
|
-
const terminateSessionMessage = `{"terminate_session":true}`;
|
|
242
|
+
const terminateSessionMessage$1 = `{"terminate_session":true}`;
|
|
204
243
|
/**
|
|
205
244
|
* RealtimeTranscriber connects to the Streaming Speech-to-Text API and lets you transcribe audio in real-time.
|
|
206
245
|
*/
|
|
@@ -389,11 +428,11 @@ class RealtimeTranscriber {
|
|
|
389
428
|
const sessionTerminatedPromise = new Promise((resolve) => {
|
|
390
429
|
this.sessionTerminatedResolve = resolve;
|
|
391
430
|
});
|
|
392
|
-
this.socket.send(terminateSessionMessage);
|
|
431
|
+
this.socket.send(terminateSessionMessage$1);
|
|
393
432
|
await sessionTerminatedPromise;
|
|
394
433
|
}
|
|
395
434
|
else {
|
|
396
|
-
this.socket.send(terminateSessionMessage);
|
|
435
|
+
this.socket.send(terminateSessionMessage$1);
|
|
397
436
|
}
|
|
398
437
|
}
|
|
399
438
|
if (this.socket?.removeAllListeners)
|
|
@@ -726,7 +765,166 @@ function dataUrlToBlob(dataUrl) {
|
|
|
726
765
|
return new Blob([u8arr], { type: mime });
|
|
727
766
|
}
|
|
728
767
|
|
|
768
|
+
const defaultStreamingUrl$1 = "wss://streaming.assemblyai.com/v3/ws";
|
|
769
|
+
const terminateSessionMessage = `{"type":"Terminate"}`;
|
|
770
|
+
class StreamingTranscriber {
|
|
771
|
+
constructor(params) {
|
|
772
|
+
this.listeners = {};
|
|
773
|
+
this.params = {
|
|
774
|
+
...params,
|
|
775
|
+
websocketBaseUrl: params.websocketBaseUrl || defaultStreamingUrl$1,
|
|
776
|
+
};
|
|
777
|
+
if ("token" in params && params.token)
|
|
778
|
+
this.token = params.token;
|
|
779
|
+
if ("apiKey" in params && params.apiKey)
|
|
780
|
+
this.apiKey = params.apiKey;
|
|
781
|
+
if (!(this.token || this.apiKey)) {
|
|
782
|
+
throw new Error("API key or temporary token is required.");
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
connectionUrl() {
|
|
786
|
+
const url = new URL(this.params.websocketBaseUrl ?? "");
|
|
787
|
+
if (url.protocol !== "wss:") {
|
|
788
|
+
throw new Error("Invalid protocol, must be wss");
|
|
789
|
+
}
|
|
790
|
+
const searchParams = new URLSearchParams();
|
|
791
|
+
searchParams.set("sample_rate", this.params.sampleRate.toString());
|
|
792
|
+
if (this.params.wordFinalizationMaxWaitTime) {
|
|
793
|
+
searchParams.set("word_finalization_max_wait_time", this.params.wordFinalizationMaxWaitTime.toString());
|
|
794
|
+
}
|
|
795
|
+
if (this.params.endOfTurnConfidenceThreshold) {
|
|
796
|
+
searchParams.set("end_of_turn_confidence_threshold", this.params.endOfTurnConfidenceThreshold.toString());
|
|
797
|
+
}
|
|
798
|
+
if (this.params.minEndOfTurnSilenceWhenConfident) {
|
|
799
|
+
searchParams.set("min_end_of_turn_silence_when_confident", this.params.minEndOfTurnSilenceWhenConfident.toString());
|
|
800
|
+
}
|
|
801
|
+
if (this.params.maxTurnSilence) {
|
|
802
|
+
searchParams.set("max_turn_silence", this.params.maxTurnSilence.toString());
|
|
803
|
+
}
|
|
804
|
+
if (this.params.formattedFinals) {
|
|
805
|
+
searchParams.set("formatted_finals", this.params.formattedFinals.toString());
|
|
806
|
+
}
|
|
807
|
+
url.search = searchParams.toString();
|
|
808
|
+
return url;
|
|
809
|
+
}
|
|
810
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
811
|
+
on(event, listener) {
|
|
812
|
+
this.listeners[event] = listener;
|
|
813
|
+
}
|
|
814
|
+
connect() {
|
|
815
|
+
return new Promise((resolve) => {
|
|
816
|
+
if (this.socket) {
|
|
817
|
+
throw new Error("Already connected");
|
|
818
|
+
}
|
|
819
|
+
const url = this.connectionUrl();
|
|
820
|
+
this.socket = factory(url.toString(), {
|
|
821
|
+
headers: { Authorization: this.token || this.apiKey },
|
|
822
|
+
});
|
|
823
|
+
this.socket.binaryType = "arraybuffer";
|
|
824
|
+
this.socket.onopen = () => { };
|
|
825
|
+
this.socket.onclose = ({ code, reason }) => {
|
|
826
|
+
if (!reason) {
|
|
827
|
+
if (code in StreamingErrorMessages) {
|
|
828
|
+
reason = StreamingErrorMessages[code];
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
this.listeners.close?.(code, reason);
|
|
832
|
+
};
|
|
833
|
+
this.socket.onerror = (event) => {
|
|
834
|
+
if (event.error)
|
|
835
|
+
this.listeners.error?.(event.error);
|
|
836
|
+
else
|
|
837
|
+
this.listeners.error?.(new Error(event.message));
|
|
838
|
+
};
|
|
839
|
+
this.socket.onmessage = ({ data }) => {
|
|
840
|
+
const message = JSON.parse(data.toString());
|
|
841
|
+
if ("error" in message) {
|
|
842
|
+
this.listeners.error?.(new StreamingError(message.error));
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
switch (message.type) {
|
|
846
|
+
case "Begin": {
|
|
847
|
+
resolve(message);
|
|
848
|
+
this.listeners.open?.(message);
|
|
849
|
+
break;
|
|
850
|
+
}
|
|
851
|
+
case "Turn": {
|
|
852
|
+
this.listeners.turn?.(message);
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
case "Termination": {
|
|
856
|
+
this.sessionTerminatedResolve?.();
|
|
857
|
+
break;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
stream() {
|
|
864
|
+
return new web.WritableStream({
|
|
865
|
+
write: (chunk) => {
|
|
866
|
+
this.sendAudio(chunk);
|
|
867
|
+
},
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
sendAudio(audio) {
|
|
871
|
+
this.send(audio);
|
|
872
|
+
}
|
|
873
|
+
send(data) {
|
|
874
|
+
if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
|
|
875
|
+
throw new Error("Socket is not open for communication");
|
|
876
|
+
}
|
|
877
|
+
this.socket.send(data);
|
|
878
|
+
}
|
|
879
|
+
async close(waitForSessionTermination = true) {
|
|
880
|
+
if (this.socket) {
|
|
881
|
+
if (this.socket.readyState === this.socket.OPEN) {
|
|
882
|
+
if (waitForSessionTermination) {
|
|
883
|
+
const sessionTerminatedPromise = new Promise((resolve) => {
|
|
884
|
+
this.sessionTerminatedResolve = resolve;
|
|
885
|
+
});
|
|
886
|
+
this.socket.send(terminateSessionMessage);
|
|
887
|
+
await sessionTerminatedPromise;
|
|
888
|
+
}
|
|
889
|
+
else {
|
|
890
|
+
this.socket.send(terminateSessionMessage);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
if (this.socket?.removeAllListeners)
|
|
894
|
+
this.socket.removeAllListeners();
|
|
895
|
+
this.socket.close();
|
|
896
|
+
}
|
|
897
|
+
this.listeners = {};
|
|
898
|
+
this.socket = undefined;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
class StreamingTranscriberFactory extends BaseService {
|
|
903
|
+
constructor(params) {
|
|
904
|
+
super(params);
|
|
905
|
+
}
|
|
906
|
+
transcriber(params) {
|
|
907
|
+
return new StreamingTranscriber(params);
|
|
908
|
+
}
|
|
909
|
+
async createTemporaryToken(params) {
|
|
910
|
+
const searchParams = new URLSearchParams();
|
|
911
|
+
// Add each param to the search params
|
|
912
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
913
|
+
if (value !== undefined && value !== null) {
|
|
914
|
+
searchParams.append(key, String(value));
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
const queryString = searchParams.toString();
|
|
918
|
+
const url = queryString ? `/v3/token?${queryString}` : "/v3/token";
|
|
919
|
+
const data = await this.fetchJson(url, {
|
|
920
|
+
method: "GET",
|
|
921
|
+
});
|
|
922
|
+
return data.token;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
729
926
|
const defaultBaseUrl = "https://api.assemblyai.com";
|
|
927
|
+
const defaultStreamingUrl = "https://streaming.assemblyai.com";
|
|
730
928
|
class AssemblyAI {
|
|
731
929
|
/**
|
|
732
930
|
* Create a new AssemblyAI client.
|
|
@@ -741,6 +939,10 @@ class AssemblyAI {
|
|
|
741
939
|
this.transcripts = new TranscriptService(params, this.files);
|
|
742
940
|
this.lemur = new LemurService(params);
|
|
743
941
|
this.realtime = new RealtimeTranscriberFactory(params);
|
|
942
|
+
this.streaming = new StreamingTranscriberFactory({
|
|
943
|
+
...params,
|
|
944
|
+
baseUrl: params.streamingBaseUrl || defaultStreamingUrl,
|
|
945
|
+
});
|
|
744
946
|
}
|
|
745
947
|
}
|
|
746
948
|
|
|
@@ -751,4 +953,5 @@ exports.RealtimeService = RealtimeService;
|
|
|
751
953
|
exports.RealtimeServiceFactory = RealtimeServiceFactory;
|
|
752
954
|
exports.RealtimeTranscriber = RealtimeTranscriber;
|
|
753
955
|
exports.RealtimeTranscriberFactory = RealtimeTranscriberFactory;
|
|
956
|
+
exports.StreamingTranscriber = StreamingTranscriber;
|
|
754
957
|
exports.TranscriptService = TranscriptService;
|
package/dist/node.mjs
CHANGED
|
@@ -20,7 +20,7 @@ if (typeof navigator !== "undefined" && navigator.userAgent) {
|
|
|
20
20
|
defaultUserAgentString += navigator.userAgent;
|
|
21
21
|
}
|
|
22
22
|
const defaultUserAgent = {
|
|
23
|
-
sdk: { name: "JavaScript", version: "4.
|
|
23
|
+
sdk: { name: "JavaScript", version: "4.13.0-beta.1" },
|
|
24
24
|
};
|
|
25
25
|
if (typeof process !== "undefined") {
|
|
26
26
|
if (process.versions.node && defaultUserAgentString.indexOf("Node") === -1) {
|
|
@@ -196,9 +196,48 @@ const RealtimeErrorMessages = {
|
|
|
196
196
|
class RealtimeError extends Error {
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
const StreamingErrorType = {
|
|
200
|
+
BadSampleRate: 4000,
|
|
201
|
+
AuthFailed: 4001,
|
|
202
|
+
InsufficientFunds: 4002,
|
|
203
|
+
FreeTierUser: 4003,
|
|
204
|
+
NonexistentSessionId: 4004,
|
|
205
|
+
SessionExpired: 4008,
|
|
206
|
+
ClosedSession: 4010,
|
|
207
|
+
RateLimited: 4029,
|
|
208
|
+
UniqueSessionViolation: 4030,
|
|
209
|
+
SessionTimeout: 4031,
|
|
210
|
+
AudioTooShort: 4032,
|
|
211
|
+
AudioTooLong: 4033,
|
|
212
|
+
AudioTooSmallToTranscode: 4034,
|
|
213
|
+
BadSchema: 4101,
|
|
214
|
+
TooManyStreams: 4102,
|
|
215
|
+
Reconnected: 4103,
|
|
216
|
+
};
|
|
217
|
+
const StreamingErrorMessages = {
|
|
218
|
+
[StreamingErrorType.BadSampleRate]: "Sample rate must be a positive integer",
|
|
219
|
+
[StreamingErrorType.AuthFailed]: "Not Authorized",
|
|
220
|
+
[StreamingErrorType.InsufficientFunds]: "Insufficient funds",
|
|
221
|
+
[StreamingErrorType.FreeTierUser]: "This feature is paid-only and requires you to add a credit card. Please visit https://app.assemblyai.com/ to add a credit card to your account.",
|
|
222
|
+
[StreamingErrorType.NonexistentSessionId]: "Session ID does not exist",
|
|
223
|
+
[StreamingErrorType.SessionExpired]: "Session has expired",
|
|
224
|
+
[StreamingErrorType.ClosedSession]: "Session is closed",
|
|
225
|
+
[StreamingErrorType.RateLimited]: "Rate limited",
|
|
226
|
+
[StreamingErrorType.UniqueSessionViolation]: "Unique session violation",
|
|
227
|
+
[StreamingErrorType.SessionTimeout]: "Session Timeout",
|
|
228
|
+
[StreamingErrorType.AudioTooShort]: "Audio too short",
|
|
229
|
+
[StreamingErrorType.AudioTooLong]: "Audio too long",
|
|
230
|
+
[StreamingErrorType.AudioTooSmallToTranscode]: "Audio too small to transcode",
|
|
231
|
+
[StreamingErrorType.BadSchema]: "Bad schema",
|
|
232
|
+
[StreamingErrorType.TooManyStreams]: "Too many streams",
|
|
233
|
+
[StreamingErrorType.Reconnected]: "This session has been reconnected. This WebSocket is no longer valid.",
|
|
234
|
+
};
|
|
235
|
+
class StreamingError extends Error {
|
|
236
|
+
}
|
|
237
|
+
|
|
199
238
|
const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
|
|
200
239
|
const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
|
|
201
|
-
const terminateSessionMessage = `{"terminate_session":true}`;
|
|
240
|
+
const terminateSessionMessage$1 = `{"terminate_session":true}`;
|
|
202
241
|
/**
|
|
203
242
|
* RealtimeTranscriber connects to the Streaming Speech-to-Text API and lets you transcribe audio in real-time.
|
|
204
243
|
*/
|
|
@@ -387,11 +426,11 @@ class RealtimeTranscriber {
|
|
|
387
426
|
const sessionTerminatedPromise = new Promise((resolve) => {
|
|
388
427
|
this.sessionTerminatedResolve = resolve;
|
|
389
428
|
});
|
|
390
|
-
this.socket.send(terminateSessionMessage);
|
|
429
|
+
this.socket.send(terminateSessionMessage$1);
|
|
391
430
|
await sessionTerminatedPromise;
|
|
392
431
|
}
|
|
393
432
|
else {
|
|
394
|
-
this.socket.send(terminateSessionMessage);
|
|
433
|
+
this.socket.send(terminateSessionMessage$1);
|
|
395
434
|
}
|
|
396
435
|
}
|
|
397
436
|
if (this.socket?.removeAllListeners)
|
|
@@ -724,7 +763,166 @@ function dataUrlToBlob(dataUrl) {
|
|
|
724
763
|
return new Blob([u8arr], { type: mime });
|
|
725
764
|
}
|
|
726
765
|
|
|
766
|
+
const defaultStreamingUrl$1 = "wss://streaming.assemblyai.com/v3/ws";
|
|
767
|
+
const terminateSessionMessage = `{"type":"Terminate"}`;
|
|
768
|
+
class StreamingTranscriber {
|
|
769
|
+
constructor(params) {
|
|
770
|
+
this.listeners = {};
|
|
771
|
+
this.params = {
|
|
772
|
+
...params,
|
|
773
|
+
websocketBaseUrl: params.websocketBaseUrl || defaultStreamingUrl$1,
|
|
774
|
+
};
|
|
775
|
+
if ("token" in params && params.token)
|
|
776
|
+
this.token = params.token;
|
|
777
|
+
if ("apiKey" in params && params.apiKey)
|
|
778
|
+
this.apiKey = params.apiKey;
|
|
779
|
+
if (!(this.token || this.apiKey)) {
|
|
780
|
+
throw new Error("API key or temporary token is required.");
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
connectionUrl() {
|
|
784
|
+
const url = new URL(this.params.websocketBaseUrl ?? "");
|
|
785
|
+
if (url.protocol !== "wss:") {
|
|
786
|
+
throw new Error("Invalid protocol, must be wss");
|
|
787
|
+
}
|
|
788
|
+
const searchParams = new URLSearchParams();
|
|
789
|
+
searchParams.set("sample_rate", this.params.sampleRate.toString());
|
|
790
|
+
if (this.params.wordFinalizationMaxWaitTime) {
|
|
791
|
+
searchParams.set("word_finalization_max_wait_time", this.params.wordFinalizationMaxWaitTime.toString());
|
|
792
|
+
}
|
|
793
|
+
if (this.params.endOfTurnConfidenceThreshold) {
|
|
794
|
+
searchParams.set("end_of_turn_confidence_threshold", this.params.endOfTurnConfidenceThreshold.toString());
|
|
795
|
+
}
|
|
796
|
+
if (this.params.minEndOfTurnSilenceWhenConfident) {
|
|
797
|
+
searchParams.set("min_end_of_turn_silence_when_confident", this.params.minEndOfTurnSilenceWhenConfident.toString());
|
|
798
|
+
}
|
|
799
|
+
if (this.params.maxTurnSilence) {
|
|
800
|
+
searchParams.set("max_turn_silence", this.params.maxTurnSilence.toString());
|
|
801
|
+
}
|
|
802
|
+
if (this.params.formattedFinals) {
|
|
803
|
+
searchParams.set("formatted_finals", this.params.formattedFinals.toString());
|
|
804
|
+
}
|
|
805
|
+
url.search = searchParams.toString();
|
|
806
|
+
return url;
|
|
807
|
+
}
|
|
808
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
809
|
+
on(event, listener) {
|
|
810
|
+
this.listeners[event] = listener;
|
|
811
|
+
}
|
|
812
|
+
connect() {
|
|
813
|
+
return new Promise((resolve) => {
|
|
814
|
+
if (this.socket) {
|
|
815
|
+
throw new Error("Already connected");
|
|
816
|
+
}
|
|
817
|
+
const url = this.connectionUrl();
|
|
818
|
+
this.socket = factory(url.toString(), {
|
|
819
|
+
headers: { Authorization: this.token || this.apiKey },
|
|
820
|
+
});
|
|
821
|
+
this.socket.binaryType = "arraybuffer";
|
|
822
|
+
this.socket.onopen = () => { };
|
|
823
|
+
this.socket.onclose = ({ code, reason }) => {
|
|
824
|
+
if (!reason) {
|
|
825
|
+
if (code in StreamingErrorMessages) {
|
|
826
|
+
reason = StreamingErrorMessages[code];
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
this.listeners.close?.(code, reason);
|
|
830
|
+
};
|
|
831
|
+
this.socket.onerror = (event) => {
|
|
832
|
+
if (event.error)
|
|
833
|
+
this.listeners.error?.(event.error);
|
|
834
|
+
else
|
|
835
|
+
this.listeners.error?.(new Error(event.message));
|
|
836
|
+
};
|
|
837
|
+
this.socket.onmessage = ({ data }) => {
|
|
838
|
+
const message = JSON.parse(data.toString());
|
|
839
|
+
if ("error" in message) {
|
|
840
|
+
this.listeners.error?.(new StreamingError(message.error));
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
switch (message.type) {
|
|
844
|
+
case "Begin": {
|
|
845
|
+
resolve(message);
|
|
846
|
+
this.listeners.open?.(message);
|
|
847
|
+
break;
|
|
848
|
+
}
|
|
849
|
+
case "Turn": {
|
|
850
|
+
this.listeners.turn?.(message);
|
|
851
|
+
break;
|
|
852
|
+
}
|
|
853
|
+
case "Termination": {
|
|
854
|
+
this.sessionTerminatedResolve?.();
|
|
855
|
+
break;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
stream() {
|
|
862
|
+
return new WritableStream({
|
|
863
|
+
write: (chunk) => {
|
|
864
|
+
this.sendAudio(chunk);
|
|
865
|
+
},
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
sendAudio(audio) {
|
|
869
|
+
this.send(audio);
|
|
870
|
+
}
|
|
871
|
+
send(data) {
|
|
872
|
+
if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
|
|
873
|
+
throw new Error("Socket is not open for communication");
|
|
874
|
+
}
|
|
875
|
+
this.socket.send(data);
|
|
876
|
+
}
|
|
877
|
+
async close(waitForSessionTermination = true) {
|
|
878
|
+
if (this.socket) {
|
|
879
|
+
if (this.socket.readyState === this.socket.OPEN) {
|
|
880
|
+
if (waitForSessionTermination) {
|
|
881
|
+
const sessionTerminatedPromise = new Promise((resolve) => {
|
|
882
|
+
this.sessionTerminatedResolve = resolve;
|
|
883
|
+
});
|
|
884
|
+
this.socket.send(terminateSessionMessage);
|
|
885
|
+
await sessionTerminatedPromise;
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
this.socket.send(terminateSessionMessage);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
if (this.socket?.removeAllListeners)
|
|
892
|
+
this.socket.removeAllListeners();
|
|
893
|
+
this.socket.close();
|
|
894
|
+
}
|
|
895
|
+
this.listeners = {};
|
|
896
|
+
this.socket = undefined;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
class StreamingTranscriberFactory extends BaseService {
|
|
901
|
+
constructor(params) {
|
|
902
|
+
super(params);
|
|
903
|
+
}
|
|
904
|
+
transcriber(params) {
|
|
905
|
+
return new StreamingTranscriber(params);
|
|
906
|
+
}
|
|
907
|
+
async createTemporaryToken(params) {
|
|
908
|
+
const searchParams = new URLSearchParams();
|
|
909
|
+
// Add each param to the search params
|
|
910
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
911
|
+
if (value !== undefined && value !== null) {
|
|
912
|
+
searchParams.append(key, String(value));
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
const queryString = searchParams.toString();
|
|
916
|
+
const url = queryString ? `/v3/token?${queryString}` : "/v3/token";
|
|
917
|
+
const data = await this.fetchJson(url, {
|
|
918
|
+
method: "GET",
|
|
919
|
+
});
|
|
920
|
+
return data.token;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
727
924
|
const defaultBaseUrl = "https://api.assemblyai.com";
|
|
925
|
+
const defaultStreamingUrl = "https://streaming.assemblyai.com";
|
|
728
926
|
class AssemblyAI {
|
|
729
927
|
/**
|
|
730
928
|
* Create a new AssemblyAI client.
|
|
@@ -739,7 +937,11 @@ class AssemblyAI {
|
|
|
739
937
|
this.transcripts = new TranscriptService(params, this.files);
|
|
740
938
|
this.lemur = new LemurService(params);
|
|
741
939
|
this.realtime = new RealtimeTranscriberFactory(params);
|
|
940
|
+
this.streaming = new StreamingTranscriberFactory({
|
|
941
|
+
...params,
|
|
942
|
+
baseUrl: params.streamingBaseUrl || defaultStreamingUrl,
|
|
943
|
+
});
|
|
742
944
|
}
|
|
743
945
|
}
|
|
744
946
|
|
|
745
|
-
export { AssemblyAI, FileService, LemurService, RealtimeService, RealtimeServiceFactory, RealtimeTranscriber, RealtimeTranscriberFactory, TranscriptService };
|
|
947
|
+
export { AssemblyAI, FileService, LemurService, RealtimeService, RealtimeServiceFactory, RealtimeTranscriber, RealtimeTranscriberFactory, StreamingTranscriber, TranscriptService };
|
package/dist/services/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { LemurService } from "./lemur";
|
|
|
3
3
|
import { RealtimeTranscriber, RealtimeTranscriberFactory, RealtimeService, RealtimeServiceFactory } from "./realtime";
|
|
4
4
|
import { TranscriptService } from "./transcripts";
|
|
5
5
|
import { FileService } from "./files";
|
|
6
|
+
import { StreamingTranscriber, StreamingTranscriberFactory } from "./streaming";
|
|
6
7
|
declare class AssemblyAI {
|
|
7
8
|
/**
|
|
8
9
|
* The files service.
|
|
@@ -20,10 +21,14 @@ declare class AssemblyAI {
|
|
|
20
21
|
* The realtime service.
|
|
21
22
|
*/
|
|
22
23
|
realtime: RealtimeTranscriberFactory;
|
|
24
|
+
/**
|
|
25
|
+
* The streaming service.
|
|
26
|
+
*/
|
|
27
|
+
streaming: StreamingTranscriberFactory;
|
|
23
28
|
/**
|
|
24
29
|
* Create a new AssemblyAI client.
|
|
25
30
|
* @param params - The parameters for the service, including the API key and base URL, if any.
|
|
26
31
|
*/
|
|
27
32
|
constructor(params: BaseServiceParams);
|
|
28
33
|
}
|
|
29
|
-
export { AssemblyAI, LemurService, RealtimeTranscriberFactory, RealtimeTranscriber, RealtimeServiceFactory, RealtimeService, TranscriptService, FileService, };
|
|
34
|
+
export { AssemblyAI, LemurService, RealtimeTranscriberFactory, RealtimeTranscriber, RealtimeServiceFactory, RealtimeService, TranscriptService, FileService, StreamingTranscriber, };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseServiceParams, StreamingTokenParams, StreamingTranscriberParams } from "../..";
|
|
2
|
+
import { StreamingTranscriber } from "./service";
|
|
3
|
+
import { BaseService } from "../base";
|
|
4
|
+
export declare class StreamingTranscriberFactory extends BaseService {
|
|
5
|
+
constructor(params: BaseServiceParams);
|
|
6
|
+
transcriber(params: StreamingTranscriberParams): StreamingTranscriber;
|
|
7
|
+
createTemporaryToken(params: StreamingTokenParams): Promise<string>;
|
|
8
|
+
}
|
|
9
|
+
export declare class StreamingServiceFactory extends StreamingTranscriberFactory {
|
|
10
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { StreamingTranscriberParams, AudioData, BeginEvent, TurnEvent } from "../..";
|
|
2
|
+
export declare class StreamingTranscriber {
|
|
3
|
+
private apiKey?;
|
|
4
|
+
private token?;
|
|
5
|
+
private params;
|
|
6
|
+
private socket?;
|
|
7
|
+
private listeners;
|
|
8
|
+
private sessionTerminatedResolve?;
|
|
9
|
+
constructor(params: StreamingTranscriberParams);
|
|
10
|
+
private connectionUrl;
|
|
11
|
+
on(event: "open", listener: (event: BeginEvent) => void): void;
|
|
12
|
+
on(event: "turn", listener: (event: TurnEvent) => void): void;
|
|
13
|
+
on(event: "error", listener: (error: Error) => void): void;
|
|
14
|
+
on(event: "close", listener: (code: number, reason: string) => void): void;
|
|
15
|
+
connect(): Promise<BeginEvent>;
|
|
16
|
+
stream(): WritableStream<AudioData>;
|
|
17
|
+
sendAudio(audio: AudioData): void;
|
|
18
|
+
private send;
|
|
19
|
+
close(waitForSessionTermination?: boolean): Promise<void>;
|
|
20
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export * from "./files";
|
|
2
2
|
export * from "./transcripts";
|
|
3
3
|
export * from "./realtime";
|
|
4
|
-
export * from "./services";
|
|
5
4
|
export * from "./asyncapi.generated";
|
|
5
|
+
export * from "./streaming";
|
|
6
|
+
export * from "./services";
|
|
6
7
|
export * from "./openapi.generated";
|
|
7
8
|
export * from "./deprecated";
|
|
8
9
|
export type UserAgentItem = {
|
|
@@ -1262,7 +1262,7 @@ export type SeverityScoreSummary = {
|
|
|
1262
1262
|
/**
|
|
1263
1263
|
* The speech model to use for the transcription.
|
|
1264
1264
|
*/
|
|
1265
|
-
export type SpeechModel = "best" | "nano" | "slam-1";
|
|
1265
|
+
export type SpeechModel = "best" | "nano" | "slam-1" | "universal";
|
|
1266
1266
|
/**
|
|
1267
1267
|
* The replacement logic for detected PII, can be "entity_name" or "hash". See {@link https://www.assemblyai.com/docs/models/pii-redaction | PII redaction } for more details.
|
|
1268
1268
|
*/
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export type StreamingTranscriberParams = {
|
|
2
|
+
websocketBaseUrl?: string;
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
token?: string;
|
|
5
|
+
sampleRate: number;
|
|
6
|
+
wordFinalizationMaxWaitTime?: number;
|
|
7
|
+
endOfTurnConfidenceThreshold?: number;
|
|
8
|
+
minEndOfTurnSilenceWhenConfident?: number;
|
|
9
|
+
maxTurnSilence?: number;
|
|
10
|
+
formattedFinals?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type StreamingEvents = "open" | "close" | "turn" | "error";
|
|
13
|
+
export type StreamingListeners = {
|
|
14
|
+
open?: (event: BeginEvent) => void;
|
|
15
|
+
close?: (code: number, reason: string) => void;
|
|
16
|
+
turn?: (event: TurnEvent) => void;
|
|
17
|
+
error?: (error: Error) => void;
|
|
18
|
+
};
|
|
19
|
+
export type StreamingTokenParams = {
|
|
20
|
+
expires_in_seconds: number;
|
|
21
|
+
max_session_duration_seconds: number;
|
|
22
|
+
};
|
|
23
|
+
export type StreamingTemporaryTokenResponse = {
|
|
24
|
+
token: string;
|
|
25
|
+
};
|
|
26
|
+
export type StreamingAudioData = ArrayBufferLike;
|
|
27
|
+
export type BeginEvent = {
|
|
28
|
+
type: "Begin";
|
|
29
|
+
id: string;
|
|
30
|
+
expires_at: number;
|
|
31
|
+
};
|
|
32
|
+
export type TurnEvent = {
|
|
33
|
+
type: "Turn";
|
|
34
|
+
turn_order: number;
|
|
35
|
+
turn_is_formatted: boolean;
|
|
36
|
+
end_of_turn: boolean;
|
|
37
|
+
transcript: string;
|
|
38
|
+
end_of_turn_confidence: number;
|
|
39
|
+
words: StreamingWord[];
|
|
40
|
+
};
|
|
41
|
+
export type StreamingWord = {
|
|
42
|
+
start: number;
|
|
43
|
+
end: number;
|
|
44
|
+
confidence: number;
|
|
45
|
+
text: string;
|
|
46
|
+
word_is_final: boolean;
|
|
47
|
+
};
|
|
48
|
+
export type TerminationEvent = {
|
|
49
|
+
type: "Termination";
|
|
50
|
+
audio_duration_seconds: number;
|
|
51
|
+
session_duration_seconds: number;
|
|
52
|
+
};
|
|
53
|
+
export type StreamingTerminateSession = {
|
|
54
|
+
type: "Terminate";
|
|
55
|
+
};
|
|
56
|
+
export type StreamingUpdateConfiguration = {
|
|
57
|
+
type: "UpdateConfiguration";
|
|
58
|
+
word_finalization_max_wait_time?: number;
|
|
59
|
+
end_of_turn_confidence_threshold?: number;
|
|
60
|
+
min_end_of_turn_silence_when_confident?: number;
|
|
61
|
+
max_turn_silence?: number;
|
|
62
|
+
formatted_finals?: boolean;
|
|
63
|
+
};
|
|
64
|
+
export type StreamingForceEndpoint = {
|
|
65
|
+
type: "ForceEndpoint";
|
|
66
|
+
};
|
|
67
|
+
export type ErrorEvent = {
|
|
68
|
+
error: string;
|
|
69
|
+
};
|
|
70
|
+
export type StreamingEventMessage = BeginEvent | TurnEvent | TerminationEvent | ErrorEvent;
|
|
71
|
+
export type StreamingOperationMessage = StreamingUpdateConfiguration | StreamingForceEndpoint | StreamingTerminateSession;
|