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 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 OnIntentRecognitionInitiatedEvent; public event EventHandler OnIntentRecognitionSucceededEvent; public event EventHandler OnIntentRecognitionFailedEvent; public event EventHandler 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 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 }