From 3e307c1881319ccf616586eef511fefa6d16703a Mon Sep 17 00:00:00 2001 From: Michael Jolley Date: Wed, 7 Jul 2021 21:14:08 -0500 Subject: [PATCH] Version 1.0.0 release * Redesign for new console * Updating custom-id to custom-string for custom models * Updating based on API changes * Fix ws close code Normal closure is 1000 rather than 0 (https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) * Updates to objects & README * Improving comments & README * Improving tests * Updating endpoints to actual endpoints * Removing project create/delete * Finishing usage endpoints and README * 1.0.1-alpha.0 * Alpha version * Updating default options * Updating for new API endpoint * Updating transcription & usage types * Updating references Co-authored-by: Frums <31263732+frumsdotxyz@users.noreply.github.com> --- .gitignore | 4 +- .npmignore | 4 + CHANGELOG.md | 37 +- README.md | 790 ++++++++++++++++-- package-lock.json | 28 +- package.json | 13 +- sample/index.js | 75 +- src/batch.ts | 81 -- src/constants/defaultOptions.ts | 2 +- src/enums/connectionState.ts | 6 + src/enums/index.ts | 2 + src/enums/liveTranscriptionEvents.ts | 6 + src/httpRequest.ts | 93 +++ src/index.ts | 85 +- src/keys.ts | 148 ++-- src/projects.ts | 33 + src/transcription/index.ts | 41 + src/transcription/liveTranscription.ts | 76 ++ src/transcription/preRecordedTranscription.ts | 52 ++ src/types/apiKeyResponse.ts | 5 - src/types/channel.ts | 19 +- src/types/hit.ts | 15 + src/types/index.ts | 26 +- src/types/key.ts | 26 +- src/types/keyResponse.ts | 11 + src/types/keyword.ts | 10 +- src/types/liveTranscriptionOptions.ts | 150 ++++ src/types/member.ts | 6 + src/types/{apiMetadata.ts => metadata.ts} | 2 +- src/types/options.ts | 17 - ....ts => prerecordedTranscriptionOptions.ts} | 29 +- src/types/project.ts | 13 + src/types/projectResponse.ts | 5 + src/types/search.ts | 15 + ...chResponse.ts => transcriptionResponse.ts} | 6 +- src/types/transcriptionSource.ts | 10 + src/types/usageCallback.ts | 4 + src/types/usageField.ts | 7 + src/types/usageFieldOptions.ts | 4 + src/types/usageOptions.ts | 23 + src/types/usageRequest.ts | 12 + src/types/usageRequestDetail.ts | 30 + src/types/usageRequestList.ts | 7 + src/types/usageRequestListOptions.ts | 7 + src/types/usageRequestMessage.ts | 3 + src/types/usageResponse.ts | 11 + src/types/usageResponseDetail.ts | 6 + src/types/wordBase.ts | 1 + src/usage.ts | 97 +++ src/userAgent.ts | 15 + tests/batch.test.ts | 24 - tests/index.test.ts | 2 +- tests/keys.test.ts | 20 +- tests/mockResults.ts | 6 +- .../preRecordedTranscription.test.ts | 25 + 55 files changed, 1800 insertions(+), 445 deletions(-) delete mode 100644 src/batch.ts create mode 100644 src/enums/connectionState.ts create mode 100644 src/enums/liveTranscriptionEvents.ts create mode 100644 src/httpRequest.ts create mode 100644 src/projects.ts create mode 100644 src/transcription/index.ts create mode 100644 src/transcription/liveTranscription.ts create mode 100644 src/transcription/preRecordedTranscription.ts delete mode 100644 src/types/apiKeyResponse.ts create mode 100644 src/types/keyResponse.ts create mode 100644 src/types/liveTranscriptionOptions.ts create mode 100644 src/types/member.ts rename src/types/{apiMetadata.ts => metadata.ts} (82%) delete mode 100644 src/types/options.ts rename src/types/{transcriptionOptions.ts => prerecordedTranscriptionOptions.ts} (95%) create mode 100644 src/types/project.ts create mode 100644 src/types/projectResponse.ts create mode 100644 src/types/search.ts rename src/types/{apiBatchResponse.ts => transcriptionResponse.ts} (52%) create mode 100644 src/types/transcriptionSource.ts create mode 100644 src/types/usageCallback.ts create mode 100644 src/types/usageField.ts create mode 100644 src/types/usageFieldOptions.ts create mode 100644 src/types/usageOptions.ts create mode 100644 src/types/usageRequest.ts create mode 100644 src/types/usageRequestDetail.ts create mode 100644 src/types/usageRequestList.ts create mode 100644 src/types/usageRequestListOptions.ts create mode 100644 src/types/usageRequestMessage.ts create mode 100644 src/types/usageResponse.ts create mode 100644 src/types/usageResponseDetail.ts create mode 100644 src/usage.ts create mode 100644 src/userAgent.ts delete mode 100644 tests/batch.test.ts create mode 100644 tests/transcription/preRecordedTranscription.test.ts diff --git a/.gitignore b/.gitignore index b79f2a40..89ef6e4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ node_modules dist out -.nyc_output \ No newline at end of file +.nyc_output +api.http +*.tgz \ No newline at end of file diff --git a/.npmignore b/.npmignore index deaa7142..ba38ad88 100644 --- a/.npmignore +++ b/.npmignore @@ -3,3 +3,7 @@ tests sample .github .nyc_output +.eslintignore +.eslintrc.js +.prettierrc +tsconfig.json \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6394bfa9..97fb7be8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +--- + +## [1.0.0] + +### Added + +#### Live transcription + +- `deepgram.transcription.live` now manages a websocket connection to Deepgram's API + for live transcription + +#### Projects + +- `deepgram.projects` allows listing projects or getting a specific project from + your Deepgram account + ### Updated +#### Pre-recorded transcription + +- `deepgram.batch` has been moved to `deepgram.transcription.preRecorded` +- Type of `source` parameter of pre-recorded transcriptions has changed. You can now + send one of two types of objects: `{ url: YOUR_FILES_URL }` or + `{ buffer: BUFFER_OF_YOUR_FILE, mimetype: FILES_MIME_TYPE }` + +#### API Key management + +- All `deepgram.keys` methods now require a `projectId`. +- `deepgram.keys.create` now requires an additional parameter `scopes`. This is a + string array specifying the scopes available to the newly created API key + +## [0.6.5] + - Added notice to README to denote the library is in a very unstable state. ## [0.6.4] @@ -21,5 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- -[unreleased]: https://github.com/vonage/vscode/compare/0.6.4...HEAD -[0.6.4]: https://github.com/vonage/vscode/compare/edc07b4...0.6.4 +[unreleased]: https://github.com/deepgram/node-sdk/compare/1.0.0...HEAD +[1.0.0]: https://github.com/deepgram/node-sdk/compare/0.6.5...1.0.0 +[0.6.5]: https://github.com/deepgram/node-sdk/compare/0.6.4...0.6.5 +[0.6.4]: https://github.com/deepgram/node-sdk/compare/edc07b4...0.6.4 diff --git a/README.md b/README.md index d8c1b4ef..82124b88 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,11 @@ ![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/deepgram/node-sdk/CI/main) ![npm (scoped)](https://img.shields.io/npm/v/@deepgram/sdk) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg?style=flat-rounded)](CODE_OF_CONDUCT.md) -> This is a pre-release SDK and is very likely to have breaking changes. Feel free to provide -> feedback via GitHub issues and suggest new features. - Official Node.js SDK for [Deepgram](https://www.deepgram.com/)'s automated speech recognition APIs. To access the API you will need a Deepgram account. Sign up for free at -[try.deepgram.com][signup]. +[signup][signup]. You can learn more about the full Deepgram API at [https://developers.deepgram.com](https://developers.deepgram.com). @@ -34,108 +31,461 @@ const { Deepgram } = require("@deepgram/sdk"); const deepgram = new Deepgram({ apiKey: DEEPGRAM_API_KEY, - apiSecret: DEEPGRAM_API_SECRET, - apiUrl: CUSTOM_API_URL, // Optionally used for on-prem customers + apiUrl: CUSTOM_API_URL, // Optionally used for on-premises customers }); ``` ## Usage -## Batch Transcription +## Transcription + +The `transcription` property can handle both pre-recorded and live transcriptions. -The `transcribe` method can receive the url to a file or a buffer with a file -to transcribe. Additional options can be provided to customize the result. +### Prerecorded Transcription + +The `transcription.preRecorded` method handles sending an existing file or +buffer to the Deepgram API to generate a transcription. [Additional options](#options) +can be provided to customize the result. ```js -const response = await deepgram.transcribe(URL_OR_BUFFER_OF_FILE, { - punctuate: true, - // other options are available -}); +// Sending a file +const fileSource = { url: URL_OF_FILE }; + +// Sending a buffer +const bufferSource = { buffer: BUFFER_OF_FILE, mimetype: MIMETYPE_OF_FILE }; + +// Both fileSource or bufferSource could be provided as the source parameter +const response = await deepgram.transcription.preRecorded( + fileSource | bufferSource, + { + punctuate: true, + // other options are available + } +); ``` -### Options +#### Prerecorded Transcription Options + +Additional transcription options can be provided for prerecorded transcriptions. ```js { - // AI model used to process submitted audio. - model?: "general" | "phonecall" | "meeting" | "", + /** + * AI model used to process submitted audio. + * @default general + * @remarks Possible values are general, phonecall, meeting or a custom string + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/model + */ + model?: Models | string; + + /** + * Version of the model to use. + * @default latest + * @remarks latest OR + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/version + */ + version: string; + /** + * BCP-47 language tag that hints at the primary spoken language. + * @default en-US + * @remarks Possible values are en-GB, en-IN, en-NZ, en-US, es, fr, ko, pt, + * pt-BR, ru, tr or null + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/language + */ + language?: string; + /** + * Indicates whether to add punctuation and capitalization to the transcript. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/punctuate + */ + punctuate?: boolean; + /** + * Indicates whether to remove profanity from the transcript. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/profanity_filter + */ + profanity_filter?: boolean; + /** + * Indicates whether to redact sensitive information, replacing redacted content with asterisks (*). + * @remarks Options include: + * `pci`: Redacts sensitive credit card information, including credit card number, expiration date, and CVV + * `numbers` (or `true)`: Aggressively redacts strings of numerals + * `ssn` (*beta*): Redacts social security numbers + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/redact + */ + redact?: Array; + /** + * Indicates whether to recognize speaker changes. When set to true, each word + * in the transcript will be assigned a speaker number starting at 0. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/diarize + */ + diarize?: boolean; + /** + * Indicates whether to transcribe each audio channel independently. When set + * to true, you will receive one transcript for each channel, which means you + * can apply a different model to each channel using the model parameter (e.g., + * set model to general:phonecall, which applies the general model to channel + * 0 and the phonecall model to channel 1). + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/multichannel + */ + multichannel?: boolean; + /** + * Maximum number of transcript alternatives to return. Just like a human listener, + * Deepgram can provide multiple possible interpretations of what it hears. + * @default 1 + */ + alternatives?: number; + /** + * Indicates whether to convert numbers from written format (e.g., one) to + * numerical format (e.g., 1). Deepgram can format numbers up to 999,999. + * @remarks Converted numbers do not include punctuation. For example, + * 999,999 would be transcribed as 999999. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/numerals + */ + numerals?: boolean; + /** + * Terms or phrases to search for in the submitted audio. Deepgram searches + * for acoustic patterns in audio rather than text patterns in transcripts + * because we have noticed that acoustic pattern matching is more performant. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/search + */ + search?: Array; + /** + * Callback URL to provide if you would like your submitted audio to be + * processed asynchronously. When passed, Deepgram will immediately respond + * with a request_id. When it has finished analyzing the audio, it will send + * a POST request to the provided URL with an appropriate HTTP status code. + * @remarks You may embed basic authentication credentials in the callback URL. + * Only ports 80, 443, 8080, and 8443 can be used for callbacks. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/callback + */ + callback?: string; + /** + * Keywords to which the model should pay particular attention to boosting + * or suppressing to help it understand context. Just like a human listener, + * Deepgram can better understand mumbled, distorted, or otherwise + * hard-to-decipher speech when it knows the context of the conversation. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/keywords + */ + keywords?: Array; + /** + * Indicates whether Deepgram will segment speech into meaningful semantic + * units, which allows the model to interact more naturally and effectively + * with speakers' spontaneous speech patterns. For example, when humans + * speak to each other conversationally, they often pause mid-sentence to + * reformulate their thoughts, or stop and restart a badly-worded sentence. + * When utterances is set to true, these utterances are identified and + * returned in the transcript results. + * + * By default, when utterances is enabled, it starts a new utterance after + * 0.8 s of silence. You can customize the length of time used to determine + * where to split utterances by submitting the utt_split parameter. + * @remarks **BETA FEATURE** + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/utterances + */ + utterances?: boolean; + /** + * Length of time in seconds of silence between words that Deepgram will + * use when determining where to split utterances. Used when utterances + * is enabled. + * @default 0.8 seconds + * @remarks **BETA FEATURE** + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/utt_split + */ + utt_split?: number; +} +``` - // BCP-47 language tag that hints at the primary spoken language. - // Defaults to en-US - language?: "en-GB" | "en-IN" | "en-NZ" | "en-US" | "es" | "fr" | "ko" | "pt" | "pt-BR" | "ru" | "tr" | null, +### Live Transcription - // Indicates whether to add punctuation and capitalization to the transcript. - punctuate?: true | false, +The `transcription.live` method provides access to a websocket connection +to the Deepgram API for generating streaming transcriptions. [Additional options](#options) +can be provided to customize the result. - // Indicates whether to remove profanity from the transcript. - profanity_filter?: true | false, +```js +const deepgramLive = deepgram.transcription.live({ punctuate: true }); - // Maximum number of transcript alternatives to return. - // Defaults to 1 - alternatives?: integer, +socket.on("microphone-stream", (stream) => { + deepgramSocket.send(stream); +}); - // Indicates whether to redact sensitive information, replacing redacted - // content with asterisks (*). - redact?: ["pci", "numbers", "ssn"], +/** + * Receive transcriptions based on sent streams + */ +deepgramLive.addListener("transcriptReceived", (transcription) => { + console.log(transcription.data); +}); +``` - // Indicates whether to recognize speaker changes. - diarize?: true | false, +#### Events - // Indicates whether to transcribe each audio channel independently. - multichannel?: true | false, +The following events are fired by the live transcription object: - // Indicates whether to convert numbers from written format (e.g., one) to - // numerical format (e.g., 1). - numerals?: true | false, +| Event | Description | Data | +| -------------------- | ----------------------------------------------------- | ------------------------------------------------- | +| `open` | The websocket connection to Deepgram has been opened. | The DG live transcription object | +| `close` | The websocket connection to Deepgram has been closed. | WebSocket.CloseEvent | +| `error` | An error occurred with the websocket connection | Error object | +| `transcriptReceived` | Deepgram has responded with a transcription | [Transcription Response](#transcription-response) | - // Terms or phrases to search for in the submitted audio. - search?: [string], +#### Live Transcription Options - // Callback URL to provide if you would like your submitted audio to be - // processed asynchronously. - callback?: string, +Additional transcription options can be provided for live transcriptions. - // Keywords to which the model should pay particular attention to boosting - // or suppressing to help it understand context. - keywords?: [string], +```js +{ + /** + * AI model used to process submitted audio. + * @default general + * @remarks Possible values are general, phonecall, meeting or a custom string + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/model + */ + model?: Models | string; + + /** + * Version of the model to use. + * @default latest + * @remarks latest OR + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/version + */ + version: string; + /** + * BCP-47 language tag that hints at the primary spoken language. + * @default en-US + * @remarks Possible values are en-GB, en-IN, en-NZ, en-US, es, fr, ko, pt, + * pt-BR, ru, tr or null + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/language + */ + language?: string; + /** + * Indicates whether to add punctuation and capitalization to the transcript. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/punctuate + */ + punctuate?: boolean; + /** + * Indicates whether to remove profanity from the transcript. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/profanity_filter + */ + profanity_filter?: boolean; + /** + * Indicates whether to redact sensitive information, replacing redacted content with asterisks (*). + * @remarks Options include: + * `pci`: Redacts sensitive credit card information, including credit card number, expiration date, and CVV + * `numbers` (or `true)`: Aggressively redacts strings of numerals + * `ssn` (*beta*): Redacts social security numbers + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/redact + */ + redact?: Array; + /** + * Indicates whether to recognize speaker changes. When set to true, each word + * in the transcript will be assigned a speaker number starting at 0. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/diarize + */ + diarize?: boolean; + /** + * Indicates whether to transcribe each audio channel independently. When set + * to true, you will receive one transcript for each channel, which means you + * can apply a different model to each channel using the model parameter (e.g., + * set model to general:phonecall, which applies the general model to channel + * 0 and the phonecall model to channel 1). + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/multichannel + */ + multichannel?: boolean; + /** + * Maximum number of transcript alternatives to return. Just like a human listener, + * Deepgram can provide multiple possible interpretations of what it hears. + * @default 1 + */ + alternatives?: number; + /** + * Indicates whether to convert numbers from written format (e.g., one) to + * numerical format (e.g., 1). Deepgram can format numbers up to 999,999. + * @remarks Converted numbers do not include punctuation. For example, + * 999,999 would be transcribed as 999999. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/numerals + */ + numerals?: boolean; + /** + * Terms or phrases to search for in the submitted audio. Deepgram searches + * for acoustic patterns in audio rather than text patterns in transcripts + * because we have noticed that acoustic pattern matching is more performant. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/search + */ + search?: Array; + /** + * Callback URL to provide if you would like your submitted audio to be + * processed asynchronously. When passed, Deepgram will immediately respond + * with a request_id. When it has finished analyzing the audio, it will send + * a POST request to the provided URL with an appropriate HTTP status code. + * @remarks You may embed basic authentication credentials in the callback URL. + * Only ports 80, 443, 8080, and 8443 can be used for callbacks. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/callback + */ + callback?: string; + /** + * Keywords to which the model should pay particular attention to boosting + * or suppressing to help it understand context. Just like a human listener, + * Deepgram can better understand mumbled, distorted, or otherwise + * hard-to-decipher speech when it knows the context of the conversation. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/keywords + */ + keywords?: Array; + /** + * Indicates whether the streaming endpoint should send you updates to its + * transcription as more audio becomes available. By default, the streaming + * endpoint returns regular updates, which means transcription results will + * likely change for a period of time. You can avoid receiving these updates + * by setting this flag to false. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeStreamingAudio/properties/interim_results + */ + interim_results?: boolean; + /** + * Indicates whether Deepgram will detect whether a speaker has finished + * speaking (or paused for a significant period of time, indicating the + * completion of an idea). When Deepgram detects an endpoint, it assumes + * that no additional data will improve its prediction, so it immediately + * finalizes the result for the processed time range and returns the + * transcript with a speech_final parameter set to true. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeStreamingAudio/properties/endpointing + */ + endpointing?: boolean; + /** + * Length of time in milliseconds of silence that voice activation detection + * (VAD) will use to detect that a speaker has finished speaking. Used when + * endpointing is enabled. Defaults to 10 ms. Deepgram customers may configure + * a value between 10 ms and 500 ms; on-premise customers may remove this + * restriction. + * @default 10 + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeStreamingAudio/properties/vad_turnoff + */ + vad_turnoff?: number; + /** + * Expected encoding of the submitted streaming audio. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeStreamingAudio/properties/encoding + */ + encoding?: string; + /** + * Number of independent audio channels contained in submitted streaming + * audio. Only read when a value is provided for encoding. + * @default 1 + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeStreamingAudio/properties/channels + */ + channels?: number; + /** + * Sample rate of submitted streaming audio. Required (and only read) + * when a value is provided for encoding. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeStreamingAudio/properties/sample_rate + */ + sample_rate?: number; +} +``` - // Indicates whether Deepgram will segment speech into meaningful semantic - // units, which allows the model to interact more naturally and effectively - // with speakers' spontaneous speech patterns. - utterances?: true | false, +### Transcription Response - // Length of time in seconds of silence between words that Deepgram will - // use when determining where to split utterances. Used when utterances - // is enabled. - // Defaults to 0.8 seconds - utt_split?: number, +```js +{ + "metadata": { + "request_id": "string", + "transaction_key": "string", + "sha256": "string", + "created": "string", + "duration": 0, + "channels": 0 + }, + "results": { + "channels": [ + { + "search": [ + { + "query": "string", + "hits": [ + { + "confidence": 0, + "start": 0, + "end": 0, + "snippet": "string" + } + ] + } + ], + "alternatives": [ + { + "transcript": "string", + "confidence": 0, + "words": [ + { + "word": "string", + "start": 0, + "end": 0, + "confidence": 0 + } + ] + } + ] + } + ] + } +} +``` + +## Project Management + +### List Projects + +Retrieve all projects - // Mimetype of the source - // Mimetype is required if the provided source is a buffer - mimetype?: string, +```js +const projects = await deepgram.projects.list(); +``` + +#### List Projects Response + +```ts +{ + projects: [ + { + id: string, + name: string, + }, + ], } ``` -### Response +### Get a Project + +Retrieves all project based on the provided project id. + +```js +const project = await deepgram.projects.get(PROJECT_ID); +``` + +#### Get a Project Response + +```ts +{ + id: string, + name: string, +} +``` ## Key Management ### List Keys -Retrieve all keys using the `keys.list` method. +Retrieves all keys for a given project. ```js -const response = await deepgram.keys.list(); +const response = await deepgram.keys.list(PROJECT_ID); ``` -#### Response +#### List Keys Response -```js +```ts { keys: [ { - key: "API KEY", - label: "KEY LABEL", + id: string, + comment: string, + created: Date, + scopes: Array }, ]; } @@ -143,20 +493,22 @@ const response = await deepgram.keys.list(); ### Create Key -Create a new API key using the `keys.create` method with a label for the -key. +Create a new API key for a project using the `keys.create` method +with a name for the key. ```js -const response = await deepgram.keys.create("label for key"); +const response = await deepgram.keys.create(PROJECT_ID, COMMENT_FOR_KEY); ``` -#### Response +#### Create Key Response -```js +```ts { - key: "API KEY", - secret: "API SECRET", - label: "LABEL PROVIDED" + id: string, + key: string, + comment: string, + created: Date, + scopes: Array } ``` @@ -166,25 +518,305 @@ Delete an existing API key using the `keys.delete` method with the key to delete. ```js -await deepgram.keys.delete("key to delete"); +await deepgram.keys.delete(PROJECT_ID, KEY_ID); +``` + +## Usage + +### Requests by Project + +Retrieves transcription requests for a project based on the provided options. + +```js +const response = await deepgram.usage.listRequests(PROJECT_ID, { + limit: 10, + // other options are available +}); +``` + +#### Requests by Project Options + +```js +{ + // The time to retrieve requests made since + // Example: "2020-01-01T00:00:00+00:00" + start?: string, + // The time to retrieve requests made until + // Example: "2021-01-01T00:00:00+00:00" + end?: string, + // Page of requests to return + // Defaults to 0 + page?: number, + // Number of requests to return per page + // Defaults to 10. Maximum of 100 + limit?: number, + // Filter by succeeded or failed requests + // By default, all requests are returned + status?: 'succeeded' | 'failed' +} +``` + +#### Requests by Project Response + +```ts +{ + page: number, + limit: number, + requests?: [ + { + id: string; + created: string; + path: string; + accessor: string; + response?: { + details: { + usd: number; + duration: number; + total_audio: number; + channels: number; + streams: number; + model: string; + method: string; + tags: Array; + features: Array; + config: { + multichannel?: boolean; + interim_results?: boolean; + punctuate?: boolean; + ner?: boolean; + utterances?: boolean; + replace?: boolean; + profanity_filter?: boolean; + keywords?: boolean; + sentiment?: boolean; + diarize?: boolean; + detect_language?: boolean; + search?: boolean; + redact?: boolean; + alternatives?: boolean; + numerals?: boolean; + }; + } + }, || + { + message?: string; + }, + callback?: { + code: number; + completed: string; + }, + }, + ]; +} +``` + +### Get Specific Request + +Retrieves a specific transcription request for a project based on the provided +`projectId` and `requestId`. + +```js +const response = await deepgram.usage.getRequest(PROJECT_ID, REQUEST_ID); +``` + +#### Specific Request Response + +```ts +{ + id: string; + created: string; + path: string; + accessor: string; + response?: { + details: { + usd: number; + duration: number; + total_audio: number; + channels: number; + streams: number; + model: string; + method: string; + tags: Array; + features: Array; + config: { + multichannel?: boolean; + interim_results?: boolean; + punctuate?: boolean; + ner?: boolean; + utterances?: boolean; + replace?: boolean; + profanity_filter?: boolean; + keywords?: boolean; + sentiment?: boolean; + diarize?: boolean; + detect_language?: boolean; + search?: boolean; + redact?: boolean; + alternatives?: boolean; + numerals?: boolean; + }; + } + }, || + { + message?: string; + }, + callback?: { + code: number; + completed: string; + } +} +``` + +### Get Usage by Project + +Retrieves aggregated usage data for a project based on the provided options. + +```js +const response = await deepgram.usage.getUsage(PROJECT_ID, { + start: "2020-01-01T00:00:00+00:00", + // other options are available +}); +``` + +#### Usage by Project Options + +```js +{ + // The time to retrieve requests made since + // Example: "2020-01-01T00:00:00+00:00" + start?: string, + // The time to retrieve requests made until + // Example: "2021-01-01T00:00:00+00:00" + end?: string, + // Specific identifer for a request + accessor?: string, + // Array of tags used in requests + tag?: Array, + // Filter requests by method + method?: "sync" | "async" | "streaming", + // Filter requests by model used + model?: string, + // Filter only requests using multichannel feature + multichannel?: boolean, + // Filter only requests using interim results feature + interim_results?: boolean, + // Filter only requests using the punctuation feature + punctuate?: boolean, + // Filter only requests using ner feature + ner?: boolean, + // Filter only requests using utterances feature + utterances?: boolean, + // Filter only requests using replace feature + replace?: boolean, + // Filter only requests using profanity_filter feature + profanity_filter?: boolean, + // Filter only requests using keywords feature + keywords?: boolean, + // Filter only requests using sentiment feature + sentiment?: boolean, + // Filter only requests using diarization feature + diarize?: boolean, + // Filter only requests using detect_language feature + detect_language?: boolean, + // Filter only requests using search feature + search?: boolean, + // Filter only requests using redact feature + redact?: boolean, + // Filter only requests using alternatives feature + alternatives?: boolean, + // Filter only requests using numerals feature + numerals?: boolean +} +``` + +#### Get Usage Response + +```ts +{ + start: string, + end: string, + resolution: { + units: string, + amount: number + }; + results: [ + { + start: string, + end: string, + hours: number, + requests: number + } + ]; +} +``` + +### Get Fields + +Retrieves features used by the provided projectId based on the provided options. + +```js +const response = await deepgram.usage.getUsage(PROJECT_ID, { + start: "2020-01-01T00:00:00+00:00", + // other options are available +}); +``` + +#### Get Fields Options + +```js +{ + // The time to retrieve requests made since + // Example: "2020-01-01T00:00:00+00:00" + start?: string, + // The time to retrieve requests made until + // Example: "2021-01-01T00:00:00+00:00" + end?: string +} +``` + +#### Get Fields Response + +```ts +{ + tags: Array, + models: Array, + processing_methods: Array, + languages: Array, + features: Array +} ``` ## Samples -A sample js file is in the `sample` directory. To run it, update the config -located at the top of the file. +To run the sample code, first run the following in your terminal: + +```bash +npm install +npm build +``` + +Then update the config object located at the top of the `index.js` +file in the sample folder. ```js const config = { - deepgramApiKey: "Your Deepgram API Key", - deepgramApiSecret: "Your Deepgram API Secret", - urlToFile: "Url to audio file", + deepgramApiKey: "YOUR_DEEPGRAM_API_KEY", + urlToFile: + "https://static.deepgram.com/examples/Bueller-Life-moves-pretty-fast.wav", }; ``` +Finally, run the sample code using the following command in your terminal: + +```bash +node sample/index.js +``` + The sample demonstrates the following uses: -- Transcribing a file from a url +- Transcribing a prerecorded file +- Retrieving usage for a project +- Getting a project - Creating an API key - Deleting an API key @@ -208,5 +840,5 @@ project, let us know! You can either: Check out the Developer Documentation at [https://developers.deepgram.com/](https://developers.deepgram.com/) -[signup]: https://try.deepgram.com?utm_source=node-sdk&utm_content=readme +[signup]: https://console.deepgram.com?utm_source=node-sdk&utm_content=readme [license]: LICENSE.txt diff --git a/package-lock.json b/package-lock.json index fb5c7577..54d09ba2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@deepgram/sdk", - "version": "0.5.0", + "version": "1.0.0-alpha.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -472,9 +472,9 @@ "dev": true }, "@types/node": { - "version": "14.14.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz", - "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==", + "version": "14.17.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz", + "integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==", "dev": true }, "@types/sinon": { @@ -486,6 +486,15 @@ "@sinonjs/fake-timers": "^7.0.4" } }, + "@types/ws": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.4.tgz", + "integrity": "sha512-d/7W23JAXPodQNbOZNXvl2K+bqAQrCMwlh/nuQsPSQk6Fq0opHoPrUw43aHsvSbIiQPr8Of2hkFbnz1XBFVyZQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@typescript-eslint/eslint-plugin": { "version": "4.22.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz", @@ -2983,9 +2992,9 @@ "dev": true }, "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", "dev": true }, "nyc": { @@ -4576,6 +4585,11 @@ "typedarray-to-buffer": "^3.1.5" } }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" + }, "xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", diff --git a/package.json b/package.json index 4f6f2b32..2df955e0 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "@deepgram/sdk", - "version": "0.6.5", + "version": "1.0.0", "description": "An SDK for the Deepgram automated speech recognition platform", "main": "dist/index.js", + "types": "dist/index.d.ts", "scripts": { "build": "tsc -p ./", "coverage": "nyc npm run test", - "lint": "eslint ./src --ext .ts", - "prettier:format": "prettier --config .prettierrc src/**/*.ts --write", + "lint": "eslint ./src --ext .ts && prettier --config .prettierrc src/**/*.ts --write", "test": "mocha -r ts-node/register tests/**/*test.ts --insect", "watch": "nodemon -e ts --watch src --exec \"npm run build\"" }, @@ -33,8 +33,9 @@ "devDependencies": { "@types/chai": "^4.2.16", "@types/mocha": "^8.2.2", - "@types/node": "^14.14.41", + "@types/node": "^14.17.4", "@types/sinon": "^10.0.0", + "@types/ws": "^7.4.4", "@typescript-eslint/eslint-plugin": "^4.22.0", "@typescript-eslint/parser": "^4.22.0", "chai": "^4.3.4", @@ -50,5 +51,7 @@ "ts-node": "^9.1.1", "typescript": "^4.2.4" }, - "dependencies": {} + "dependencies": { + "ws": "^7.4.6" + } } \ No newline at end of file diff --git a/sample/index.js b/sample/index.js index aa2fcc76..b374830e 100644 --- a/sample/index.js +++ b/sample/index.js @@ -1,46 +1,55 @@ const { Deepgram } = require('../dist'); const config = { - deepgramApiKey: 'Your Deepgram API Key', - deepgramApiSecret: 'Your Deepgram API Secret', - urlToFile: 'Url to audio file' + deepgramApiKey: 'YOUR_DEEPGRAM_API_KEY', + urlToFile: 'https://static.deepgram.com/examples/Bueller-Life-moves-pretty-fast.wav' } function main() { return new Promise((resolve, reject) => { + (async () => { - const deepgram = new Deepgram({ apiKey: config.deepgramApiKey, apiSecret: config.deepgramApiSecret }); - - deepgram.transcribe(config.urlToFile, { punctuate: true }) - .then((result) => { - console.log(result.results.channels[0]); - }) - .catch((err) => { - console.log(err); - }); - - let key = 'Xwr5JGdWhUciiNLZ'; - deepgram.keys.create('test') - .then((result) => { - key = result.key - - if (key) { - deepgram.keys.delete(key) - .then((result) => { - console.log(`Successfully deleted key: ${JSON.stringify(result)}`); - }) - .catch((err) => { - console.log(err); - }); - } - - }) - .catch((err) => { - console.log(err); - }); + try { + const deepgram = new Deepgram(config.deepgramApiKey); + + /** Get a project to test with */ + const projects = await deepgram.projects.list(); + if (projects.projects.length === 0) resolve(); + + const project = projects.projects[0]; + + /** Create an API key in the project */ + const apiKey = await deepgram.keys.create(project.id, "test key", ['member']); + console.log(`Key created: ${apiKey.id}`); + + const newDeepgram = new Deepgram(apiKey.key); + + /** Send a pre-recorded file for transcription */ + const transcription = await newDeepgram.transcription.preRecorded({ + url: config.urlToFile + }, { + punctuate: true + }); + console.dir(transcription, { depth: null }); + + /** Retrieve & log usage for this project */ + const usage = await newDeepgram.usage.listRequests(project.id); + console.dir(usage, { depth: null }); + + await deepgram.keys.delete(project.id, apiKey.id); + console.log(`Key deleted: ${apiKey.id}`); + + resolve(); + } + catch (err) { + console.log(`Err: ${err}`); + reject(err); + } + })() }); } -main(); \ No newline at end of file +main(); + diff --git a/src/batch.ts b/src/batch.ts deleted file mode 100644 index 0af1c933..00000000 --- a/src/batch.ts +++ /dev/null @@ -1,81 +0,0 @@ -import querystring from "querystring"; -import { request } from "https"; -import { ApiBatchResponse, TranscriptionOptions } from "./types"; - -/** - * Transcribes audio from a file or buffer - * @param credentials Base64 encoded API key & secret - * @param source Url or Buffer of file to transcribe - * @param options Options to modify transcriptions - */ -export const transcribe = async ( - credentials: string, - apiUrl: string, - source: string | Buffer, - options?: TranscriptionOptions -): Promise => { - const transcriptionOptions = { ...{}, ...options }; - - if ( - typeof source !== "string" && - transcriptionOptions.mimetype === undefined - ) { - throw new Error("DG: Mimetype must be provided if the source is a Buffer"); - } - - return await _listen(credentials, apiUrl, source, transcriptionOptions); -}; - -const _listen = async ( - credentials: string, - apiUrl: string, - source: string | Buffer, - options: TranscriptionOptions -): Promise => { - const requestOptions = { - host: apiUrl, - path: `/v2/listen?${querystring.stringify(options)}`, - method: "POST", - headers: { - "Content-Type": - typeof source === "string" ? "application/json" : options.mimetype, - Authorization: `Basic ${credentials}`, - }, - }; - - return new Promise((resolve, reject) => { - try { - const httpRequest = request(requestOptions, (dgRes) => { - let dgResContent = ""; - - dgRes.on("data", (chunk) => { - dgResContent += chunk; - }); - - dgRes.on("end", () => { - const dgResJson = JSON.parse(dgResContent); - if (dgResJson.error) { - reject(`DG: ${dgResContent}`); - } - resolve(dgResJson); - }); - - dgRes.on("error", (err) => { - reject(`DG: ${err}`); - }); - }); - - httpRequest.on("error", (err) => { - reject(`DG: ${err}`); - }); - - const payload = - typeof source === "string" ? JSON.stringify({ url: source }) : source; - - httpRequest.write(payload); - httpRequest.end(); - } catch (err) { - reject(err); - } - }); -}; diff --git a/src/constants/defaultOptions.ts b/src/constants/defaultOptions.ts index d50d9c3b..431e2e26 100644 --- a/src/constants/defaultOptions.ts +++ b/src/constants/defaultOptions.ts @@ -2,5 +2,5 @@ * Default SDK options */ export const DefaultOptions = { - apiUrl: "brain.deepgram.com", + apiUrl: "api.deepgram.com", }; diff --git a/src/enums/connectionState.ts b/src/enums/connectionState.ts new file mode 100644 index 00000000..75856450 --- /dev/null +++ b/src/enums/connectionState.ts @@ -0,0 +1,6 @@ +export enum ConnectionState { + CONNECTING = 0, + OPEN = 1, + CLOSING = 2, + CLOSED = 3, +} diff --git a/src/enums/index.ts b/src/enums/index.ts index a2b7e9cb..c8713c89 100644 --- a/src/enums/index.ts +++ b/src/enums/index.ts @@ -1,5 +1,7 @@ export * from "./alternatives"; +export * from "./connectionState"; export * from "./diarization"; +export * from "./liveTranscriptionEvents"; export * from "./models"; export * from "./punctuation"; export * from "./searchKind"; diff --git a/src/enums/liveTranscriptionEvents.ts b/src/enums/liveTranscriptionEvents.ts new file mode 100644 index 00000000..92e830bf --- /dev/null +++ b/src/enums/liveTranscriptionEvents.ts @@ -0,0 +1,6 @@ +export const enum LiveTranscriptionEvents { + Open = "open", + Close = "close", + TranscriptReceived = "transcriptReceived", + Error = "error", +} diff --git a/src/httpRequest.ts b/src/httpRequest.ts new file mode 100644 index 00000000..5ca05bd1 --- /dev/null +++ b/src/httpRequest.ts @@ -0,0 +1,93 @@ +import { request, RequestOptions } from "https"; +import { userAgent } from "./userAgent"; + +const _requestOptions = ( + api_key: string, + apiUrl: string, + path: string, + method: string, + payload?: string | Buffer, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + override_options?: any +): RequestOptions => { + const options = { + host: apiUrl, + path, + method, + headers: { + "User-Agent": userAgent(), + "Content-Type": "application/json", + Authorization: `token ${api_key}`, + "Content-Length": payload ? Buffer.byteLength(payload) : undefined, + }, + }; + if (payload === undefined) { + delete options.headers["Content-Length"]; + } + let headers = options.headers; + if (override_options && override_options.headers) { + headers = { ...headers, ...override_options.headers }; + } + + return { ...options, ...override_options, ...{ headers } }; +}; + +export function _request( + method: string, + api_key: string, + apiUrl: string, + path: string, + payload?: string | Buffer, + // eslint-disable-next-line @typescript-eslint/ban-types + options?: Object +): Promise { + const requestOptions = _requestOptions( + api_key, + apiUrl, + path, + method, + payload, + options + ); + return new Promise((resolve, reject) => { + try { + const httpRequest = request(requestOptions, (dgRes) => { + let dgResContent = ""; + + dgRes.on("data", (chunk) => { + dgResContent += chunk; + }); + + dgRes.on("end", () => { + let dgResponse; + try { + dgResponse = JSON.parse(dgResContent); + } catch (err) { + dgResponse = { error: dgResContent }; + } + + if (dgResponse.error) { + reject(`DG: ${dgResContent}`); + } + resolve(dgResponse); + }); + + dgRes.on("error", (err) => { + reject(`DG: ${err}`); + }); + }); + + httpRequest.on("error", (err) => { + reject(`DG: ${err}`); + }); + + if (payload) { + httpRequest.write(payload); + } + + httpRequest.end(); + } catch (err) { + reject(`DG: ${err}`); + } + }); +} diff --git a/src/index.ts b/src/index.ts index ee97d536..fcc86adf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,77 +1,40 @@ import { DefaultOptions } from "./constants"; -import { - ApiBatchResponse, - ApiKeyResponse, - Key, - Options, - TranscriptionOptions, -} from "./types"; -import { transcribe } from "./batch"; import { Keys } from "./keys"; +import { Projects } from "./projects"; +import { Transcriber } from "./transcription"; +import { Usage } from "./usage"; export class Deepgram { - private _credentials: string; + private _apiUrl: string; + private _apiKey: string; + + keys: Keys; + projects: Projects; + transcription: Transcriber; + usage: Usage; + + constructor(apiKey: string, apiUrl?: string) { + this._apiKey = apiKey; + this._apiUrl = apiUrl || DefaultOptions.apiUrl; - constructor(private options: Options) { this._validateOptions(); - this._credentials = Buffer.from( - `${options.apiKey}:${options.apiSecret}` - ).toString("base64"); + this.keys = new Keys(this._apiKey, this._apiUrl); + this.projects = new Projects(this._apiKey, this._apiUrl); + this.transcription = new Transcriber(this._apiKey, this._apiUrl); + this.usage = new Usage(this._apiKey, this._apiUrl); } /** - * Ensures that the provided credentials were provided + * Ensures that the provided options were provided */ private _validateOptions() { - this.options = { ...DefaultOptions, ...this.options }; - - if ( - !this.options.apiKey || - this.options.apiKey.trim().length === 0 || - !this.options.apiSecret || - this.options.apiSecret.trim().length === 0 - ) { - throw new Error("DG: API key & secret are required"); + if (!this._apiKey || this._apiKey.trim().length === 0) { + throw new Error("DG: API key is required"); } - } - /** - * Transcribes audio from a file or buffer - * @param source Url or Buffer of file to transcribe - * @param options Options to modify transcriptions - */ - async transcribe( - source: string | Buffer, - options?: TranscriptionOptions - ): Promise { - return await transcribe( - this._credentials, - this.options.apiUrl || "", - source, - options - ); + if (!this._apiUrl || this._apiUrl.trim().length === 0) { + throw new Error("DG: API url should be a valid url or not provided"); + } } - - public keys = { - list: async (): Promise => { - return await Keys.list(this._credentials, this.options.apiUrl || ""); - }, - - create: async (label: string): Promise => { - return await Keys.create( - this._credentials, - this.options.apiUrl || "", - label - ); - }, - - delete: async (key: string): Promise => { - return await Keys.delete( - this._credentials, - this.options.apiUrl || "", - key - ); - }, - }; } diff --git a/src/keys.ts b/src/keys.ts index 8d00e91a..9f157484 100644 --- a/src/keys.ts +++ b/src/keys.ts @@ -1,93 +1,69 @@ -import { request } from "https"; -import { RequestOptions } from "node:https"; -import { ApiKeyResponse, Key } from "./types"; +import { _request } from "./httpRequest"; +import { KeyResponse, Key } from "./types"; -export const Keys = { - async list(credentials: string, apiUrl: string): Promise { - return _request("GET", credentials, apiUrl); - }, +export class Keys { + constructor(private _credentials: string, private _apiUrl: string) {} - async create( - credentials: string, - apiUrl: string, - label: string - ): Promise { - return _request("POST", credentials, apiUrl, { label }); - }, - - async delete( - credentials: string, - apiUrl: string, - key: string - ): Promise { - return _request("DELETE", credentials, apiUrl, { key }); - }, -}; - -const _requestOptions = ( - credentials: string, - apiUrl: string, - method: string, - payload?: unknown -): RequestOptions => { - return { - host: apiUrl, - path: "/v2/keys", - method, - headers: { - "Content-Type": "application/json", - Authorization: `Basic ${credentials}`, - "Content-Length": payload - ? typeof payload === "string" - ? Buffer.byteLength(payload) - : Buffer.byteLength(JSON.stringify(payload)) - : undefined, - }, - }; -}; - -function _request( - method: string, - credentials: string, - apiUrl: string, - payload?: unknown -): Promise { - const requestOptions = _requestOptions(credentials, apiUrl, method, payload); - return new Promise((resolve, reject) => { - try { - const httpRequest = request(requestOptions, (dgRes) => { - let dgResContent = ""; + private apiPath = "/v1/projects"; - dgRes.on("data", (chunk) => { - dgResContent += chunk; - }); + /** + * Retrieves all keys associated with the provided projectId + * @param projectId Unique identifier of the project containing API keys + */ + async list(projectId: string): Promise { + return _request( + "GET", + this._credentials, + this._apiUrl, + `${this.apiPath}/${projectId}/keys` + ); + } - dgRes.on("end", () => { - const dgResJson = JSON.parse(dgResContent); - if (dgResJson.error) { - reject(`DG: ${dgResContent}`); - } - resolve(dgResJson); - }); + /** + * Retrieves a specific key associated with the provided projectId + * @param projectId Unique identifier of the project containing API keys + * @param keyId Unique identifier for the key to retrieve + */ + async get(projectId: string, keyId: string): Promise { + return _request( + "GET", + this._credentials, + this._apiUrl, + `${this.apiPath}/${projectId}/keys/${keyId}` + ); + } - dgRes.on("error", (err) => { - reject(`DG: ${err}`); - }); - }); - - httpRequest.on("error", (err) => { - reject(`DG: ${err}`); - }); - - if (payload) { - httpRequest.write( - typeof payload === "string" ? payload : JSON.stringify(payload) - ); - } + /** + * Creates an API key with the provided scopes + * @param projectId Unique identifier of the project to create an API key under + * @param comment Comment to describe the key + * @param scopes Permission scopes associated with the API key + */ + async create( + projectId: string, + comment: string, + scopes: Array + ): Promise { + return _request( + "POST", + this._credentials, + this._apiUrl, + `${this.apiPath}/${projectId}/keys`, + JSON.stringify({ comment, scopes }) + ); + } - httpRequest.end(); - } catch (err) { - reject(err); - } - }); + /** + * Deletes an API key + * @param projectId Unique identifier of the project to create an API key under + * @param keyId Unique identifier for the key to delete + */ + async delete(projectId: string, keyId: string): Promise { + return _request( + "DELETE", + this._credentials, + this._apiUrl, + `${this.apiPath}/${projectId}/keys/${keyId}` + ); + } } diff --git a/src/projects.ts b/src/projects.ts new file mode 100644 index 00000000..6c678cf5 --- /dev/null +++ b/src/projects.ts @@ -0,0 +1,33 @@ +import { _request } from "./httpRequest"; +import { Project, ProjectResponse } from "./types"; + +export class Projects { + constructor(private _credentials: string, private _apiUrl: string) {} + + private apiPath = "/v1/projects"; + + /** + * Returns all projects accessible by the API key + */ + async list(): Promise { + return _request( + "GET", + this._credentials, + this._apiUrl, + this.apiPath + ); + } + + /** + * Retrieves a specific project based on the provided projectId + * @param projectId Unique identifier of the project to retrieve + */ + async get(projectId: string): Promise { + return _request( + "GET", + this._credentials, + this._apiUrl, + `${this.apiPath}/${projectId}` + ); + } +} diff --git a/src/transcription/index.ts b/src/transcription/index.ts new file mode 100644 index 00000000..4e24c9ac --- /dev/null +++ b/src/transcription/index.ts @@ -0,0 +1,41 @@ +import { + LiveTranscriptionOptions, + PrerecordedTranscriptionOptions, + TranscriptionResponse, + TranscriptionSource, +} from "../types"; +import { LiveTranscription } from "./liveTranscription"; +import { preRecordedTranscription } from "./preRecordedTranscription"; + +export class Transcriber { + constructor(private _credentials: string, private _apiUrl: string) {} + + /** + * Transcribes prerecorded audio from a file or buffer + * @param source Url or Buffer of file to transcribe + * @param options Options to modify transcriptions + */ + async preRecorded( + source: TranscriptionSource, + options?: PrerecordedTranscriptionOptions + ): Promise { + return await preRecordedTranscription( + this._credentials, + this._apiUrl || "", + source, + options + ); + } + + /** + * Opens a websocket to Deepgram's API for live transcriptions + * @param options Options to modify transcriptions + */ + live(options?: LiveTranscriptionOptions): LiveTranscription { + return new LiveTranscription( + this._credentials, + this._apiUrl || "", + options + ); + } +} diff --git a/src/transcription/liveTranscription.ts b/src/transcription/liveTranscription.ts new file mode 100644 index 00000000..a9375c1d --- /dev/null +++ b/src/transcription/liveTranscription.ts @@ -0,0 +1,76 @@ +import EventEmitter from "events"; +import querystring from "querystring"; +import WebSocket from "ws"; +import { ConnectionState, LiveTranscriptionEvents } from "../enums"; +import { LiveTranscriptionOptions } from "../types"; +import { userAgent } from "../userAgent"; + +export class LiveTranscription extends EventEmitter { + private _socket: WebSocket; + + constructor( + credentials: string, + apiUrl: string, + options?: LiveTranscriptionOptions + ) { + super(undefined); + this._socket = new WebSocket( + `wss://${apiUrl}/v1/listen?${querystring.stringify(options)}`, + { + headers: { + Authorization: `Basic ${credentials}`, + "User-Agent": userAgent(), + }, + } + ); + this._bindSocketEvents(); + } + + private _bindSocketEvents(): void { + this._socket.onopen = () => { + this.emit(LiveTranscriptionEvents.Open, this); + }; + + this._socket.onclose = (event: WebSocket.CloseEvent) => { + this.emit(LiveTranscriptionEvents.Close, event); + }; + + this._socket.onerror = (event) => { + this.emit(LiveTranscriptionEvents.Error, event); + }; + + this._socket.onmessage = (m) => { + this.emit(LiveTranscriptionEvents.TranscriptReceived, m); + }; + } + + /** + * Returns the ready state of the websocket connection + */ + public getReadyState(): ConnectionState { + return this._socket.readyState; + } + + /** + * Sends data to the Deepgram API via websocket connection + * @param data Audio data to send to Deepgram + */ + public send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void { + if (this._socket.readyState === ConnectionState.OPEN) { + this._socket.send(data); + } else { + this.emit( + LiveTranscriptionEvents.Error, + "Could not send. Connection not open." + ); + } + } + + /** + * Denote that you are finished sending audio and close + * the websocket connection when transcription is finished + */ + public finish(): void { + this._socket.close(1000); + } +} diff --git a/src/transcription/preRecordedTranscription.ts b/src/transcription/preRecordedTranscription.ts new file mode 100644 index 00000000..e1a58d5a --- /dev/null +++ b/src/transcription/preRecordedTranscription.ts @@ -0,0 +1,52 @@ +import querystring from "querystring"; +import { + TranscriptionResponse, + PrerecordedTranscriptionOptions, + TranscriptionSource, + UrlSource, +} from "../types"; +import { _request } from "../httpRequest"; + +function isUrlSource( + providedSource: TranscriptionSource +): providedSource is UrlSource { + if ((providedSource as UrlSource).url) return true; + return false; +} + +/** + * Transcribes audio from a file or buffer + * @param credentials Base64 encoded API key & secret + * @param source Url or Buffer of file to transcribe + * @param options Options to modify transcriptions + */ +export const preRecordedTranscription = async ( + apiKey: string, + apiUrl: string, + source: TranscriptionSource, + options?: PrerecordedTranscriptionOptions +): Promise => { + const transcriptionOptions = { ...{}, ...options }; + + if ( + !isUrlSource(source) && + (source.mimetype === undefined || source.mimetype.length === 0) + ) { + throw new Error("DG: Mimetype must be provided if the source is a Buffer"); + } + + return await _request( + "POST", + apiKey, + apiUrl, + `/v1/listen?${querystring.stringify(transcriptionOptions)}`, + isUrlSource(source) ? JSON.stringify(source) : source.buffer, + isUrlSource(source) + ? undefined + : { + headers: { + "Content-Type": source.mimetype, + }, + } + ); +}; diff --git a/src/types/apiKeyResponse.ts b/src/types/apiKeyResponse.ts deleted file mode 100644 index 8b120a26..00000000 --- a/src/types/apiKeyResponse.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Key } from "node:readline"; - -export type ApiKeyResponse = { - keys: Array; -}; diff --git a/src/types/channel.ts b/src/types/channel.ts index 7dcc6bd1..7086cab3 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -1,11 +1,26 @@ -import { Hit } from "./hit"; +import { Search } from "./search"; import { WordBase } from "./wordBase"; +/** + * Channel of speech identified by Deepgram + */ export type Channel = { - search: Array<{ query: string; hits: Array }>; + /** + * Searched terms & results + */ + search: Array; alternatives: Array<{ + /** + * Text of speech identified by API + */ transcript: string; + /** + * Confidence in transcript generated + */ confidence: number; + /** + * Array of words included in the transcript + */ words: Array; }>; }; diff --git a/src/types/hit.ts b/src/types/hit.ts index be73666b..7a06a116 100644 --- a/src/types/hit.ts +++ b/src/types/hit.ts @@ -1,6 +1,21 @@ +/** + * Represents an identified search term in the transcript + */ export type Hit = { + /** + * Value between 0 and 1 that indicates the model's relative confidence in this hit. + */ confidence: number; + /** + * Offset in seconds from the start of the audio to where the hit occurs. + */ start: number; + /** + * Offset in seconds from the start of the audio to where the hit ends. + */ end: number; + /** + * Transcript that corresponds to the time between start and end. + */ snippet: string; }; diff --git a/src/types/index.ts b/src/types/index.ts index 3b9ac22d..3f123fb9 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,10 +1,24 @@ -export * from "./apiMetadata"; -export * from "./apiBatchResponse"; -export * from "./apiKeyResponse"; export * from "./channel"; export * from "./hit"; export * from "./key"; -export * from "./keyword"; -export * from "./options"; -export * from "./transcriptionOptions"; +export * from "./keyResponse"; +export * from "./liveTranscriptionOptions"; +export * from "./member"; +export * from "./metadata"; +export * from "./prerecordedTranscriptionOptions"; +export * from "./project"; +export * from "./projectResponse"; +export * from "./search"; +export * from "./transcriptionResponse"; +export * from "./transcriptionSource"; +export * from "./usageCallback"; +export * from "./usageField"; +export * from "./usageFieldOptions"; +export * from "./usageOptions"; +export * from "./usageRequest"; +export * from "./usageRequestDetail"; +export * from "./usageRequestList"; +export * from "./usageRequestListOptions"; +export * from "./usageResponse"; +export * from "./usageResponseDetail"; export * from "./wordBase"; diff --git a/src/types/key.ts b/src/types/key.ts index dd5842c4..3b0e35ee 100644 --- a/src/types/key.ts +++ b/src/types/key.ts @@ -1,5 +1,25 @@ +/** + * API key used for authenticating with the Deepgram API + */ export type Key = { - key: string; - secret?: string; - label: string; + /** + * Unique identifier of the key to use in API requests + */ + id: string; + /** + * API key to send in API requests (Only displayed when first created) + */ + key?: string; + /** + * Comment for user reference + */ + comment: string; + /** + * Timestamp of the date/time the key was created + */ + created: Date; + /** + * Array of scopes assigned to the key + */ + scopes: Array; }; diff --git a/src/types/keyResponse.ts b/src/types/keyResponse.ts new file mode 100644 index 00000000..b72e85ec --- /dev/null +++ b/src/types/keyResponse.ts @@ -0,0 +1,11 @@ +import { Key } from "./key"; + +/** + * Response from the Deepgram API to list keys + */ +export type KeyResponse = { + /** + * Array of API keys associated with the project + */ + api_keys: Array; +}; diff --git a/src/types/keyword.ts b/src/types/keyword.ts index 9b8db607..34dd9e8b 100644 --- a/src/types/keyword.ts +++ b/src/types/keyword.ts @@ -1,6 +1,4 @@ -export type Keyword = - | string - | { - word: string; - boost: number; - }; +export type Keyword = { + keyword: string; + boost?: number; +}; diff --git a/src/types/liveTranscriptionOptions.ts b/src/types/liveTranscriptionOptions.ts new file mode 100644 index 00000000..da103e8c --- /dev/null +++ b/src/types/liveTranscriptionOptions.ts @@ -0,0 +1,150 @@ +import { Models } from "../enums"; + +/** + * Options for transcription + */ +export type LiveTranscriptionOptions = { + /** + * AI model used to process submitted audio. + * @default general + * @remarks Possible values are general, phonecall, meeting or a custom string + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/model + */ + model?: Models | string; + + /** + * Version of the model to use. + * @default latest + * @remarks latest OR + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/version + */ + version: string; + /** + * BCP-47 language tag that hints at the primary spoken language. + * @default en-US + * @remarks Possible values are en-GB, en-IN, en-NZ, en-US, es, fr, ko, pt, + * pt-BR, ru, tr or null + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/language + */ + language?: string; + /** + * Indicates whether to add punctuation and capitalization to the transcript. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/punctuate + */ + punctuate?: boolean; + /** + * Indicates whether to remove profanity from the transcript. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/profanity_filter + */ + profanity_filter?: boolean; + /** + * Indicates whether to redact sensitive information, replacing redacted content with asterisks (*). + * @remarks Options include: + * `pci`: Redacts sensitive credit card information, including credit card number, expiration date, and CVV + * `numbers` (or `true)`: Aggressively redacts strings of numerals + * `ssn` (*beta*): Redacts social security numbers + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/redact + */ + redact?: Array; + /** + * Indicates whether to recognize speaker changes. When set to true, each word + * in the transcript will be assigned a speaker number starting at 0. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/diarize + */ + diarize?: boolean; + /** + * Indicates whether to transcribe each audio channel independently. When set + * to true, you will receive one transcript for each channel, which means you + * can apply a different model to each channel using the model parameter (e.g., + * set model to general:phonecall, which applies the general model to channel + * 0 and the phonecall model to channel 1). + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/multichannel + */ + multichannel?: boolean; + /** + * Maximum number of transcript alternatives to return. Just like a human listener, + * Deepgram can provide multiple possible interpretations of what it hears. + * @default 1 + */ + alternatives?: number; + /** + * Indicates whether to convert numbers from written format (e.g., one) to + * numerical format (e.g., 1). Deepgram can format numbers up to 999,999. + * @remarks Converted numbers do not include punctuation. For example, + * 999,999 would be transcribed as 999999. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/numerals + */ + numerals?: boolean; + /** + * Terms or phrases to search for in the submitted audio. Deepgram searches + * for acoustic patterns in audio rather than text patterns in transcripts + * because we have noticed that acoustic pattern matching is more performant. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/search + */ + search?: Array; + /** + * Callback URL to provide if you would like your submitted audio to be + * processed asynchronously. When passed, Deepgram will immediately respond + * with a request_id. When it has finished analyzing the audio, it will send + * a POST request to the provided URL with an appropriate HTTP status code. + * @remarks You may embed basic authentication credentials in the callback URL. + * Only ports 80, 443, 8080, and 8443 can be used for callbacks. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/callback + */ + callback?: string; + /** + * Keywords to which the model should pay particular attention to boosting + * or suppressing to help it understand context. Just like a human listener, + * Deepgram can better understand mumbled, distorted, or otherwise + * hard-to-decipher speech when it knows the context of the conversation. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/keywords + */ + keywords?: Array; + /** + * Indicates whether the streaming endpoint should send you updates to its + * transcription as more audio becomes available. By default, the streaming + * endpoint returns regular updates, which means transcription results will + * likely change for a period of time. You can avoid receiving these updates + * by setting this flag to false. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeStreamingAudio/properties/interim_results + */ + interim_results?: boolean; + /** + * Indicates whether Deepgram will detect whether a speaker has finished + * speaking (or paused for a significant period of time, indicating the + * completion of an idea). When Deepgram detects an endpoint, it assumes + * that no additional data will improve its prediction, so it immediately + * finalizes the result for the processed time range and returns the + * transcript with a speech_final parameter set to true. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeStreamingAudio/properties/endpointing + */ + endpointing?: boolean; + /** + * Length of time in milliseconds of silence that voice activation detection + * (VAD) will use to detect that a speaker has finished speaking. Used when + * endpointing is enabled. Defaults to 10 ms. Deepgram customers may configure + * a value between 10 ms and 500 ms; on-premise customers may remove this + * restriction. + * @default 10 + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeStreamingAudio/properties/vad_turnoff + */ + vad_turnoff?: number; + /** + * Expected encoding of the submitted streaming audio. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeStreamingAudio/properties/encoding + */ + encoding?: string; + /** + * Number of independent audio channels contained in submitted streaming + * audio. Only read when a value is provided for encoding. + * @default 1 + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeStreamingAudio/properties/channels + */ + channels?: number; + /** + * Sample rate of submitted streaming audio. Required (and only read) + * when a value is provided for encoding. + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeStreamingAudio/properties/sample_rate + */ + sample_rate?: number; +}; diff --git a/src/types/member.ts b/src/types/member.ts new file mode 100644 index 00000000..0c958aff --- /dev/null +++ b/src/types/member.ts @@ -0,0 +1,6 @@ +export type Member = { + id: string; + name: string; + scopes: Array; + email: string; +}; diff --git a/src/types/apiMetadata.ts b/src/types/metadata.ts similarity index 82% rename from src/types/apiMetadata.ts rename to src/types/metadata.ts index 08dbd940..39d41d23 100644 --- a/src/types/apiMetadata.ts +++ b/src/types/metadata.ts @@ -1,4 +1,4 @@ -export type ApiMetadata = { +export type Metadata = { request_id: string; transaction_key: string; sha256: string; diff --git a/src/types/options.ts b/src/types/options.ts deleted file mode 100644 index 38052f9e..00000000 --- a/src/types/options.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Options to initialize the Deepgram SDK - */ -export type Options = { - /** - * Deepgram API Key used to access the API - */ - apiKey: string; - /** - * Corresponding API Secret used to access the API - */ - apiSecret: string; - /** - * Optional custom API endpoint url - */ - apiUrl?: string; -}; diff --git a/src/types/transcriptionOptions.ts b/src/types/prerecordedTranscriptionOptions.ts similarity index 95% rename from src/types/transcriptionOptions.ts rename to src/types/prerecordedTranscriptionOptions.ts index f8d9538d..b631c1a5 100644 --- a/src/types/transcriptionOptions.ts +++ b/src/types/prerecordedTranscriptionOptions.ts @@ -3,14 +3,22 @@ import { Models } from "../enums"; /** * Options for transcription */ -export type TranscriptionOptions = { +export type PrerecordedTranscriptionOptions = { /** * AI model used to process submitted audio. * @default general - * @remarks Possible values are general, phonecall, meeting or a custom-id + * @remarks Possible values are general, phonecall, meeting or a custom string * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/model */ model?: Models | string; + + /** + * Version of the model to use. + * @default latest + * @remarks latest OR + * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/version + */ + version: string; /** * BCP-47 language tag that hints at the primary spoken language. * @default en-US @@ -29,12 +37,6 @@ export type TranscriptionOptions = { * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/profanity_filter */ profanity_filter?: boolean; - /** - * Maximum number of transcript alternatives to return. Just like a human listener, - * Deepgram can provide multiple possible interpretations of what it hears. - * @default 1 - */ - alternatives?: number; /** * Indicates whether to redact sensitive information, replacing redacted content with asterisks (*). * @remarks Options include: @@ -59,6 +61,12 @@ export type TranscriptionOptions = { * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/multichannel */ multichannel?: boolean; + /** + * Maximum number of transcript alternatives to return. Just like a human listener, + * Deepgram can provide multiple possible interpretations of what it hears. + * @default 1 + */ + alternatives?: number; /** * Indicates whether to convert numbers from written format (e.g., one) to * numerical format (e.g., 1). Deepgram can format numbers up to 999,999. @@ -117,9 +125,4 @@ export type TranscriptionOptions = { * @see https://developers.deepgram.com/api-reference/speech-recognition-api#operation/transcribeAudio/properties/utt_split */ utt_split?: number; - /** - * Mimetype of the source - * @remarks Mimetype is required if the provided source is a buffer - */ - mimetype?: string; }; diff --git a/src/types/project.ts b/src/types/project.ts new file mode 100644 index 00000000..47a9bbe2 --- /dev/null +++ b/src/types/project.ts @@ -0,0 +1,13 @@ +/** + * Deepgram project + */ +export type Project = { + /** + * Unique identifier of the project + */ + id: string; + /** + * User provided name of the project + */ + name: string; +}; diff --git a/src/types/projectResponse.ts b/src/types/projectResponse.ts new file mode 100644 index 00000000..c947f9f7 --- /dev/null +++ b/src/types/projectResponse.ts @@ -0,0 +1,5 @@ +import { Project } from "./project"; + +export type ProjectResponse = { + projects: Array; +}; diff --git a/src/types/search.ts b/src/types/search.ts new file mode 100644 index 00000000..311eb46a --- /dev/null +++ b/src/types/search.ts @@ -0,0 +1,15 @@ +import { Hit } from "./hit"; + +/** + * Search result for a transcription + */ +export type Search = { + /** + * Term for which Deepgram is searching. + */ + query: string; + /** + * Instances of query found in transcript + */ + hits: Array; +}; diff --git a/src/types/apiBatchResponse.ts b/src/types/transcriptionResponse.ts similarity index 52% rename from src/types/apiBatchResponse.ts rename to src/types/transcriptionResponse.ts index 32eb76d4..d37ae10a 100644 --- a/src/types/apiBatchResponse.ts +++ b/src/types/transcriptionResponse.ts @@ -1,9 +1,9 @@ -import { ApiMetadata } from "./apiMetadata"; +import { Metadata } from "./metadata"; import { Channel } from "./channel"; -export type ApiBatchResponse = { +export type TranscriptionResponse = { request_id?: string; - metadata?: ApiMetadata; + metadata?: Metadata; results?: { channels: Array; }; diff --git a/src/types/transcriptionSource.ts b/src/types/transcriptionSource.ts new file mode 100644 index 00000000..fa1b37a4 --- /dev/null +++ b/src/types/transcriptionSource.ts @@ -0,0 +1,10 @@ +export type TranscriptionSource = UrlSource | BufferSource; + +export type UrlSource = { + url: string; +}; + +export type BufferSource = { + buffer: Buffer; + mimetype: string; +}; diff --git a/src/types/usageCallback.ts b/src/types/usageCallback.ts new file mode 100644 index 00000000..91497e74 --- /dev/null +++ b/src/types/usageCallback.ts @@ -0,0 +1,4 @@ +export type UsageCallback = { + code: number; + completed: string; +}; diff --git a/src/types/usageField.ts b/src/types/usageField.ts new file mode 100644 index 00000000..412cd9a8 --- /dev/null +++ b/src/types/usageField.ts @@ -0,0 +1,7 @@ +export type UsageField = { + tags: Array; + models: Array; + processing_methods: Array; + languages: Array; + features: Array; +}; diff --git a/src/types/usageFieldOptions.ts b/src/types/usageFieldOptions.ts new file mode 100644 index 00000000..c7a5e857 --- /dev/null +++ b/src/types/usageFieldOptions.ts @@ -0,0 +1,4 @@ +export type UsageFieldOptions = { + start?: string; + end?: string; +}; diff --git a/src/types/usageOptions.ts b/src/types/usageOptions.ts new file mode 100644 index 00000000..0fd6e58a --- /dev/null +++ b/src/types/usageOptions.ts @@ -0,0 +1,23 @@ +export type UsageOptions = { + start?: string; + end?: string; + accessor?: string; + tag?: Array; + method?: "sync" | "async" | "streaming"; + model?: string; + multichannel?: boolean; + interim_results?: boolean; + punctuate?: boolean; + ner?: boolean; + utterances?: boolean; + replace?: boolean; + profanity_filter?: boolean; + keywords?: boolean; + sentiment?: boolean; + diarize?: boolean; + detect_language?: boolean; + search?: boolean; + redact?: boolean; + alternatives?: boolean; + numerals?: boolean; +}; diff --git a/src/types/usageRequest.ts b/src/types/usageRequest.ts new file mode 100644 index 00000000..3082a340 --- /dev/null +++ b/src/types/usageRequest.ts @@ -0,0 +1,12 @@ +import { UsageCallback } from "./usageCallback"; +import { UsageRequestDetail } from "./usageRequestDetail"; +import { UsageRequestMessage } from "./usageRequestMessage"; + +export type UsageRequest = { + id: string; + created: string; + path: string; + accessor: string; + response?: UsageRequestDetail | UsageRequestMessage; + callback?: UsageCallback; +}; diff --git a/src/types/usageRequestDetail.ts b/src/types/usageRequestDetail.ts new file mode 100644 index 00000000..79f79a6c --- /dev/null +++ b/src/types/usageRequestDetail.ts @@ -0,0 +1,30 @@ +export type UsageRequestDetail = { + details: { + usd: number; + duration: number; + total_audio: number; + channels: number; + streams: number; + model: string; + method: "sync" | "async" | "streaming"; + tags: Array; + features: Array; + config: { + multichannel?: boolean; + interim_results?: boolean; + punctuate?: boolean; + ner?: boolean; + utterances?: boolean; + replace?: boolean; + profanity_filter?: boolean; + keywords?: boolean; + sentiment?: boolean; + diarize?: boolean; + detect_language?: boolean; + search?: boolean; + redact?: boolean; + alternatives?: boolean; + numerals?: boolean; + }; + }; +}; diff --git a/src/types/usageRequestList.ts b/src/types/usageRequestList.ts new file mode 100644 index 00000000..0e45f567 --- /dev/null +++ b/src/types/usageRequestList.ts @@ -0,0 +1,7 @@ +import { UsageRequest } from "./usageRequest"; + +export type UsageRequestList = { + page: number; + limit: number; + requests?: Array; +}; diff --git a/src/types/usageRequestListOptions.ts b/src/types/usageRequestListOptions.ts new file mode 100644 index 00000000..d14c501e --- /dev/null +++ b/src/types/usageRequestListOptions.ts @@ -0,0 +1,7 @@ +export type UsageRequestListOptions = { + start?: string; + end?: string; + page?: number; + limit?: number; + status?: "succeeded" | "failed"; +}; diff --git a/src/types/usageRequestMessage.ts b/src/types/usageRequestMessage.ts new file mode 100644 index 00000000..b748dcd5 --- /dev/null +++ b/src/types/usageRequestMessage.ts @@ -0,0 +1,3 @@ +export type UsageRequestMessage = { + message?: string; +}; diff --git a/src/types/usageResponse.ts b/src/types/usageResponse.ts new file mode 100644 index 00000000..d8cb33b9 --- /dev/null +++ b/src/types/usageResponse.ts @@ -0,0 +1,11 @@ +import { UsageResponseDetail } from "./usageResponseDetail"; + +export type UsageResponse = { + start: string; + end: string; + resolution: { + units: string; + amount: number; + }; + results: Array; +}; diff --git a/src/types/usageResponseDetail.ts b/src/types/usageResponseDetail.ts new file mode 100644 index 00000000..dc313de4 --- /dev/null +++ b/src/types/usageResponseDetail.ts @@ -0,0 +1,6 @@ +export type UsageResponseDetail = { + start: string; + end: string; + hours: number; + requests: number; +}; diff --git a/src/types/wordBase.ts b/src/types/wordBase.ts index 2a7f2cb8..43ddd822 100644 --- a/src/types/wordBase.ts +++ b/src/types/wordBase.ts @@ -3,4 +3,5 @@ export type WordBase = { start: number; end: number; confidence: number; + punctuated_word?: string; }; diff --git a/src/usage.ts b/src/usage.ts new file mode 100644 index 00000000..6d2b7257 --- /dev/null +++ b/src/usage.ts @@ -0,0 +1,97 @@ +import querystring from "querystring"; +import { _request } from "./httpRequest"; +import { + UsageField, + UsageFieldOptions, + UsageOptions, + UsageRequest, + UsageRequestList, + UsageRequestListOptions, + UsageResponse, +} from "./types"; + +export class Usage { + constructor(private _credentials: string, private _apiUrl: string) {} + + private apiPath = "/v1/projects"; + + /** + * Retrieves all requests associated with the provided projectId based + * on the provided options + * @param projectId Unique identifier of the project + * @param options Additional filter options + */ + async listRequests( + projectId: string, + options?: UsageRequestListOptions + ): Promise { + const requestOptions = { ...{}, ...options }; + return _request( + "GET", + this._credentials, + this._apiUrl, + `${this.apiPath}/${projectId}/requests?${querystring.stringify( + requestOptions + )}` + ); + } + + /** + * Retrieves a specific request associated with the provided projectId + * @param projectId Unique identifier of the project + * @param requestId Unique identifier for the request to retrieve + */ + async getRequest( + projectId: string, + requestId: string + ): Promise { + return _request( + "GET", + this._credentials, + this._apiUrl, + `${this.apiPath}/${projectId}/requests/${requestId}` + ); + } + + /** + * Retrieves usage associated with the provided projectId based + * on the provided options + * @param projectId Unique identifier of the project + * @param options Options to filter usage + */ + async getUsage( + projectId: string, + options?: UsageOptions + ): Promise { + const requestOptions = { ...{}, ...options }; + return _request( + "GET", + this._credentials, + this._apiUrl, + `${this.apiPath}/${projectId}/usage?${querystring.stringify( + requestOptions + )}` + ); + } + + /** + * Retrieves features used by the provided projectId based + * on the provided options + * @param projectId Unique identifier of the project + * @param options Options to filter usage + */ + async getFields( + projectId: string, + options?: UsageFieldOptions + ): Promise { + const requestOptions = { ...{}, ...options }; + return _request( + "GET", + this._credentials, + this._apiUrl, + `${this.apiPath}/${projectId}/usage/fields?${querystring.stringify( + requestOptions + )}` + ); + } +} diff --git a/src/userAgent.ts b/src/userAgent.ts new file mode 100644 index 00000000..8a10e54f --- /dev/null +++ b/src/userAgent.ts @@ -0,0 +1,15 @@ +import path from "path"; + +export function userAgent(): string { + let agent = "@deepgram/sdk/UNKNOWN node/UNKNOWN"; + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const packageDetails = require(path.join(__dirname, "..", "package.json")); + agent = `@deepgram/sdk/${ + packageDetails.version + } node/${process.version.replace("v", "")}`; + } catch (e) { + console.warn("Could not load package details"); + } + return agent; +} diff --git a/tests/batch.test.ts b/tests/batch.test.ts deleted file mode 100644 index 381a1a0c..00000000 --- a/tests/batch.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import chai from "chai"; - -import { transcribe } from "../src/batch"; - -chai.should(); - -describe("Batch tests", () => { - it("Providing buffer without mimetype will return error", (done) => { - transcribe("testKey:testSecret", "fakeUrl", Buffer.allocUnsafe(100)).catch( - (e) => { - try { - e.message.should.eq( - "DG: Mimetype must be provided if the source is a Buffer" - ); - } catch (assertionError) { - done(assertionError); - return; - } - } - ); - - done(); - }); -}); diff --git a/tests/index.test.ts b/tests/index.test.ts index 6c1ecdba..37dccddd 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -7,7 +7,7 @@ chai.should(); describe("Constructor", () => { it("Providing no credentials returns error", () => { expect(() => { - new Deepgram({ apiKey: "", apiSecret: "" }); + new Deepgram(""); }).to.throw(); }); }); diff --git a/tests/keys.test.ts b/tests/keys.test.ts index 3752bbf5..26a05417 100644 --- a/tests/keys.test.ts +++ b/tests/keys.test.ts @@ -10,14 +10,18 @@ chai.should(); const fakeCredentials = "testKey:testSecret"; const fakeUrl = "fake.url"; +const fakeProjectId = "27e92bb2-8edc-4fdf-9a16-b56c78d39c5b"; +const fakeKeyId = "ad9c6799-d380-4db7-8c22-92c20a291229"; describe("Key tests", () => { const sandbox = Sinon.createSandbox(); let requestStub: Sinon.SinonStub; + let keys: Keys; beforeEach(function () { requestStub = Sinon.stub(https, "request"); + keys = new Keys(fakeCredentials, fakeUrl); }); afterEach(function () { @@ -30,27 +34,31 @@ describe("Key tests", () => { const expectedError = `DG: ${JSON.stringify(mockInvalidCredentials)}`; nock(`https://${fakeUrl}`) - .get("/v2/keys") + .get(`/v1/projects/${fakeProjectId}/keys`) .reply(200, mockInvalidCredentials); - Keys.list(fakeCredentials, fakeUrl).catch((err) => { + keys.list(fakeProjectId).catch((err) => { assert.equal(err, expectedError); }); }); it("Create resolves", function () { - nock(`https://${fakeUrl}`).post("/v2/keys").reply(200, mockKey); + nock(`https://${fakeUrl}`) + .post(`/v1/projects/${fakeProjectId}/keys`) + .reply(200, mockKey); - Keys.create(fakeCredentials, fakeUrl, "testLabel").then((response) => { + keys.create(fakeProjectId, "test Comment", ["member"]).then((response) => { response.should.deep.eq(mockKey); requestStub.calledOnce.should.eq(true); }); }); it("Delete resolves", function () { - nock(`https://${fakeUrl}`).delete("/v2/keys").reply(200); + nock(`https://${fakeUrl}`) + .delete(`/v1/projects/${fakeProjectId}/keys/${fakeKeyId}`) + .reply(200); - Keys.delete(fakeCredentials, fakeUrl, "testLabel").then(() => { + keys.delete(fakeProjectId, fakeKeyId).then(() => { requestStub.calledOnce.should.eq(true); }); }); diff --git a/tests/mockResults.ts b/tests/mockResults.ts index f065d784..bcd98989 100644 --- a/tests/mockResults.ts +++ b/tests/mockResults.ts @@ -16,7 +16,9 @@ export const mockListKeys = { }; export const mockKey = { + id: "string", key: "string", - secret: "string", - label: "string", + comment: "string", + created: "Date", + scopes: ["member"], }; diff --git a/tests/transcription/preRecordedTranscription.test.ts b/tests/transcription/preRecordedTranscription.test.ts new file mode 100644 index 00000000..958ef0d3 --- /dev/null +++ b/tests/transcription/preRecordedTranscription.test.ts @@ -0,0 +1,25 @@ +import chai from "chai"; + +import { preRecordedTranscription } from "../../src/transcription/preRecordedTranscription"; + +chai.should(); + +describe("Transcription: Pre-recorded tests", () => { + it("Providing buffer without mimetype will return error", (done) => { + preRecordedTranscription("testKey:testSecret", "fakeUrl", { + buffer: Buffer.allocUnsafe(100), + mimetype: "", + }).catch((e) => { + try { + e.message.should.eq( + "DG: Mimetype must be provided if the source is a Buffer" + ); + } catch (assertionError) { + done(assertionError); + return; + } + }); + + done(); + }); +});