301 lines
8.4 KiB
C#
301 lines
8.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
#region Enums
|
|
|
|
public enum EIntentRecognitionState
|
|
{
|
|
Idle = 0,
|
|
ListeningForUserInput = 10,
|
|
WaitingForInternRecognition = 20,
|
|
}
|
|
|
|
#endregion
|
|
|
|
public class IntentRecognitionService : MonoBehaviour
|
|
{
|
|
|
|
#region Inspector Properties
|
|
|
|
[Header("Config Values")]
|
|
[SerializeField]
|
|
private bool debugModeIsActive;
|
|
|
|
[SerializeField]
|
|
private bool intentTrackingIsActive;
|
|
|
|
[Header("Scene Objects")]
|
|
[SerializeField]
|
|
private OpenAIServices openAIServices_IntentRecognizer;
|
|
|
|
[Header("Asset Objects")]
|
|
[SerializeField]
|
|
private AudioClip waitingForIntentRecognitionClip;
|
|
|
|
[SerializeField]
|
|
private AudioClip intentRecognitionCompletedClip;
|
|
|
|
#endregion
|
|
|
|
#region Public Properties
|
|
|
|
#region IntentRecognitionState
|
|
|
|
private EIntentRecognitionState _intentRecognitionState = EIntentRecognitionState.Idle;
|
|
|
|
public EIntentRecognitionState IntentRecognitionState
|
|
{
|
|
get { return this._intentRecognitionState; }
|
|
set
|
|
{
|
|
if (value != this._intentRecognitionState)
|
|
{
|
|
this.logIfInDebugMode($"IntentRecognitionState changed, new value= {value}");
|
|
|
|
this._intentRecognitionState = value;
|
|
|
|
this.OnIntentRecognitionStateChangedEvent?.Invoke(this, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
public event EventHandler<EIntentRecognitionState> OnIntentRecognitionStateChangedEvent;
|
|
|
|
#endregion
|
|
|
|
public RequestDataModel PendingRequestDataModel { get; private set; } = null;
|
|
|
|
#endregion
|
|
|
|
#region Private Properties
|
|
|
|
private ViaggioAIManager vaim { get { return ViaggioAIManager.Instance; } }
|
|
|
|
#endregion
|
|
|
|
#region Framework Functions
|
|
|
|
void OnEnable()
|
|
{
|
|
this.updateOpenAIServiceState();
|
|
|
|
this.subscribeToEvents();
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
this.unsubscribeFromEvents();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Events
|
|
|
|
public event EventHandler<bool> OnIntentRecognitionInitiatedEvent;
|
|
public event EventHandler<string> OnIntentRecognitionSucceededEvent;
|
|
public event EventHandler<bool> OnIntentRecognitionFailedEvent;
|
|
public event EventHandler<bool> OnIntentRecognitionTrackingUploadFailedEvent;
|
|
|
|
#endregion
|
|
|
|
#region Private Events
|
|
|
|
private async void subscribeToEvents()
|
|
{
|
|
while (vaim == null)
|
|
{
|
|
await System.Threading.Tasks.Task.Delay(10);
|
|
}
|
|
|
|
vaim.OnViaggioAIStateChangedEvent += this.onViaggioAIStateChanged;
|
|
|
|
vaim.SpeechRecognitionService.OnFullTranscriptionChangedEvent += this.onFullTranscriptionChanged;
|
|
|
|
this.openAIServices_IntentRecognizer.OnLastBotReplyChangedEvent += this.onLastBotReplyChanged;
|
|
this.openAIServices_IntentRecognizer.OnLastBotErrorChangedEvent += this.onLastBotErrorChanged;
|
|
}
|
|
|
|
private void unsubscribeFromEvents()
|
|
{
|
|
if (vaim != null)
|
|
{
|
|
vaim.OnViaggioAIStateChangedEvent -= this.onViaggioAIStateChanged;
|
|
}
|
|
if (vaim?.SpeechRecognitionService != null)
|
|
{
|
|
vaim.SpeechRecognitionService.OnFullTranscriptionChangedEvent -= this.onFullTranscriptionChanged;
|
|
}
|
|
if (this?.openAIServices_IntentRecognizer)
|
|
{
|
|
this.openAIServices_IntentRecognizer.OnLastBotReplyChangedEvent -= this.onLastBotReplyChanged;
|
|
this.openAIServices_IntentRecognizer.OnLastBotErrorChangedEvent -= this.onLastBotErrorChanged;
|
|
}
|
|
}
|
|
|
|
private void onViaggioAIStateChanged(object sender, EViaggioAIState e)
|
|
{
|
|
this.updateOpenAIServiceState();
|
|
}
|
|
|
|
private void onFullTranscriptionChanged(object sender, string fullTranscription)
|
|
{
|
|
if (vaim.ViaggioAIState != EViaggioAIState.IntentRecognition)
|
|
{
|
|
return;
|
|
}
|
|
this.sendIntentRequest(fullTranscription);
|
|
}
|
|
|
|
private void onLastBotReplyChanged(object sender, string replyText)
|
|
{
|
|
if (vaim.ViaggioAIState != EViaggioAIState.IntentRecognition)
|
|
{
|
|
return;
|
|
}
|
|
this.processBotReply(replyText);
|
|
}
|
|
|
|
private void onLastBotErrorChanged(object sender, string errorText)
|
|
{
|
|
if (vaim.ViaggioAIState != EViaggioAIState.IntentRecognition)
|
|
{
|
|
return;
|
|
}
|
|
this.handleError(sender, errorText);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Functions
|
|
|
|
public void StartIntentRecognition(Dictionary<string, string> possibleIntents)
|
|
{
|
|
vaim.SpeechRecognitionService.StartListeningOnceAsync();
|
|
|
|
this.PendingRequestDataModel = new RequestDataModel() { PossibleIntents = possibleIntents };
|
|
|
|
this.IntentRecognitionState = EIntentRecognitionState.ListeningForUserInput;
|
|
this.OnIntentRecognitionInitiatedEvent?.Invoke(this, true);
|
|
}
|
|
|
|
public void StopIntentRecognition()
|
|
{
|
|
this.PendingRequestDataModel = null;
|
|
|
|
vaim.SpeechRecognitionService.StopListening();
|
|
this.openAIServices_IntentRecognizer.Stop();
|
|
|
|
this.IntentRecognitionState = EIntentRecognitionState.Idle;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Functions
|
|
|
|
private async void updateOpenAIServiceState()
|
|
{
|
|
while (vaim == null)
|
|
{
|
|
await System.Threading.Tasks.Task.Delay(10);
|
|
}
|
|
|
|
bool newIsEnabled = vaim.ViaggioAIState == EViaggioAIState.IntentRecognition;
|
|
|
|
this.openAIServices_IntentRecognizer.enabled = newIsEnabled;
|
|
}
|
|
|
|
private void sendIntentRequest(string text)
|
|
{
|
|
this.PendingRequestDataModel.InputText = text;
|
|
|
|
string requestText = this.PendingRequestDataModel.GetRequestText();
|
|
this.openAIServices_IntentRecognizer.SendTextToBot(requestText);
|
|
|
|
this.logIfInDebugMode("Sending intent request to ChatBot");
|
|
|
|
this.IntentRecognitionState = EIntentRecognitionState.ListeningForUserInput;
|
|
vaim.PlayAudioIfEnabled(this.waitingForIntentRecognitionClip);
|
|
}
|
|
|
|
private void processBotReply(string reply)
|
|
{
|
|
if (this.PendingRequestDataModel == null)
|
|
{
|
|
this.logIfInDebugMode($"Ignoring incoming bot reply because intent recognition was stopped.");
|
|
return;
|
|
}
|
|
|
|
if (this.PendingRequestDataModel.PossibleIntents.ContainsKey(reply))
|
|
{
|
|
this.logIfInDebugMode($"Recognition succeeded, Key = {reply}");
|
|
this.OnIntentRecognitionSucceededEvent?.Invoke(this, reply);
|
|
}
|
|
else
|
|
{
|
|
this.logIfInDebugMode($"Recognition failed, Reply = {reply}");
|
|
this.OnIntentRecognitionFailedEvent?.Invoke(this, false);
|
|
}
|
|
|
|
if (this.intentTrackingIsActive)
|
|
{
|
|
this.trackIntentRecognition(this.PendingRequestDataModel, reply);
|
|
}
|
|
|
|
this.IntentRecognitionState = EIntentRecognitionState.Idle;
|
|
vaim.PlayAudioIfEnabled(this.intentRecognitionCompletedClip);
|
|
}
|
|
|
|
private async void trackIntentRecognition(RequestDataModel pendingRequestDataModel, string reply)
|
|
{
|
|
if (string.IsNullOrEmpty(pendingRequestDataModel?.InputText))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(reply))
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.logIfInDebugMode($"Uploading IntentRecogntionTrackingItem");
|
|
|
|
IntentRecogntionTrackingItem newIntentRecogntionTrackingItem = new IntentRecogntionTrackingItem()
|
|
{
|
|
UserInput = pendingRequestDataModel.InputText,
|
|
Intent = reply
|
|
};
|
|
|
|
bool uploadOK = await vaim.APIService.TrackIntentRecognition(newIntentRecogntionTrackingItem);
|
|
|
|
this.logIfInDebugMode($"Upload of IntentRecogntionTrackingItem done. Result ok={uploadOK}");
|
|
|
|
if (!uploadOK)
|
|
{
|
|
this.OnIntentRecognitionTrackingUploadFailedEvent?.Invoke(this, false);
|
|
}
|
|
}
|
|
|
|
private void handleError(object sender, string errorText)
|
|
{
|
|
string logText = $"{sender} Error: {errorText}";
|
|
|
|
this.logIfInDebugMode(logText);
|
|
|
|
vaim.RaiseViaggioAIError(logText);
|
|
}
|
|
|
|
private void logIfInDebugMode(string message)
|
|
{
|
|
if (!this.debugModeIsActive)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Debug.Log($"(ViaggioAI IntentRecognitionService) => {message}");
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|