aws / aws-sdk-js-v3

Modularized AWS SDK for JavaScript.
Apache License 2.0
3.12k stars 579 forks source link

Lex - PostContentCommand - ReferenceError: Buffer is not defined #2168

Closed lavarith closed 3 years ago

lavarith commented 3 years ago

Describe the bug

After building and deploying my code, when I send an audio clip to Lex, the console reads "ReferenceError: Buffer is not defined". I've tried including Buffer as a dependency, but it appears to be some failure when the response is parsed by the browser that I can't seem to resolve.

The network shows appropriate header transcription based on what I asked (ie, if I say "what time is it" that's what is in the header). The output to console is also as expected if I use PostTextCommand instead of PostContentCommand, so, it seems to be a problem with the PostResponse object when dealing with audio.

Your environment

SDK version number

@aws-sdk/client-lex-runtime-service@3.9.0

Is the issue in the browser/Node.js/ReactNative?

Browser

Details of the browser/Node.js/ReactNative version

Paste output of npx envinfo --browsers or node -v or react-native -v

  Browsers:
    Chrome: 89.0.4389.90
    Edge: 89.0.774.45
    Firefox: 78.7.0
    Safari: 14.0.3

Steps to reproduce

My index.js is fairly simple and utilizes jquery and lex-audio to export a wav.

The goal is to allow a user to press the spacebar, capture audio, and then send it to Lex and receive back the transcription.

index.js

const Buffer = require("buffer");
const { LexRuntimeServiceClient } = require("@aws-sdk/client-lex-runtime-service")
const { DeleteSessionCommand, PostContentCommand, PostTextCommand } = require("@aws-sdk/client-lex-runtime-service");
const { CognitoIdentityClient } = require("@aws-sdk/client-cognito-identity");
const { fromCognitoIdentityPool } = require("@aws-sdk/credential-provider-cognito-identity");

//const DeleteSessionCommand = AWS.LexRuntime.DeleteSessionCommand;
//const PostContentCommand = AWS.LexRuntime.PostContentCommand;
//const PostTextCommand = AWS.LexRuntime.PostTextCommand;
//const fromCognitoIdentityPool = AWS.fromCognitoIdentityPool;

var isRecording = false;

function HandleError(error) {
    console.log('GetUserMedia error: ', error.message, error.name);
}

function GotDevices(mediaDevices) {
    const videoSelector = $("#video-options");
    const audioSelector = $("#microphone-options");

    let count = 1;
    console.log("Available Devices", mediaDevices);
    videoSelector.empty();
    audioSelector.empty();
    mediaDevices.forEach(mediaDevice => {
        // Populate video options
        if (mediaDevice.kind === 'videoinput') {
            var label = mediaDevice.label || `Camera ${count++}`;
            var new_option = `<option value="${mediaDevice.deviceId}">${label}</option>`;
            videoSelector.append(new_option);
        }
        // Populate microphone options
        if (mediaDevice.kind === 'audioinput'){
            var label = mediaDevice.label || `Microphone ${count++}`;
            var new_option = `<option value="${mediaDevice.deviceId}">${label}</option>`;
            audioSelector.append(new_option);
        }
    });

    // Select the device in dropdown if they are in use
    if(window.audioStream){
        window.audioStream.getTracks().forEach(track => {
            audioSelector.val(track.getSettings().deviceId);
        });
    }
    if(window.videoStream){
        window.videoStream.getTracks().forEach(track => {
            videoSelector.val(track.getSettings().deviceId);
        });
    }
}

async function SendLexCommand(command){
    try {
        // Initialize the Amazon Cognito credentials provider
        const lexRuntime = new LexRuntimeServiceClient({
            region: "us-west-2",
            credentials: fromCognitoIdentityPool({
                client: new CognitoIdentityClient({ region: "us-west-2" }),
                identityPoolId: '<identity pool>'
            })
        });
        const data = await lexRuntime.send(command);
        // process data.
        console.log(data);
    } catch (error) {
        // error handling.
        console.log(error);
    } finally {
        // finally.
        console.log("Sent Command");
    }
}

function GotStream(stream){
    const videoElement = document.getElementById('video');

    // Split the stream
    const audioStream = new MediaStream(stream.getAudioTracks());
    const videoStream = new MediaStream(stream.getVideoTracks());

    // Assign separately so the window can access them
    window.videoStream = videoStream;
    window.audioStream = audioStream;

    // Populate Video
    videoElement.srcObject = videoStream;
    videoElement.play();

    // Start Audio Controls
    var audioControl = new LexAudio.audioControl();

    $('body').bind("keypress", function(e){
        if (e.which == 32 && !isRecording){//space bar
            isRecording = true;
            console.log('Start Recording');
            audioControl.startRecording();
        } 
    });

    $('body').bind("keyup", function(e){
        if (e.which == 32){//space bar
            isRecording = false;
            console.log('Stop Recording');
            audioControl.stopRecording();
            audioControl.exportWAV(function(blob){
                //audioControl.play(blob);
                //var contentType = "text/plain; charset=utf-8";
                var contentType = 'audio/x-l16; sample-rate=16000; channel-count=1';
                const params = {
                    /** input parameters */
                    botName: "LexTestBot",
                    botAlias: "$LATEST",
                    userId: "Testtest",
                    contentType: contentType,
                    accept: "text/plain; charset=utf-8",
                    inputStream: blob
                    //inputText: "what time is it"
                };
                //const command = new PostTextCommand(params);
                const command = new PostContentCommand(params);
                //const command = new DeleteSessionCommand(params);

                SendLexCommand(command);
            });
        } 
    });

    // Refresh button list in case labels have become available
    return navigator.mediaDevices.enumerateDevices();
}

function Start(){
    const videoSelector = $("#video-options");
    const audioSelector = $("#microphone-options");

    (async function () {
        // Stop tracks if they already are running
        if(window.audioStream){
            window.audioStream.getTracks().forEach(track => {
                track.stop();
            });
        }
        if(window.videoStream){
            window.videoStream.getTracks().forEach(track => {
                track.stop();
            });
        }

        const audioSource = audioSelector.val();
        const videoSource = videoSelector.val();
        const constraints = {
            audio: {deviceId: audioSource ? {exact: audioSource} : undefined},
            video: {deviceId: videoSource ? {exact: videoSource} : undefined}
        };

        // Try to load camera and microphone selections
        try {
            // Load camera and micrphone inputs
            $.when(RequestStream(constraints))
                .then(GotStream)
                .then(GotDevices)
                .catch(HandleError);
        } catch(err){
            console.log(err);
        }
    })();
}

$("#save-settings").on("click", Start);

Start();

Observed behavior

When I press spacebar, the audio is sent to Lex, and when the response is received, I get ReferenceError: Buffer is not defined

When I check the network headers for the response, the headers look right (ie,. it says "what time is it" as the transcribed header from lex), but it is throwing up somewhere when it tries to parse the response.

Expected behavior

Console should output the response data.

Screenshots

If applicable, add screenshots to help explain your problem.

Additional context

Here is my webpack.config.js

// Import path for resolving file paths
const path = require("path");
const webpack = require('webpack');

module.exports = {
  // Specify the entry point for our app.
  entry: [
      path.join(__dirname, "chalicelib/js/website/index.js")
  ],
  // Specify the output file containing our bundled code.
  output: {
      path: __dirname,
      filename: 'chalicelib/js/website/index.min.js',
  },
  // Enable WebPack to use the 'path' package.
  resolve:{
    fallback: { path: require.resolve("path-browserify")}
  },
  externals: {
    jquery: 'jQuery',
  },
  optimization: {
    minimize: false
  },
  plugins: [
    new webpack.ProvidePlugin({
      Buffer: ['Buffer', 'buffer']
    }),
  ]
  /**
  * In Webpack version v2.0.0 and earlier, you must tell 
  * webpack how to use "json-loader" to load 'json' files.
  * To do this Enter 'npm --save-dev install json-loader' at the 
  * command line to install the "json-loader' package, and include the 
  * following entry in your webpack.config.js.
  module: {
    rules: [{test: /\.json$/, use: use: "json-loader"}]
  }
  **/
};

And my package.json

{
    "name": "aws-webpack",
    "version": "1.0.0",
    "description": "",
    "main": "aws-sdk.min.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
        "@aws-sdk/client-cognito-identity": "^3.9.0",
        "@aws-sdk/client-iam": "^3.0.0",
        "@aws-sdk/client-lambda": "^3.9.0",
        "@aws-sdk/client-lex-model-building-service": "^3.9.0",
        "@aws-sdk/client-lex-runtime-service": "^3.9.0",
        "@aws-sdk/client-s3": "^3.3.0",
        "@aws-sdk/credential-provider-cognito-identity": "^3.9.0",
        "@aws-sdk/util-buffer-from": "^3.6.1",
        "buffer": "^5.2.1",
        "he": "^1.2.0",
        "safer-buffer": "^2.1.2"
    },
    "devDependencies": {
        "browserify": "^17.0.0",
        "path-browserify": "^1.0.1",
        "webpack": "^5.0.0",
        "webpack-cli": "^4.5.0"
    }
}
lavarith commented 3 years ago

Additional note, if I change the command to PostTextCommand and send text, it works as expected and outputs the WhatTimeIsItIntent in the console.

lavarith commented 3 years ago

Updated my webpack and followed this tutorial https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/cross-service-submitdata-browser-script.html and the error went away.

github-actions[bot] commented 3 years ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in this thread.