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,72 @@
/*
* 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.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace Meta.WitAi.Utilities
{
public static class AssetDatabaseUtility
{
// Find Unity asset
public static T FindUnityAsset<T>(string filter) where T : UnityEngine.Object
{
T[] results = FindUnityAssets<T>(filter, true);
if (results != null && results.Length > 0)
{
return results[0];
}
return null;
}
// Get all unity objects matching the name
public static T[] FindUnityAssets<T>(string filter, bool ignoreAdditional = false)
where T : UnityEngine.Object
{
List<T> results = new List<T>();
string[] guids = AssetDatabase.FindAssets(filter);
if (guids != null && guids.Length > 0)
{
foreach (var guid in guids)
{
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
T asset = AssetDatabase.LoadAssetAtPath<T>(assetPath);
if (asset != null && !results.Contains(asset))
{
results.Add(asset);
if (ignoreAdditional)
{
break;
}
}
}
}
return results.ToArray();
}
// Gets the absolute string path of the asset(s) with the matching name
public static string[] FindUnityAssetPath(string filter, bool ignoreAdditional = false)
{
var relativePaths = AssetDatabase.FindAssets(filter);
char d = Path.DirectorySeparatorChar;
List<string> results = new List<string>();
foreach (var relPath in relativePaths)
{
results.Add($"{Application.dataPath}{d}..{d}{AssetDatabase.GUIDToAssetPath(relPath)}");
if (ignoreAdditional)
{
break;
}
}
return results.ToArray();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d247ae64c6547e4282a35e0b6b17b6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,310 @@
/*
* 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.IO;
using Meta.Conduit.Editor;
using Meta.Voice.TelemetryUtilities;
using Meta.WitAi.Data.Configuration;
using Meta.WitAi.Utilities;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
namespace Meta.WitAi.Windows
{
/// <summary>
/// Manages the Conduit manifest generation.
/// </summary>
public class ConduitManifestGenerationManager: IPreprocessBuildWithReport
{
/// <summary>
/// The priority for preprocess build callback.
/// </summary>
public int callbackOrder => 0;
/// <summary>
/// The assembly miner.
/// </summary>
private static readonly AssemblyMiner AssemblyMiner = new AssemblyMiner(new WitParameterValidator());
/// <summary>
/// Maps individual configurations to their associated managers. This is needed to allow static resolution
/// on global events like building or running.
/// </summary>
private static readonly Dictionary<string, ConduitManifestGenerationManager> ConfigurationToManagerMap =
new Dictionary<string, ConduitManifestGenerationManager>();
/// <summary>
/// Locally collected Conduit statistics.
/// </summary>
private static ConduitStatistics _statistics;
/// <summary>
/// Set to true if code had changed since last manifest generation.
/// We start with this set to true to handle changes when the editor is not running.
/// </summary>
private static bool _codeChanged = true;
/// <summary>
/// The manifest generator used for this configuration.
/// </summary>
private readonly ManifestGenerator _manifestGenerator;
/// <summary>
/// True when a manifest exists locally for this configuration.
/// </summary>
public bool ManifestAvailable { get; private set; }= false;
/// <summary>
/// The assembly walker associated with this configuration.
/// </summary>
internal AssemblyWalker AssemblyWalker { get; private set; } = new AssemblyWalker();
/// <summary>
/// Locally collected Conduit statistics.
/// </summary>
private static ConduitStatistics Statistics
{
get
{
if (_statistics == null)
{
_statistics = new ConduitStatistics(new PersistenceLayer());
}
return _statistics;
}
}
/// <summary>
/// Factory method that creates a manager for the configuration if none exists. Otherwise, creates a new one.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <returns>An instance of this class.</returns>
public static ConduitManifestGenerationManager GetInstance(WitConfiguration configuration)
{
// This key has to match what we set in the constructor.
var configurationKey = configuration.name;
if (!ConfigurationToManagerMap.ContainsKey(configurationKey))
{
ConfigurationToManagerMap[configurationKey] = new ConduitManifestGenerationManager(configuration);
}
ConfigurationToManagerMap[configurationKey].GenerateManifestIfNeeded(configuration);
return ConfigurationToManagerMap[configurationKey];
}
static ConduitManifestGenerationManager()
{
EditorApplication.playModeStateChanged += OnPlayModeChanged;
}
/// <summary>
/// This default constructor is intended to be used by Unity only. Do not call it directly.
/// </summary>
public ConduitManifestGenerationManager()
{
}
private ConduitManifestGenerationManager(WitConfiguration configuration)
{
_manifestGenerator = new ManifestGenerator(AssemblyWalker, AssemblyMiner);
ConfigurationToManagerMap[configuration.name] = this;
}
public void OnPreprocessBuild(BuildReport report)
{
if (_codeChanged)
{
GenerateAllManifests();
}
else
{
GenerateMissingManifests();
}
}
[UnityEditor.Callbacks.DidReloadScripts]
private static void OnScriptsReloaded()
{
_codeChanged = true;
}
private static void OnPlayModeChanged(PlayModeStateChange state)
{
if (state != PlayModeStateChange.EnteredPlayMode)
{
return;
}
if (_codeChanged)
{
GenerateAllManifests();
}
else
{
GenerateMissingManifests();
}
}
public static void PersistStatistics()
{
Statistics.Persist();
}
private static void GenerateMissingManifests()
{
foreach (var configuration in WitConfigurationUtility.GetLoadedConfigurations())
{
if (configuration == null || !configuration.useConduit)
{
continue;
}
var manager = GetInstance(configuration);
manager.GenerateManifest(configuration, false);
}
}
private static void GenerateAllManifests()
{
foreach (var configuration in WitConfigurationUtility.GetLoadedConfigurations())
{
if (configuration != null && configuration.useConduit)
{
var manager = GetInstance(configuration);
manager.GenerateManifest(configuration, false);
}
}
_codeChanged = false;
}
public List<string> ExtractManifestData()
{
return _manifestGenerator.ExtractManifestData();
}
/// <summary>
/// Generate a manifest with empty entities and actions lists.
/// </summary>
/// <param name="domain">A friendly name to use for this app.</param>
/// <param name="id">The App ID.</param>
/// <returns>A JSON representation of the empty manifest.</returns>
public string GenerateEmptyManifest(string domain, string id)
{
return _manifestGenerator.GenerateEmptyManifest(domain, id);
}
private void GenerateManifestIfNeeded(WitConfiguration configuration)
{
if (!configuration.useConduit || configuration == null)
{
return;
}
// Get full manifest path & ensure it exists
var manifestPath = configuration.GetManifestEditorPath();
ManifestAvailable = File.Exists(manifestPath);
// Auto-generate manifest
if (!ManifestAvailable)
{
GenerateManifest(configuration, false);
}
}
private static string GetManifestFullPath(WitConfiguration configuration, bool shouldCreateDirectoryIfNotExist = false)
{
string directory = Application.dataPath + "/Oculus/Voice/Resources";
if (shouldCreateDirectoryIfNotExist)
{
IOUtility.CreateDirectory(directory, true);
}
return directory + "/" + configuration.ManifestLocalPath;
}
/// <summary>
/// Generates a manifest and optionally opens it in the editor.
/// </summary>
/// <param name="configuration">The configuration that we are generating the manifest for.</param>
/// <param name="openManifest">If true, will open the manifest file in the code editor.</param>
public void GenerateManifest(WitConfiguration configuration, bool openManifest)
{
var instanceKey = Telemetry.StartEvent(Telemetry.TelemetryEventId.GenerateManifest);
AssemblyWalker.AssembliesToIgnore = new HashSet<string>(configuration.excludedAssemblies);
// Generate
var startGenerationTime = DateTime.UtcNow;
var appInfo = configuration.GetApplicationInfo();
var manifest = _manifestGenerator.GenerateManifest(appInfo.name, appInfo.id);
var endGenerationTime = DateTime.UtcNow;
// Get file path
var fullPath = configuration.GetManifestEditorPath();
if (string.IsNullOrEmpty(fullPath) || !File.Exists(fullPath))
{
fullPath = GetManifestFullPath(configuration, true);
}
// Write to file
try
{
var writer = new StreamWriter(fullPath);
writer.NewLine = "\n";
writer.WriteLine(manifest);
writer.Close();
}
catch (Exception e)
{
VLog.E($"Conduit manifest failed to generate\nPath: {fullPath}\n{e}");
Telemetry.EndEventWithFailure(instanceKey, e.Message);
return;
}
try
{
var incompatibleSignatures = string.Join(" ", AssemblyMiner.IncompatibleSignatureFrequency.Keys);
Telemetry.AnnotateEvent(instanceKey, Telemetry.AnnotationKey.IncompatibleSignatures, incompatibleSignatures);
var compatibleSignatures = string.Join(" ", AssemblyMiner.SignatureFrequency.Keys);
Telemetry.AnnotateEvent(instanceKey, Telemetry.AnnotationKey.CompatibleSignatures, compatibleSignatures);
}
catch (Exception e)
{
VLog.W($"Failed to collect signature telemetry. Exception: {e}");
}
Telemetry.EndEvent(instanceKey, Telemetry.ResultType.Success);
Statistics.SuccessfulGenerations++;
Statistics.AddFrequencies(AssemblyMiner.SignatureFrequency);
Statistics.AddIncompatibleFrequencies(AssemblyMiner.IncompatibleSignatureFrequency);
var generationTime = endGenerationTime - startGenerationTime;
var unityPath = fullPath.Replace(Application.dataPath, "Assets");
AssetDatabase.ImportAsset(unityPath);
ManifestAvailable = true;
var configName = configuration.name;
var manifestName = Path.GetFileNameWithoutExtension(unityPath);
#if UNITY_2021_2_OR_NEWER
var configPath = AssetDatabase.GetAssetPath(configuration);
configName = $"<a href=\"{configPath}\">{configName}</a>";
manifestName = $"<a href=\"{unityPath}\">{manifestName}</a>";
#endif
VLog.D($"Conduit manifest generated\nConfiguration: {configName}\nManifest: {manifestName}\nGeneration Time: {generationTime.TotalMilliseconds} ms");
if (openManifest)
{
UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(fullPath, 1);
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 440fa12149634e98aee13b1ef38d530a
timeCreated: 1685991879
@@ -0,0 +1,714 @@
/*
* 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.IO;
using Meta.WitAi.CallbackHandlers;
using Meta.WitAi.Configuration;
using Meta.WitAi.Data;
using Meta.WitAi.Json;
using Meta.WitAi.Requests;
using UnityEditor;
using UnityEngine;
using UnityEngine.Serialization;
namespace Meta.WitAi.Windows
{
public class WitUnderstandingViewer : WitConfigurationWindow
{
[FormerlySerializedAs("witHeader")] [SerializeField] private Texture2D _witHeader;
[FormerlySerializedAs("responseText")] [SerializeField] private string _responseText;
private string _utterance;
private WitResponseNode _response;
private Dictionary<string, bool> _foldouts;
// Current service
private VoiceService[] _services;
private string[] _serviceNames;
private int _currentService = -1;
public VoiceService service => _services != null && _currentService >= 0 && _currentService < _services.Length ? _services[_currentService] : null;
public bool HasWit => service != null;
private DateTime _submitStart;
private TimeSpan _requestLength;
private string _status;
private int _responseCode;
private VoiceServiceRequest _request;
private int _savePopup;
private GUIStyle _hamburgerButton;
private enum HamburgerMenu
{
None = -1,
Save = 0,
CopyToClipboard = 1,
CopyRequestID = 2
}
private string[] HambergerMenuStrings = new string[]
{
"Save", "Copy to Clipboard", "Copy Request ID"
};
class Content
{
public static GUIContent CopyPath;
public static GUIContent CopyCode;
public static GUIContent CreateStringValue;
public static GUIContent CreateIntValue;
public static GUIContent CreateFloatValue;
static Content()
{
CreateStringValue = new GUIContent("Create Value Reference/Create String");
CreateIntValue = new GUIContent("Create Value Reference/Create Int");
CreateFloatValue = new GUIContent("Create Value Reference/Create Float");
CopyPath = new GUIContent("Copy Path to Clipboard");
CopyCode = new GUIContent("Copy Code to Clipboard");
}
}
protected override GUIContent Title => WitTexts.UnderstandingTitleContent;
protected override WitTexts.WitAppEndpointType HeaderEndpointType => WitTexts.WitAppEndpointType.Understanding;
protected override void OnEnable()
{
base.OnEnable();
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
RefreshVoiceServices();
if (!string.IsNullOrEmpty(_responseText))
{
_response = WitResponseNode.Parse(_responseText);
}
_status = WitTexts.Texts.UnderstandingViewerPromptLabel;
}
protected override void OnDisable()
{
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
}
private void OnPlayModeStateChanged(PlayModeStateChange state)
{
if (state == PlayModeStateChange.EnteredPlayMode && !HasWit)
{
RefreshVoiceServices();
}
}
private void OnSelectionChange()
{
if (Selection.activeGameObject)
{
SetVoiceService(Selection.activeGameObject.GetComponent<VoiceService>());
}
}
private void ResetStartTime()
{
_submitStart = System.DateTime.Now;
Repaint();
}
private void OnSend(VoiceServiceRequest request)
{
_request = request;
ResetStartTime();
Repaint();
}
private void ShowTranscription(string transcription)
{
_utterance = transcription;
Repaint();
}
// On gui
protected override void OnGUI()
{
base.OnGUI();
EditorGUILayout.BeginHorizontal();
WitEditorUI.LayoutStatusLabel(_status);
GUILayout.BeginVertical(GUILayout.Width(24));
GUILayout.Space(4);
GUILayout.BeginHorizontal();
GUILayout.Space(4);
var rect = GUILayoutUtility.GetLastRect();
if (null == _hamburgerButton)
{
// GUI.skin must be called from OnGUI
_hamburgerButton = new GUIStyle(GUI.skin.GetStyle("PaneOptions"));
_hamburgerButton.imagePosition = ImagePosition.ImageOnly;
}
var value = (HamburgerMenu) EditorGUILayout.Popup(-1, HambergerMenuStrings, _hamburgerButton, GUILayout.Width(24));
switch (value)
{
case HamburgerMenu.Save:
{
var path = EditorUtility.SaveFilePanel("Save Response Json", Application.dataPath,
"result", "json");
if (!string.IsNullOrEmpty(path))
{
File.WriteAllText(path, _response.ToString());
}
break;
}
case HamburgerMenu.CopyToClipboard:
{
EditorGUIUtility.systemCopyBuffer = _response?.ToString() ?? _responseText;
break;
}
case HamburgerMenu.CopyRequestID:
{
var requestId = _request?.Options?.RequestId;
if (!string.IsNullOrEmpty(requestId))
{
EditorGUIUtility.systemCopyBuffer = requestId;
_status = $"{requestId} copied to clipboard.";
}
else
{
_status = "No request id to copy!";
}
Repaint();
break;
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
}
protected override void LayoutContent()
{
// Get service
VoiceService voiceService = null;
// Runtime Mode
if (Application.isPlaying)
{
// Refresh services
if (_services == null)
{
RefreshVoiceServices();
}
// Services missing
if (_services == null || _serviceNames == null || _services.Length == 0)
{
WitEditorUI.LayoutErrorLabel(WitTexts.Texts.UnderstandingViewerMissingServicesLabel);
return;
}
// Voice service select
int newService = _currentService;
bool serviceUpdate = false;
GUILayout.BeginHorizontal();
// Clamp
if (newService < 0 || newService >= _services.Length)
{
newService = 0;
serviceUpdate = true;
}
// Layout
WitEditorUI.LayoutPopup(WitTexts.Texts.UnderstandingViewerServicesLabel, _serviceNames, ref newService, ref serviceUpdate);
// Update
if (serviceUpdate)
{
SetVoiceService(newService);
}
// Select
if (_currentService >= 0 && _currentService < _services.Length && WitEditorUI.LayoutTextButton(WitTexts.Texts.UnderstandingViewerSelectLabel))
{
Selection.activeObject = _services[_currentService];
}
// Refresh
if (WitEditorUI.LayoutTextButton(WitTexts.Texts.ConfigurationRefreshButtonLabel))
{
RefreshVoiceServices();
}
GUILayout.EndHorizontal();
// Ensure service exists
voiceService = service;
}
// Editor Only
else
{
// Configuration select
base.LayoutContent();
// Ensure configuration exists
if (!witConfiguration)
{
WitEditorUI.LayoutErrorLabel(WitTexts.Texts.UnderstandingViewerMissingConfigLabel);
return;
}
// Check client access token
string clientAccessToken = witConfiguration.GetClientAccessToken();
if (string.IsNullOrEmpty(clientAccessToken))
{
WitEditorUI.LayoutErrorLabel(WitTexts.Texts.UnderstandingViewerMissingClientTokenLabel);
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (WitEditorUI.LayoutTextButton(WitTexts.Texts.UnderstandingViewerSettingsButtonLabel))
{
Selection.activeObject = witConfiguration;
}
GUILayout.EndHorizontal();
return;
}
}
// Determine if input is allowed
bool allowInput = !Application.isPlaying || (service != null && !service.Active);
GUI.enabled = allowInput;
// Utterance field
bool updated = false;
WitEditorUI.LayoutTextField(new GUIContent(WitTexts.Texts.UnderstandingViewerUtteranceLabel), ref _utterance, ref updated);
// Begin Buttons
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
// Submit utterance
if (allowInput && WitEditorUI.LayoutTextButton(WitTexts.Texts.UnderstandingViewerSubmitButtonLabel))
{
_responseText = "";
if (!string.IsNullOrEmpty(_utterance))
{
SubmitUtterance();
}
else
{
_response = null;
}
}
// Service buttons
GUI.enabled = true;
if (EditorApplication.isPlaying && voiceService)
{
if (!voiceService.Active)
{
// Activate
if (WitEditorUI.LayoutTextButton(WitTexts.Texts.UnderstandingViewerActivateButtonLabel))
{
_request = voiceService.Activate(new VoiceServiceRequestEvents());
}
}
else
{
// Deactivate
if (WitEditorUI.LayoutTextButton(WitTexts.Texts.UnderstandingViewerDeactivateButtonLabel))
{
voiceService.Deactivate();
}
// Abort
if (WitEditorUI.LayoutTextButton(WitTexts.Texts.UnderstandingViewerAbortButtonLabel))
{
voiceService.DeactivateAndAbortRequest();
}
}
}
GUILayout.EndHorizontal();
// Results
GUILayout.BeginVertical(EditorStyles.helpBox);
if (_response != null)
{
DrawResponse();
}
else if (voiceService && voiceService.MicActive)
{
WitEditorUI.LayoutWrapLabel(WitTexts.Texts.UnderstandingViewerListeningLabel);
}
else if (voiceService && voiceService.IsRequestActive)
{
WitEditorUI.LayoutWrapLabel(WitTexts.Texts.UnderstandingViewerLoadingLabel);
}
else if (string.IsNullOrEmpty(_responseText))
{
WitEditorUI.LayoutWrapLabel(WitTexts.Texts.UnderstandingViewerPromptLabel);
}
else
{
WitEditorUI.LayoutWrapLabel(_responseText);
}
GUILayout.FlexibleSpace();
GUILayout.EndVertical();
}
private void SubmitUtterance()
{
// Remove response
_response = null;
if (Application.isPlaying)
{
if (service)
{
_status = WitTexts.Texts.UnderstandingViewerListeningLabel;
_responseText = _status;
_request = service.Activate(_utterance, new VoiceServiceRequestEvents());
// Hack to watch for loading to complete. Response does not
// come back on the main thread so Repaint in onResponse in
// the editor does nothing.
EditorApplication.update += WatchForWitResponse;
}
}
else
{
_status = WitTexts.Texts.UnderstandingViewerLoadingLabel;
_responseText = _status;
_submitStart = System.DateTime.Now;
_request = witConfiguration.CreateMessageRequest(new WitRequestOptions(), new VoiceServiceRequestEvents());
_request.Options.Text = _utterance;
_request.Events.OnSend.AddListener(OnSend);
_request.Events.OnComplete.AddListener(OnComplete);
_request.Send();
}
}
private void WatchForWitResponse()
{
if (service && !service.Active)
{
Repaint();
EditorApplication.update -= WatchForWitResponse;
}
}
private void OnComplete(VoiceServiceRequest request)
{
_responseCode = request.StatusCode;
if (null != request.ResponseData)
{
ShowResponse(request.ResponseData, false);
}
else if (!string.IsNullOrEmpty(request.Results.Message))
{
_responseText = request.Results.Message;
}
else
{
_responseText = "No response. Status: " + request.StatusCode;
}
Repaint();
}
private void ShowResponse(WitResponseNode r, bool isPartial)
{
_response = r;
_responseText = _response.ToString();
_requestLength = DateTime.Now - _submitStart;
_status = $"{(isPartial ? "Partial" : "Full")}Response time: {_requestLength}";
}
private void DrawResponse()
{
DrawResponseNode(_response);
}
private void DrawResponseNode(WitResponseNode witResponseNode, string path = "")
{
if (null == witResponseNode?.AsObject) return;
if(string.IsNullOrEmpty(path)) DrawNode(witResponseNode["text"], "text", path);
var names = witResponseNode.AsObject.ChildNodeNames;
Array.Sort(names);
foreach (string child in names)
{
if (!(string.IsNullOrEmpty(path) && child == "text"))
{
var childNode = witResponseNode[child];
DrawNode(childNode, child, path);
}
}
}
private void DrawNode(WitResponseNode childNode, string child, string path, bool isArrayElement = false)
{
if (childNode == null)
{
return;
}
string childPath;
if (path.Length > 0)
{
childPath = isArrayElement ? $"{path}[{child}]" : $"{path}.{child}";
}
else
{
childPath = child;
}
if (!string.IsNullOrEmpty(childNode.Value))
{
GUILayout.BeginHorizontal();
GUILayout.Space(15 * EditorGUI.indentLevel);
if (GUILayout.Button($"{child} = {childNode.Value}", WitStyles.LabelWrap))
{
ShowNodeMenu(childNode, childPath);
}
GUILayout.EndHorizontal();
}
else
{
var childObject = childNode.AsObject;
var childArray = childNode.AsArray;
if ((null != childObject || null != childArray) && Foldout(childPath, child))
{
EditorGUI.indentLevel++;
if (null != childObject)
{
DrawResponseNode(childNode, childPath);
}
if (null != childArray)
{
DrawArray(childArray, childPath);
}
EditorGUI.indentLevel--;
}
}
}
private void ShowNodeMenu(WitResponseNode node, string path)
{
GenericMenu menu = new GenericMenu();
menu.AddItem(Content.CreateStringValue, false, () => WitDataCreation.CreateStringValue(path));
menu.AddItem(Content.CreateIntValue, false, () => WitDataCreation.CreateIntValue(path));
menu.AddItem(Content.CreateFloatValue, false, () => WitDataCreation.CreateFloatValue(path));
menu.AddSeparator("");
menu.AddItem(Content.CopyPath, false, () =>
{
EditorGUIUtility.systemCopyBuffer = path;
});
menu.AddItem(Content.CopyCode, false, () =>
{
EditorGUIUtility.systemCopyBuffer = WitResultUtilities.GetCodeFromPath(path);
});
if (Selection.activeGameObject)
{
menu.AddSeparator("");
var label =
new GUIContent($"Add response matcher to {Selection.activeObject.name}");
menu.AddItem(label, false, () =>
{
var valueHandler = Selection.activeGameObject.AddComponent<WitResponseMatcher>();
valueHandler.intent = _response.GetIntentName();
valueHandler.valueMatchers = new ValuePathMatcher[]
{
new ValuePathMatcher() { path = path }
};
});
AddMultiValueUpdateItems(path, menu);
}
menu.ShowAsContext();
}
private void AddMultiValueUpdateItems(string path, GenericMenu menu)
{
string name = path;
int index = path.LastIndexOf('.');
if (index > 0)
{
name = name.Substring(index + 1);
}
var mvhs = Selection.activeGameObject.GetComponents<WitResponseMatcher>();
if (mvhs.Length > 1)
{
for (int i = 0; i < mvhs.Length; i++)
{
var handler = mvhs[i];
menu.AddItem(
new GUIContent($"Add {name} matcher to {Selection.activeGameObject.name}/Handler {(i + 1)}"),
false, (h) => AddNewEventHandlerPath((WitResponseMatcher) h, path), handler);
}
}
else if (mvhs.Length == 1)
{
var handler = mvhs[0];
menu.AddItem(
new GUIContent($"Add {name} matcher to {Selection.activeGameObject.name}'s Response Matcher"),
false, (h) => AddNewEventHandlerPath((WitResponseMatcher) h, path), handler);
}
}
private void AddNewEventHandlerPath(WitResponseMatcher handler, string path)
{
Array.Resize(ref handler.valueMatchers, handler.valueMatchers.Length + 1);
handler.valueMatchers[handler.valueMatchers.Length - 1] = new ValuePathMatcher()
{
path = path
};
}
private void DrawArray(WitResponseArray childArray, string childPath)
{
for (int i = 0; i < childArray.Count; i++)
{
DrawNode(childArray[i], i.ToString(), childPath, true);
}
}
private bool Foldout(string path, string label)
{
if (null == _foldouts) _foldouts = new Dictionary<string, bool>();
if (!_foldouts.TryGetValue(path, out var state))
{
state = false;
_foldouts[path] = state;
}
var newState = EditorGUILayout.Foldout(state, label);
if (newState != state)
{
_foldouts[path] = newState;
}
return newState;
}
#region SERVICES
// Refresh voice services
protected void RefreshVoiceServices()
{
// Remove previous service
VoiceService previous = service;
SetVoiceService(-1);
// Get all services
VoiceService[] services = Resources.FindObjectsOfTypeAll<VoiceService>();
// Get unique services
List<GameObject> serviceGOs = new List<GameObject>();
List<VoiceService> serviceList = new List<VoiceService>();
foreach (var s in services)
{
// Add unique gameobjects
GameObject serviceGO = s.gameObject;
if (serviceGO.scene.rootCount > 0 && !serviceGOs.Contains(serviceGO))
{
serviceGOs.Add(serviceGO);
serviceList.Add(serviceGO.GetComponent<VoiceService>());
}
}
// Get service gameobject names
_services = serviceList.ToArray();
_serviceNames = new string[_services.Length];
for (int i = 0; i < _services.Length; i++)
{
_serviceNames[i] = GetVoiceServiceName(_services[i]);
}
// Set as first found
if (previous == null)
{
SetVoiceService(0);
}
// Set as previous
else
{
SetVoiceService(previous);
}
}
// Get voice service name
private string GetVoiceServiceName(VoiceService service)
{
IWitRuntimeConfigProvider configProvider = service.GetComponent<IWitRuntimeConfigProvider>();
if (configProvider != null && configProvider.RuntimeConfiguration != null && configProvider.RuntimeConfiguration.witConfiguration != null)
{
return $"{configProvider.RuntimeConfiguration.witConfiguration.name} [{service.gameObject.name}]";
}
return service.gameObject.name;
}
// Set voice service
protected void SetVoiceService(VoiceService newService)
{
// Cannot set without services
if (_services == null)
{
return;
}
// Find & apply
int newServiceIndex = Array.FindIndex(_services, (s) => s == newService);
// Apply
SetVoiceService(newServiceIndex);
}
// Set
protected void SetVoiceService(int newServiceIndex)
{
// Cannot set without services
if (_services == null)
{
return;
}
// Remove listeners to current service
RemoveVoiceListeners(service);
// Get current index
_currentService = newServiceIndex;
// Add listeners to current service
AddVoiceListeners(service);
}
// Add listeners
private void AddVoiceListeners(VoiceService voiceService)
{
// Ignore
if (voiceService == null)
{
return;
}
// Add delegates
voiceService.VoiceEvents.OnSend.AddListener(OnSend);
voiceService.VoiceEvents.OnComplete.AddListener(OnComplete);
voiceService.VoiceEvents.OnPartialTranscription.AddListener(ShowTranscription);
voiceService.VoiceEvents.OnFullTranscription.AddListener(ShowTranscription);
voiceService.VoiceEvents.OnStoppedListening.AddListener(ResetStartTime);
}
// Remove listeners
private void RemoveVoiceListeners(VoiceService voiceService)
{
// Ignore
if (voiceService == null)
{
return;
}
// Remove delegates
voiceService.VoiceEvents.OnSend.RemoveListener(OnSend);
voiceService.VoiceEvents.OnComplete.RemoveListener(OnComplete);
voiceService.VoiceEvents.OnFullTranscription.RemoveListener(ShowTranscription);
voiceService.VoiceEvents.OnPartialTranscription.RemoveListener(ShowTranscription);
voiceService.VoiceEvents.OnStoppedListening.RemoveListener(ResetStartTime);
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 18eb340e3f39b0a4f8f9e9749e97f9b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,137 @@
/*
* 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.Events;
using UnityEngine;
using UnityEngine.Events;
namespace Meta.WitAi.Windows
{
/// <summary>
/// This class provides a common API for accessing properties and methods of the various *Service classes
/// used in the Understanding Viewer. In order to add support for a new Service type, derive a child
/// class from this one and provide implementations for the getter methods.
/// </summary>
public abstract class WitUnderstandingViewerServiceAPI
{
/// <summary>
/// The base Component/Service class this object wraps.
/// Child classes will provide a properly-cast reference to the component in their implementation.
/// </summary>
private MonoBehaviour _serviceComponent;
/// <summary>
/// The name of the service. Gotten once and cached for future use.
/// Child classes may override the base implementation if necessary.
/// </summary>
private String _serviceName;
/// <summary>
/// Flags to indicate whether or not the component supports voice and/or text activation.
/// </summary>
protected bool _hasVoiceActivation;
protected bool _hasTextActivation;
/// <summary>
/// This flag dictates whether or not the service should send a network request as part of its
/// (de)activation handling.
/// </summary>
protected bool _shouldSubmitUtterance;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="serviceComponent">The Service Component (VoiceService, DictationService, etc.
/// this API will wrap.</param>
public WitUnderstandingViewerServiceAPI(MonoBehaviour serviceComponent)
{
_serviceComponent = serviceComponent;
}
public MonoBehaviour ServiceComponent
{
get => _serviceComponent;
}
public bool HasVoiceActivation
{
get => _hasVoiceActivation;
}
public bool HasTextActivation
{
get => _hasTextActivation;
}
public bool ShouldSubmitUtterance
{
get => _shouldSubmitUtterance;
}
/// <summary>
/// Most services have a common way of querying their service name. For those that don't,
/// override this method. The name is cached after first query.
/// </summary>
public virtual string ServiceName {
get
{
// Has the service name been cached to the local variable yet?
if (_serviceName == null)
{
if (_serviceComponent == null)
return "";
var configProvider = _serviceComponent.GetComponent<IWitRuntimeConfigProvider>();
if (configProvider != null)
{
// If no Witconfig is set for the component we get a NullPointerException here
if (configProvider.RuntimeConfiguration.witConfiguration == null)
{
VLog.E($"No Wit configuration found for {_serviceComponent.gameObject.name}");
return "";
}
_serviceName =
$"{configProvider.RuntimeConfiguration.witConfiguration.name} [{_serviceComponent.gameObject.name}]";
}
else
{
_serviceName = _serviceComponent.name;
}
}
return _serviceName;
}
}
public abstract bool Active { get; }
public abstract bool MicActive { get; }
public abstract bool IsRequestActive { get; }
// API methods - override these to provide functionality
public abstract void Activate();
public abstract void Activate(string text);
public abstract void Deactivate();
public abstract void DeactivateAndAbortRequest();
// Event Callback Registration
public abstract VoiceServiceRequestEvent OnSend { get; }
public abstract WitTranscriptionEvent OnPartialTranscription { get; }
public abstract WitTranscriptionEvent OnFullTranscription { get; }
public abstract UnityEvent OnStoppedListening { get; }
public abstract VoiceServiceRequestEvent OnComplete { get; }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b05455360ebd3644ebe86cbb40446615
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,40 @@
/*
* 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;
using System.Collections.Generic;
using UnityEngine;
namespace Meta.WitAi.Windows
{
public static class WitUnderstandingViewerServiceApiFactory
{
public delegate WitUnderstandingViewerServiceAPI Create(MonoBehaviour m);
private static Dictionary<string, Create> factoryMethods = new Dictionary<string, Create>();
public static void Register(string interfaceName, Create method)
{
factoryMethods.Add(interfaceName, method);
}
public static WitUnderstandingViewerServiceAPI CreateWrapper(MonoBehaviour service)
{
foreach (var interfaceType in service.GetType().GetInterfaces())
{
if (factoryMethods.ContainsKey(interfaceType.Name))
{
return factoryMethods[interfaceType.Name](service);
}
}
return null;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fcd6b77ae500a7b43923e35d15cc5144
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,100 @@
/*
* 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.Events;
using UnityEditor;
using UnityEngine;
using UnityEngine.Events;
namespace Meta.WitAi.Windows
{
[InitializeOnLoad]
public class WitUnderstandingViewerVoiceServiceAPI : WitUnderstandingViewerServiceAPI
{
private VoiceService _service;
static WitUnderstandingViewerVoiceServiceAPI()
{
WitUnderstandingViewerServiceApiFactory.Register("IVoiceService", CreateApiWrapper);
}
public WitUnderstandingViewerVoiceServiceAPI(VoiceService service) : base(service)
{
_service = service;
_hasVoiceActivation = true;
_hasTextActivation = true;
_shouldSubmitUtterance = true;
}
public override bool Active
{
get => _service.Active;
}
public override bool MicActive
{
get => _service.MicActive;
}
public override bool IsRequestActive
{
get => _service.IsRequestActive;
}
public override void Activate()
{
_service.Activate();
}
public override void Activate(string text)
{
_service.Activate(text);
}
public override void Deactivate()
{
_service.Deactivate();
}
public override void DeactivateAndAbortRequest()
{
_service.DeactivateAndAbortRequest();
}
public override VoiceServiceRequestEvent OnSend
{
get => _service.VoiceEvents.OnSend;
}
public override WitTranscriptionEvent OnPartialTranscription
{
get => _service.VoiceEvents.OnPartialTranscription;
}
public override WitTranscriptionEvent OnFullTranscription
{
get => _service.VoiceEvents.OnFullTranscription;
}
public override UnityEvent OnStoppedListening
{
get => _service.VoiceEvents.OnStoppedListening;
}
public override VoiceServiceRequestEvent OnComplete
{
get => _service.VoiceEvents.OnComplete;
}
public static WitUnderstandingViewerServiceAPI CreateApiWrapper(MonoBehaviour service)
{
return new WitUnderstandingViewerVoiceServiceAPI((VoiceService)service);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dd3a9e82a16233040a917414603b56f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: