Initial Commit

This commit is contained in:
2025-07-04 20:33:06 +03:00
commit 8f09347ae0
9219 changed files with 2447903 additions and 0 deletions
@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using UnityEngine.Events;
namespace Meta.WitAi.CallbackHandlers
{
[Serializable]
public class ConfidenceRange
{
public float minConfidence;
public float maxConfidence;
public UnityEvent onWithinConfidenceRange = new UnityEvent();
public UnityEvent onOutsideConfidenceRange = new UnityEvent();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8a9a869d94ca2214fb1f2f5c3aeef455
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,53 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Meta.WitAi.Attributes;
using Meta.WitAi.Json;
using Meta.WitAi.Utilities;
using UnityEngine;
using Utilities;
namespace Meta.WitAi.CallbackHandlers
{
/// <summary>
/// Triggers an event when no intents were recognized in an utterance.
/// </summary>
[AddComponentMenu("Wit.ai/Response Matchers/Out Of Domain")]
public class OutOfScopeUtteranceHandler : WitResponseHandler
{
[Tooltip("If set to a value greater than zero, any intent that returns with a confidence lower than this value will be treated as out of domain/scope.")]
[Range(0, 1f)]
[SerializeField] private float confidenceThreshold = 0.0f;
[Space(WitRuntimeStyles.HeaderPaddingTop)]
[TooltipBox("Triggered when a activation on the associated AppVoiceExperience does not return any intents.")]
[SerializeField] private StringEvent onOutOfDomain = new StringEvent();
protected override string OnValidateResponse(WitResponseNode response, bool isEarlyResponse)
{
if (response == null)
{
return "Response is null";
}
if (response["intents"].Count > 0)
{
if (response.GetFirstIntent()["confidence"].AsFloat < confidenceThreshold)
{
return string.Empty;
}
return "Intents found";
}
return string.Empty;
}
protected override void OnResponseInvalid(WitResponseNode response, string error) {}
protected override void OnResponseSuccess(WitResponseNode response)
{
onOutOfDomain?.Invoke(response.GetTranscription());
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 960c102c70216114fa5c3800d4172d67
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,64 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.WitAi.Json;
using UnityEngine;
using UnityEngine.Events;
namespace Meta.WitAi.CallbackHandlers
{
[AddComponentMenu("Wit.ai/Response Matchers/Simple Intent Handler")]
public class SimpleIntentHandler : WitIntentMatcher
{
[SerializeField] private UnityEvent onIntentTriggered = new UnityEvent();
[Tooltip("Confidence ranges are executed in order. If checked, all confidence values will be checked instead of stopping on the first one that matches.")]
[SerializeField] public bool allowConfidenceOverlap;
#if UNITY_2021_3_2 || UNITY_2021_3_3 || UNITY_2021_3_4 || UNITY_2021_3_5
[NonReorderable]
#endif
[SerializeField] public ConfidenceRange[] confidenceRanges;
public UnityEvent OnIntentTriggered => onIntentTriggered;
protected override void OnResponseSuccess(WitResponseNode response)
{
onIntentTriggered.Invoke();
UpdateRanges(response);
}
protected override void OnResponseInvalid(WitResponseNode response, string error)
{
UpdateRanges(response);
}
private void UpdateRanges(WitResponseNode response)
{
// Find intents if possible
var intents = response?.GetIntents();
if (intents == null)
{
return;
}
// Iterate intents
foreach (var intentData in intents)
{
if (string.Equals(intent, intentData.name, StringComparison.CurrentCultureIgnoreCase))
{
// Found intent
RefreshConfidenceRange(intentData.confidence, confidenceRanges, allowConfidenceOverlap);
return;
}
}
// Not matched
RefreshConfidenceRange(0, confidenceRanges, allowConfidenceOverlap);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8d2d3ff93ff48bd40ab5bca3cf4e6d2c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,60 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.WitAi.Json;
using UnityEngine;
using UnityEngine.Events;
namespace Meta.WitAi.CallbackHandlers
{
[AddComponentMenu("Wit.ai/Response Matchers/Simple String Entity Handler")]
public class SimpleStringEntityHandler : WitIntentMatcher
{
[SerializeField] public string entity;
[SerializeField] public string format;
[SerializeField] private StringEntityMatchEvent onIntentEntityTriggered
= new StringEntityMatchEvent();
public StringEntityMatchEvent OnIntentEntityTriggered => onIntentEntityTriggered;
protected override string OnValidateResponse(WitResponseNode response, bool isEarlyResponse)
{
// Return base
string result = base.OnValidateResponse(response, isEarlyResponse);
if (!string.IsNullOrEmpty(result))
{
return result;
}
// Not found
var entityValue = response.GetFirstEntityValue(entity);
if (string.IsNullOrEmpty(entityValue))
{
return $"Missing required entity: {entity}";
}
// Fail
return string.Empty;
}
protected override void OnResponseInvalid(WitResponseNode response, string error) { }
protected override void OnResponseSuccess(WitResponseNode response)
{
var entityValue = response.GetFirstEntityValue(entity);
if (!string.IsNullOrEmpty(format))
{
onIntentEntityTriggered.Invoke(format.Replace("{value}", entityValue));
}
else
{
onIntentEntityTriggered.Invoke(entityValue);
}
}
}
[Serializable]
public class StringEntityMatchEvent : UnityEvent<string> {}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 77dceeee73817ec4c844564714e05c77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,81 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.Conduit;
using Meta.WitAi.Json;
using Meta.WitAi.Data.Intents;
using UnityEngine;
using UnityEngine.Serialization;
namespace Meta.WitAi.CallbackHandlers
{
// Abstract class to share confidence handling
public abstract class WitIntentMatcher : WitResponseHandler
{
/// <summary>
/// Intent name to be matched
/// </summary>
[Header("Intent Settings")]
[SerializeField] public string intent;
/// <summary>
/// Confidence threshold
/// </summary>
[FormerlySerializedAs("confidence")]
[Range(0, 1f), SerializeField] public float confidenceThreshold = .6f;
// Handle simple intent validation
protected override string OnValidateResponse(WitResponseNode response, bool isEarlyResponse)
{
// No response
if (response == null)
{
return "No response";
}
// Check against all intents
WitIntentData[] intents = response.GetIntents();
if (intents == null || intents.Length == 0)
{
return "No intents found";
}
// Find intent
WitIntentData found = null;
foreach (var intentData in intents)
{
if (string.Equals(intent, intentData.name, StringComparison.CurrentCultureIgnoreCase))
{
found = intentData;
break;
}
}
if (found == null)
{
return $"Missing required intent '{intent}'";
}
// Check confidence
if (found.confidence < confidenceThreshold)
{
return $"Required intent '{intent}' confidence too low: {found.confidence:0.000}\nRequired: {confidenceThreshold:0.000}";
}
return string.Empty;
}
protected override void OnEnable()
{
Manifest.WitResponseMatcherIntents.Add(intent);
base.OnEnable();
}
protected override void OnDisable()
{
Manifest.WitResponseMatcherIntents.Remove(intent);
base.OnDisable();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9dca9f31f9df1bd4d9cda88e81bf3082
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,163 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.WitAi.Data;
using Meta.WitAi.Json;
using Meta.WitAi.Requests;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Serialization;
namespace Meta.WitAi.CallbackHandlers
{
[Serializable]
public class WitResponseEvent : UnityEvent<WitResponseNode> {}
[Serializable]
public class WitResponseErrorEvent : UnityEvent<WitResponseNode, string> {}
public abstract class WitResponseHandler : MonoBehaviour
{
[FormerlySerializedAs("wit")]
[SerializeField] public VoiceService Voice;
[SerializeField] public bool ValidateEarly = false;
// Whether validated early
private bool _validated = false;
private void OnValidate()
{
if (!Voice) Voice = FindObjectOfType<VoiceService>();
}
protected virtual void OnEnable()
{
if (!Voice) Voice = FindObjectOfType<VoiceService>();
if (!Voice)
{
VLog.E($"VoiceService not found in scene.\nDisabling {GetType().Name} on {gameObject.name}");
enabled = false;
}
else
{
Voice.VoiceEvents.OnSend.AddListener(OnRequestSend);
Voice.VoiceEvents.OnValidatePartialResponse.AddListener(HandleValidateEarlyResponse);
Voice.VoiceEvents.OnResponse.AddListener(HandleFinalResponse);
}
}
protected virtual void OnDisable()
{
if (Voice)
{
Voice.VoiceEvents.OnSend.RemoveListener(OnRequestSend);
Voice.VoiceEvents.OnValidatePartialResponse.RemoveListener(HandleValidateEarlyResponse);
Voice.VoiceEvents.OnResponse.RemoveListener(HandleFinalResponse);
}
}
protected virtual void OnRequestSend(VoiceServiceRequest request)
{
_validated = false;
}
protected virtual void HandleValidateEarlyResponse(VoiceSession session)
{
if (ValidateEarly && !_validated)
{
string invalidReason = OnValidateResponse(session.response, true);
if (string.IsNullOrEmpty(invalidReason))
{
_validated = true;
OnResponseSuccess(session.response);
session.validResponse = true;
}
}
}
protected virtual void HandleFinalResponse(WitResponseNode response)
{
// Not yet handled
if (!_validated)
{
string invalidReason = OnValidateResponse(response, false);
if (!string.IsNullOrEmpty(invalidReason))
{
OnResponseInvalid(response, invalidReason);
}
else
{
OnResponseSuccess(response);
}
_validated = true;
}
}
// Handle validation
protected abstract string OnValidateResponse(WitResponseNode response, bool isEarlyResponse);
// Handle invalid
protected abstract void OnResponseInvalid(WitResponseNode response, string error);
// Handle valid
protected abstract void OnResponseSuccess(WitResponseNode response);
#if UNITY_EDITOR
// For tests
public void HandleResponse(WitResponseNode response) => HandleFinalResponse(response);
#endif
/// <summary>
/// Refresh confidence range
/// </summary>
/// <param name="confidence"></param>
/// <param name="confidenceRanges"></param>
/// <param name="allowConfidenceOverlap"></param>
/// <returns></returns>
public static bool RefreshConfidenceRange(float confidence, ConfidenceRange[] confidenceRanges, bool allowConfidenceOverlap)
{
// Whether found
bool foundIn = false;
bool foundOut = false;
// Iterate confidences
for (int i = 0; null != confidenceRanges && i < confidenceRanges.Length; i++)
{
var range = confidenceRanges[i];
// Within Range
if (confidence >= range.minConfidence &&
confidence <= range.maxConfidence)
{
// Ignore overlap
if (!allowConfidenceOverlap && foundIn)
{
continue;
}
// Within delegate
range.onWithinConfidenceRange?.Invoke();
foundIn = true;
}
// Out of Range
else
{
// Ignore overlap
if (!allowConfidenceOverlap && foundOut)
{
continue;
}
// Outside delegate
range.onOutsideConfidenceRange?.Invoke();
foundOut = true;
}
}
// Return if found within
return foundIn;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 097381abbd7a3364f80ac4460551a2cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,358 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Meta.WitAi.Attributes;
using Meta.WitAi.Data;
using Meta.WitAi.Json;
using Meta.WitAi.Utilities;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Serialization;
namespace Meta.WitAi.CallbackHandlers
{
[AddComponentMenu("Wit.ai/Response Matchers/Response Matcher")]
public class WitResponseMatcher : WitIntentMatcher
{
[FormerlySerializedAs("valuePaths")]
[Header("Value Matching")]
#if UNITY_2021_3_2 || UNITY_2021_3_3 || UNITY_2021_3_4 || UNITY_2021_3_5
[NonReorderable]
#endif
[SerializeField] public ValuePathMatcher[] valueMatchers;
[Header("Output")]
#if UNITY_2021_3_2 || UNITY_2021_3_3 || UNITY_2021_3_4 || UNITY_2021_3_5
[NonReorderable]
#endif
[SerializeField] private FormattedValueEvents[] formattedValueEvents;
[SerializeField] private MultiValueEvent onMultiValueEvent = new MultiValueEvent();
[TooltipBox("Triggered if the matching conditions did not match. The parameter will be the transcription that was received. This will only trigger if there were values for intents or entities, but those values didn't match this matcher.")]
[SerializeField] private StringEvent onDidNotMatch = new StringEvent();
[TooltipBox("Triggered if a request was checked and no intents were found. This will still trigger if entities match and only applies to intents. The parameter will be the transcription.")]
[SerializeField] private StringEvent onOutOfDomain = new StringEvent();
private static Regex valueRegex = new Regex(Regex.Escape("{value}"), RegexOptions.Compiled);
// Handle validation
protected override string OnValidateResponse(WitResponseNode response, bool isEarlyResponse)
{
// Return base
string result = base.OnValidateResponse(response, isEarlyResponse);
if (!string.IsNullOrEmpty(result))
{
return result;
}
// Only check value matches on early
if (isEarlyResponse && !ValueMatches(response))
{
return "No value matches";
}
// Success
return string.Empty;
}
// Ignore for mismatched intent
protected override void OnResponseInvalid(WitResponseNode response, string error)
{
if (response.GetIntents().Length > 0 || response.EntityCount() > 0)
{
onDidNotMatch?.Invoke(response.GetTranscription());
}
if (response.GetIntents().Length == 0)
{
onOutOfDomain?.Invoke(response.GetTranscription());
}
}
// Handle valid callback
protected override void OnResponseSuccess(WitResponseNode response)
{
// Check value matches
if (ValueMatches(response))
{
for (int j = 0; j < formattedValueEvents.Length; j++)
{
var formatEvent = formattedValueEvents[j];
var result = formatEvent.format;
for (int i = 0; i < valueMatchers.Length; i++)
{
var reference = valueMatchers[i].Reference;
var value = reference.GetStringValue(response);
if (!string.IsNullOrEmpty(formatEvent.format))
{
if (!string.IsNullOrEmpty(value))
{
result = valueRegex.Replace(result, value, 1);
result = result.Replace("{" + i + "}", value);
}
else if (result.Contains("{" + i + "}"))
{
result = "";
break;
}
}
}
if (!string.IsNullOrEmpty(result))
{
formatEvent.onFormattedValueEvent?.Invoke(result);
}
}
}
else
{
onDidNotMatch?.Invoke(response.GetTranscription());
}
// Get all values & perform multi value event
List<string> values = new List<string>();
foreach (var matcher in valueMatchers)
{
// Add value
var value = matcher.Reference.GetStringValue(response);
values.Add(value);
// Refresh confidence
if (matcher.ConfidenceReference != null)
{
float confidenceValue = ValueMatches(response, matcher)
? matcher.ConfidenceReference.GetFloatValue(response)
: 0f;
RefreshConfidenceRange(confidenceValue, matcher.confidenceRanges, matcher.allowConfidenceOverlap);
}
}
onMultiValueEvent.Invoke(values.ToArray());
}
private bool ValueMatches(WitResponseNode response)
{
bool matches = true;
for (int i = 0; i < valueMatchers.Length && matches; i++)
{
matches &= ValueMatches(response, valueMatchers[i]);
}
return matches;
}
private bool ValueMatches(WitResponseNode response, ValuePathMatcher matcher)
{
var value = matcher.Reference.GetStringValue(response);
bool result = !matcher.contentRequired || !string.IsNullOrEmpty(value);
switch (matcher.matchMethod)
{
case MatchMethod.RegularExpression:
result &= Regex.Match(value, matcher.matchValue).Success;
break;
case MatchMethod.Text:
result &= value == matcher.matchValue;
break;
case MatchMethod.IntegerComparison:
result &= CompareInt(value, matcher);
break;
case MatchMethod.FloatComparison:
result &= CompareFloat(value, matcher);
break;
case MatchMethod.DoubleComparison:
result &= CompareDouble(value, matcher);
break;
}
return result;
}
private bool CompareDouble(string value, ValuePathMatcher matcher)
{
// This one is freeform based on the input so we will retrun false if it is not parsable
if (!double.TryParse(value, out double dValue)) return false;
// We will throw an exception if match value is not a numeric value. This is a developer
// error.
double dMatchValue = double.Parse(matcher.matchValue);
switch (matcher.comparisonMethod)
{
case ComparisonMethod.Equals:
return Math.Abs(dValue - dMatchValue) < matcher.floatingPointComparisonTolerance;
case ComparisonMethod.NotEquals:
return Math.Abs(dValue - dMatchValue) > matcher.floatingPointComparisonTolerance;
case ComparisonMethod.Greater:
return dValue > dMatchValue;
case ComparisonMethod.Less:
return dValue < dMatchValue;
case ComparisonMethod.GreaterThanOrEqualTo:
return dValue >= dMatchValue;
case ComparisonMethod.LessThanOrEqualTo:
return dValue <= dMatchValue;
}
return false;
}
private bool CompareFloat(string value, ValuePathMatcher matcher)
{
// This one is freeform based on the input so we will retrun false if it is not parsable
if (!float.TryParse(value, out float dValue)) return false;
// We will throw an exception if match value is not a numeric value. This is a developer
// error.
float dMatchValue = float.Parse(matcher.matchValue);
switch (matcher.comparisonMethod)
{
case ComparisonMethod.Equals:
return Math.Abs(dValue - dMatchValue) <
matcher.floatingPointComparisonTolerance;
case ComparisonMethod.NotEquals:
return Math.Abs(dValue - dMatchValue) >
matcher.floatingPointComparisonTolerance;
case ComparisonMethod.Greater:
return dValue > dMatchValue;
case ComparisonMethod.Less:
return dValue < dMatchValue;
case ComparisonMethod.GreaterThanOrEqualTo:
return dValue >= dMatchValue;
case ComparisonMethod.LessThanOrEqualTo:
return dValue <= dMatchValue;
}
return false;
}
private bool CompareInt(string value, ValuePathMatcher matcher)
{
// This one is freeform based on the input so we will retrun false if it is not parsable
if (!int.TryParse(value, out int dValue)) return false;
// We will throw an exception if match value is not a numeric value. This is a developer
// error.
int dMatchValue = int.Parse(matcher.matchValue);
switch (matcher.comparisonMethod)
{
case ComparisonMethod.Equals:
return dValue == dMatchValue;
case ComparisonMethod.NotEquals:
return dValue != dMatchValue;
case ComparisonMethod.Greater:
return dValue > dMatchValue;
case ComparisonMethod.Less:
return dValue < dMatchValue;
case ComparisonMethod.GreaterThanOrEqualTo:
return dValue >= dMatchValue;
case ComparisonMethod.LessThanOrEqualTo:
return dValue <= dMatchValue;
}
return false;
}
}
[Serializable]
public class MultiValueEvent : UnityEvent<string[]>
{
}
[Serializable]
public class ValueEvent : UnityEvent<string>
{ }
[Serializable]
public class FormattedValueEvents
{
[Tooltip("Modify the string output, values can be inserted with {value} or {0}, {1}, {2}")]
public string format;
public ValueEvent onFormattedValueEvent = new ValueEvent();
}
[Serializable]
public class ValuePathMatcher
{
[Tooltip("The path to a value within a WitResponseNode")]
public string path;
[Tooltip("A reference to a wit value object")]
public WitValue witValueReference;
[Tooltip("Does this path need to have text in the value to be considered a match")]
public bool contentRequired = true;
[Tooltip("If set the match value will be treated as a regular expression.")]
public MatchMethod matchMethod;
[Tooltip("The operator used to compare the value with the match value. Ex: response.value > matchValue")]
public ComparisonMethod comparisonMethod;
[Tooltip("Value used to compare with the result when Match Required is set")]
public string matchValue;
[Tooltip("The variance allowed when comparing two floating point values for equality")]
public double floatingPointComparisonTolerance = .0001f;
[Tooltip("Confidence ranges are executed in order. If checked, all confidence values will be checked instead of stopping on the first one that matches.")]
[SerializeField] public bool allowConfidenceOverlap;
[Tooltip("The confidence levels to handle for this value.\nNOTE: The selected node must have a confidence sibling node.")]
public ConfidenceRange[] confidenceRanges;
private WitResponseReference pathReference;
private WitResponseReference confidencePathReference;
public WitResponseReference ConfidenceReference
{
get
{
if (null != confidencePathReference) return confidencePathReference;
var confidencePath = Reference?.path;
if (!string.IsNullOrEmpty(confidencePath))
{
confidencePath = confidencePath.Substring(0, confidencePath.LastIndexOf("."));
confidencePath += ".confidence";
confidencePathReference = WitResultUtilities.GetWitResponseReference(confidencePath);
}
return confidencePathReference;
}
}
public WitResponseReference Reference
{
get
{
if (witValueReference) return witValueReference.Reference;
if (null == pathReference || pathReference.path != path)
{
pathReference = WitResultUtilities.GetWitResponseReference(path);
}
return pathReference;
}
}
}
public enum ComparisonMethod
{
Equals,
NotEquals,
Greater,
GreaterThanOrEqualTo,
Less,
LessThanOrEqualTo
}
public enum MatchMethod
{
None,
Text,
RegularExpression,
IntegerComparison,
FloatComparison,
DoubleComparison
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 591c3d6f017c11b4faa41506d75635b9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,76 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System.Text.RegularExpressions;
using Meta.WitAi.Json;
using Meta.WitAi.Utilities;
using UnityEngine;
namespace Meta.WitAi.CallbackHandlers
{
[AddComponentMenu("Wit.ai/Response Matchers/Utterance Matcher")]
public class WitUtteranceMatcher : WitResponseHandler
{
[SerializeField] private string searchText;
[SerializeField] private bool exactMatch = true;
[SerializeField] private bool useRegex;
[SerializeField] private StringEvent onUtteranceMatched = new StringEvent();
private Regex regex;
protected override string OnValidateResponse(WitResponseNode response, bool isEarlyResponse)
{
var text = response["text"].Value;
if (!IsMatch(text))
{
return "Required utterance does not match";
}
return "";
}
protected override void OnResponseInvalid(WitResponseNode response, string error){}
protected override void OnResponseSuccess(WitResponseNode response)
{
var text = response["text"].Value;
onUtteranceMatched?.Invoke(text);
}
private bool IsMatch(string text)
{
if (useRegex)
{
if (null == regex)
{
regex = new Regex(searchText, RegexOptions.Compiled | RegexOptions.IgnoreCase);
}
var match = regex.Match(text);
if (match.Success)
{
if (exactMatch && match.Value == text)
{
return true;
}
else
{
return true;
}
}
}
else if (exactMatch && text.ToLower() == searchText.ToLower())
{
return true;
}
else if (text.ToLower().Contains(searchText.ToLower()))
{
return true;
}
return false;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0a93535f06eabe47bf93b4b504873c8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: