Skip to content

Commit 005116c

Browse files
MasterOdindpopp07
authored andcommitted
refactor: add typing to all non-generated functions of speech-to-text (watson-developer-cloud#954)
1 parent 263d713 commit 005116c

File tree

2 files changed

+137
-98
lines changed

2 files changed

+137
-98
lines changed

lib/recognize-stream.ts

Lines changed: 20 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -14,62 +14,21 @@
1414
* limitations under the License
1515
*/
1616

17-
import { OutgoingHttpHeaders } from 'http';
1817
import { Authenticator, contentType, qs } from 'ibm-cloud-sdk-core';
1918
import omit = require('object.omit');
2019
import pick = require('object.pick');
2120
import { Duplex, DuplexOptions } from 'stream';
2221
import { w3cwebsocket as w3cWebSocket } from 'websocket';
22+
import SpeechToTextV1 = require('../speech-to-text/v1');
2323
import { processUserParameters } from './websocket-utils';
2424

25-
// these options represent the superset of the base params,
26-
// query params, and opening message params, with the keys
27-
// in lowerCamelCase format so we can expose a consistent style
28-
// to the user. this object should be updated any time either
29-
// openingMessageParamsAllowed or queryParamsAllowed is changed
30-
interface Options extends DuplexOptions {
31-
/* Base Properties */
32-
authenticator: Authenticator;
33-
url?: string;
34-
headers?: OutgoingHttpHeaders;
35-
readableObjectMode?: boolean;
36-
objectMode?: boolean;
37-
disableSslVerification?: boolean;
38-
39-
/* Query Params*/
40-
accessToken: string;
41-
watsonToken: string;
42-
model: string;
43-
languageCustomizationId: string;
44-
acousticCustomizationId: string;
45-
baseModelVersion: string;
46-
xWatsonLearningOptOut: boolean;
47-
xWatsonMetadata: string;
48-
49-
/* Opening Message Params */
50-
contentType: string;
51-
customizationWeight: number;
52-
inactivityTimeout: number;
53-
interimResults: boolean;
54-
keywords: string[];
55-
keywordsThreshold: number;
56-
maxAlternatives: number;
57-
wordAlternativesThreshold: number;
58-
wordConfidence: boolean;
59-
timestamps: boolean;
60-
profanityFilter: boolean;
61-
smartFormatting: boolean;
62-
speakerLabels: boolean;
63-
grammarName: string;
64-
redaction: boolean;
65-
processingMetrics: boolean;
66-
processingMetricsInterval: number;
67-
audioMetrics: boolean;
25+
interface WritableState {
26+
highWaterMark: number;
6827
}
6928

7029
interface RecognizeStream extends Duplex {
71-
_writableState;
72-
readableObjectMode;
30+
_writableState: WritableState;
31+
readableObjectMode: boolean;
7332
}
7433

7534
/**
@@ -92,12 +51,12 @@ class RecognizeStream extends Duplex {
9251
return contentType.fromHeader(buffer);
9352
}
9453

95-
private options: Options;
54+
private options: RecognizeStream.Options;
9655
private authenticator: Authenticator;
9756
private listening: boolean;
9857
private initialized: boolean;
9958
private finished: boolean;
100-
private socket;
59+
private socket: w3cWebSocket;
10160

10261
/**
10362
* pipe()-able Node.js Duplex stream - accepts binary audio and emits text/objects in it's `data` events.
@@ -144,7 +103,7 @@ class RecognizeStream extends Duplex {
144103
* @param {boolean} [options.audioMetrics] - If true, requests detailed information about the signal characteristics of the input audio (detailed=false)
145104
* @constructor
146105
*/
147-
constructor(options: Options) {
106+
constructor(options: RecognizeStream.Options) {
148107
// this stream only supports objectMode on the output side.
149108
// It must receive binary data input.
150109
if (options.objectMode) {
@@ -511,4 +470,16 @@ class RecognizeStream extends Duplex {
511470
}
512471
}
513472

473+
namespace RecognizeStream {
474+
export interface Options extends DuplexOptions, SpeechToTextV1.RecognizeWebSocketParams {
475+
// these options represent the superset of the base params,
476+
// query params, and opening message params, with the keys
477+
// in lowerCamelCase format so we can expose a consistent style
478+
// to the user.
479+
authenticator: Authenticator;
480+
disableSslVerification?: boolean;
481+
url?: string;
482+
}
483+
}
484+
514485
export = RecognizeStream;

speech-to-text/v1.ts

Lines changed: 117 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import async = require('async');
22
import extend = require('extend');
3+
import { OutgoingHttpHeaders } from 'http';
34
import isStream = require('isstream');
45
import { getSdkHeaders } from '../lib/common';
56
import RecognizeStream = require('../lib/recognize-stream');
@@ -47,57 +48,60 @@ class SpeechToTextV1 extends GeneratedSpeechToTextV1 {
4748
* @param {Number} [params.times=30] - maximum number of attempts
4849
* @param {Function} callback
4950
*/
50-
whenCorporaAnalyzed(params, callback) {
51+
whenCorporaAnalyzed(params: SpeechToTextV1.WhenCorporaAnalyzedParams, callback: SpeechToTextV1.Callback<GeneratedSpeechToTextV1.Corpora>): void {
5152
const self = this;
5253

5354
async.parallel(
5455
[
5556
// validate that it has at least one corpus
56-
(next) => {
57+
(next: SpeechToTextV1.Callback<GeneratedSpeechToTextV1.Corpora>) => {
5758
self.listCorpora(params, (err, res) => {
5859
const result = res.result;
5960
if (err) {
6061
return next(err);
6162
}
6263
if (!result.corpora.length) {
63-
err = new Error(
64+
const sttError: SpeechToTextV1.SpeechToTextError = new Error(
6465
'Customization has no corpa and therefore corpus cannot be analyzed'
6566
);
66-
err.code = SpeechToTextV1.ERR_NO_CORPORA;
67-
return next(err);
67+
sttError.code = SpeechToTextV1.ERR_NO_CORPORA;
68+
return next(sttError);
6869
}
69-
next();
70+
next(null);
7071
});
7172
},
7273
// check the customization status repeatedly until it's available
73-
(next) => {
74-
const options = extend(
74+
(next: SpeechToTextV1.Callback<GeneratedSpeechToTextV1.Corpora>) => {
75+
const options: SpeechToTextV1.WhenCorporaAnalyzedOptions = extend(
7576
{
7677
interval: 5000,
7778
times: 30
7879
},
79-
params
80+
params,
81+
{
82+
errorFilter: (err: SpeechToTextV1.SpeechToTextError): boolean => {
83+
// if it's a timeout error, then listCorpora is called again after params.interval
84+
// otherwise the error is passed back to the user
85+
// if the params.times limit is reached, the error will be passed to the user regardless
86+
return err.code === SpeechToTextV1.ERR_TIMEOUT;
87+
}
88+
}
8089
);
81-
options.errorFilter = (err) => {
82-
// if it's a timeout error, then listCorpora is called again after params.interval
83-
// otherwise the error is passed back to the user
84-
// if the params.times limit is reached, the error will be passed to the user regardless
85-
return err.code === SpeechToTextV1.ERR_TIMEOUT;
86-
};
90+
8791
async.retry(
8892
options,
89-
(done) => {
93+
(done: SpeechToTextV1.Callback<GeneratedSpeechToTextV1.Corpora>) => {
9094
self.listCorpora(params, (err, res) => {
9195
const corpora = res.result;
9296
if (err) {
9397
done(err);
9498
} else if (corpora !== undefined && isProcessing(corpora)) {
9599
// if the loop times out, async returns the last error, which will be this one.
96-
err = new Error(
100+
const sttError: SpeechToTextV1.SpeechToTextError = new Error(
97101
'Corpora is still being processed, try increasing interval or times params'
98102
);
99-
err.code = SpeechToTextV1.ERR_TIMEOUT;
100-
done(err);
103+
sttError.code = SpeechToTextV1.ERR_TIMEOUT;
104+
done(sttError);
101105
} else if (corpora !== undefined && isAnalyzed(corpora)) {
102106
done(null, corpora);
103107
} else {
@@ -109,8 +113,8 @@ class SpeechToTextV1 extends GeneratedSpeechToTextV1 {
109113
);
110114
}
111115
],
112-
(err, res) => {
113-
const result = res.result;
116+
(err: Error | SpeechToTextV1.SpeechToTextError | null, res?: [null, GeneratedSpeechToTextV1.Corpora]) => {
117+
const result = res;
114118
if (err) {
115119
return callback(err);
116120
}
@@ -119,18 +123,18 @@ class SpeechToTextV1 extends GeneratedSpeechToTextV1 {
119123
);
120124
}
121125

122-
/**
123-
* Use the recognize function with a single 2-way stream over websockets
124-
*
125-
* @param {Object} params The parameters
126-
* @return {RecognizeStream}
127-
*/
128-
recognizeUsingWebSocket(params) {
126+
recognizeUsingWebSocket(params: SpeechToTextV1.RecognizeWebSocketParams): RecognizeStream {
129127
params = params || {};
130-
params.url = this.baseOptions.url;
131128

132-
// pass the Authenticator to the RecognizeStream object
133-
params.authenticator = this.getAuthenticator();
129+
const streamParams: RecognizeStream.Options = extend(
130+
params,
131+
{
132+
// pass the Authenticator to the RecognizeStream object
133+
authenticator: this.getAuthenticator()
134+
}
135+
);
136+
137+
streamParams.url = this.baseOptions.url;
134138

135139
// if the user configured a custom https client, use it in the websocket method
136140
// let httpsAgent take precedence, default to null
@@ -139,16 +143,16 @@ class SpeechToTextV1 extends GeneratedSpeechToTextV1 {
139143
// include analytics headers
140144
const sdkHeaders = getSdkHeaders('speech_to_text', 'v1', 'recognizeUsingWebSocket');
141145

142-
params.headers = extend(
146+
streamParams.headers = extend(
143147
true,
144148
sdkHeaders,
145-
params.headers
149+
streamParams.headers
146150
);
147151

148152
// allow user to disable ssl verification when using websockets
149-
params.disableSslVerification = this.baseOptions.disableSslVerification;
153+
streamParams.disableSslVerification = this.baseOptions.disableSslVerification;
150154

151-
return new RecognizeStream(params);
155+
return new RecognizeStream(streamParams);
152156
}
153157

154158
recognize(params: GeneratedSpeechToTextV1.RecognizeParams, callback: GeneratedSpeechToTextV1.Callback<GeneratedSpeechToTextV1.SpeechRecognitionResults>): Promise<any> | void {
@@ -173,27 +177,29 @@ class SpeechToTextV1 extends GeneratedSpeechToTextV1 {
173177
* @param {Number} [params.times=30] - maximum number of attempts
174178
* @param {Function} callback
175179
*/
176-
whenCustomizationReady(params, callback) {
180+
whenCustomizationReady(params: SpeechToTextV1.WhenCustomizationReadyParams, callback: SpeechToTextV1.Callback<GeneratedSpeechToTextV1.LanguageModel>): void {
177181
const self = this;
178182

179183
// check the customization status repeatedly until it's ready or available
180184

181-
const options = extend(
185+
const options: SpeechToTextV1.WhenCustomizationReadyOptions = extend(
182186
{
183187
interval: 5000,
184188
times: 30
185189
},
186-
params
190+
params,
191+
{
192+
errorFilter: (err: SpeechToTextV1.SpeechToTextError) => {
193+
// if it's a timeout error, then getLanguageModel is called again after params.interval
194+
// otherwise the error is passed back to the user
195+
// if the params.times limit is reached, the error will be passed to the user regardless
196+
return err.code === SpeechToTextV1.ERR_TIMEOUT;
197+
}
198+
}
187199
);
188-
options.errorFilter = (err) => {
189-
// if it's a timeout error, then getLanguageModel is called again after params.interval
190-
// otherwise the error is passed back to the user
191-
// if the params.times limit is reached, the error will be passed to the user regardless
192-
return err.code === SpeechToTextV1.ERR_TIMEOUT;
193-
};
194200
async.retry(
195201
options,
196-
(next) => {
202+
(next: SpeechToTextV1.Callback<GeneratedSpeechToTextV1.LanguageModel>) => {
197203
self.getLanguageModel(params, (err, res) => {
198204
const customization = err ? null : res.result;
199205
if (err) {
@@ -203,11 +209,11 @@ class SpeechToTextV1 extends GeneratedSpeechToTextV1 {
203209
customization.status === 'training'
204210
) {
205211
// if the loop times out, async returns the last error, which will be this one.
206-
err = new Error(
207-
'Customization is still pending, try increasing interval or times params'
212+
const sttError: SpeechToTextV1.SpeechToTextError = new Error(
213+
'Customization is still pending, try increasing interval or times params',
208214
);
209-
err.code = SpeechToTextV1.ERR_TIMEOUT;
210-
next(err);
215+
sttError.code = SpeechToTextV1.ERR_TIMEOUT;
216+
next(sttError);
211217
} else if (
212218
customization.status === 'ready' ||
213219
customization.status === 'available'
@@ -229,4 +235,66 @@ class SpeechToTextV1 extends GeneratedSpeechToTextV1 {
229235
}
230236
}
231237

238+
namespace SpeechToTextV1 {
239+
export type Callback<T> = (err: Error | SpeechToTextError | null, res?: T) => void;
240+
241+
export interface SpeechToTextError extends Error {
242+
message: string;
243+
code?: string;
244+
}
245+
246+
export interface CheckParams {
247+
/** How long to wait in milliseconds between status checks, defaults to 5000 milliseconds */
248+
interval?: number;
249+
/** maximum number of attempts to check, defaults to 30 */
250+
times?: number;
251+
}
252+
253+
export type WhenCorporaAnalyzedParams = GeneratedSpeechToTextV1.ListCorporaParams & CheckParams;
254+
export interface WhenCorporaAnalyzedOptions extends WhenCorporaAnalyzedParams {
255+
errorFilter: (err: SpeechToTextError) => boolean;
256+
}
257+
258+
export type WhenCustomizationReadyParams = GeneratedSpeechToTextV1.GetLanguageModelParams & CheckParams;
259+
export interface WhenCustomizationReadyOptions extends WhenCorporaAnalyzedParams {
260+
errorFilter: (err: SpeechToTextError) => boolean;
261+
}
262+
263+
export interface RecognizeWebSocketParams {
264+
headers?: OutgoingHttpHeaders;
265+
readableObjectMode?: boolean;
266+
objectMode?: boolean;
267+
268+
/* Query Params*/
269+
accessToken?: string;
270+
watsonToken?: string;
271+
model?: string;
272+
languageCustomizationId?: string;
273+
acousticCustomizationId?: string;
274+
baseModelVersion?: string;
275+
xWatsonLearningOptOut?: boolean;
276+
xWatsonMetadata?: string;
277+
278+
/* Opening Message Params */
279+
contentType?: string;
280+
customizationWeight?: number;
281+
inactivityTimeout?: number;
282+
interimResults?: boolean;
283+
keywords?: string[];
284+
keywordsThreshold?: number;
285+
maxAlternatives?: number;
286+
wordAlternativesThreshold?: number;
287+
wordConfidence?: boolean;
288+
timestamps?: boolean;
289+
profanityFilter?: boolean;
290+
smartFormatting?: boolean;
291+
speakerLabels?: boolean;
292+
grammarName?: string;
293+
redaction?: boolean;
294+
processingMetrics?: boolean;
295+
processingMetricsInterval?: number;
296+
audioMetrics?: boolean;
297+
}
298+
}
299+
232300
export = SpeechToTextV1;

0 commit comments

Comments
 (0)