2

I just got to a point where I could set-up the Watson Assistant V2 with Unity and trying to converse with an Assistant I created with a single Skill. I need further help to get the assistant set up to work with my Unity app.

In V1 of the assistant, it was possible to target a workspace and also the response was returning the intent, nodes visited so on. My queries were correctly processed and the responses were identical to the one on the "try it" application in the IBM cloud dashboard.

In the new version though, I am getting the same response for any query I send to the assistant. How can I target the right skill or rather pass correct settings to the assistant to get the correct responses?

IMAGE - Unity log showing assistant responses

[IMAGE - Assistant trial on dashboard][2]

The code I'm using to send queries and get responses is:

IEnumerator TokenExample()
{
    //  Create IAM token options and supply the apikey. IamUrl is the URL used to get the 
    //  authorization token using the IamApiKey. It defaults to https://iam.bluemix.net/identity/token
    TokenOptions iamTokenOptions = new TokenOptions()
    {
        IamApiKey = "API KEY",
        IamUrl = "https://iam.bluemix.net/identity/token"

    };
    //  Create credentials using the IAM token options

    _credentials = new Credentials(iamTokenOptions, "https://gateway-fra.watsonplatform.net/assistant/api");
    while (!_credentials.HasIamTokenData())
        yield return null;

    _assistant = new Assistant(_credentials);
    _assistant.VersionDate = "2018-11-01";

    Debug.Log(_assistant.GetServiceID()); // returns "AssitantV2"

}

public void PingAssistantV2() // triggered from a button press in UI
{
    _assistant.CreateSession(OnCreateSession, OnFail, AssistantID); // Assistant ID is entered through the Inspector

}

public void OnCreateSession(SessionResponse response, Dictionary<string, object> customData)
{
    Log.Debug("ExampleAssistantV2.OnMessage()", "Assistant: Create Session Response: {0}", customData["json"].ToString());

    string _si = response.SessionId;
    Debug.Log("SessionID: " +_si);

    MessageInput mi = new MessageInput();
    mi.Text = Query.textComponent.text; // get user query from an input field in unity UI


    MessageRequest messageRequest = new MessageRequest()
    {
        Input = mi

    };
    Debug.LogFormat("<b> Query Sent: {0} </b>", Query.textComponent.text);
    if (response.SessionId != null ) _assistant.Message(OnMessage, OnFail, AssistantID, _si, messageRequest);
}

private void OnMessage(MessageResponse AssistantResponse, Dictionary<string, object> customData)
{
    Log.Debug("ExampleAssistant.OnMessage()", "Response: {0}", customData["json"].ToString());
    Debug.LogFormat("<b> SUCCESS </b>");
    Debug.Log(customData["json"].ToString());

    //  Convert resp to fsdata
    fsData fsdata = null;
    fsResult r = _serializer.TrySerialize(AssistantResponse.GetType(), AssistantResponse, out fsdata);
    if (!r.Succeeded)
        throw new WatsonException(r.FormattedMessages);

    //  Convert fsdata to MessageResponse
    IBM.WatsonDeveloperCloud.Assistant.v2.MessageResponse messageResponse = new IBM.WatsonDeveloperCloud.Assistant.v2.MessageResponse();
    object obj = messageResponse;
    r = _serializer.TryDeserialize(fsdata, obj.GetType(), ref obj);
    if (!r.Succeeded)
        throw new WatsonException(r.FormattedMessages);

    Response.text = AssistantResponse.Output.Generic.First().Text; // send response to unity UI text box

}

private void OnFail(RESTConnector.Error error, Dictionary<string, object> customData)
{
    Log.Debug("OnFail()", "Failed: {0}", error.ToString());
    Debug.LogFormat("<b> Failed </b>");
    Debug.Log(error.ToString());
}

EDIT to address @Taj's comment

The Issue persists even with the sample for assitant V2 in the SDK:

Wrong Unity Responses v/s appropriate responses in dashboardtrail

The code adopted from the example included in the SDK:

/**
* Copyright 2018 IBM Corp. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using IBM.Watson.DeveloperCloud.Connection;
using IBM.Watson.DeveloperCloud.Logging;
using IBM.Watson.DeveloperCloud.Utilities;
using IBM.WatsonDeveloperCloud.Assistant.v2;
using UnityEngine;
using TMPro;

namespace IBM.Watson.DeveloperCloud.Services.Assistant.v2
{
    public class ExampleAssistantV2 : MonoBehaviour
    {
        #region PLEASE SET THESE VARIABLES IN THE INSPECTOR
        [Space(10)]
        [Tooltip("The service URL (optional). This defaults to \"https://gateway.watsonplatform.net/assistant/api\"")]
        [SerializeField]
        private string _serviceUrl;
        [Tooltip("The assistantId to run the example.")]
        [SerializeField]
        private string _assistantId;
        [Tooltip("The version date with which you would like to use the service in the form YYYY-MM-DD.")]
        [SerializeField]
        private string _versionDate;
        [Header("CF Authentication")]
        [Tooltip("The authentication username.")]
        [SerializeField]
        private string _username;
        [Tooltip("The authentication password.")]
        [SerializeField]
        private string _password;
        [Header("IAM Authentication")]
        [Tooltip("The IAM apikey.")]
        [SerializeField]
        private string _iamApikey;
        [Tooltip("The IAM url used to authenticate the apikey (optional). This defaults to \"https://iam.bluemix.net/identity/token\".")]
        [SerializeField]
        private string _iamUrl;
        #endregion

        private Assistant _service;

        private bool _createSessionTested = false;
        private bool _messageTested = false;
        private bool _deleteSessionTested = false;
        private string _sessionId;

        public TMP_InputField query;
        public TextMeshProUGUI response;

        private void Start()
        {
            LogSystem.InstallDefaultReactors();
            Runnable.Run(CreateService());
        }



        private IEnumerator CreateService()
        {
            //  Create credential and instantiate service
            Credentials credentials = null;
            if (!string.IsNullOrEmpty(_username) && !string.IsNullOrEmpty(_password))
            {
                //  Authenticate using username and password
                credentials = new Credentials(_username, _password, _serviceUrl);
            }
            else if (!string.IsNullOrEmpty(_iamApikey))
            {
                //  Authenticate using iamApikey
                TokenOptions tokenOptions = new TokenOptions()
                {
                    IamApiKey = _iamApikey,
                    IamUrl = _iamUrl
                };

                credentials = new Credentials(tokenOptions, _serviceUrl);

                //  Wait for tokendata
                while (!credentials.HasIamTokenData())
                    yield return null;
            }
            else
            {
                throw new WatsonException("Please provide either username and password or IAM apikey to authenticate the service.");
            }

            _service = new Assistant(credentials);
            _service.VersionDate = _versionDate;

            Runnable.Run(SessionCreate());
        }

        private IEnumerator SessionCreate()
        {
            Log.Debug("ExampleAssistantV2.Examples()", "Attempting to CreateSession");
            _service.CreateSession(OnCreateSession, OnFail, _assistantId);

            while (!_createSessionTested)
            {
                yield return null;
            }

        }

        private IEnumerator Examples()
        {


            Log.Debug("ExampleAssistantV2.Examples()", "Attempting to Message");

            MessageInput mi = new MessageInput(); // construct a messgae input
            mi.Text = query.textComponent.text;


            MessageRequest messageRequest = new MessageRequest() // construct a message request
            {
                Input = mi

            };

            Log.Debug("ExampleAssistantV2.OnDeleteSession()", "<b>Query: </b> <b>{0}</b>", messageRequest.Input.Text);

            _service.Message(OnMessage, OnFail, _assistantId, _sessionId,messageRequest); // send a message request

            while (!_messageTested)
            {
                yield return null;
            }

            //Log.Debug("ExampleAssistantV2.Examples()", "Attempting to DeleteSession");
            //_service.DeleteSession(OnDeleteSession, OnFail, _assistantId, _sessionId);

            //while (!_deleteSessionTested)
            //{
            //    yield return null;
            //}

            //Log.Debug("ExampleAssistantV2.Examples()", "Assistant examples complete.");
        }

        private void OnDeleteSession(object response, Dictionary<string, object> customData)
        {
            Log.Debug("ExampleAssistantV2.OnDeleteSession()", "Session deleted.");
            _createSessionTested = true;
        }

        private void OnMessage(MessageResponse _response, Dictionary<string, object> customData)
        {
            _messageTested = true;
            response.text = _response.Output.Generic.First().Text; // trying to get response

            Log.Debug("ExampleAssistantV2.OnDeleteSession()", "<b>RESPONSE: </b> <b>{0}</b>", response.text);
        }

        private void OnCreateSession(SessionResponse response, Dictionary<string, object> customData)
        {
            Log.Debug("ExampleAssistantV2.OnCreateSession()", "Session: <b>{0}</b>", response.SessionId);
            _sessionId = response.SessionId;
            _createSessionTested = true;

        }

        private void OnFail(RESTConnector.Error error, Dictionary<string, object> customData)
        {
            Log.Debug("ExampleAssistantV2.OnFail()", "Call failed: {0}: {1}", error.ErrorCode, error.ErrorMessage);
        }

        public void PingAssitant ()
        {
            Runnable.Run(Examples());
        }
    }
}
  • The v1 API is still supported - if it worked ok for your usecase I would just continue using v1 instead of refactoring to v2. – Michal Bida Nov 16 '18 at 09:37
  • Hi Michal, How to do that? From Unity I can choose the API version to use but how do I set it in my Watson dashboard? The structure has completely changed from being Assistant -> Workplace structure to Assistant -> Skill structure now. – the_nandavar Nov 16 '18 at 23:11
  • You do not need to set anything in Watson dashboard. You can access your assistant either through v1 api or v2 api. Both of them work at the same time. – Michal Bida Nov 18 '18 at 07:58

2 Answers2

2

I can see from your log that you have a new sessionId each time you message. You do not need to create a session each time you send a message. The session should persist during the conversation. I moved the call to CreateSession to your TokenExample() and call PingAssistantV2() once you have a sessionId.

string _si = "";

IEnumerator TokenExample()
{
    //  Create IAM token options and supply the apikey. IamUrl is the URL used to get the 
    //  authorization token using the IamApiKey. It defaults to https://iam.bluemix.net/identity/token
    TokenOptions iamTokenOptions = new TokenOptions()
    {
        IamApiKey = "API KEY",
        IamUrl = "https://iam.bluemix.net/identity/token"

    };
    //  Create credentials using the IAM token options

    _credentials = new Credentials(iamTokenOptions, "https://gateway-fra.watsonplatform.net/assistant/api");
    while (!_credentials.HasIamTokenData())
        yield return null;

    _assistant = new Assistant(_credentials);
    _assistant.VersionDate = "2018-11-01";

    Debug.Log(_assistant.GetServiceID()); // returns "AssitantV2"

    _assistant.CreateSession(OnCreateSession, OnFail, AssistantID); // Assistant ID is entered through the Inspector
}

public void PingAssistantV2() // triggered from a button press in UI
{
    MessageInput mi = new MessageInput();
    mi.Text = Query.textComponent.text; // get user query from an input field in unity UI

    MessageRequest messageRequest = new MessageRequest()
    {
        Input = mi

    };
    Debug.LogFormat("<b> Query Sent: {0} </b>", Query.textComponent.text);
    if (response.SessionId != null ) _assistant.Message(OnMessage, OnFail, AssistantID, _si, messageRequest);
}

public void OnCreateSession(SessionResponse response, Dictionary<string, object> customData)
{
    Log.Debug("ExampleAssistantV2.OnMessage()", "Assistant: Create Session Response: {0}", customData["json"].ToString());

    _si = response.SessionId;
    Debug.Log("SessionID: " +_si);

    PingAssistantV2();
}

private void OnMessage(MessageResponse AssistantResponse, Dictionary<string, object> customData)
{
    Log.Debug("ExampleAssistant.OnMessage()", "Response: {0}", customData["json"].ToString());
    Debug.LogFormat("<b> SUCCESS </b>");
    Debug.Log(customData["json"].ToString());

    //  Convert resp to fsdata
    fsData fsdata = null;
    fsResult r = _serializer.TrySerialize(AssistantResponse.GetType(), AssistantResponse, out fsdata);
    if (!r.Succeeded)
        throw new WatsonException(r.FormattedMessages);

    //  Convert fsdata to MessageResponse
    IBM.WatsonDeveloperCloud.Assistant.v2.MessageResponse messageResponse = new IBM.WatsonDeveloperCloud.Assistant.v2.MessageResponse();
    object obj = messageResponse;
    r = _serializer.TryDeserialize(fsdata, obj.GetType(), ref obj);
    if (!r.Succeeded)
        throw new WatsonException(r.FormattedMessages);

    Response.text = AssistantResponse.Output.Generic.First().Text; // send response to unity UI text box

}

private void OnFail(RESTConnector.Error error, Dictionary<string, object> customData)
{
    Log.Debug("OnFail()", "Failed: {0}", error.ToString());
    Debug.LogFormat("<b> Failed </b>");
    Debug.Log(error.ToString());
}
taj
  • 1,128
  • 1
  • 9
  • 23
  • Alright, I rectified the way I create a session before sending messages to the assitant. But, what about the incorrect responses and absence of Information on Intents detected and Nodes visited in the response Class? If I ask the same queries in the web app or on dashboard preview conversation, I get appropriate responses, but not from Unity app. Can you post an example of how you create an assitantv2, create a session and query it to get appropriate answers in Unity, responses comparable to those obtained on the dashboard app? – the_nandavar Nov 14 '18 at 16:06
  • Have you seen the example [here](https://github.com/watson-developer-cloud/unity-sdk/blob/develop/Examples/ServiceExamples/Scripts/ExampleAssistantV2.cs)? Creating a session will automatically track conversation context in the service instead of needing to persist context as the conversation continues. – taj Nov 14 '18 at 16:40
  • Yes, The same Issue. Please refer to the edited question now. I am adding the screenshot and the new code. – the_nandavar Nov 14 '18 at 17:37
  • The image of the log you posted does not match the code you posted above. How are you invoking your `TokenExample()` method? You need to do this before anything since you need to authenticate the service. Your code shows you are creating a session each button click. You need to create the session only once per conversation, not with each request. – taj Nov 14 '18 at 22:59
  • Hi Taj, i'm afraid you didn't see the part of the question after the EDIT. I have posted the full code, which is the example you sent a link to, edited a little to match user input from UI. I'm calling the Create Session from the start function, hence creating a session only once. This is what I'm doing Start -> Authnticate -> Crete session. Then, on each button click, I'm calling the assitant.message() with a message request included. – the_nandavar Nov 15 '18 at 08:01
  • Also, the exmple that you linked in one of the previous comments doesn't show any response, it just calls the assistant.message() and confirms if the message was successfully sent to the assistant. Can you please add an example where you use the built-in store assitant skill to query opening hours and get appropriate responses for the user queries sent from Unity? my suspicsion is that the Skill is not being used when you query from assistant.message(); – the_nandavar Nov 15 '18 at 08:09
  • Ah I see the issue now - There is a problem with serialization of the `MessageInput` object. Please test out this [PR](https://github.com/watson-developer-cloud/unity-sdk/pull/480) and I will update the answer once this is merged into develop. – taj Nov 15 '18 at 19:33
  • Hi Taj, I tried the same code with the SDK I pulled from the branch: https://github.com/watson-developer-cloud/unity-sdk/tree/hotfix-assistant-serialization – the_nandavar Nov 16 '18 at 23:44
  • Hi Taj, I tried the same code with the SDK I pulled from the branch: https://github.com/watson-developer-cloud/unity-sdk/tree/hotfix-assistant-serialization. The issue persists. I get the fallback response no matter what I send. I still cannot see which Skill/Workspace is being used for the responses, niether can I choose which Skill/Workspace to use (Which I believe isn't necesary). Please post an example if you manage to get it working from Unity. I'm eagerly waiting for it to work :) Thank you for your efforts in advance! – the_nandavar Nov 16 '18 at 23:51
  • Please Have a look at: https://github.com/watson-developer-cloud/unity-sdk/issues/478?_pjax=%23js-repo-pjax-container#issuecomment-439644855. – the_nandavar Nov 17 '18 at 20:24
1

@taj your develop branch at https://github.com/watson-developer-cloud/unity-sdk/archive/develop.zip is now working! Thank you for your commitment towards sorting this out. I do not have sufficient rep to upvote your answer but ya, Kudos!

Working Code as provided by @taj on the WDC Slack channel:

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using IBM.Watson.DeveloperCloud.Connection;
using IBM.Watson.DeveloperCloud.Logging;
using IBM.Watson.DeveloperCloud.Utilities;
using IBM.WatsonDeveloperCloud.Assistant.v2;
using UnityEngine;
using TMPro;
namespace IBM.Watson.DeveloperCloud.Services.Assistant.v2
{
    public class ExampleAssistantV2b : MonoBehaviour
    {
        #region PLEASE SET THESE VARIABLES IN THE INSPECTOR
        [Space(10)]
        [Tooltip("The service URL (optional). This defaults to \"https://gateway.watsonplatform.net/assistant/api\"")]
        [SerializeField]
        private string _serviceUrl;
        [Tooltip("The assistantId to run the example.")]
        [SerializeField]
        private string _assistantId;
        [Tooltip("The version date with which you would like to use the service in the form YYYY-MM-DD.")]
        [SerializeField]
        private string _versionDate;
        [Header("CF Authentication")]
        [Tooltip("The authentication username.")]
        [SerializeField]
        private string _username;
        [Tooltip("The authentication password.")]
        [SerializeField]
        private string _password;
        [Header("IAM Authentication")]
        [Tooltip("The IAM apikey.")]
        [SerializeField]
        private string _iamApikey;
        [Tooltip("The IAM url used to authenticate the apikey (optional). This defaults to \"https://iam.bluemix.net/identity/token\".")]
        [SerializeField]
        private string _iamUrl;
        #endregion
        private Assistant _service;
        private string _sessionId;
        public TMP_InputField query;
        public TextMeshProUGUI response;
        public List<string> testQueryList;
        public int queryNum = 0;
        private void Start()
        {
            LogSystem.InstallDefaultReactors();
            testQueryList = new List<string>()
            {
                "",
                "What are your hours?",
                "Are you open on Christmas?",
                "I would like to make an appointment",
                "Friday at 12pm",
                "yes"
            };
            Runnable.Run(CreateService());
        }
        private IEnumerator CreateService()
        {
            //  Create credential and instantiate service
            Credentials credentials = null;
            if (!string.IsNullOrEmpty(_username) && !string.IsNullOrEmpty(_password))
            {
                //  Authenticate using username and password
                credentials = new Credentials(_username, _password, _serviceUrl);
            }
            else if (!string.IsNullOrEmpty(_iamApikey))
            {
                //  Authenticate using iamApikey
                TokenOptions tokenOptions = new TokenOptions()
                {
                    IamApiKey = _iamApikey,
                    IamUrl = _iamUrl
                };
                credentials = new Credentials(tokenOptions, _serviceUrl);
                //  Wait for tokendata
                while (!credentials.HasIamTokenData())
                    yield return null;
            }
            else
            {
                throw new WatsonException("Please provide either username and password or IAM apikey to authenticate the service.");
            }
            _service = new Assistant(credentials);
            _service.VersionDate = _versionDate;
            SessionCreate();
        }
        private void SessionCreate()
        {
            Log.Debug("ExampleAssistantV2.Examples()", "Attempting to CreateSession");
            _service.CreateSession(OnCreateSession, OnFail, _assistantId);
        }
        private void Examples()
        {
            Log.Debug("ExampleAssistantV2.Examples()", "Attempting to Message");
            MessageInput mi = new MessageInput(); // construct a messgae input
            //mi.Text = query.textComponent.text;
            mi.Text = testQueryList[queryNum];
            MessageRequest messageRequest = new MessageRequest() // construct a message request
            {
                Input = mi
            };
            Log.Debug("ExampleAssistantV2.OnDeleteSession()", "<b>Query: </b> <b>{0}</b>", messageRequest.Input.Text);
            _service.Message(OnMessage, OnFail, _assistantId, _sessionId, messageRequest); // send a message request
        }
        private void OnMessage(MessageResponse _response, Dictionary<string, object> customData)
        {
            //response.text = _response.Output.Generic[0].Text; // trying to get response
            string assistantResponse = _response.Output.Generic[0].Text; // trying to get response
            Log.Debug("ExampleAssistantV2.OnDeleteSession()", "<b>RESPONSE: </b> <b>{0}</b>", assistantResponse);
            queryNum++;
        }
        private void OnCreateSession(SessionResponse response, Dictionary<string, object> customData)
        {
            Log.Debug("ExampleAssistantV2.OnCreateSession()", "Session: <b>{0}</b>", response.SessionId);
            _sessionId = response.SessionId;
        }
        private void OnFail(RESTConnector.Error error, Dictionary<string, object> customData)
        {
            Log.Debug("ExampleAssistantV2.OnFail()", "Call failed: {0}: {1}", error.ErrorCode, error.ErrorMessage);
        }
        public void PingAssitant()
        {
            Examples();
        }
    }
}