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 HTMLOften, 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:
- A watsonx Assistant instance
- 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
- Open your web app's JavaScript file.
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.onmethod handles this step.instance.on({ type: "customResponse", handler: (event, instance) => { ... } })The web chat raises the
user-file-uploadcustom event to display the File Upload button. The following JavaScript code listens to that event and invokes afileUploadCustomResponseHandlerhandler 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
Create two HTML components inside the web chat (the button and hidden file input). In the following example,
element.innerHTMLholds 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>`;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
addEventListenermethod is used for the binding event.const uploadInput = element.querySelector("#uploadInput"); const button = element.querySelector("#uploadButton"); button.addEventListener("click", () => { ... }); uploadInput.addEventListener("change", (event) => { ... });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
Send the file (browsed and selected by the user) to the server; for example, if you must post it to the
uploadendpoint. 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/uploadendpoint.# 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}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."); } }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
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.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.

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

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.

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.

Final result
The final result is a button that lets a user upload a file, as shown in the following 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.