IBM Watson Speech to Text Websocket closing (with code 1000) without result - node.js

I try to consume basic Speech-To-Text service through websocket using ws package. But after successfully open the connection and send initial message, I never get the listening state.
I also try to send the audio and empty binary (to indicate that uploading process is done), but the server always return close with code 1000.
Following is my code
'use strict';
var fs = require('fs');
var request = require('request');
var WS = require('ws');
var wsURI = 'wss://stream.watsonplatform.net/speech-to-text/api/v1/recognize?watson-token=[TOKEN]&model=en-UK_NarrowbandModell&x-watson-learning-opt-out=1';
var getTokenForm = {
method: 'GET',
uri: 'https://[USER_ID]:[PASSWORD]#stream.watsonplatform.net/authorization/api/v1/token?url=https://stream.watsonplatform.net/speech-to-text/api',
};
var filepath = 'C:/Temp/test1.wav';
request(getTokenForm, function(error, response, body) {
wsURI = wsURI.replace('[TOKEN]', body);
var message = {
'action': 'start',
'content-type': 'audio/wav',
'continuous': true,
'inactivity_timeout': -1
};
var ws = new WS(wsURI);
['message', 'error', 'close', 'open', 'connection'].forEach(function(eventName) {
ws.on(eventName, console.log.bind(console, eventName + ' event: '));
});
ws.on('open', function(evt) {
ws.send(JSON.stringify(message));
setTimeout(function timeout() {
var readStream = fs.createReadStream(filepath);
readStream.on('data', function(data) {
ws.send(data, {
binary: true,
mask: false,
});
});
readStream.on('end', function() {
ws.send(new Buffer(0), {
binary: true,
mask: false,
});
});
}, 1000);
});
ws.on('close', function(data) {
console.log(data)
});
});
Also try to send the file directly (without the stream).
var sound = fs.readFileSync(filepath);
ws.send(sound, { binary: true, mask: false});
And try to add custom header Authorization
var authorization = 'Basic ' + new Buffer('USER_ID:PASSWORD').toString('base64');
var ws = new WS(wsURI, {
headers: {
'Authorization': authorization,
}
});
But no luck so far.

there are a couple of things here. The main issue is that the model in the querystring has a typo - there should be only one 'l' on the end. (Although, not responding with an error message is a bug in the service that I'm going to report to the team.)
So, fix that and you get an error that frames should be masked. That's an easy fix, just switch mask: false to true in both places.
Then, once you've finished sending your audio & ending message, the service will send your final results and then another {"state": "listening"} message. This second state: listening should be your trigger to close the connection. Otherwise it will eventually timeout and close automatically (inactivity_timeout applies when you're sending audio with no speech in it, not when you aren't sending any data at all.)

Related

Client Timeout to NodeJS on Docker in EC2 behind AWS ALB

I have a Koajs node app in a docker container on an EC2 instance. The app is behind an AWS Application Load Balancer.
The app simply takes a POSTed file and responds with a stream that the client can view events on.
So my server is doing the right thing (sending file data), and my client is doing the right thing (receiving file data and sending back progress), but the ALB is timing out. I don't understand why it's timing out. Both client and server are sending and receiving data to/from each other, so I would think that would qualify as keep alive traffic.
Here's the code that each is running.
Client:
const request = require('request-promise');
const fs = require('fs');
const filePath = './1Gfile.txt';
const file = fs.createReadStream(filePath);
(async () => {
// PUT File
request.put({
uri: `http://server/test`,
formData: { file },
headers: { Connection: 'keep-alive' },
timeout: 200000,
})
.on('data', (data) => {
const progressString = data.toString();
console.log({ progressString });
});
})();
Server:
const { Readable } = require('stream');
const Koa = require('koa');
const router = require('koa-router')();
(async () => {
const app = module.exports = new Koa();
router.get('/healthcheck', async (ctx) => {
ctx.status = 200;
});
router.put('/test', test);
async function test(ctx) {
const read = new Readable({
objectMode: true,
read() { },
});
ctx.body = read;
let i = 1;
setInterval(() => {
read.push(`${process.hrtime()}, ${i}`);
ctx.res.write('a');
i++;
}, 3000);
}
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000, (err) => {
if (err) throw err;
console.info(`App started on port 3000 with environment localhost`);
});
})();
Both server and client are logging the correct things, but the ALB just times out at whatever I set it's idle timeout to. Is there some trick to tell the ALB that traffic is really flowing?
Thanks so much for any light you can shed on it.
Just a quick guess, you need to enable keepAlive when using the request-promise. add forever: true in options. Try this:
request.put({
uri: `http://server/test`,
formData: { file },
headers: { Connection: 'keep-alive' },
timeout: 200000,
forever: true,
})
We have a similar issue about timeout when using request-promise-native. We fixed by adding this option. Hopfully it works out for you.

Using Bing Speech Recognition API with node.js Bot Framework on Skype

I would like to use the Bing Speech Recognition API to convert speech to text when sending audio attachments in Skype to my node.js chatbot. I have tried using the code from BotBuilder-Samples intelligence-SpeechToText, however the speech recognition only works in the Emulator. When sending an audio/wave file in Skype, the bot does not respond at all instead of "You said: What’s the weather like?".
I suspected that the issue might be due to the fact that a JWT Token is required to access attachments in Skype. Hence, I have tried accessing the audio attachment in Skype using the code from BotBuilder-Samples core-ReceiveAttachment which uses request-promise instead of needle to make the HTTP request. However, the result from request-promise is not a stream and cannot be processed by the function getTextFromAudioStream().
I there would like to ask how to get speech recognition to work with audio attachments in Skype.
Thanks and best regards!
// Add your requirements
var restify = require("restify");
var builder = require("botbuilder");
var fs = require("fs");
var needle = require("needle");
var request = require("request");
var speechService = require("./speech-service.js");
var Promise = require('bluebird');
var request = require('request-promise').defaults({ encoding: null });
//=========================================================
// Bot Setup
//=========================================================
// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.PORT || 3000, function() {
console.log("%s listening to %s", server.name, server.url);
});
// Create chat bot
var connector = new builder.ChatConnector ({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
server.post("/api/messages", connector.listen());
var bot = new builder.UniversalBot(connector);
//=========================================================
// Bots Middleware
//=========================================================
// Anytime the major version is incremented any existing conversations will be restarted.
bot.use(builder.Middleware.dialogVersion({ version: 1.0, resetCommand: /^reset/i }));
//=========================================================
// Bots Dialogs
//=========================================================
bot.dialog("/", [
function (session, results, next) {
var msg = session.message;
if (hasAudioAttachment(msg)) {
// Message with attachment, proceed to download it.
// Skype attachment URLs are secured by a JwtToken, so we need to pass the token from our bot.
var attachment = msg.attachments[0];
var fileDownload = isSkypeMessage(msg)
? requestWithToken(attachment.contentUrl)
: request(attachment.contentUrl);
fileDownload.then(
function (response) {
// Send reply with attachment type & size
var reply = new builder.Message(session)
.text('Attachment from %s of %s type and size of %s bytes received.', msg.source, attachment.contentType, response.length);
session.send(reply);
}).catch(function (err) {
console.log('Error downloading attachment:', { statusCode: err.statusCode, message: err.response.statusMessage });
});
var stream = isSkypeMessage(msg)
? getAudioStreamWithToken(attachment)
: getAudioStream(attachment);
speechService.getTextFromAudioStream(stream)
.then(text => {
session.send("You said: " + text);
})
.catch(error => {
session.send("Oops! Something went wrong. Try again later.");
console.error(error);
});
}
else {
session.send("Did you upload an audio file? I'm more of an audible person. Try sending me a wav file");
}
}
]);
function getAudioStream(attachment) {
return needle.get(attachment.contentUrl, { headers: {'Content-Type': "audio/wav"} });
}
function getAudioStreamWithToken(attachment) {
var headers = {};
connector.getAccessToken((error, token) => {
headers['Authorization'] = 'Bearer ' + token;
});
headers['Content-Type'] = attachment.contentType;
return needle.get(attachment.contentUrl, { headers: headers });
}
// Request file with Authentication Header
function requestWithToken(url) {
return obtainToken().then(function (token) {
return request({
url: url,
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/octet-stream'
}
});
});
};
// Promise for obtaining JWT Token (requested once)
var obtainToken = Promise.promisify(connector.getAccessToken.bind(connector));
function isSkypeMessage(message) {
return message.source === "skype";
};
The code in the sample is already considering Skype when accessing to the attachments (see here). I think the problem you were hitting is because the key in the sample exceeded the quota. Yesterday a new Bing Speech Key was added to the sample, so I would suggest you to try again.
Also, an updated version of the sample is going to be added soon. The code is currently under code review.

Sending multiple HTTP requests in Node.js: Not receiving any responses or timeouts

I am trying to process Wikipedia articles, and want to receive a list of all Wikipedia articles. In order to do this I am frequently sending http requests to the Wikipedia API, which allows you to receive 500 titles at time and also returns an apcontinue string, which, when used in the following request, returns title starting from that string.
In order to do this, I am using the agentkeepalive module:
var http = require('http');
var Agent = require('agentkeepalive');
var keepaliveAgent = new Agent({
keepAlive: true,
maxSockets: 5,
timeout: 5000,
keepAliveTimeout: 3000
});
To send an http request to Wikipedia, I use the following code:
function wikipediaApiCall(params, callback) {
var options = {
host: 'en.wikipedia.org',
path: '/w/api.php?' + createParamString(params),
method: 'GET',
agent: keepaliveAgent
};
var callbackFunc = function(response) {
var err;
var str = '';
if (('' + response.statusCode).match(/^5\d\d$/)) {
err = new Error('Server error');
}
//another chunk of data has been recieved, so append it to `str`
response.on('data', function (chunk) {
str += chunk;
});
response.on('error', function (e) {
err = new Error('Request error');
});
response.on('timeout', function () {
err = new Error('Timeout');
response.abort();
callback(err);
});
response.on('end', function () {
var obj = JSON.parse(str);
if (obj.warnings) {
err = new Error('Request error');
}
callback(err, obj);
});
}
var req = http.request(options, callbackFunc);
req.setTimeout(5000);
req.on('error', function(err) {
callback(err, null);
return;
});
req.on('timeout', function () {
err = new Error('Timeout');
response.abort();
callback(err);
});
req.on('finish', function(){
console.log('ended');
});
req.end();
}
However, after sending between 16 and 20 request, I am not getting any response, but my request also does not time out.
Any ideas why this is happening?
Update
The request I send to Wikipedia contains the following parameters:
var params = {
list: 'allpages',
aplimit: limit,
apfrom: from,
continue: cont,
// apfilterredir: 'nonredirects'
};
Interestingly, after leaving out the nonredirects setting, I was able to send and receive up to 330 requests, but no more than that.
Update 2
I was able to register a finished event. It appears to be fired for the request that is failing as well. I modified the code accordingly.
Perhaps you need a bot flag to have higher API limits. Maybe there are too many requests in parallel; WMF recommendation is to make requests serially in case of such big tasks. Also, you should use the maxlag parameter with low values, per WMF API Etiquette.

Module request in nodejs don't stop pipe

I have used dicer for parsing pipe by request but I don't know which command for stop pipe.
dicer.on('part', function(part) {
var frameEncoded = '';
part.setEncoding('base64');
part.on('header', function(header) { });
part.on('data', function(data) { frameEncoded += data; });
part.on('end', function() { console.log(frameEncoded); });
});
var options = {
method: 'GET',
uri: 'http://192.168.1.2/video.mjpeg/'
}
request(options).pipe(dicer)
//only for test
setTimeout(function() {
console.log('stop request.pipe');
var options = {
method: 'GET',
uri: 'http://192.168.1.2/video.mjpeg/'
}
request(options).end() //<-- ?? which command?? This not work...
}, 5000);
The uri pointing to a stream of ipcamera and impossible grab event connection close of request because stream it's infinite.
One way to get access to the underlying socket for the request is to listen for the socket event on the request object. Then it's just a matter of closing that socket. For example:
var req = request(options);
req.on('socket', function(sock) {
setTimeout(function() {
console.log('stop request.pipe');
sock.end(); // or sock.destroy();
}, 5000);
});
req.pipe(dicer);
Note though, the request may not have started yet when the socket event is emitted. The socket event basically lets you know that a socket has been assigned to be used for the request (this is especially useful when you are using an http Agent that has maxSockets set to some finite value so you may not necessarily get assigned a socket right away).
You could also try:
var req = request(options);
req.on('response', function(res) {
setTimeout(function() {
console.log('stop request.pipe');
res.socket.end(); // or res.socket.destroy();
}, 5000);
});
req.pipe(dicer);
#mscdex's method works for me. Here is the code :-
request("https://www.linkedin.com/afhdjalsdjhdks")
.on("error", function (error) { process.stderr.write(error.code) })
.on("response", function (resp) {
if ((resp.statusCode !== 200) && (resp.statusCode !== 201)) {
process.stderr.write(format("%s - %s", resp.statusCode, resp.statusMessage))
resp.socket.destroy()
}
})
// .pipe(StreamAll())
.pipe(process.stdout)

Node.js request web page

I need to connect to a web page and return the status code of the page, which I've been able to achieve using http.request however the pages I need to request can take a long time, sometimes several minutes, so I'm always getting a socket hang up error.
I'm using the following code so far:
var reqPage = function(urlString, cb) {
// Resolve the URL
var path = url.parse(urlString);
var req = http.request({
host: path.hostname,
path: path.pathname,
port: 80,
method: 'GET'
});
req.on('end', function() {
cb.call(this, res);
});
req.on('error', function(e) {
winston.error(e.message);
});
};
What do I need to do to ensure that my application still attempts to connect to the page even if it's going to take a few minutes?
Use the request module and set the timeout option to an appropriate value (in milliseconds)
var request = require('request')
var url = 'http://www.google.com' // input your url here
// use a timeout value of 10 seconds
var timeoutInMilliseconds = 10*1000
var opts = {
url: url,
timeout: timeoutInMilliseconds
}
request(opts, function (err, res, body) {
if (err) {
console.dir(err)
return
}
var statusCode = res.statusCode
console.log('status code: ' + statusCode)
})
Add this if you don't want to use a higher level http client like request or superagent , then add this...
req.on("connection", function(socket){
socket.setTimeout((1000*60*5)); //5 mins
});

Resources