Skip to content

Commit

Permalink
Merge pull request #12 from AssemblyAI/E9359DC41F924F39F84D34AB645F4BF7
Browse files Browse the repository at this point in the history
V2.0.2: Client updates and bugfixes
  • Loading branch information
Swimburger authored Oct 13, 2023
2 parents 106be7d + ae4e500 commit ea15913
Show file tree
Hide file tree
Showing 20 changed files with 166 additions and 59 deletions.
16 changes: 14 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
{
"extends": "standard"
}
"root": true,
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": { "project": ["./tsconfig.json"] },
"plugins": [
"@typescript-eslint"
],
"rules": {
},
"ignorePatterns": ["test/**/*", "dist/**/*", "node_modules/**/*"]
}
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
[![Discord](https://img.shields.io/discord/875120158014853141?logo=discord&label=Discord&link=https%3A%2F%2Fdiscord.com%2Fchannels%2F875120158014853141&style=social)
](https://discord.gg/5aQNZyq3)


# AssemblyAI Node.js SDK

The AssemblyAI Node.js SDK provides an easy-to-use interface for interacting with the AssemblyAI API,
Expand Down Expand Up @@ -165,13 +164,13 @@ const { response } = await client.lemur.task({
Create the real-time service.

```typescript
const service = client.realtime.createService();
const rt = client.realtime.createService();
```

You can also pass in the following options.

```typescript
const service = client.realtime.createService({
const rt = client.realtime.createService({
realtimeUrl: 'wss://localhost/override',
apiKey: process.env.ASSEMBLYAI_API_KEY // The API key passed to `AssemblyAI` will be used by default,
sampleRate: 16_000,
Expand Down Expand Up @@ -209,14 +208,19 @@ After configuring your events, connect to the server.
await rt.connect();
```

Send audio data.
Send audio data via chunks.

```typescript
// Pseudo code for getting audio
getAudio((chunk) => {
rt.sendAudio(chunk);
});
```
Or send audio data via a stream by piping to the realtime stream.

```typescript
audioStream.pipe(rt.stream());
```

Close the connection when you're finished.

Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "assemblyai",
"version": "2.0.1",
"version": "2.0.2",
"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.",
"main": "dist/index.js",
"module": "dist/index.esm.js",
Expand All @@ -18,7 +18,7 @@
"scripts": {
"build": "pnpm clean && pnpm rollup -c",
"clean": "rimraf dist",
"lint": "tslint -p tsconfig.json",
"lint": "eslint -c .eslintrc.json 'src/**/*'",
"test": "pnpm lint && pnpm test:unit",
"test:unit": "jest --config jest.config.rollup.ts",
"format": "prettier --write 'src/**/*.ts'",
Expand All @@ -45,8 +45,9 @@
"@types/jest": "^29.5.5",
"@types/node": "^20.5.7",
"@types/ws": "^8.5.5",
"@typescript-eslint/eslint-plugin": "^6.7.5",
"dotenv": "^16.3.1",
"eslint": "^8.43.0",
"eslint": "^8.48.0",
"i": "^0.3.7",
"jest": "^29.5.0",
"jest-cli": "^29.5.0",
Expand All @@ -63,7 +64,6 @@
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"tslib": "^2.5.3",
"tslint": "^6.1.3",
"typescript": "^5.2.2"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ module.exports = [
{ file: pkg.module, format: 'es' }
],
plugins,
external: ['axios', 'fs/promises', 'ws']
external: ['axios', 'fs/promises', 'stream', 'ws']
},
]
28 changes: 17 additions & 11 deletions scripts/kitchensink.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createReadStream } from 'fs'
import 'dotenv/config'
import AssemblyAI, { Transcript, CreateTranscriptParameters } from '../src/index';
import { FinalTranscript, PartialTranscript, RealtimeTranscript } from '../src/types'
import { FinalTranscript, LemurBaseResponse, PartialTranscript, RealtimeTranscript } from '../src/types'

const client = new AssemblyAI({
apiKey: process.env.ASSEMBLYAI_API_KEY || '',
Expand Down Expand Up @@ -52,7 +52,7 @@ const client = new AssemblyAI({
})();

const audioUrl = 'https://storage.googleapis.com/aai-docs-samples/espn.m4a';
const createTranscriptParams: CreateTranscriptParameters = {
const createTranscriptParams: CreateTranscriptParameters = {
audio_url: audioUrl,
boost_param: 'high',
word_boost: ['Chicago', 'draft'],
Expand All @@ -79,10 +79,10 @@ const createTranscriptParams: CreateTranscriptParameters = {

(async function runLemurModels() {
const transcript = await client.transcripts.create(createTranscriptParams);
await lemurSummary(transcript);
await lemurQuestionAnswer(transcript);
await lemurActionPoints(transcript);
await lemurCustomTask(transcript);
await lemurSummary(transcript).then(purgeLemurRequestData);
await lemurQuestionAnswer(transcript).then(purgeLemurRequestData);
await lemurActionPoints(transcript).then(purgeLemurRequestData);
await lemurCustomTask(transcript).then(purgeLemurRequestData);
await deleteTranscript(transcript);
})();

Expand Down Expand Up @@ -255,11 +255,8 @@ const createTranscriptParams: CreateTranscriptParameters = {
})();

async function searchTranscript(transcript: Transcript) {
console.error('Search is not yet implemented');
// const result = await client.transcripts.search(transcript.id, {
// words: ['draft', 'football']
// });
// console.log(result);
const result = await client.transcripts.wordSearch(transcript.id, ['draft', 'football']);
console.log(result);
}

async function exportAsSubtitles(transcript: Transcript) {
Expand Down Expand Up @@ -294,6 +291,7 @@ async function lemurSummary(transcript: Transcript) {
answer_format: 'bullet points'
})
console.log(response.response);
return response;
}

async function lemurQuestionAnswer(transcript: Transcript) {
Expand All @@ -316,6 +314,7 @@ async function lemurQuestionAnswer(transcript: Transcript) {
max_output_size: 3000
})
console.log(response.response);
return response;
}

async function lemurActionPoints(transcript: Transcript) {
Expand All @@ -326,6 +325,7 @@ async function lemurActionPoints(transcript: Transcript) {
max_output_size: 3000
})
console.log(response.response);
return response;
}

async function lemurCustomTask(transcript: Transcript) {
Expand All @@ -337,4 +337,10 @@ async function lemurCustomTask(transcript: Transcript) {
max_output_size: 3000
})
console.log(response.response);
return response;
}

async function purgeLemurRequestData(lemurResponse: LemurBaseResponse) {
const { response } = await client.lemur.purgeRequestData(lemurResponse.request_id);
console.log(response);
};
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import AssemblyAI from "./services";

import * as services from "./services";
import { AssemblyAI } from "./services";
export * from "./services";
export type * from "./types";
export default AssemblyAI;
class AssemblyAIExports extends AssemblyAI {}
module.exports = Object.assign(AssemblyAIExports, services);
4 changes: 1 addition & 3 deletions src/services/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ import { AxiosInstance } from "axios";
/**
* Base class for services that communicate with the API.
*/
abstract class BaseService {
export abstract class BaseService {
/**
* Create a new service.
* @param params The AxiosInstance to send HTTP requests to the API.
*/
constructor(protected client: AxiosInstance) {}
}

export default BaseService;
4 changes: 2 additions & 2 deletions src/services/files/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { readFile } from "fs/promises";
import BaseService from "@/services/base";
import { BaseService } from "@/services/base";
import { UploadedFile } from "@/types";

export default class FileService extends BaseService {
export class FileService extends BaseService {
/**
* Upload a local file to AssemblyAI.
* @param path The local file to upload.
Expand Down
9 changes: 5 additions & 4 deletions src/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { createAxiosClient } from "@/utils/axios";
import { BaseServiceParams } from "@/types";
import LemurService from "./lemur";
import { LemurService } from "./lemur";
import { RealtimeService, RealtimeServiceFactory } from "./realtime";
import TranscriptService from "./transcripts";
import FileService from "./files";
import { TranscriptService } from "./transcripts";
import { FileService } from "./files";

export default class AssemblyAI {
class AssemblyAI {
/**
* The files service.
*/
Expand Down Expand Up @@ -41,6 +41,7 @@ export default class AssemblyAI {
}

export {
AssemblyAI,
LemurService,
RealtimeServiceFactory,
RealtimeService,
Expand Down
16 changes: 14 additions & 2 deletions src/services/lemur/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import {
LemurQuestionAnswerResponse,
LemurActionItemsResponse,
LemurTaskResponse,
PurgeLemurRequestDataResponse,
} from "@/types";
import BaseService from "@/services/base";
import { BaseService } from "@/services/base";

export default class LemurService extends BaseService {
export class LemurService extends BaseService {
async summary(params: LemurSummaryParameters): Promise<LemurSummaryResponse> {
const { data } = await this.client.post<LemurSummaryResponse>(
"/lemur/v3/generate/summary",
Expand Down Expand Up @@ -46,4 +47,15 @@ export default class LemurService extends BaseService {
);
return data;
}

/**
* Delete the data for a previously submitted LeMUR request.
* @param id ID of the LeMUR request
*/
async purgeRequestData(id: string): Promise<PurgeLemurRequestDataResponse> {
const { data } = await this.client.delete<PurgeLemurRequestDataResponse>(
`/lemur/v3/${id}`
);
return data;
}
}
16 changes: 14 additions & 2 deletions src/services/realtime/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
RealtimeErrorMessages,
RealtimeErrorType,
} from "@/utils/errors";
import { Writable } from "stream";

const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";

Expand Down Expand Up @@ -75,12 +76,13 @@ export class RealtimeService {
): void;
on(event: "error", listener: (error: Error) => void): void;
on(event: "close", listener: (code: number, reason: string) => void): void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
on(event: RealtimeEvents, listener: (...args: any[]) => void) {
this.listeners[event] = listener;
}

connect() {
return new Promise<SessionBeginsEventData>((resolve, _) => {
return new Promise<SessionBeginsEventData>((resolve) => {
if (this.socket) {
throw new Error("Already connected");
}
Expand Down Expand Up @@ -160,12 +162,22 @@ export class RealtimeService {
this.socket.send(JSON.stringify(payload));
}

stream(): Writable {
const stream = new Writable({
write: (chunk: Buffer, encoding, next) => {
this.sendAudio(chunk);
next();
},
});
return stream;
}

async close(waitForSessionTermination = true) {
if (this.socket) {
if (this.socket.readyState === WebSocket.OPEN) {
const terminateSessionMessage = `{"terminate_session": true}`;
if (waitForSessionTermination) {
const sessionTerminatedPromise = new Promise<void>((resolve, _) => {
const sessionTerminatedPromise = new Promise<void>((resolve) => {
this.sessionTerminatedResolve = resolve;
});
this.socket.send(terminateSessionMessage);
Expand Down
27 changes: 24 additions & 3 deletions src/services/transcripts/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import BaseService from "@/services/base";
import { BaseService } from "@/services/base";
import {
ParagraphsResponse,
SentencesResponse,
Expand All @@ -12,11 +12,12 @@ import {
Retrieveable,
SubtitleFormat,
RedactedAudioResponse,
WordSearchResponse,
} from "@/types";
import { AxiosInstance } from "axios";
import FileService from "../files";
import { FileService } from "../files";

export default class TranscriptService
export class TranscriptService
extends BaseService
implements
Createable<Transcript, CreateTranscriptParameters, CreateTranscriptOptions>,
Expand Down Expand Up @@ -58,6 +59,7 @@ export default class TranscriptService
options?: CreateTranscriptOptions
): Promise<Transcript> {
const startTime = Date.now();
// eslint-disable-next-line no-constant-condition
while (true) {
const transcript = await this.get(transcriptId);
if (transcript.status === "completed" || transcript.status === "error") {
Expand Down Expand Up @@ -115,6 +117,25 @@ export default class TranscriptService
return res.data;
}

/**
* Search through the transcript for a specific set of keywords.
* You can search for individual words, numbers, or phrases containing up to five words or numbers.
* @param id The identifier of the transcript.
* @param id Keywords to search for.
* @return A promise that resolves to the sentences.
*/
async wordSearch(id: string, words: string[]): Promise<WordSearchResponse> {
const { data } = await this.client.get<WordSearchResponse>(
`/v2/transcript/${id}/word-search`,
{
params: {
words: JSON.stringify(words),
},
}
);
return data;
}

/**
* Retrieve all sentences of a transcript.
* @param id The identifier of the transcript.
Expand Down
2 changes: 1 addition & 1 deletion src/types/services/abstractions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @template T The type of the resource.
* @template Parameters The type of the parameters required to create the resource.
*/
interface Createable<T, Parameters, Options = Record<string, any>> {
interface Createable<T, Parameters, Options = Record<string, unknown>> {
/**
* Create a new resource.
* @param params The parameters of the new resource.
Expand Down
2 changes: 1 addition & 1 deletion src/utils/errors/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export {
default as RealtimeError,
RealtimeError,
RealtimeErrorType,
RealtimeErrorMessages,
} from "./realtime";
Loading

0 comments on commit ea15913

Please sign in to comment.