ai.muna.muna 0.0.45 → 0.0.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Editor/Build/AndroidBuildHandler.cs +3 -3
- package/Editor/Build/iOSBuildHandler.cs +4 -4
- package/Editor/Build/macOSBuildHandler.cs +6 -6
- package/Editor/MunaMenu.cs +9 -11
- package/Plugins/Android/Muna.aar +0 -0
- package/Plugins/macOS/Function.dylib.meta +26 -25
- package/README.md +1 -1
- package/Runtime/Beta/OpenAI/SpeechService.cs +6 -0
- package/Runtime/Beta/OpenAI/Types.cs +0 -1
- package/Runtime/Beta/Remote/RemotePredictionService.cs +43 -42
- package/Runtime/Beta/{Remote → Types}/Value.cs +2 -2
- package/{Unity → Runtime/Beta}/Types.meta +1 -1
- package/Runtime/Muna.cs +1 -1
- package/Unity/API/PredictionCacheClient.cs +44 -51
- package/Unity/Converters/Color.cs +46 -0
- package/Unity/Converters/Color.cs.meta +2 -0
- package/Unity/Converters/Rect.cs +230 -0
- package/Unity/Converters/Rect.cs.meta +2 -0
- package/Unity/Converters/Vector2.cs +44 -0
- package/Unity/Converters/Vector2.cs.meta +2 -0
- package/Unity/Converters/Vector3.cs +45 -0
- package/Unity/Converters/Vector3.cs.meta +2 -0
- package/Unity/Converters/Vector4.cs +46 -0
- package/Unity/Converters/Vector4.cs.meta +2 -0
- package/Unity/Converters.meta +8 -0
- package/Unity/Internal/MunaSettings.cs +1 -1
- package/Unity/Internal/PredictionCache.cs +160 -0
- package/Unity/Internal/PredictionCache.cs.meta +2 -0
- package/Unity/MunaUnity.cs +68 -20
- package/package.json +1 -1
- package/Unity/Types/CachedPrediction.cs +0 -36
- package/Unity/Types/CachedPrediction.cs.meta +0 -11
- /package/Runtime/Beta/{Remote → Types}/Value.cs.meta +0 -0
|
@@ -15,12 +15,12 @@ namespace Muna.Editor.Build {
|
|
|
15
15
|
using UnityEditor.Android;
|
|
16
16
|
using UnityEditor.Build.Reporting;
|
|
17
17
|
using API;
|
|
18
|
+
using Internal;
|
|
18
19
|
using Services;
|
|
19
|
-
using MunaSettings = Internal.MunaSettings;
|
|
20
20
|
|
|
21
21
|
internal sealed class AndroidBuildHandler : BuildHandler, IPostGenerateGradleAndroidProject {
|
|
22
22
|
|
|
23
|
-
private static List<CachedPrediction> cache;
|
|
23
|
+
private static List<PredictionCache.CachedPrediction> cache;
|
|
24
24
|
private static Dictionary<AndroidArchitecture, string> ArchToClientId = new() {
|
|
25
25
|
[AndroidArchitecture.ARMv7] = @"android-armeabi-v7a",
|
|
26
26
|
[AndroidArchitecture.ARM64] = @"android-arm64-v8a",
|
|
@@ -49,7 +49,7 @@ namespace Muna.Editor.Build {
|
|
|
49
49
|
clientId: target,
|
|
50
50
|
configurationId: @""
|
|
51
51
|
)).Result;
|
|
52
|
-
return
|
|
52
|
+
return prediction.AsCached(target);
|
|
53
53
|
} catch (AggregateException ex) {
|
|
54
54
|
Debug.LogWarning($"Muna: Failed to embed {tag} predictor with error: {ex.InnerException}. Predictions with this predictor will likely fail at runtime.");
|
|
55
55
|
return null;
|
|
@@ -16,7 +16,7 @@ namespace Muna.Editor.Build {
|
|
|
16
16
|
using UnityEditor.Build;
|
|
17
17
|
using UnityEditor.Build.Reporting;
|
|
18
18
|
using API;
|
|
19
|
-
using
|
|
19
|
+
using Internal;
|
|
20
20
|
|
|
21
21
|
#if UNITY_IOS || UNITY_VISIONOS
|
|
22
22
|
using UnityEditor.iOS.Xcode;
|
|
@@ -25,7 +25,7 @@ namespace Muna.Editor.Build {
|
|
|
25
25
|
|
|
26
26
|
internal sealed class iOSBuildHandler : BuildHandler, IPostprocessBuildWithReport {
|
|
27
27
|
|
|
28
|
-
private List<CachedPrediction> cache;
|
|
28
|
+
private List<PredictionCache.CachedPrediction> cache;
|
|
29
29
|
private static readonly Dictionary<BuildTarget, string> ClientIds = new () {
|
|
30
30
|
[BuildTarget.iOS] = @"ios-arm64",
|
|
31
31
|
[BuildTarget.VisionOS] = @"visionos-arm64"
|
|
@@ -37,7 +37,7 @@ namespace Muna.Editor.Build {
|
|
|
37
37
|
var projectSettings = MunaProjectSettings.instance;
|
|
38
38
|
var settings = MunaSettings.Create(projectSettings.accessKey);
|
|
39
39
|
var embeds = GetEmbeds();
|
|
40
|
-
var cache = new List<CachedPrediction>();
|
|
40
|
+
var cache = new List<PredictionCache.CachedPrediction>();
|
|
41
41
|
var clientId = ClientIds[report.summary.platform];
|
|
42
42
|
foreach (var embed in embeds) {
|
|
43
43
|
var client = new DotNetClient(embed.url, embed.accessKey);
|
|
@@ -50,7 +50,7 @@ namespace Muna.Editor.Build {
|
|
|
50
50
|
clientId: clientId,
|
|
51
51
|
configurationId: @""
|
|
52
52
|
)).Result;
|
|
53
|
-
return
|
|
53
|
+
return prediction.AsCached(clientId);
|
|
54
54
|
} catch (AggregateException ex) {
|
|
55
55
|
Debug.LogWarning($"Muna: Failed to embed {tag} predictor with error: {ex.InnerException}. Predictions with this predictor will likely fail at runtime.");
|
|
56
56
|
return null;
|
|
@@ -15,17 +15,17 @@ namespace Muna.Editor.Build {
|
|
|
15
15
|
using UnityEditor.Build;
|
|
16
16
|
using UnityEditor.Build.Reporting;
|
|
17
17
|
using API;
|
|
18
|
+
using Internal;
|
|
18
19
|
using Services;
|
|
19
|
-
using MunaSettings = Internal.MunaSettings;
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
#if UNITY_STANDALONE_OSX
|
|
22
22
|
using UnityEditor.iOS.Xcode;
|
|
23
23
|
using UnityEditor.iOS.Xcode.Extensions;
|
|
24
|
-
|
|
24
|
+
#endif
|
|
25
25
|
|
|
26
26
|
internal sealed class macOSBuildHandler : BuildHandler, IPostprocessBuildWithReport {
|
|
27
27
|
|
|
28
|
-
private List<CachedPrediction> cache;
|
|
28
|
+
private List<PredictionCache.CachedPrediction> cache;
|
|
29
29
|
private static readonly string[] ClientIds = new[] {
|
|
30
30
|
"macos-arm64",
|
|
31
31
|
"macos-x86_64"
|
|
@@ -41,7 +41,7 @@ namespace Muna.Editor.Build {
|
|
|
41
41
|
var settings = MunaSettings.Create(projectSettings.accessKey);
|
|
42
42
|
// Embed predictors
|
|
43
43
|
var embeds = GetEmbeds();
|
|
44
|
-
var cache = new List<CachedPrediction>();
|
|
44
|
+
var cache = new List<PredictionCache.CachedPrediction>();
|
|
45
45
|
foreach (var embed in embeds) {
|
|
46
46
|
var client = new DotNetClient(embed.url, embed.accessKey);
|
|
47
47
|
var muna = new Muna(client);
|
|
@@ -54,7 +54,7 @@ namespace Muna.Editor.Build {
|
|
|
54
54
|
clientId: clientId,
|
|
55
55
|
configurationId: @""
|
|
56
56
|
)).Result;
|
|
57
|
-
return
|
|
57
|
+
return prediction.AsCached(clientId);
|
|
58
58
|
}
|
|
59
59
|
catch (AggregateException ex) {
|
|
60
60
|
Debug.LogWarning($"Muna: Failed to embed {tag} predictor with error: {ex.InnerException}. Predictions with this predictor will likely fail at runtime.");
|
package/Editor/MunaMenu.cs
CHANGED
|
@@ -7,35 +7,33 @@ namespace Muna.Editor {
|
|
|
7
7
|
|
|
8
8
|
using System.IO;
|
|
9
9
|
using UnityEditor;
|
|
10
|
+
using Internal;
|
|
10
11
|
|
|
11
12
|
internal static class MunaMenu {
|
|
12
13
|
|
|
13
14
|
private const int BasePriority = -50;
|
|
14
15
|
|
|
15
|
-
[MenuItem(@"Muna/Muna " + Muna.Version, false, BasePriority)]
|
|
16
|
+
[MenuItem(@"Tools/Muna/Muna " + Muna.Version, false, BasePriority)]
|
|
16
17
|
private static void Version() { }
|
|
17
18
|
|
|
18
|
-
[MenuItem(@"Muna/Muna " + Muna.Version, true, BasePriority)]
|
|
19
|
+
[MenuItem(@"Tools/Muna/Muna " + Muna.Version, true, BasePriority)]
|
|
19
20
|
private static bool EnableVersion() => false;
|
|
20
21
|
|
|
21
|
-
[MenuItem(@"Muna/Get Access Key", false, BasePriority + 1)]
|
|
22
|
+
[MenuItem(@"Tools/Muna/Get Access Key", false, BasePriority + 1)]
|
|
22
23
|
private static void GetAccessKey() => Help.BrowseURL(@"https://muna.ai/settings/developer");
|
|
23
24
|
|
|
24
|
-
[MenuItem(@"Muna/Explore Predictors", false, BasePriority + 2)]
|
|
25
|
+
[MenuItem(@"Tools/Muna/Explore Predictors", false, BasePriority + 2)]
|
|
25
26
|
private static void OpenExplore() => Help.BrowseURL(@"https://muna.ai/explore");
|
|
26
27
|
|
|
27
|
-
[MenuItem(@"Muna/View the Docs", false, BasePriority + 3)]
|
|
28
|
+
[MenuItem(@"Tools/Muna/View the Docs", false, BasePriority + 3)]
|
|
28
29
|
private static void OpenDocs() => Help.BrowseURL(@"https://docs.muna.ai");
|
|
29
30
|
|
|
30
|
-
[MenuItem(@"Muna/Report an Issue", false, BasePriority + 4)]
|
|
31
|
+
[MenuItem(@"Tools/Muna/Report an Issue", false, BasePriority + 4)]
|
|
31
32
|
private static void ReportIssue() => Help.BrowseURL(@"https://github.com/muna-ai/muna-unity");
|
|
32
33
|
|
|
33
|
-
[MenuItem(@"Muna/Clear Predictor Cache", false, BasePriority + 5)]
|
|
34
|
+
[MenuItem(@"Tools/Muna/Clear Predictor Cache", false, BasePriority + 5)]
|
|
34
35
|
private static void ClearPredictorCache() {
|
|
35
|
-
Directory.Delete(
|
|
36
|
-
global::Muna.API.PredictionCacheClient.PredictorCachePath,
|
|
37
|
-
true
|
|
38
|
-
);
|
|
36
|
+
Directory.Delete(PredictionCache.PredictorCachePath, true);
|
|
39
37
|
UnityEngine.Debug.Log("Muna: Cleared predictor cache.");
|
|
40
38
|
}
|
|
41
39
|
}
|
package/Plugins/Android/Muna.aar
CHANGED
|
Binary file
|
|
@@ -2,7 +2,7 @@ fileFormatVersion: 2
|
|
|
2
2
|
guid: 62aec3fdf35a340f78ab3a076fc7ca3e
|
|
3
3
|
PluginImporter:
|
|
4
4
|
externalObjects: {}
|
|
5
|
-
serializedVersion:
|
|
5
|
+
serializedVersion: 2
|
|
6
6
|
iconMap: {}
|
|
7
7
|
executionOrder: {}
|
|
8
8
|
defineConstraints: []
|
|
@@ -11,52 +11,53 @@ PluginImporter:
|
|
|
11
11
|
isExplicitlyReferenced: 0
|
|
12
12
|
validateReferences: 1
|
|
13
13
|
platformData:
|
|
14
|
-
|
|
14
|
+
- first:
|
|
15
|
+
:
|
|
16
|
+
second:
|
|
15
17
|
enabled: 0
|
|
16
|
-
settings:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Any:
|
|
18
|
+
settings: {}
|
|
19
|
+
- first:
|
|
20
|
+
: Any
|
|
21
|
+
second:
|
|
21
22
|
enabled: 0
|
|
22
23
|
settings:
|
|
23
|
-
Exclude Android: 1
|
|
24
24
|
Exclude Editor: 0
|
|
25
25
|
Exclude Linux64: 1
|
|
26
26
|
Exclude OSXUniversal: 0
|
|
27
|
-
Exclude WebGL: 1
|
|
28
27
|
Exclude Win: 1
|
|
29
28
|
Exclude Win64: 1
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
- first:
|
|
30
|
+
Editor: Editor
|
|
31
|
+
second:
|
|
32
32
|
enabled: 1
|
|
33
33
|
settings:
|
|
34
34
|
CPU: ARM64
|
|
35
35
|
DefaultValueInitialized: true
|
|
36
36
|
OS: OSX
|
|
37
|
-
|
|
37
|
+
- first:
|
|
38
|
+
Standalone: Linux64
|
|
39
|
+
second:
|
|
38
40
|
enabled: 0
|
|
39
41
|
settings:
|
|
40
|
-
CPU:
|
|
41
|
-
|
|
42
|
+
CPU: x86_64
|
|
43
|
+
- first:
|
|
44
|
+
Standalone: OSXUniversal
|
|
45
|
+
second:
|
|
42
46
|
enabled: 1
|
|
43
47
|
settings:
|
|
44
48
|
CPU: ARM64
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
CPU: AnyCPU
|
|
49
|
-
Win64:
|
|
49
|
+
- first:
|
|
50
|
+
Standalone: Win
|
|
51
|
+
second:
|
|
50
52
|
enabled: 0
|
|
51
53
|
settings:
|
|
52
|
-
CPU:
|
|
53
|
-
|
|
54
|
+
CPU: x86
|
|
55
|
+
- first:
|
|
56
|
+
Standalone: Win64
|
|
57
|
+
second:
|
|
54
58
|
enabled: 0
|
|
55
59
|
settings:
|
|
56
|
-
|
|
57
|
-
CPU: AnyCPU
|
|
58
|
-
CompileFlags:
|
|
59
|
-
FrameworkDependencies:
|
|
60
|
+
CPU: x86_64
|
|
60
61
|
userData:
|
|
61
62
|
assetBundleName:
|
|
62
63
|
assetBundleVariant:
|
package/README.md
CHANGED
|
@@ -190,6 +190,12 @@ namespace Muna.Beta.OpenAI {
|
|
|
190
190
|
StreamFormat streamFormat,
|
|
191
191
|
object acceleration
|
|
192
192
|
) => {
|
|
193
|
+
// Check response format
|
|
194
|
+
if (responseFormat != ResponseFormat.PCM)
|
|
195
|
+
throw new ArgumentException($"Cannot create speech with response format {responseFormat} because only `ResponseFormat.PCM` is supported.");
|
|
196
|
+
// Check stream format
|
|
197
|
+
if (streamFormat != StreamFormat.Audio)
|
|
198
|
+
throw new ArgumentException($"Cannot create speech with stream format {streamFormat} because only `StreamFormat.Audio` is supported.");
|
|
193
199
|
// Build prediction input map
|
|
194
200
|
var inputMap = new Dictionary<string, object?> {
|
|
195
201
|
[inputParam.name] = input,
|
|
@@ -69,51 +69,52 @@ namespace Muna.Beta.Services {
|
|
|
69
69
|
|
|
70
70
|
internal RemotePredictionService(MunaClient client) => this.client = client;
|
|
71
71
|
|
|
72
|
-
private async Task<
|
|
73
|
-
null => new
|
|
74
|
-
float x => new
|
|
75
|
-
double x => new
|
|
76
|
-
sbyte x => new
|
|
77
|
-
short x => new
|
|
78
|
-
int x => new
|
|
79
|
-
long x => new
|
|
80
|
-
byte x => new
|
|
81
|
-
ushort x => new
|
|
82
|
-
uint x => new
|
|
83
|
-
ulong x => new
|
|
84
|
-
bool x => new
|
|
85
|
-
float[] x => new
|
|
86
|
-
double[] x => new
|
|
87
|
-
sbyte[] x => new
|
|
88
|
-
short[] x => new
|
|
89
|
-
int[] x => new
|
|
90
|
-
long[] x => new
|
|
91
|
-
byte[] x => new
|
|
92
|
-
ushort[] x => new
|
|
93
|
-
uint[] x => new
|
|
94
|
-
ulong[] x => new
|
|
95
|
-
bool[] x => new
|
|
96
|
-
Tensor<float> x => new
|
|
97
|
-
Tensor<double> x => new
|
|
98
|
-
Tensor<sbyte> x => new
|
|
99
|
-
Tensor<short> x => new
|
|
100
|
-
Tensor<int> x => new
|
|
101
|
-
Tensor<long> x => new
|
|
102
|
-
Tensor<byte> x => new
|
|
103
|
-
Tensor<ushort> x => new
|
|
104
|
-
Tensor<uint> x => new
|
|
105
|
-
Tensor<ulong> x => new
|
|
106
|
-
Tensor<bool> x => new
|
|
107
|
-
string x => new
|
|
108
|
-
IList x => new
|
|
109
|
-
IDictionary x => new
|
|
110
|
-
Image x => new
|
|
111
|
-
Stream x => new
|
|
72
|
+
private async Task<RemoteValue> ToValue(object? value) => value switch { // INCOMPLETE // Image
|
|
73
|
+
null => new() { type = Dtype.Null },
|
|
74
|
+
float x => new() { data = await Upload(new [] { x }.ToStream()), type = Dtype.Float32, shape = new int[0] },
|
|
75
|
+
double x => new() { data = await Upload(new [] { x }.ToStream()), type = Dtype.Float64, shape = new int[0] },
|
|
76
|
+
sbyte x => new() { data = await Upload(new [] { x }.ToStream()), type = Dtype.Int8, shape = new int[0] },
|
|
77
|
+
short x => new() { data = await Upload(new [] { x }.ToStream()), type = Dtype.Int16, shape = new int[0] },
|
|
78
|
+
int x => new() { data = await Upload(new [] { x }.ToStream()), type = Dtype.Int32, shape = new int[0] },
|
|
79
|
+
long x => new() { data = await Upload(new [] { x }.ToStream()), type = Dtype.Int64, shape = new int[0] },
|
|
80
|
+
byte x => new() { data = await Upload(new [] { x }.ToStream()), type = Dtype.Uint8, shape = new int[0] },
|
|
81
|
+
ushort x => new() { data = await Upload(new [] { x }.ToStream()), type = Dtype.Uint16, shape = new int[0] },
|
|
82
|
+
uint x => new() { data = await Upload(new [] { x }.ToStream()), type = Dtype.Uint32, shape = new int[0] },
|
|
83
|
+
ulong x => new() { data = await Upload(new [] { x }.ToStream()), type = Dtype.Uint64, shape = new int[0] },
|
|
84
|
+
bool x => new() { data = await Upload(new [] { x }.ToStream()), type = Dtype.Bool, shape = new int[0] },
|
|
85
|
+
float[] x => new() { data = await Upload(x.ToStream()), type = Dtype.Float32, shape = new [] { x.Length } },
|
|
86
|
+
double[] x => new() { data = await Upload(x.ToStream()), type = Dtype.Float64, shape = new [] { x.Length } },
|
|
87
|
+
sbyte[] x => new() { data = await Upload(x.ToStream()), type = Dtype.Int8, shape = new [] { x.Length } },
|
|
88
|
+
short[] x => new() { data = await Upload(x.ToStream()), type = Dtype.Int16, shape = new [] { x.Length } },
|
|
89
|
+
int[] x => new() { data = await Upload(x.ToStream()), type = Dtype.Int32, shape = new [] { x.Length } },
|
|
90
|
+
long[] x => new() { data = await Upload(x.ToStream()), type = Dtype.Int64, shape = new [] { x.Length } },
|
|
91
|
+
byte[] x => new() { data = await Upload(x.ToStream()), type = Dtype.Uint8, shape = new [] { x.Length } },
|
|
92
|
+
ushort[] x => new() { data = await Upload(x.ToStream()), type = Dtype.Uint16, shape = new [] { x.Length } },
|
|
93
|
+
uint[] x => new() { data = await Upload(x.ToStream()), type = Dtype.Uint32, shape = new [] { x.Length } },
|
|
94
|
+
ulong[] x => new() { data = await Upload(x.ToStream()), type = Dtype.Uint64, shape = new [] { x.Length } },
|
|
95
|
+
bool[] x => new() { data = await Upload(x.ToStream()), type = Dtype.Bool, shape = new [] { x.Length } },
|
|
96
|
+
Tensor<float> x => new() { data = await Upload(x.data.ToStream()), type = Dtype.Float32, shape = x.shape },
|
|
97
|
+
Tensor<double> x => new() { data = await Upload(x.data.ToStream()), type = Dtype.Float64, shape = x.shape },
|
|
98
|
+
Tensor<sbyte> x => new() { data = await Upload(x.data.ToStream()), type = Dtype.Int8, shape = x.shape },
|
|
99
|
+
Tensor<short> x => new() { data = await Upload(x.data.ToStream()), type = Dtype.Int16, shape = x.shape },
|
|
100
|
+
Tensor<int> x => new() { data = await Upload(x.data.ToStream()), type = Dtype.Int32, shape = x.shape },
|
|
101
|
+
Tensor<long> x => new() { data = await Upload(x.data.ToStream()), type = Dtype.Int64, shape = x.shape },
|
|
102
|
+
Tensor<byte> x => new() { data = await Upload(x.data.ToStream()), type = Dtype.Uint8, shape = x.shape },
|
|
103
|
+
Tensor<ushort> x => new() { data = await Upload(x.data.ToStream()), type = Dtype.Uint16, shape = x.shape },
|
|
104
|
+
Tensor<uint> x => new() { data = await Upload(x.data.ToStream()), type = Dtype.Uint32, shape = x.shape },
|
|
105
|
+
Tensor<ulong> x => new() { data = await Upload(x.data.ToStream()), type = Dtype.Uint64, shape = x.shape },
|
|
106
|
+
Tensor<bool> x => new() { data = await Upload(x.data.ToStream()), type = Dtype.Bool, shape = x.shape },
|
|
107
|
+
string x => new() { data = await Upload(x.ToStream(), mime: @"text/plain"), type = Dtype.String },
|
|
108
|
+
IList x => new() { data = await Upload(JsonConvert.SerializeObject(x).ToStream(), mime: @"application/json"), type = Dtype.List },
|
|
109
|
+
IDictionary x => new() { data = await Upload(JsonConvert.SerializeObject(x).ToStream(), mime: @"application/json"), type = Dtype.Dict },
|
|
110
|
+
Image x => new() { data = "", type = Dtype.Image },
|
|
111
|
+
Stream x => new() { data = await Upload(x), type = Dtype.Binary },
|
|
112
112
|
Enum x => await ToValue(x.ToObject()),
|
|
113
|
+
RemoteValue x => x,
|
|
113
114
|
_ => throw new InvalidOperationException($"Failed to serialize value '{value}' of type `{value.GetType()}` because it is not supported"),
|
|
114
115
|
};
|
|
115
116
|
|
|
116
|
-
private async Task<object?> ToObject(
|
|
117
|
+
private async Task<object?> ToObject(RemoteValue value) { // INCOMPLETE // Image
|
|
117
118
|
if (value.type == Dtype.Null)
|
|
118
119
|
return null;
|
|
119
120
|
using var stream = await Download(value.data!);
|
|
@@ -159,7 +160,7 @@ namespace Muna.Beta.Services {
|
|
|
159
160
|
|
|
160
161
|
[Preserve, Serializable]
|
|
161
162
|
private class RemotePrediction : Prediction {
|
|
162
|
-
public new
|
|
163
|
+
public new RemoteValue[]? results;
|
|
163
164
|
}
|
|
164
165
|
#endregion
|
|
165
166
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
#nullable enable
|
|
7
7
|
|
|
8
|
-
namespace Muna.Beta
|
|
8
|
+
namespace Muna.Beta {
|
|
9
9
|
|
|
10
10
|
using System;
|
|
11
11
|
using System.IO;
|
|
@@ -18,7 +18,7 @@ namespace Muna.Beta.Services {
|
|
|
18
18
|
/// Remote prediction value.
|
|
19
19
|
/// </summary>
|
|
20
20
|
[Preserve, Serializable]
|
|
21
|
-
|
|
21
|
+
public sealed class RemoteValue {
|
|
22
22
|
|
|
23
23
|
/// <summary>
|
|
24
24
|
/// Value URL.
|
package/Runtime/Muna.cs
CHANGED
|
@@ -7,13 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
namespace Muna.API {
|
|
9
9
|
|
|
10
|
-
using System;
|
|
11
10
|
using System.Collections.Generic;
|
|
12
11
|
using System.IO;
|
|
13
12
|
using System.Linq;
|
|
14
13
|
using System.Threading.Tasks;
|
|
15
14
|
using UnityEngine;
|
|
16
|
-
using
|
|
15
|
+
using Internal;
|
|
17
16
|
using Services;
|
|
18
17
|
|
|
19
18
|
/// <summary>
|
|
@@ -32,9 +31,8 @@ namespace Muna.API {
|
|
|
32
31
|
/// <param name="cache">Prediction cache.</param>
|
|
33
32
|
public PredictionCacheClient(
|
|
34
33
|
string url,
|
|
35
|
-
string? accessKey
|
|
36
|
-
|
|
37
|
-
) : base(url, accessKey) => this.cache = cache ?? new();
|
|
34
|
+
string? accessKey
|
|
35
|
+
) : base(url, accessKey) { }
|
|
38
36
|
|
|
39
37
|
/// <summary>
|
|
40
38
|
/// Perform a request to a Muna REST endpoint.
|
|
@@ -52,11 +50,9 @@ namespace Muna.API {
|
|
|
52
50
|
Dictionary<string, string>? headers = default
|
|
53
51
|
) where T : class {
|
|
54
52
|
// Check payload
|
|
55
|
-
var tag = payload
|
|
56
|
-
var clientId = payload
|
|
57
|
-
var configurationId = payload
|
|
58
|
-
configuration as string :
|
|
59
|
-
null;
|
|
53
|
+
var tag = GetValue<string>(payload, @"tag");
|
|
54
|
+
var clientId = GetValue<string>(payload, @"clientId");
|
|
55
|
+
var configurationId = GetValue<string>(payload, @"configurationId");
|
|
60
56
|
if (
|
|
61
57
|
method != @"POST" ||
|
|
62
58
|
path != @"/predictions" ||
|
|
@@ -65,38 +61,35 @@ namespace Muna.API {
|
|
|
65
61
|
string.IsNullOrEmpty(configurationId)
|
|
66
62
|
)
|
|
67
63
|
return await base.Request<T>(method, path, payload, headers);
|
|
68
|
-
// Get
|
|
69
|
-
var
|
|
70
|
-
var
|
|
71
|
-
|
|
64
|
+
// Get embedded prediction if available
|
|
65
|
+
var cache = MunaSettings.Instance!.cache;
|
|
66
|
+
var embeddedPrediction = cache.FirstOrDefault(p =>
|
|
67
|
+
p.tag == tag &&
|
|
68
|
+
MatchClientIds(p.clientId!, clientId)
|
|
69
|
+
);
|
|
70
|
+
// Load from prediction cache
|
|
71
|
+
if (PredictionCache.Get(
|
|
72
|
+
tag,
|
|
72
73
|
clientId,
|
|
73
74
|
configurationId,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (cachedPrediction != null)
|
|
75
|
+
embeddedPrediction?.resources,
|
|
76
|
+
out var cachedPrediction
|
|
77
|
+
))
|
|
78
78
|
return cachedPrediction as T;
|
|
79
|
-
// Create prediction
|
|
80
|
-
var predictionId = cache.FirstOrDefault(p => p.tag == tag && p.clientId == clientId)?.id;
|
|
79
|
+
// Create prediction and cache
|
|
81
80
|
var prediction = await base.Request<Prediction>(
|
|
82
81
|
method: @"POST",
|
|
83
82
|
path: @"/predictions",
|
|
84
|
-
payload: new
|
|
83
|
+
payload: new() {
|
|
85
84
|
[@"tag"] = tag,
|
|
86
85
|
[@"clientId"] = clientId,
|
|
87
86
|
[@"configurationId"] = configurationId,
|
|
88
|
-
[@"predictionId"] =
|
|
87
|
+
[@"predictionId"] = embeddedPrediction?.id,
|
|
89
88
|
},
|
|
90
89
|
headers
|
|
91
90
|
);
|
|
92
91
|
prediction!.resources = await Task.WhenAll(prediction.resources.Select(GetCachedResource));
|
|
93
|
-
|
|
94
|
-
var predictionJson = JsonConvert.SerializeObject(
|
|
95
|
-
new CachedPrediction(prediction, clientId),
|
|
96
|
-
Formatting.Indented
|
|
97
|
-
);
|
|
98
|
-
Directory.CreateDirectory(Path.GetDirectoryName(cachePath));
|
|
99
|
-
File.WriteAllText(cachePath, predictionJson);
|
|
92
|
+
PredictionCache.Add(prediction.AsCached(clientId, configurationId));
|
|
100
93
|
// Return
|
|
101
94
|
return prediction as T;
|
|
102
95
|
}
|
|
@@ -104,15 +97,9 @@ namespace Muna.API {
|
|
|
104
97
|
|
|
105
98
|
|
|
106
99
|
#region --Operations--
|
|
107
|
-
private readonly List<CachedPrediction> cache;
|
|
108
|
-
private static string CacheRoot => Application.isEditor ?
|
|
109
|
-
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), @".fxn") :
|
|
110
|
-
Path.Combine(Application.persistentDataPath, @"fxn");
|
|
111
|
-
private static string ResourceCachePath => Path.Combine(CacheRoot, @"cache");
|
|
112
|
-
internal static string PredictorCachePath => Path.Combine(CacheRoot, @"predictors");
|
|
113
100
|
|
|
114
101
|
private async Task<PredictionResource> GetCachedResource(PredictionResource resource) {
|
|
115
|
-
var path = PredictionService.GetResourcePath(resource, ResourceCachePath);
|
|
102
|
+
var path = PredictionService.GetResourcePath(resource, PredictionCache.ResourceCachePath);
|
|
116
103
|
if (!File.Exists(path)) {
|
|
117
104
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
|
118
105
|
using var dataStream = await Download(resource.url);
|
|
@@ -122,22 +109,28 @@ namespace Muna.API {
|
|
|
122
109
|
return new PredictionResource { type = resource.type, url = $"file://{path}" };
|
|
123
110
|
}
|
|
124
111
|
|
|
125
|
-
private static
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
var
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return
|
|
112
|
+
private static T? GetValue<T>(
|
|
113
|
+
Dictionary<string, object?>? payload,
|
|
114
|
+
string key
|
|
115
|
+
) {
|
|
116
|
+
if (payload?.TryGetValue(key, out var value) ?? false)
|
|
117
|
+
return (T?)value;
|
|
118
|
+
else
|
|
119
|
+
return default;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private static bool MatchClientIds(string a, string b) {
|
|
123
|
+
if (a == b)
|
|
124
|
+
return true;
|
|
125
|
+
if (a.Contains("android") && b.Contains("android")) {
|
|
126
|
+
var ARM32 = new[] { "armeabi-v7a", "armv7l", "armv8l" };
|
|
127
|
+
var ARM64 = new[] { "arm64", "aarch64", "armv8" };
|
|
128
|
+
if (ARM32.Any(s => a.Contains(s)) && ARM32.Any(s => b.Contains(s)))
|
|
129
|
+
return true;
|
|
130
|
+
if (ARM64.Any(s => a.Contains(s)) && ARM64.Any(s => b.Contains(s)))
|
|
131
|
+
return true;
|
|
138
132
|
}
|
|
139
|
-
|
|
140
|
-
return prediction;
|
|
133
|
+
return false;
|
|
141
134
|
}
|
|
142
135
|
#endregion
|
|
143
136
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Muna
|
|
3
|
+
* Copyright © 2025 NatML Inc. All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#nullable enable
|
|
7
|
+
|
|
8
|
+
namespace Muna.Converters {
|
|
9
|
+
|
|
10
|
+
using System;
|
|
11
|
+
using UnityEngine;
|
|
12
|
+
using Newtonsoft.Json;
|
|
13
|
+
using Newtonsoft.Json.Linq;
|
|
14
|
+
|
|
15
|
+
/// <summary>
|
|
16
|
+
/// Convert an array field to a `Color`.
|
|
17
|
+
/// The array MUST contain three or four numbers.
|
|
18
|
+
/// </summary>
|
|
19
|
+
public sealed class ArrayToColorConverter : JsonConverter<Color> {
|
|
20
|
+
|
|
21
|
+
public override void WriteJson(
|
|
22
|
+
JsonWriter writer,
|
|
23
|
+
Color value,
|
|
24
|
+
JsonSerializer serializer
|
|
25
|
+
) {
|
|
26
|
+
var obj = new JArray { value.r, value.g, value.b, value.a };
|
|
27
|
+
obj.WriteTo(writer);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public override Color ReadJson(
|
|
31
|
+
JsonReader reader,
|
|
32
|
+
Type type,
|
|
33
|
+
Color existing,
|
|
34
|
+
bool hasExisting,
|
|
35
|
+
JsonSerializer s
|
|
36
|
+
) {
|
|
37
|
+
var arr = JArray.Load(reader);
|
|
38
|
+
return new Color(
|
|
39
|
+
r: (float)arr[0],
|
|
40
|
+
g: (float)arr[1],
|
|
41
|
+
b: (float)arr[2],
|
|
42
|
+
a: arr.Count > 3 ? (float)arr[3] : 1f
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|