IBM Developer

Tutorial

Enable a file upload capability within your watsonx Assistant web chat

Upload an attachment from inside a web chat in a few simple steps using JavaScript and HTML

By Ankit Katba

Often, you might find it challenging to provide the file upload facility within an IBM watsonx Assistant web chat because this is not a direct feature of watsonx Assistant. However, there is a solution. This tutorial explains how you can enable a file upload capability within your web chat. In just a few simple steps using JavaScript and HTML, you can provide users with a way to upload an attachment from inside the web chat.

Prerequisite

To complete this tutorial, you need:

  1. A watsonx Assistant instance
  2. A web application that uses HTML and JavaScript

Estimated time

It should take you approximately one hour to complete the tutorial.

Steps

Step 1. Register a custom response listener

  1. Open your web app's JavaScript file.
  2. Register an event listener in the JavaScript file to handle the event that is triggered by an assistant. See the following code where the instance.on method handles this step.

     instance.on({
         type: "customResponse",
         handler: (event, instance) => {
             ...
         }
     })
    
  3. The web chat raises the user-file-upload custom event to display the File Upload button. The following JavaScript code listens to that event and invokes a fileUploadCustomResponseHandler handler when it receives the event. Add the following code to your web app JavaScript code.

     var g_wa_instance;
     window.watsonAssistantChatOptions = {
       integrationID: <your-wa-integration-id>,
       region: <your-wa-region>,
       serviceInstanceID: <your-service-instance-id>,
       onLoad: function (instance) {
         g_wa_instance = instance;
    
         instance.on({
           type: "customResponse",
           handler: (event, instance) => {
             if (
               event.data.message.user_defined &&
               event.data.message.user_defined.user_defined_type ===
                 "user-file-upload"
             ) {
               fileUploadCustomResponseHandler(event, instance);
             }
           },
         });
    
         instance.render();
       }
     };
    

Step 2. Create handler for creating file upload button

  1. Create two HTML components inside the web chat (the button and hidden file input). In the following example, element.innerHTML holds the two HTML components.

     element.innerHTML = `
         <div>
             <input type="file" id="uploadInput" style="display: none;">
             <button id="uploadButton" class="WAC__button--primary WAC__button--primaryMd"> Upload a File </button>
         </div>`;
    
  2. Bind the click event to the button and change the event to the file input. When the user clicks the button, a system file browser opens for the user to select a file to upload. In the following code, the addEventListener method is used for the binding event.

     const uploadInput = element.querySelector("#uploadInput");
     const button = element.querySelector("#uploadButton");
     button.addEventListener("click", () => {
         ...
     });
     uploadInput.addEventListener("change", (event) => {
         ...
     });
    
  3. Add the following handler code to your web app JavaScript code.

     function fileUploadCustomResponseHandler(event, instance) {
       const { element } = event.data;
    
       element.innerHTML = `
             <div>
                 <input type="file" id="uploadInput" style="display: none;">
                 <button id="uploadButton" class="WAC__button--primary WAC__button--primaryMd"> Upload a File </button>
             </div>`;
    
       const uploadInput = element.querySelector("#uploadInput");
       const button = element.querySelector("#uploadButton");
       button.addEventListener("click", () => {
         uploadInput.click();
       });
       uploadInput.addEventListener("change", (event) => {
         const selectedFile = event.target.files[0];
         if (selectedFile) {
           // You can access the selected file using selectedFile variable
           // console.log("Selected file:", selectedFile.name);
           uploadFileFromAsst(selectedFile);
         }
       });
     }
    

Step 3. Send file to the server

  1. Send the file (browsed and selected by the user) to the server; for example, if you must post it to the upload endpoint. The server should have its own logic to authorize, validate, and store the received file appropriately and then respond with proper successful or failure messages. The following code provides a sample Python Flask server /upload endpoint.

     # Upload endpoint on server
     @app.route('/upload', methods=["POST"])
     def upload_file():
         # get file data from formData
         file = request.files['uploaded_file']
         split_tup = os.path.splitext(file.filename)
         unique_filename = uuid.uuid4().hex
         # Consuming local folder 'upload' to store uploaded file
         file_path = 'upload/{}{}'.format(unique_filename, split_tup[1])
         # Save file on server /upload folder with generated unique_filename
         file.save(file_path)
         # perform tasks
         # return the response
         return {"msg": "{}{}".format(split_tup[0],split_tup[1]), "success": true}
    
  2. The following code shows the function to send the file to your back-end server. Replace <your-backend-server> in the code with your back-end server URL.

     function uploadFileFromAsst(selectedFile) {
       if (selectedFile) {
         const formData = new FormData();
         formData.append("uploaded_file", selectedFile);
    
         //Invoke server endpoint to upload file
         const SERVER = <your-backend-server>;
         fetch(SERVER + "/upload", {
           method: "POST",
           body: formData,
         })
           .then((response) => {
             if (response.ok) {
               return response.json();
             } else {
               throw new Error("File upload failed.");
             }
           })
           .then(async (data) => {
             var msg = data["msg"] ? data["msg"] : "";
             if (!msg) {
               alert("File upload error. Please try after some time.");
               return;
             }
             messageChatbot(msg, true);
           })
           .catch((error) => {
             console.error("Error while file uploading: ", error);
           });
       } else {
         console.error("No file selected.");
       }
     }
    
  3. The following code is the utility method to send messages to a watsonx Assistant chatbot from JavaScript. Add this code as well to your JavaScript, along with all the previous functions.

     function messageChatbot(txt, silent = false) {
       const maxChars = 2040;
       txt = txt.substring(0, maxChars);
       var send_obj = { input: { message_type: "text", text: txt } };
    
       g_wa_instance.send(send_obj, { silent }).catch(function (error) {
         console.error("Sending message to chatbot failed");
       });
     }
    

    For more information, see watsonx Assistant Instance methods.

Step 4. Create action and step in watsonx Assistant

  1. Create an action and a conversation step in your watsonx Assistant to trigger a custom event named user-file-upload. You created this event's listener in Step 1. The following JSON code has a "response_type": "user_defined", which is used to raise a user-defined event from the assistant.

  2. Go to your watsonx Assistant action step, switch to the JSON editor, and enter the following JSON code.

     {
       "generic": [
         {
           "values": [
             {
               "text_expression": {
                 "concat": [
                   {
                     "scalar": "Kindly browse and upload a file"
                   }
                 ]
               }
             }
           ],
           "response_type": "text",
           "selection_policy": "sequential",
           "repeat_on_reprompt": false
         },
         {
           "user_defined": {
             "user_defined_type": "user-file-upload"
           },
           "response_type": "user_defined",
           "repeat_on_reprompt": false
         }
       ]
     }
    

    The following image shows an example of how your watsonx Assistant JSON editor should look.

    Putting in JSON editor

  3. Save your changes. The following image shows how your watsonx Assistant should look after saving your changes.

    This is how step looks after save

  4. Set the customer response type in watsonx Assistant to Free text, as shown in the following image. This allows you to make a conditional flow for success and failure scenarios while uploading the file.

    Customer response type set to: Free text

The following example shows the conditional flow, where if the response contains "Failure", then the assistant displays the failure message and repeats the steps to upload a file. If the response matches the pattern *.pdf, then the assistant displays the file upload success message.

Conditional flows

Final result

The final result is a button that lets a user upload a file, as shown in the following demonstration.

Demonstration

Conclusion

This tutorial provided a simple way to show how you can enrich your web chat with additional HTML components in just a few easy steps. You can explore the use case demo using this same feature on the dsce.ibm.com platform, or try it out yourself by embedding more components to your web chat. For more information, refer to the watsonx Assistant demo site.

If you'd like to experiment more with watsonx Assistant, take a look at the watsonx Assistant documentation. You can also try it out.