assemblyai 1.0.1 → 2.0.1-beta
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/LICENSE +21 -0
- package/README.md +192 -83
- package/dist/index.d.ts +4 -0
- package/dist/index.esm.js +473 -0
- package/dist/index.js +482 -0
- package/dist/services/base.d.ts +13 -0
- package/dist/services/files/index.d.ts +9 -0
- package/dist/services/index.d.ts +29 -0
- package/dist/services/lemur/index.d.ts +8 -0
- package/dist/services/realtime/factory.d.ts +10 -0
- package/dist/services/realtime/index.d.ts +2 -0
- package/dist/services/realtime/service.d.ts +22 -0
- package/dist/services/transcripts/index.d.ts +59 -0
- package/dist/types/asyncapi.generated.d.ts +87 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/openapi.generated.d.ts +685 -0
- package/dist/types/realtime/index.d.ts +36 -0
- package/dist/types/services/abstractions.d.ts +52 -0
- package/dist/types/services/index.d.ts +6 -0
- package/dist/types/transcripts/index.d.ts +5 -0
- package/dist/utils/axios.d.ts +3 -0
- package/dist/utils/errors/index.d.ts +1 -0
- package/dist/utils/errors/realtime.d.ts +23 -0
- package/package.json +58 -21
- package/src/index.ts +5 -0
- package/src/services/base.ts +14 -0
- package/src/services/files/index.ts +22 -0
- package/src/services/index.ts +49 -0
- package/src/services/lemur/index.ts +49 -0
- package/src/services/realtime/factory.ts +32 -0
- package/src/services/realtime/index.ts +2 -0
- package/src/services/realtime/service.ts +184 -0
- package/src/services/transcripts/index.ts +178 -0
- package/src/types/asyncapi.generated.ts +124 -0
- package/src/types/index.ts +5 -0
- package/src/types/openapi.generated.ts +834 -0
- package/src/types/realtime/index.ts +68 -0
- package/src/types/services/abstractions.ts +56 -0
- package/src/types/services/index.ts +7 -0
- package/src/types/transcripts/index.ts +5 -0
- package/src/utils/.gitkeep +0 -0
- package/src/utils/axios.ts +19 -0
- package/src/utils/errors/index.ts +5 -0
- package/src/utils/errors/realtime.ts +45 -0
- package/.eslintrc.json +0 -3
- package/index.js +0 -15
- package/src/Client.js +0 -28
- package/src/api/Http/Request.js +0 -108
- package/src/api/Http/Response.js +0 -23
- package/src/api/Model.js +0 -17
- package/src/api/Transcript.js +0 -16
- package/src/api/Upload.js +0 -41
- package/src/api/util.js +0 -48
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { FinalTranscript, PartialTranscript, RealtimeTranscript, RealtimeTranscriptType } from "../asyncapi.generated";
|
|
2
|
+
type CreateRealtimeServiceParams = {
|
|
3
|
+
realtimeUrl?: string;
|
|
4
|
+
sampleRate?: number;
|
|
5
|
+
wordBoost?: string[];
|
|
6
|
+
} & ({
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
} | {
|
|
9
|
+
token: string;
|
|
10
|
+
});
|
|
11
|
+
type RealtimeServiceParams = {
|
|
12
|
+
realtimeUrl?: string;
|
|
13
|
+
sampleRate?: number;
|
|
14
|
+
wordBoost?: string[];
|
|
15
|
+
} & ({
|
|
16
|
+
apiKey: string;
|
|
17
|
+
} | {
|
|
18
|
+
token: string;
|
|
19
|
+
});
|
|
20
|
+
type RealtimeEvents = "open" | "close" | "transcript" | "transcript.partial" | "transcript.final" | "error";
|
|
21
|
+
type SessionBeginsEventData = {
|
|
22
|
+
sessionId: string;
|
|
23
|
+
expiresAt: Date;
|
|
24
|
+
};
|
|
25
|
+
type RealtimeListeners = {
|
|
26
|
+
open?: (event: SessionBeginsEventData) => void;
|
|
27
|
+
close?: (code: number, reason: string) => void;
|
|
28
|
+
transcript?: (transcript: RealtimeTranscript) => void;
|
|
29
|
+
"transcript.partial"?: (transcript: PartialTranscript) => void;
|
|
30
|
+
"transcript.final"?: (transcript: FinalTranscript) => void;
|
|
31
|
+
error?: (error: Error) => void;
|
|
32
|
+
};
|
|
33
|
+
type RealtimeTokenParams = {
|
|
34
|
+
expires_in: number;
|
|
35
|
+
};
|
|
36
|
+
export type { CreateRealtimeServiceParams, RealtimeServiceParams, RealtimeEvents, RealtimeTranscriptType, SessionBeginsEventData, RealtimeListeners, RealtimeTokenParams, };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for classes that can create resources.
|
|
3
|
+
* @template T The type of the resource.
|
|
4
|
+
* @template Parameters The type of the parameters required to create the resource.
|
|
5
|
+
*/
|
|
6
|
+
interface Createable<T, Parameters, Options = Record<string, any>> {
|
|
7
|
+
/**
|
|
8
|
+
* Create a new resource.
|
|
9
|
+
* @param params The parameters of the new resource.
|
|
10
|
+
* @param options The options used for creating the new resource.
|
|
11
|
+
* @return A promise that resolves to the newly created resource.
|
|
12
|
+
*/
|
|
13
|
+
create(params: Parameters, options?: Options): Promise<T>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Interface for classes that can retrieve resources.
|
|
17
|
+
* @template T The type of the resource.
|
|
18
|
+
* @template Id The type of the resource's identifier. Defaults to string.
|
|
19
|
+
*/
|
|
20
|
+
interface Retrieveable<T, Id = string> {
|
|
21
|
+
/**
|
|
22
|
+
* Get a resource.
|
|
23
|
+
* @param id The identifier of the resource to retrieve.
|
|
24
|
+
* @return A promise that resolves to the retrieved resource.
|
|
25
|
+
*/
|
|
26
|
+
get(id: Id): Promise<T>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Interface for classes that can delete resources.
|
|
30
|
+
* @template T The type of the resource.
|
|
31
|
+
* @template Id The type of the resource's identifier. Defaults to string.
|
|
32
|
+
*/
|
|
33
|
+
interface Deletable<T, Id = string> {
|
|
34
|
+
/**
|
|
35
|
+
* Delete a resource.
|
|
36
|
+
* @param id The identifier of the resource to delete.
|
|
37
|
+
* @return A promise that resolves to a boolean indicating whether the deletion was successful.
|
|
38
|
+
*/
|
|
39
|
+
delete(id: Id): Promise<T>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Interface for classes that can list resources.
|
|
43
|
+
* @template T The type of the resource.
|
|
44
|
+
*/
|
|
45
|
+
interface Listable<T, Page = string> {
|
|
46
|
+
/**
|
|
47
|
+
* List all resources.
|
|
48
|
+
* @return A promise that resolves to an array of resources.
|
|
49
|
+
*/
|
|
50
|
+
list(page?: Page): Promise<T>;
|
|
51
|
+
}
|
|
52
|
+
export type { Createable, Retrieveable, Deletable, Listable };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as RealtimeError, RealtimeErrorType, RealtimeErrorMessages, } from "./realtime";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
declare enum RealtimeErrorType {
|
|
2
|
+
BadSampleRate = 4000,
|
|
3
|
+
AuthFailed = 4001,
|
|
4
|
+
InsufficientFundsOrFreeAccount = 4002,
|
|
5
|
+
NonexistentSessionId = 4004,
|
|
6
|
+
SessionExpired = 4008,
|
|
7
|
+
ClosedSession = 4010,
|
|
8
|
+
RateLimited = 4029,
|
|
9
|
+
UniqueSessionViolation = 4030,
|
|
10
|
+
SessionTimeout = 4031,
|
|
11
|
+
AudioTooShort = 4032,
|
|
12
|
+
AudioTooLong = 4033,
|
|
13
|
+
BadJson = 4100,
|
|
14
|
+
BadSchema = 4101,
|
|
15
|
+
TooManyStreams = 4102,
|
|
16
|
+
Reconnected = 4103,
|
|
17
|
+
ReconnectAttemptsExhausted = 1013
|
|
18
|
+
}
|
|
19
|
+
declare const RealtimeErrorMessages: Record<RealtimeErrorType, string>;
|
|
20
|
+
declare class RealtimeError extends Error {
|
|
21
|
+
}
|
|
22
|
+
export { RealtimeErrorType, RealtimeErrorMessages };
|
|
23
|
+
export default RealtimeError;
|
package/package.json
CHANGED
|
@@ -1,31 +1,68 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assemblyai",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
"version": "2.0.1-beta",
|
|
4
|
+
"description": "The AssemblyAI Node.js 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
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.esm.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"typings": "dist/index.d.ts",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/AssemblyAI/assemblyai-
|
|
11
|
+
"url": "git+https://github.com/AssemblyAI/assemblyai-typescript-sdk.git"
|
|
12
12
|
},
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"url": "https://github.com/AssemblyAI/assemblyai-node-sdk/issues"
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"tag": "beta",
|
|
15
|
+
"access": "public",
|
|
16
|
+
"registry": "https://registry.npmjs.org/"
|
|
18
17
|
},
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
"
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "pnpm clean && pnpm rollup -c",
|
|
20
|
+
"clean": "rimraf dist",
|
|
21
|
+
"lint": "tslint -p tsconfig.json",
|
|
22
|
+
"test": "pnpm lint && pnpm test:unit",
|
|
23
|
+
"test:unit": "jest --config jest.config.rollup.ts",
|
|
24
|
+
"prettier": "prettier --write 'src/**/*.ts'",
|
|
25
|
+
"generate-types": "tsx ./scripts/generate-types.ts && pnpm prettier"
|
|
22
26
|
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"AssemblyAI",
|
|
29
|
+
"Speech-to-text"
|
|
30
|
+
],
|
|
31
|
+
"author": "AssemblyAI (https://www.assemblyai.com)",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"homepage": "https://www.assemblyai.com/docs",
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"src",
|
|
37
|
+
"types"
|
|
38
|
+
],
|
|
23
39
|
"devDependencies": {
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"eslint
|
|
29
|
-
"
|
|
40
|
+
"@types/jest": "^29.5.5",
|
|
41
|
+
"@types/node": "^20.5.7",
|
|
42
|
+
"@types/ws": "^8.5.5",
|
|
43
|
+
"dotenv": "^16.3.1",
|
|
44
|
+
"eslint": "^8.43.0",
|
|
45
|
+
"i": "^0.3.7",
|
|
46
|
+
"jest": "^29.5.0",
|
|
47
|
+
"jest-cli": "^29.5.0",
|
|
48
|
+
"jest-junit": "^16.0.0",
|
|
49
|
+
"jest-mock-extended": "^3.0.4",
|
|
50
|
+
"jest-websocket-mock": "^2.4.1",
|
|
51
|
+
"mock-socket": "^9.2.1",
|
|
52
|
+
"npm": "^9.7.1",
|
|
53
|
+
"openapi-typescript": "^6.6.1",
|
|
54
|
+
"prettier": "^2.8.8",
|
|
55
|
+
"rimraf": "^5.0.1",
|
|
56
|
+
"rollup": "^3.25.1",
|
|
57
|
+
"rollup-plugin-typescript2": "^0.34.1",
|
|
58
|
+
"ts-jest": "^29.1.0",
|
|
59
|
+
"ts-node": "^10.9.1",
|
|
60
|
+
"tslib": "^2.5.3",
|
|
61
|
+
"tslint": "^6.1.3",
|
|
62
|
+
"typescript": "^5.2.2"
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"axios": "^1.4.0",
|
|
66
|
+
"ws": "^8.13.0"
|
|
30
67
|
}
|
|
31
68
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AxiosInstance } from "axios";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base class for services that communicate with the API.
|
|
5
|
+
*/
|
|
6
|
+
abstract class BaseService {
|
|
7
|
+
/**
|
|
8
|
+
* Create a new service.
|
|
9
|
+
* @param params The AxiosInstance to send HTTP requests to the API.
|
|
10
|
+
*/
|
|
11
|
+
constructor(protected client: AxiosInstance) {}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default BaseService;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
import BaseService from "@/services/base";
|
|
3
|
+
import { UploadedFile } from "@/types";
|
|
4
|
+
|
|
5
|
+
export default class FileService extends BaseService {
|
|
6
|
+
/**
|
|
7
|
+
* Upload a local file to AssemblyAI.
|
|
8
|
+
* @param path The local file to upload.
|
|
9
|
+
* @return A promise that resolves to the uploaded file URL.
|
|
10
|
+
*/
|
|
11
|
+
async upload(path: string): Promise<string> {
|
|
12
|
+
const file = await readFile(path);
|
|
13
|
+
|
|
14
|
+
const { data } = await this.client.post<UploadedFile>("/v2/upload", file, {
|
|
15
|
+
headers: {
|
|
16
|
+
"Content-Type": "application/octet-stream",
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return data.upload_url;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createAxiosClient } from "@/utils/axios";
|
|
2
|
+
import { BaseServiceParams } from "@/types";
|
|
3
|
+
import LemurService from "./lemur";
|
|
4
|
+
import { RealtimeService, RealtimeServiceFactory } from "./realtime";
|
|
5
|
+
import TranscriptService from "./transcripts";
|
|
6
|
+
import FileService from "./files";
|
|
7
|
+
|
|
8
|
+
export default class AssemblyAI {
|
|
9
|
+
/**
|
|
10
|
+
* The files service.
|
|
11
|
+
*/
|
|
12
|
+
public files: FileService;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The transcripts service.
|
|
16
|
+
*/
|
|
17
|
+
public transcripts: TranscriptService;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The LeMUR service.
|
|
21
|
+
*/
|
|
22
|
+
public lemur: LemurService;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The realtime service.
|
|
26
|
+
*/
|
|
27
|
+
public realtime: RealtimeServiceFactory;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create a new AssemblyAI client.
|
|
31
|
+
* @param params The parameters for the service, including the API key and base URL, if any.
|
|
32
|
+
*/
|
|
33
|
+
constructor(params: BaseServiceParams) {
|
|
34
|
+
params.baseUrl = params.baseUrl || "https://api.assemblyai.com";
|
|
35
|
+
const client = createAxiosClient(params);
|
|
36
|
+
this.files = new FileService(client);
|
|
37
|
+
this.transcripts = new TranscriptService(client, this.files);
|
|
38
|
+
this.lemur = new LemurService(client);
|
|
39
|
+
this.realtime = new RealtimeServiceFactory(client, params);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
LemurService,
|
|
45
|
+
RealtimeServiceFactory,
|
|
46
|
+
RealtimeService,
|
|
47
|
+
TranscriptService,
|
|
48
|
+
FileService,
|
|
49
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LemurSummaryParameters,
|
|
3
|
+
LemurActionItemsParameters,
|
|
4
|
+
LemurQuestionAnswerParameters,
|
|
5
|
+
LemurTaskParameters,
|
|
6
|
+
LemurSummaryResponse,
|
|
7
|
+
LemurQuestionAnswerResponse,
|
|
8
|
+
LemurActionItemsResponse,
|
|
9
|
+
LemurTaskResponse,
|
|
10
|
+
} from "@/types";
|
|
11
|
+
import BaseService from "@/services/base";
|
|
12
|
+
|
|
13
|
+
export default class LemurService extends BaseService {
|
|
14
|
+
async summary(params: LemurSummaryParameters): Promise<LemurSummaryResponse> {
|
|
15
|
+
const { data } = await this.client.post<LemurSummaryResponse>(
|
|
16
|
+
"/lemur/v3/generate/summary",
|
|
17
|
+
params
|
|
18
|
+
);
|
|
19
|
+
return data;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async questionAnswer(
|
|
23
|
+
params: LemurQuestionAnswerParameters
|
|
24
|
+
): Promise<LemurQuestionAnswerResponse> {
|
|
25
|
+
const { data } = await this.client.post<LemurQuestionAnswerResponse>(
|
|
26
|
+
"/lemur/v3/generate/question-answer",
|
|
27
|
+
params
|
|
28
|
+
);
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async actionItems(
|
|
33
|
+
params: LemurActionItemsParameters
|
|
34
|
+
): Promise<LemurActionItemsResponse> {
|
|
35
|
+
const { data } = await this.client.post<LemurActionItemsResponse>(
|
|
36
|
+
"/lemur/v3/generate/action-items",
|
|
37
|
+
params
|
|
38
|
+
);
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async task(params: LemurTaskParameters): Promise<LemurTaskResponse> {
|
|
43
|
+
const { data } = await this.client.post<LemurTaskResponse>(
|
|
44
|
+
"/lemur/v3/generate/task",
|
|
45
|
+
params
|
|
46
|
+
);
|
|
47
|
+
return data;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseServiceParams,
|
|
3
|
+
RealtimeTokenParams,
|
|
4
|
+
CreateRealtimeServiceParams,
|
|
5
|
+
RealtimeServiceParams,
|
|
6
|
+
} from "@/types";
|
|
7
|
+
import { AxiosInstance } from "axios";
|
|
8
|
+
import { RealtimeService } from "./service";
|
|
9
|
+
|
|
10
|
+
export class RealtimeServiceFactory {
|
|
11
|
+
constructor(
|
|
12
|
+
private client: AxiosInstance,
|
|
13
|
+
private params: BaseServiceParams
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
createService(params?: CreateRealtimeServiceParams): RealtimeService {
|
|
17
|
+
if (!params) params = { apiKey: this.params.apiKey };
|
|
18
|
+
else if (!("token" in params) && !params.apiKey) {
|
|
19
|
+
params.apiKey = this.params.apiKey;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return new RealtimeService(params as RealtimeServiceParams);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async createTemporaryToken(params: RealtimeTokenParams) {
|
|
26
|
+
const response = await this.client.post<{ token: string }>(
|
|
27
|
+
"/v2/realtime/token",
|
|
28
|
+
params
|
|
29
|
+
);
|
|
30
|
+
return response.data.token;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import WebSocket from "ws";
|
|
2
|
+
import {
|
|
3
|
+
RealtimeEvents,
|
|
4
|
+
RealtimeListeners,
|
|
5
|
+
RealtimeServiceParams,
|
|
6
|
+
RealtimeMessage,
|
|
7
|
+
RealtimeTranscript,
|
|
8
|
+
PartialTranscript,
|
|
9
|
+
FinalTranscript,
|
|
10
|
+
SessionBeginsEventData,
|
|
11
|
+
} from "@/types";
|
|
12
|
+
import {
|
|
13
|
+
RealtimeError,
|
|
14
|
+
RealtimeErrorMessages,
|
|
15
|
+
RealtimeErrorType,
|
|
16
|
+
} from "@/utils/errors";
|
|
17
|
+
|
|
18
|
+
const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
|
|
19
|
+
|
|
20
|
+
export class RealtimeService {
|
|
21
|
+
private realtimeUrl: string;
|
|
22
|
+
private sampleRate: number;
|
|
23
|
+
private wordBoost?: string[];
|
|
24
|
+
private apiKey?: string;
|
|
25
|
+
private token?: string;
|
|
26
|
+
private socket?: WebSocket;
|
|
27
|
+
private listeners: RealtimeListeners = {};
|
|
28
|
+
private sessionTerminatedResolve?: () => void;
|
|
29
|
+
|
|
30
|
+
constructor(params: RealtimeServiceParams) {
|
|
31
|
+
this.realtimeUrl = params.realtimeUrl ?? defaultRealtimeUrl;
|
|
32
|
+
this.sampleRate = params.sampleRate ?? 16_000;
|
|
33
|
+
this.wordBoost = params.wordBoost;
|
|
34
|
+
this.realtimeUrl = params.realtimeUrl ?? defaultRealtimeUrl;
|
|
35
|
+
if ("apiKey" in params) this.apiKey = params.apiKey;
|
|
36
|
+
if ("token" in params) this.token = params.token;
|
|
37
|
+
|
|
38
|
+
if (!(this.apiKey || this.token)) {
|
|
39
|
+
throw new Error("API key or temporary token is required.");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private connectionUrl(): URL {
|
|
44
|
+
const url = new URL(this.realtimeUrl);
|
|
45
|
+
|
|
46
|
+
if (url.protocol !== "wss:") {
|
|
47
|
+
throw new Error("Invalid protocol, must be wss");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const searchParams = new URLSearchParams();
|
|
51
|
+
if (this.token) {
|
|
52
|
+
searchParams.set("token", this.token);
|
|
53
|
+
}
|
|
54
|
+
searchParams.set("sample_rate", this.sampleRate.toString());
|
|
55
|
+
if (this.wordBoost && this.wordBoost.length > 0) {
|
|
56
|
+
searchParams.set("word_boost", JSON.stringify(this.wordBoost));
|
|
57
|
+
}
|
|
58
|
+
url.search = searchParams.toString();
|
|
59
|
+
|
|
60
|
+
return url;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
on(event: "open", listener: (event: SessionBeginsEventData) => void): void;
|
|
64
|
+
on(
|
|
65
|
+
event: "transcript",
|
|
66
|
+
listener: (transcript: RealtimeTranscript) => void
|
|
67
|
+
): void;
|
|
68
|
+
on(
|
|
69
|
+
event: "transcript.partial",
|
|
70
|
+
listener: (transcript: PartialTranscript) => void
|
|
71
|
+
): void;
|
|
72
|
+
on(
|
|
73
|
+
event: "transcript.final",
|
|
74
|
+
listener: (transcript: FinalTranscript) => void
|
|
75
|
+
): void;
|
|
76
|
+
on(event: "error", listener: (error: Error) => void): void;
|
|
77
|
+
on(event: "close", listener: (code: number, reason: string) => void): void;
|
|
78
|
+
on(event: RealtimeEvents, listener: (...args: any[]) => void) {
|
|
79
|
+
this.listeners[event] = listener;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
connect() {
|
|
83
|
+
return new Promise<SessionBeginsEventData>((resolve, _) => {
|
|
84
|
+
if (this.socket) {
|
|
85
|
+
throw new Error("Already connected");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const url = this.connectionUrl();
|
|
89
|
+
|
|
90
|
+
let headers;
|
|
91
|
+
if (this.token) {
|
|
92
|
+
headers = undefined;
|
|
93
|
+
} else if (this.apiKey) {
|
|
94
|
+
headers = { Authorization: this.apiKey };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.socket = new WebSocket(url.toString(), { headers });
|
|
98
|
+
|
|
99
|
+
this.socket.onclose = ({ code, reason }: WebSocket.CloseEvent) => {
|
|
100
|
+
if (!reason) {
|
|
101
|
+
if (code in RealtimeErrorType) {
|
|
102
|
+
reason = RealtimeErrorMessages[code as RealtimeErrorType];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
this.listeners.close?.(code, reason);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
this.socket.onerror = (errorEvent: WebSocket.ErrorEvent) => {
|
|
109
|
+
if (errorEvent.error) this.listeners.error?.(errorEvent.error as Error);
|
|
110
|
+
else this.listeners.error?.(new Error(errorEvent.message));
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
this.socket.onmessage = ({ data }: WebSocket.MessageEvent) => {
|
|
114
|
+
const message = JSON.parse(data.toString()) as RealtimeMessage;
|
|
115
|
+
if ("error" in message) {
|
|
116
|
+
this.listeners.error?.(new RealtimeError(message.error));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
switch (message.message_type) {
|
|
120
|
+
case "SessionBegins": {
|
|
121
|
+
const openObject: SessionBeginsEventData = {
|
|
122
|
+
sessionId: message.session_id,
|
|
123
|
+
expiresAt: new Date(message.expires_at),
|
|
124
|
+
};
|
|
125
|
+
resolve(openObject);
|
|
126
|
+
this.listeners.open?.(openObject);
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
case "PartialTranscript": {
|
|
130
|
+
// message.created is actually a string when coming from the socket
|
|
131
|
+
message.created = new Date(message.created);
|
|
132
|
+
this.listeners.transcript?.(message);
|
|
133
|
+
this.listeners["transcript.partial"]?.(message);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case "FinalTranscript": {
|
|
137
|
+
// message.created is actually a string when coming from the socket
|
|
138
|
+
message.created = new Date(message.created);
|
|
139
|
+
this.listeners.transcript?.(message);
|
|
140
|
+
this.listeners["transcript.final"]?.(message);
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
case "SessionTerminated": {
|
|
144
|
+
this.sessionTerminatedResolve?.();
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
sendAudio(audio: ArrayBuffer) {
|
|
153
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
154
|
+
throw new Error("Socket is not open for communication");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const payload = {
|
|
158
|
+
audio_data: Buffer.from(audio).toString("base64"),
|
|
159
|
+
};
|
|
160
|
+
this.socket.send(JSON.stringify(payload));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async close(waitForSessionTermination = true) {
|
|
164
|
+
if (this.socket) {
|
|
165
|
+
if (this.socket.readyState === WebSocket.OPEN) {
|
|
166
|
+
const terminateSessionMessage = `{"terminate_session": true}`;
|
|
167
|
+
if (waitForSessionTermination) {
|
|
168
|
+
const sessionTerminatedPromise = new Promise<void>((resolve, _) => {
|
|
169
|
+
this.sessionTerminatedResolve = resolve;
|
|
170
|
+
});
|
|
171
|
+
this.socket.send(terminateSessionMessage);
|
|
172
|
+
await sessionTerminatedPromise;
|
|
173
|
+
} else {
|
|
174
|
+
this.socket.send(terminateSessionMessage);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
this.socket.removeAllListeners();
|
|
178
|
+
this.socket.close();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.listeners = {};
|
|
182
|
+
this.socket = undefined;
|
|
183
|
+
}
|
|
184
|
+
}
|