assemblyai 3.1.3 → 4.0.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/CHANGELOG.md +92 -0
- package/README.md +6 -5
- package/dist/assemblyai.umd.js +683 -0
- package/dist/assemblyai.umd.min.js +1 -0
- package/dist/index.cjs +36 -20
- package/dist/index.mjs +36 -20
- package/dist/index.node.cjs +618 -0
- package/dist/index.node.mjs +611 -0
- package/dist/polyfills/no-fs.d.ts +6 -0
- package/dist/services/realtime/service.d.ts +3 -2
- package/docs/compat.md +59 -0
- package/package.json +37 -10
- package/src/polyfills/no-fs.ts +8 -0
- package/src/services/files/index.ts +0 -2
- package/src/services/realtime/service.ts +34 -24
package/docs/compat.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# SDK Compatibility
|
|
2
|
+
|
|
3
|
+
The JavaScript SDK is developed for Node.js but is also compatible with other runtimes
|
|
4
|
+
such as the browser, Deno, Bun, Cloudflare Workers, etc.
|
|
5
|
+
|
|
6
|
+
## Browser compatibility
|
|
7
|
+
|
|
8
|
+
To make the SDK compatible with the browser, the SDK aims to use web standards as much as possible.
|
|
9
|
+
However, there are still incompatibilities between Node.js and the browser.
|
|
10
|
+
|
|
11
|
+
- `RealtimeService` doesn't support the AssemblyAI API key in the browser.
|
|
12
|
+
Instead, you have to generate a temporary auth token using `client.realtime.createTemporaryToken`, and pass in the resulting token to the real-time transcriber.
|
|
13
|
+
|
|
14
|
+
Generate a temporary auth token on the server.
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
import { AssemblyAI } from "assemblyai"
|
|
18
|
+
// Ideally, to avoid embedding your API key client side,
|
|
19
|
+
// you generate this token on the server, and pass it to the client via an API.
|
|
20
|
+
const client = new AssemblyAI({ apiKey: "YOUR_API_KEY" });
|
|
21
|
+
const token = await client.realtime.createTemporaryToken({ expires_in = 480 });
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
> [!NOTE]
|
|
25
|
+
> We recommend generating the token on the server, so you don't embed your AssemblyAI API key in your client app.
|
|
26
|
+
> If you embed the API key on the client, everyone can see it and use it for themselves.
|
|
27
|
+
|
|
28
|
+
Then pass the token via an API to the client.
|
|
29
|
+
On the client, create an instance of `RealtimeService` using the token.
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import { RealtimeService } from "assemblyai";
|
|
33
|
+
// or the following if you're using UMD
|
|
34
|
+
// const { RealtimeService } = assemblyai;
|
|
35
|
+
|
|
36
|
+
const token = getToken(); // getToken is a function for you to implement
|
|
37
|
+
|
|
38
|
+
const rt = new RealtimeService({
|
|
39
|
+
token: token,
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- You can't pass local audio file paths to `client.files.upload`, `client.transcripts.transcribe`, and `client.transcripts.submit`. If you do, you'll get the following error: "'fs' is not supported in this environment.".
|
|
44
|
+
If you want to transcribe audio files, you must use a public URL, a stream, or a buffer.
|
|
45
|
+
|
|
46
|
+
> [!WARNING]
|
|
47
|
+
> The SDK is usable from the browser, but we strongly recommend you don't embed the AssemblyAI API key into your client apps.
|
|
48
|
+
> If you embed the API key on the client, everyone can see it and use it for themselves.
|
|
49
|
+
> Instead, create use the SDK on the server and provide APIs for your client to call.
|
|
50
|
+
|
|
51
|
+
## Deno, Bun, Cloudflare Workers, etc.
|
|
52
|
+
|
|
53
|
+
Most server-side JavaScript runtimes include a compatibility layer with Node.js.
|
|
54
|
+
Our SDK is developed for Node.js, which makes it compatible with other runtimes through their compatibility layer.
|
|
55
|
+
The bugs in these compatibility layers may introduce issues in our SDK.
|
|
56
|
+
|
|
57
|
+
## Report issues
|
|
58
|
+
|
|
59
|
+
If you find any (undocumented) bugs when using the SDK, [submit a GitHub issue](https://github.com/AssemblyAI/assemblyai-node-sdk). We'll try to fix it or at least document the compatibility issue.
|
package/package.json
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assemblyai",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "The AssemblyAI
|
|
3
|
+
"version": "4.0.0-beta.1",
|
|
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
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
|
+
"node": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.node.mjs",
|
|
11
|
+
"require": "./dist/index.node.cjs"
|
|
12
|
+
},
|
|
13
|
+
"bun": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.node.mjs",
|
|
16
|
+
"require": "./dist/index.node.cjs"
|
|
17
|
+
},
|
|
18
|
+
"deno": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.node.mjs",
|
|
21
|
+
"require": "./dist/index.node.cjs"
|
|
22
|
+
},
|
|
23
|
+
"workerd": "./dist/index.mjs",
|
|
24
|
+
"browser": "./dist/index.mjs",
|
|
8
25
|
"import": "./dist/index.mjs",
|
|
9
26
|
"require": "./dist/index.cjs",
|
|
10
27
|
"default": "./dist/index.cjs"
|
|
@@ -12,10 +29,11 @@
|
|
|
12
29
|
"./package.json": "./package.json"
|
|
13
30
|
},
|
|
14
31
|
"type": "commonjs",
|
|
15
|
-
"main": "dist/index.cjs",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
32
|
+
"main": "./dist/index.cjs",
|
|
33
|
+
"require": "./dist/index.cjs",
|
|
34
|
+
"module": "./dist/index.mjs",
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"typings": "./dist/index.d.ts",
|
|
19
37
|
"repository": {
|
|
20
38
|
"type": "git",
|
|
21
39
|
"url": "git+https://github.com/AssemblyAI/assemblyai-node-sdk.git"
|
|
@@ -29,8 +47,7 @@
|
|
|
29
47
|
"build": "pnpm clean && pnpm rollup -c",
|
|
30
48
|
"clean": "rimraf dist",
|
|
31
49
|
"lint": "eslint -c .eslintrc.json '{src,tests}/**/*.{js,ts}' && publint",
|
|
32
|
-
"test": "
|
|
33
|
-
"test:unit": "jest --config jest.config.rollup.ts",
|
|
50
|
+
"test": "jest --config jest.config.rollup.ts",
|
|
34
51
|
"format": "prettier '**/*' --write",
|
|
35
52
|
"generate-types": "tsx ./scripts/generate-types.ts && pnpm format",
|
|
36
53
|
"copybara:dry-run": "./copybara.sh dry_run --init-history",
|
|
@@ -48,11 +65,19 @@
|
|
|
48
65
|
"homepage": "https://www.assemblyai.com/docs",
|
|
49
66
|
"files": [
|
|
50
67
|
"dist",
|
|
51
|
-
"src"
|
|
68
|
+
"src",
|
|
69
|
+
"package.json",
|
|
70
|
+
"README.md",
|
|
71
|
+
"CHANGELOG.md",
|
|
72
|
+
"docs"
|
|
52
73
|
],
|
|
53
74
|
"devDependencies": {
|
|
75
|
+
"@rollup/plugin-alias": "^5.0.1",
|
|
76
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
77
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
54
78
|
"@types/jest": "^29.5.5",
|
|
55
79
|
"@types/node": "^20.5.7",
|
|
80
|
+
"@types/websocket": "^1.0.8",
|
|
56
81
|
"@types/ws": "^8.5.5",
|
|
57
82
|
"@typescript-eslint/eslint-plugin": "^6.7.5",
|
|
58
83
|
"dotenv": "^16.3.1",
|
|
@@ -78,6 +103,8 @@
|
|
|
78
103
|
"typescript": "^5.2.2"
|
|
79
104
|
},
|
|
80
105
|
"dependencies": {
|
|
106
|
+
"@swimburger/isomorphic-streams": "^1.1.1",
|
|
107
|
+
"isomorphic-ws": "^5.0.0",
|
|
81
108
|
"ws": "^8.13.0"
|
|
82
109
|
}
|
|
83
|
-
}
|
|
110
|
+
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// import the fs module instead if specific named exports
|
|
2
|
-
// to keep the assemblyai module more compatible. Some fs polyfills don't include `createReadStream`.
|
|
3
1
|
import fs from "fs";
|
|
4
2
|
import { BaseService } from "../base";
|
|
5
3
|
import { UploadedFile, FileUploadParams, FileUploadData } from "../..";
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { WritableStream } from "@swimburger/isomorphic-streams";
|
|
2
|
+
import WebSocket from "isomorphic-ws";
|
|
3
|
+
import { ErrorEvent, MessageEvent, CloseEvent } from "ws";
|
|
2
4
|
import {
|
|
3
5
|
RealtimeEvents,
|
|
4
6
|
RealtimeListeners,
|
|
@@ -14,7 +16,6 @@ import {
|
|
|
14
16
|
RealtimeErrorMessages,
|
|
15
17
|
RealtimeErrorType,
|
|
16
18
|
} from "../../utils/errors";
|
|
17
|
-
import Stream from "stream";
|
|
18
19
|
|
|
19
20
|
const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
|
|
20
21
|
|
|
@@ -32,8 +33,8 @@ export class RealtimeService {
|
|
|
32
33
|
this.realtimeUrl = params.realtimeUrl ?? defaultRealtimeUrl;
|
|
33
34
|
this.sampleRate = params.sampleRate ?? 16_000;
|
|
34
35
|
this.wordBoost = params.wordBoost;
|
|
35
|
-
if ("apiKey" in params) this.apiKey = params.apiKey;
|
|
36
|
-
if ("token" in params) this.token = params.token;
|
|
36
|
+
if ("apiKey" in params && params.apiKey) this.apiKey = params.apiKey;
|
|
37
|
+
if ("token" in params && params.token) this.token = params.token;
|
|
37
38
|
|
|
38
39
|
if (!(this.apiKey || this.token)) {
|
|
39
40
|
throw new Error("API key or temporary token is required.");
|
|
@@ -88,16 +89,15 @@ export class RealtimeService {
|
|
|
88
89
|
|
|
89
90
|
const url = this.connectionUrl();
|
|
90
91
|
|
|
91
|
-
let headers;
|
|
92
92
|
if (this.token) {
|
|
93
|
-
|
|
94
|
-
} else
|
|
95
|
-
|
|
93
|
+
this.socket = new WebSocket(url.toString());
|
|
94
|
+
} else {
|
|
95
|
+
this.socket = new WebSocket(url.toString(), {
|
|
96
|
+
headers: { Authorization: this.apiKey },
|
|
97
|
+
});
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
this.socket =
|
|
99
|
-
|
|
100
|
-
this.socket.onclose = ({ code, reason }: WebSocket.CloseEvent) => {
|
|
100
|
+
this.socket.onclose = ({ code, reason }: CloseEvent) => {
|
|
101
101
|
if (!reason) {
|
|
102
102
|
if (code in RealtimeErrorType) {
|
|
103
103
|
reason = RealtimeErrorMessages[code as RealtimeErrorType];
|
|
@@ -106,12 +106,12 @@ export class RealtimeService {
|
|
|
106
106
|
this.listeners.close?.(code, reason);
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
-
this.socket.onerror = (
|
|
110
|
-
if (
|
|
111
|
-
else this.listeners.error?.(new Error(
|
|
109
|
+
this.socket.onerror = (event: ErrorEvent) => {
|
|
110
|
+
if (event.error) this.listeners.error?.(event.error as Error);
|
|
111
|
+
else this.listeners.error?.(new Error(event.message));
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
-
this.socket.onmessage = ({ data }:
|
|
114
|
+
this.socket.onmessage = ({ data }: MessageEvent) => {
|
|
115
115
|
const message = JSON.parse(data.toString()) as RealtimeMessage;
|
|
116
116
|
if ("error" in message) {
|
|
117
117
|
this.listeners.error?.(new RealtimeError(message.error));
|
|
@@ -150,25 +150,35 @@ export class RealtimeService {
|
|
|
150
150
|
});
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
sendAudio(audio:
|
|
153
|
+
sendAudio(audio: ArrayBufferLike) {
|
|
154
154
|
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
155
155
|
throw new Error("Socket is not open for communication");
|
|
156
156
|
}
|
|
157
|
-
|
|
157
|
+
let audioData;
|
|
158
|
+
if (typeof Buffer !== "undefined") {
|
|
159
|
+
audioData = Buffer.from(audio).toString("base64");
|
|
160
|
+
} else {
|
|
161
|
+
// Buffer is not available in the browser by default
|
|
162
|
+
// https://stackoverflow.com/a/42334410/2919731
|
|
163
|
+
audioData = btoa(
|
|
164
|
+
new Uint8Array(audio).reduce(
|
|
165
|
+
(data, byte) => data + String.fromCharCode(byte),
|
|
166
|
+
""
|
|
167
|
+
)
|
|
168
|
+
);
|
|
169
|
+
}
|
|
158
170
|
const payload = {
|
|
159
|
-
audio_data:
|
|
171
|
+
audio_data: audioData,
|
|
160
172
|
};
|
|
161
173
|
this.socket.send(JSON.stringify(payload));
|
|
162
174
|
}
|
|
163
175
|
|
|
164
|
-
stream():
|
|
165
|
-
|
|
166
|
-
write: (chunk:
|
|
176
|
+
stream(): WritableStream<ArrayBufferLike> {
|
|
177
|
+
return new WritableStream<ArrayBufferLike>({
|
|
178
|
+
write: (chunk: ArrayBufferLike) => {
|
|
167
179
|
this.sendAudio(chunk);
|
|
168
|
-
next();
|
|
169
180
|
},
|
|
170
181
|
});
|
|
171
|
-
return stream;
|
|
172
182
|
}
|
|
173
183
|
|
|
174
184
|
async close(waitForSessionTermination = true) {
|
|
@@ -185,7 +195,7 @@ export class RealtimeService {
|
|
|
185
195
|
this.socket.send(terminateSessionMessage);
|
|
186
196
|
}
|
|
187
197
|
}
|
|
188
|
-
this.socket.removeAllListeners();
|
|
198
|
+
if ("removeAllListeners" in this.socket) this.socket.removeAllListeners();
|
|
189
199
|
this.socket.close();
|
|
190
200
|
}
|
|
191
201
|
|