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
}