ai.muna.muna 0.0.42
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 +96 -0
- package/Editor/Build/AndroidBuildHandler.cs.meta +11 -0
- package/Editor/Build/BuildHandler.cs +128 -0
- package/Editor/Build/BuildHandler.cs.meta +11 -0
- package/Editor/Build/LinuxBuildHandler.cs +26 -0
- package/Editor/Build/LinuxBuildHandler.cs.meta +11 -0
- package/Editor/Build/WebGLBuildHandler.cs +69 -0
- package/Editor/Build/WebGLBuildHandler.cs.meta +11 -0
- package/Editor/Build/WindowsBuildHandler.cs +24 -0
- package/Editor/Build/WindowsBuildHandler.cs.meta +11 -0
- package/Editor/Build/iOSBuildHandler.cs +119 -0
- package/Editor/Build/iOSBuildHandler.cs.meta +11 -0
- package/Editor/Build/macOSBuildHandler.cs +133 -0
- package/Editor/Build/macOSBuildHandler.cs.meta +11 -0
- package/Editor/Build.meta +8 -0
- package/Editor/Muna.Editor.asmdef +18 -0
- package/Editor/Muna.Editor.asmdef.meta +7 -0
- package/Editor/MunaMenu.cs +32 -0
- package/Editor/MunaMenu.cs.meta +11 -0
- package/Editor/MunaProjectSettings.cs +43 -0
- package/Editor/MunaProjectSettings.cs.meta +11 -0
- package/Editor/MunaSettingsProvider.cs +27 -0
- package/Editor/MunaSettingsProvider.cs.meta +11 -0
- package/Editor.meta +8 -0
- package/LICENSE +201 -0
- package/LICENSE.meta +7 -0
- package/Plugins/Android/Function.aar +0 -0
- package/Plugins/Android/Function.aar.meta +32 -0
- package/Plugins/Android.meta +8 -0
- package/Plugins/Linux/arm64/libFunction.so +0 -0
- package/Plugins/Linux/arm64/libFunction.so.meta +62 -0
- package/Plugins/Linux/arm64.meta +8 -0
- package/Plugins/Linux/x86_64/libFunction.so +0 -0
- package/Plugins/Linux/x86_64/libFunction.so.meta +82 -0
- package/Plugins/Linux/x86_64.meta +8 -0
- package/Plugins/Linux.meta +8 -0
- package/Plugins/Web/libFunction.a +0 -0
- package/Plugins/Web/libFunction.a.meta +87 -0
- package/Plugins/Web.meta +8 -0
- package/Plugins/Windows/arm64/Function.dll +0 -0
- package/Plugins/Windows/arm64/Function.dll.meta +58 -0
- package/Plugins/Windows/arm64.meta +8 -0
- package/Plugins/Windows/x86_64/Function.dll +0 -0
- package/Plugins/Windows/x86_64/Function.dll.meta +82 -0
- package/Plugins/Windows/x86_64.meta +8 -0
- package/Plugins/Windows.meta +8 -0
- package/Plugins/iOS/Function.xcframework/Info.plist +75 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/Function +0 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/Headers/FXNConfiguration.h +313 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/Headers/FXNPrediction.h +165 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/Headers/FXNPredictionStream.h +63 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/Headers/FXNPredictor.h +108 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/Headers/FXNStatus.h +42 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/Headers/FXNValue.h +395 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/Headers/FXNValueMap.h +148 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/Headers/FXNVersion.h +26 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/Headers/Function.h +18 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/Info.plist +0 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/Modules/module.modulemap +6 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/PrivacyInfo.xcprivacy +36 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64/Function.framework/_CodeSignature/CodeResources +223 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/Function +0 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/Headers/FXNConfiguration.h +313 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/Headers/FXNPrediction.h +165 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/Headers/FXNPredictionStream.h +63 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/Headers/FXNPredictor.h +108 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/Headers/FXNStatus.h +42 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/Headers/FXNValue.h +395 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/Headers/FXNValueMap.h +148 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/Headers/FXNVersion.h +26 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/Headers/Function.h +18 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/Info.plist +0 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/Modules/module.modulemap +6 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/PrivacyInfo.xcprivacy +36 -0
- package/Plugins/iOS/Function.xcframework/ios-arm64_x86_64-simulator/Function.framework/_CodeSignature/CodeResources +223 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/Function +0 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/Headers/FXNConfiguration.h +313 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/Headers/FXNPrediction.h +165 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/Headers/FXNPredictionStream.h +63 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/Headers/FXNPredictor.h +108 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/Headers/FXNStatus.h +42 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/Headers/FXNValue.h +395 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/Headers/FXNValueMap.h +148 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/Headers/FXNVersion.h +26 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/Headers/Function.h +18 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/Info.plist +0 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/Modules/module.modulemap +6 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/PrivacyInfo.xcprivacy +36 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64/Function.framework/_CodeSignature/CodeResources +223 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/Function +0 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/Headers/FXNConfiguration.h +313 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/Headers/FXNPrediction.h +165 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/Headers/FXNPredictionStream.h +63 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/Headers/FXNPredictor.h +108 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/Headers/FXNStatus.h +42 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/Headers/FXNValue.h +395 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/Headers/FXNValueMap.h +148 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/Headers/FXNVersion.h +26 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/Headers/Function.h +18 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/Info.plist +0 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/Modules/module.modulemap +6 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/PrivacyInfo.xcprivacy +36 -0
- package/Plugins/iOS/Function.xcframework/xros-arm64_x86_64-simulator/Function.framework/_CodeSignature/CodeResources +223 -0
- package/Plugins/iOS/Function.xcframework.meta +83 -0
- package/Plugins/iOS.meta +8 -0
- package/Plugins/macOS/Function.dylib +0 -0
- package/Plugins/macOS/Function.dylib.meta +82 -0
- package/Plugins/macOS.meta +8 -0
- package/Plugins.meta +8 -0
- package/README.md +79 -0
- package/README.md.meta +7 -0
- package/Runtime/API/DotNetClient.cs +106 -0
- package/Runtime/API/DotNetClient.cs.meta +11 -0
- package/Runtime/API/MunaClient.cs +96 -0
- package/Runtime/API/MunaClient.cs.meta +11 -0
- package/Runtime/API.meta +8 -0
- package/Runtime/Beta/BetaClient.cs +33 -0
- package/Runtime/Beta/BetaClient.cs.meta +11 -0
- package/Runtime/Beta/PredictionService.cs +32 -0
- package/Runtime/Beta/PredictionService.cs.meta +11 -0
- package/Runtime/Beta/RemoteAcceleration.cs +38 -0
- package/Runtime/Beta/RemoteAcceleration.cs.meta +11 -0
- package/Runtime/Beta/RemotePredictionService.cs +187 -0
- package/Runtime/Beta/RemotePredictionService.cs.meta +11 -0
- package/Runtime/Beta/Value.cs +92 -0
- package/Runtime/Beta/Value.cs.meta +11 -0
- package/Runtime/Beta.meta +8 -0
- package/Runtime/C/Configuration.cs +139 -0
- package/Runtime/C/Configuration.cs.meta +11 -0
- package/Runtime/C/Function.cs +294 -0
- package/Runtime/C/Function.cs.meta +11 -0
- package/Runtime/C/Prediction.cs +70 -0
- package/Runtime/C/Prediction.cs.meta +11 -0
- package/Runtime/C/PredictionStream.cs +36 -0
- package/Runtime/C/PredictionStream.cs.meta +11 -0
- package/Runtime/C/Predictor.cs +42 -0
- package/Runtime/C/Predictor.cs.meta +11 -0
- package/Runtime/C/Value.cs +194 -0
- package/Runtime/C/Value.cs.meta +11 -0
- package/Runtime/C/ValueMap.cs +50 -0
- package/Runtime/C/ValueMap.cs.meta +11 -0
- package/Runtime/C.meta +8 -0
- package/Runtime/Muna.Runtime.asmdef +27 -0
- package/Runtime/Muna.Runtime.asmdef.meta +7 -0
- package/Runtime/Muna.cs +109 -0
- package/Runtime/Muna.cs.meta +11 -0
- package/Runtime/Services/Prediction.cs +247 -0
- package/Runtime/Services/Prediction.cs.meta +11 -0
- package/Runtime/Services/Predictor.cs +44 -0
- package/Runtime/Services/Predictor.cs.meta +11 -0
- package/Runtime/Services/User.cs +43 -0
- package/Runtime/Services/User.cs.meta +11 -0
- package/Runtime/Services.meta +8 -0
- package/Runtime/Types/Dtype.cs +118 -0
- package/Runtime/Types/Dtype.cs.meta +11 -0
- package/Runtime/Types/Image.cs +93 -0
- package/Runtime/Types/Image.cs.meta +11 -0
- package/Runtime/Types/Prediction.cs +113 -0
- package/Runtime/Types/Prediction.cs.meta +11 -0
- package/Runtime/Types/Predictor.cs +193 -0
- package/Runtime/Types/Predictor.cs.meta +11 -0
- package/Runtime/Types/Tensor.cs +58 -0
- package/Runtime/Types/Tensor.cs.meta +11 -0
- package/Runtime/Types/User.cs +54 -0
- package/Runtime/Types/User.cs.meta +11 -0
- package/Runtime/Types.meta +8 -0
- package/Runtime.meta +8 -0
- package/Unity/API/PredictionCacheClient.cs +144 -0
- package/Unity/API/PredictionCacheClient.cs.meta +11 -0
- package/Unity/API/UnityClient.cs +147 -0
- package/Unity/API/UnityClient.cs.meta +11 -0
- package/Unity/API.meta +8 -0
- package/Unity/Internal/MunaSettings.cs +59 -0
- package/Unity/Internal/MunaSettings.cs.meta +11 -0
- package/Unity/Internal.meta +8 -0
- package/Unity/Muna.Unity.asmdef +29 -0
- package/Unity/Muna.Unity.asmdef.meta +7 -0
- package/Unity/MunaUnity.cs +130 -0
- package/Unity/MunaUnity.cs.meta +11 -0
- package/Unity/Types/CachedPrediction.cs +36 -0
- package/Unity/Types/CachedPrediction.cs.meta +11 -0
- package/Unity/Types.meta +8 -0
- package/Unity.meta +8 -0
- package/package.json +38 -0
- package/package.json.meta +7 -0
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Muna for Unity Engine
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Run AI inference in your Unity applications. In a few steps:
|
|
6
|
+
|
|
7
|
+
## Installing Muna
|
|
8
|
+
Add the following items to your Unity project's `Packages/manifest.json`:
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"scopedRegistries": [
|
|
12
|
+
{
|
|
13
|
+
"name": "Muna",
|
|
14
|
+
"url": "https://registry.npmjs.com",
|
|
15
|
+
"scopes": ["ai.muna"]
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"ai.muna.muna": "0.0.43"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Retrieving your Access Key
|
|
25
|
+
Head over to [muna.ai](https://muna.ai) to create an account by logging in. Once you do, generate an access key:
|
|
26
|
+
|
|
27
|
+

|
|
28
|
+
|
|
29
|
+
Then add it to your Unity project in `Project Settings > Muna`:
|
|
30
|
+
|
|
31
|
+

|
|
32
|
+
|
|
33
|
+
## Making a Prediction
|
|
34
|
+
First, create a Muna client:
|
|
35
|
+
```csharp
|
|
36
|
+
using Muna;
|
|
37
|
+
|
|
38
|
+
// 💥 Create a Muna client
|
|
39
|
+
var muna = MunaUnity.Create();
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Then make a prediction:
|
|
43
|
+
```csharp
|
|
44
|
+
// 🔥 Make a prediction
|
|
45
|
+
var prediction = await muna.Predictions.Create(
|
|
46
|
+
tag: "@fxn/greeting",
|
|
47
|
+
inputs: new () { ["name"] = "Roberta" }
|
|
48
|
+
);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Then use the results:
|
|
52
|
+
```csharp
|
|
53
|
+
// 🚀 Use the results
|
|
54
|
+
Debug.Log(prediction.results[0]);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
___
|
|
58
|
+
|
|
59
|
+
## Requirements
|
|
60
|
+
- Unity 2022.3+
|
|
61
|
+
|
|
62
|
+
## Supported Platforms
|
|
63
|
+
- Android API Level 24+
|
|
64
|
+
- iOS 14+
|
|
65
|
+
- macOS 12+ (Apple Silicon and Intel)
|
|
66
|
+
- Windows 10+ (64-bit only)
|
|
67
|
+
- WebGL:
|
|
68
|
+
- Chrome 91+
|
|
69
|
+
- Firefox 90+
|
|
70
|
+
- Safari 16.4+
|
|
71
|
+
|
|
72
|
+
## Useful Links
|
|
73
|
+
- [Discover predictors to use in your apps](https://muna.ai/explore).
|
|
74
|
+
- [Join our Discord community](https://muna.ai/community).
|
|
75
|
+
- [Check out our docs](https://docs.muna.ai).
|
|
76
|
+
- Learn more about us [on our blog](https://blog.muna.ai).
|
|
77
|
+
- Reach out to us at [hi@muna.ai](mailto:hi@muna.ai).
|
|
78
|
+
|
|
79
|
+
Thank you very much!
|
package/README.md.meta
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Muna
|
|
3
|
+
* Copyright © 2025 NatML Inc. All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#nullable enable
|
|
7
|
+
|
|
8
|
+
namespace Muna.API {
|
|
9
|
+
|
|
10
|
+
using System.Collections.Generic;
|
|
11
|
+
using System.IO;
|
|
12
|
+
using System.Net.Http;
|
|
13
|
+
using System.Net.Http.Headers;
|
|
14
|
+
using System.Text;
|
|
15
|
+
using System.Threading.Tasks;
|
|
16
|
+
using Newtonsoft.Json;
|
|
17
|
+
|
|
18
|
+
/// <summary>
|
|
19
|
+
/// Muna API client for .NET.
|
|
20
|
+
/// </summary>
|
|
21
|
+
public sealed class DotNetClient : MunaClient {
|
|
22
|
+
|
|
23
|
+
#region --Client API--
|
|
24
|
+
/// <summary>
|
|
25
|
+
/// Create the .NET Muna API client.
|
|
26
|
+
/// </summary>
|
|
27
|
+
/// <param name="url">Muna API URL.</param>
|
|
28
|
+
/// <param name="accessKey">Muna access key.</param>
|
|
29
|
+
/// <param name="clientId">Client identifier.</param>
|
|
30
|
+
/// <param name="deviceId">Device model identifier.</param>
|
|
31
|
+
/// <param name="cachePath">Prediction resource cache path.</param>
|
|
32
|
+
public DotNetClient(
|
|
33
|
+
string url,
|
|
34
|
+
string? accessKey = default
|
|
35
|
+
) : base(url.TrimEnd('/'), accessKey) {
|
|
36
|
+
client = new();
|
|
37
|
+
var ua = new ProductInfoHeaderValue(@"MunaDotNet", Muna.Version);
|
|
38
|
+
client.DefaultRequestHeaders.UserAgent.Add(ua);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// <summary>
|
|
42
|
+
/// Make a request to a REST endpoint.
|
|
43
|
+
/// </summary>
|
|
44
|
+
/// <typeparam name="T">Deserialized response type.</typeparam>
|
|
45
|
+
/// <param name="method">HTTP request method.</param>
|
|
46
|
+
/// <param name="path">Endpoint path.</param>
|
|
47
|
+
/// <param name="payload">Request body.</param>
|
|
48
|
+
/// <param name="headers">Request headers.</param>
|
|
49
|
+
/// <returns>Deserialized response.</returns>
|
|
50
|
+
public override async Task<T?> Request<T>(
|
|
51
|
+
string method,
|
|
52
|
+
string path,
|
|
53
|
+
Dictionary<string, object?>? payload = default,
|
|
54
|
+
Dictionary<string, string>? headers = default
|
|
55
|
+
) where T : class {
|
|
56
|
+
using var message = new HttpRequestMessage(new HttpMethod(method), $"{url}{path}");
|
|
57
|
+
if (!string.IsNullOrEmpty(accessKey))
|
|
58
|
+
message.Headers.Authorization = new AuthenticationHeaderValue(@"Bearer", accessKey);
|
|
59
|
+
if (headers != null)
|
|
60
|
+
foreach (var header in headers)
|
|
61
|
+
message.Headers.Add(header.Key, header.Value);
|
|
62
|
+
if (payload != null) {
|
|
63
|
+
var serializationSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
|
|
64
|
+
var payloadStr = JsonConvert.SerializeObject(payload, serializationSettings);
|
|
65
|
+
message.Content = new StringContent(payloadStr, Encoding.UTF8, @"application/json");
|
|
66
|
+
}
|
|
67
|
+
using var response = await client.SendAsync(message);
|
|
68
|
+
var responseStr = await response.Content.ReadAsStringAsync();
|
|
69
|
+
if ((int)response.StatusCode >= 400) {
|
|
70
|
+
var errorPayload = JsonConvert.DeserializeObject<ErrorResponse>(responseStr);
|
|
71
|
+
var error = errorPayload?.errors?[0]?.message ?? @"An unknown error occurred";
|
|
72
|
+
throw new MunaAPIException(error, (int)response.StatusCode);
|
|
73
|
+
}
|
|
74
|
+
return JsonConvert.DeserializeObject<T>(responseStr)!;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/// <summary>
|
|
78
|
+
/// Download a file.
|
|
79
|
+
/// </summary>
|
|
80
|
+
/// <param name="url">Data URL.</param>
|
|
81
|
+
public override Task<Stream> Download(string url) => client.GetStreamAsync(url);
|
|
82
|
+
|
|
83
|
+
/// <summary>
|
|
84
|
+
/// Upload a data stream.
|
|
85
|
+
/// </summary>
|
|
86
|
+
/// <param name="stream">Data stream.</param>
|
|
87
|
+
/// <param name="url">Upload URL.</param>
|
|
88
|
+
/// <param name="mime">MIME type.</param>
|
|
89
|
+
public override async Task Upload(
|
|
90
|
+
Stream stream,
|
|
91
|
+
string url,
|
|
92
|
+
string? mime = null
|
|
93
|
+
) {
|
|
94
|
+
using var content = new StreamContent(stream);
|
|
95
|
+
content.Headers.ContentType = new MediaTypeHeaderValue(mime ?? @"application/octet-stream");
|
|
96
|
+
using var response = await client.PutAsync(url, content);
|
|
97
|
+
response.EnsureSuccessStatusCode();
|
|
98
|
+
}
|
|
99
|
+
#endregion
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
#region --Operations--
|
|
103
|
+
private readonly HttpClient client;
|
|
104
|
+
#endregion
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Muna
|
|
3
|
+
* Copyright © 2025 NatML Inc. All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#nullable enable
|
|
7
|
+
#pragma warning disable 8618
|
|
8
|
+
|
|
9
|
+
namespace Muna.API {
|
|
10
|
+
|
|
11
|
+
using System;
|
|
12
|
+
using System.Collections.Generic;
|
|
13
|
+
using System.IO;
|
|
14
|
+
using System.Threading.Tasks;
|
|
15
|
+
|
|
16
|
+
/// <summary>
|
|
17
|
+
/// Muna API client.
|
|
18
|
+
/// </summary>
|
|
19
|
+
public abstract class MunaClient {
|
|
20
|
+
|
|
21
|
+
#region --Client API--
|
|
22
|
+
/// <summary>
|
|
23
|
+
/// Muna API URL.
|
|
24
|
+
/// </summary>
|
|
25
|
+
public readonly string url;
|
|
26
|
+
|
|
27
|
+
/// <summary>
|
|
28
|
+
/// Make a request to a REST endpoint.
|
|
29
|
+
/// </summary>
|
|
30
|
+
/// <typeparam name="T">Deserialized response type.</typeparam>
|
|
31
|
+
/// <param name="method">HTTP request method.</param>
|
|
32
|
+
/// <param name="path">Endpoint path.</param>
|
|
33
|
+
/// <param name="payload">Request body.</param>
|
|
34
|
+
/// <param name="headers">Request headers.</param>
|
|
35
|
+
/// <returns>Deserialized response.</returns>
|
|
36
|
+
public abstract Task<T?> Request<T>(
|
|
37
|
+
string method,
|
|
38
|
+
string path,
|
|
39
|
+
Dictionary<string, object?>? payload = default,
|
|
40
|
+
Dictionary<string, string>? headers = default
|
|
41
|
+
) where T : class;
|
|
42
|
+
|
|
43
|
+
/// <summary>
|
|
44
|
+
/// Download a file.
|
|
45
|
+
/// </summary>
|
|
46
|
+
/// <param name="url">URL</param>
|
|
47
|
+
public abstract Task<Stream> Download(string url);
|
|
48
|
+
|
|
49
|
+
/// <summary>
|
|
50
|
+
/// Upload a data stream.
|
|
51
|
+
/// </summary>
|
|
52
|
+
/// <param name="stream">Data stream.</param>
|
|
53
|
+
/// <param name="url">Upload URL.</param>
|
|
54
|
+
/// <param name="mime">MIME type.</param>
|
|
55
|
+
public abstract Task Upload(
|
|
56
|
+
Stream stream,
|
|
57
|
+
string url,
|
|
58
|
+
string? mime = null
|
|
59
|
+
);
|
|
60
|
+
#endregion
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
#region --Operations--
|
|
64
|
+
/// <summary>
|
|
65
|
+
/// Muna access key.
|
|
66
|
+
/// </summary>
|
|
67
|
+
protected internal readonly string? accessKey;
|
|
68
|
+
|
|
69
|
+
protected MunaClient(string url, string? accessKey) {
|
|
70
|
+
this.url = url;
|
|
71
|
+
this.accessKey = accessKey;
|
|
72
|
+
}
|
|
73
|
+
#endregion
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// <summary>
|
|
77
|
+
/// Muna API error response.
|
|
78
|
+
/// </summary>
|
|
79
|
+
[Preserve]
|
|
80
|
+
public sealed class ErrorResponse {
|
|
81
|
+
public Error[] errors;
|
|
82
|
+
public sealed class Error {
|
|
83
|
+
public string message;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// <summary>
|
|
88
|
+
/// Muna API exception.
|
|
89
|
+
/// </summary>
|
|
90
|
+
public sealed class MunaAPIException : Exception {
|
|
91
|
+
|
|
92
|
+
public readonly int status;
|
|
93
|
+
|
|
94
|
+
public MunaAPIException(string message, int status) : base(message) => this.status = status;
|
|
95
|
+
}
|
|
96
|
+
}
|
package/Runtime/API.meta
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Muna
|
|
3
|
+
* Copyright © 2025 NatML Inc. All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#nullable enable
|
|
7
|
+
|
|
8
|
+
namespace Muna.Beta {
|
|
9
|
+
|
|
10
|
+
using API;
|
|
11
|
+
using Services;
|
|
12
|
+
|
|
13
|
+
/// <summary>
|
|
14
|
+
/// Client for incubating features.
|
|
15
|
+
/// </summary>
|
|
16
|
+
public sealed class BetaClient {
|
|
17
|
+
|
|
18
|
+
#region --Client API--
|
|
19
|
+
/// <summary>
|
|
20
|
+
/// Make predictions.
|
|
21
|
+
/// </summary>
|
|
22
|
+
public readonly PredictionService Predictions;
|
|
23
|
+
#endregion
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
#region --Operations--
|
|
27
|
+
|
|
28
|
+
internal BetaClient(MunaClient client) {
|
|
29
|
+
this.Predictions = new PredictionService(client);
|
|
30
|
+
}
|
|
31
|
+
#endregion
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Muna
|
|
3
|
+
* Copyright © 2025 NatML Inc. All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#nullable enable
|
|
7
|
+
|
|
8
|
+
namespace Muna.Beta.Services {
|
|
9
|
+
|
|
10
|
+
using API;
|
|
11
|
+
|
|
12
|
+
/// <summary>
|
|
13
|
+
/// Make predictions.
|
|
14
|
+
/// </summary>
|
|
15
|
+
public sealed class PredictionService {
|
|
16
|
+
|
|
17
|
+
#region --Client API--
|
|
18
|
+
/// <summary>
|
|
19
|
+
/// Make remote predictions.
|
|
20
|
+
/// </summary>
|
|
21
|
+
public readonly RemotePredictionService Remote;
|
|
22
|
+
#endregion
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
#region --Operations--
|
|
26
|
+
|
|
27
|
+
internal PredictionService (MunaClient client) {
|
|
28
|
+
this.Remote = new RemotePredictionService(client);
|
|
29
|
+
}
|
|
30
|
+
#endregion
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Muna
|
|
3
|
+
* Copyright © 2025 NatML Inc. All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
namespace Muna.Beta {
|
|
7
|
+
|
|
8
|
+
using System.Runtime.Serialization;
|
|
9
|
+
using Newtonsoft.Json;
|
|
10
|
+
using Newtonsoft.Json.Converters;
|
|
11
|
+
|
|
12
|
+
/// <summary>
|
|
13
|
+
/// Remote acceleration.
|
|
14
|
+
/// </summary>
|
|
15
|
+
[JsonConverter(typeof(StringEnumConverter))]
|
|
16
|
+
public enum RemoteAcceleration : int {
|
|
17
|
+
/// <summary>
|
|
18
|
+
/// Automatically choose the best acceleration.
|
|
19
|
+
/// </summary>
|
|
20
|
+
[EnumMember(Value = @"remote_auto")]
|
|
21
|
+
Auto = 0,
|
|
22
|
+
/// <summary>
|
|
23
|
+
/// Predictions run on a CPU.
|
|
24
|
+
/// </summary>
|
|
25
|
+
[EnumMember(Value = @"remote_cpu")]
|
|
26
|
+
CPU = 1,
|
|
27
|
+
/// <summary>
|
|
28
|
+
/// Predictions run on an Nvidia A40 GPU.
|
|
29
|
+
/// </summary>
|
|
30
|
+
[EnumMember(Value = @"remote_a40")]
|
|
31
|
+
A40 = 2,
|
|
32
|
+
/// <summary>
|
|
33
|
+
/// Predictions run on an Nvidia A100 GPU.
|
|
34
|
+
/// </summary>
|
|
35
|
+
[EnumMember(Value = @"remote_a100")]
|
|
36
|
+
A100 = 3,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Muna
|
|
3
|
+
* Copyright © 2025 NatML Inc. All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#nullable enable
|
|
7
|
+
|
|
8
|
+
namespace Muna.Beta.Services {
|
|
9
|
+
|
|
10
|
+
using System;
|
|
11
|
+
using System.Collections;
|
|
12
|
+
using System.Collections.Generic;
|
|
13
|
+
using System.IO;
|
|
14
|
+
using System.Linq;
|
|
15
|
+
using System.Threading.Tasks;
|
|
16
|
+
using Newtonsoft.Json;
|
|
17
|
+
using Newtonsoft.Json.Linq;
|
|
18
|
+
using API;
|
|
19
|
+
using Configuration = C.Configuration;
|
|
20
|
+
|
|
21
|
+
/// <summary>
|
|
22
|
+
/// Make remote predictions.
|
|
23
|
+
/// </summary>
|
|
24
|
+
public sealed class RemotePredictionService {
|
|
25
|
+
|
|
26
|
+
#region --Client API--
|
|
27
|
+
/// <summary>
|
|
28
|
+
/// Create a prediction by invoking it on a cloud instance.
|
|
29
|
+
/// </summary>
|
|
30
|
+
/// <param name="tag">Predictor tag.</param>
|
|
31
|
+
/// <param name="inputs">Input values.</param>
|
|
32
|
+
/// <param name="acceleration">Prediction acceleration.</param>
|
|
33
|
+
/// <returns></returns>
|
|
34
|
+
public async Task<Prediction> Create(
|
|
35
|
+
string tag,
|
|
36
|
+
Dictionary<string, object?> inputs,
|
|
37
|
+
RemoteAcceleration acceleration = default
|
|
38
|
+
) {
|
|
39
|
+
await Configuration.InitializationTask;
|
|
40
|
+
var inputMap = (await Task.WhenAll(inputs.Select(async pair => (
|
|
41
|
+
name: pair.Key,
|
|
42
|
+
value: await ToValue(pair.Value, pair.Key)
|
|
43
|
+
)))).ToDictionary(pair => pair.name, pair => pair.value);
|
|
44
|
+
var prediction = (await client.Request<RemotePrediction>(
|
|
45
|
+
method: @"POST",
|
|
46
|
+
path: $"/predictions/remote",
|
|
47
|
+
payload: new () {
|
|
48
|
+
[@"tag"] = tag,
|
|
49
|
+
[@"inputs"] = inputMap,
|
|
50
|
+
[@"acceleration"] = acceleration,
|
|
51
|
+
[@"clientId"] = Configuration.ClientId,
|
|
52
|
+
}
|
|
53
|
+
))!;
|
|
54
|
+
return new Prediction {
|
|
55
|
+
id = prediction.id,
|
|
56
|
+
tag = prediction.tag,
|
|
57
|
+
created = prediction.created,
|
|
58
|
+
results = prediction.results != null ?await Task.WhenAll(prediction.results.Select(ToObject)) : null,
|
|
59
|
+
latency = prediction.latency,
|
|
60
|
+
error = prediction.error,
|
|
61
|
+
logs = prediction.logs,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
#endregion
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
#region --Operations--
|
|
68
|
+
private readonly MunaClient client;
|
|
69
|
+
|
|
70
|
+
internal RemotePredictionService(MunaClient client) => this.client = client;
|
|
71
|
+
|
|
72
|
+
private async Task<Value> ToValue( // INCOMPLETE // Image
|
|
73
|
+
object? value,
|
|
74
|
+
string name,
|
|
75
|
+
int maxDataUrlSize = 4 * 1024 * 1024
|
|
76
|
+
) => value switch {
|
|
77
|
+
null => new Value { type = Dtype.Null },
|
|
78
|
+
float x => new Value { data = await Upload(new [] { x }.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Float32, shape = new int[0] },
|
|
79
|
+
double x => new Value { data = await Upload(new [] { x }.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Float64, shape = new int[0] },
|
|
80
|
+
sbyte x => new Value { data = await Upload(new [] { x }.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Int8, shape = new int[0] },
|
|
81
|
+
short x => new Value { data = await Upload(new [] { x }.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Int16, shape = new int[0] },
|
|
82
|
+
int x => new Value { data = await Upload(new [] { x }.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Int32, shape = new int[0] },
|
|
83
|
+
long x => new Value { data = await Upload(new [] { x }.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Int64, shape = new int[0] },
|
|
84
|
+
byte x => new Value { data = await Upload(new [] { x }.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Uint8, shape = new int[0] },
|
|
85
|
+
ushort x => new Value { data = await Upload(new [] { x }.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Uint16, shape = new int[0] },
|
|
86
|
+
uint x => new Value { data = await Upload(new [] { x }.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Uint32, shape = new int[0] },
|
|
87
|
+
ulong x => new Value { data = await Upload(new [] { x }.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Uint64, shape = new int[0] },
|
|
88
|
+
bool x => new Value { data = await Upload(new [] { x }.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Bool, shape = new int[0] },
|
|
89
|
+
float[] x => new Value { data = await Upload(x.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Float32, shape = new [] { x.Length } },
|
|
90
|
+
double[] x => new Value { data = await Upload(x.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Float64, shape = new [] { x.Length } },
|
|
91
|
+
sbyte[] x => new Value { data = await Upload(x.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Int8, shape = new [] { x.Length } },
|
|
92
|
+
short[] x => new Value { data = await Upload(x.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Int16, shape = new [] { x.Length } },
|
|
93
|
+
int[] x => new Value { data = await Upload(x.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Int32, shape = new [] { x.Length } },
|
|
94
|
+
long[] x => new Value { data = await Upload(x.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Int64, shape = new [] { x.Length } },
|
|
95
|
+
byte[] x => new Value { data = await Upload(x.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Uint8, shape = new [] { x.Length } },
|
|
96
|
+
ushort[] x => new Value { data = await Upload(x.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Uint16, shape = new [] { x.Length } },
|
|
97
|
+
uint[] x => new Value { data = await Upload(x.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Uint32, shape = new [] { x.Length } },
|
|
98
|
+
ulong[] x => new Value { data = await Upload(x.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Uint64, shape = new [] { x.Length } },
|
|
99
|
+
bool[] x => new Value { data = await Upload(x.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Bool, shape = new [] { x.Length } },
|
|
100
|
+
Tensor<float> x => new Value { data = await Upload(x.data.ToStream(),name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Float32, shape = x.shape },
|
|
101
|
+
Tensor<double> x => new Value { data = await Upload(x.data.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Float64, shape = x.shape },
|
|
102
|
+
Tensor<sbyte> x => new Value { data = await Upload(x.data.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Int8, shape = x.shape },
|
|
103
|
+
Tensor<short> x => new Value { data = await Upload(x.data.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Int16, shape = x.shape },
|
|
104
|
+
Tensor<int> x => new Value { data = await Upload(x.data.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Int32, shape = x.shape },
|
|
105
|
+
Tensor<long> x => new Value { data = await Upload(x.data.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Int64, shape = x.shape },
|
|
106
|
+
Tensor<byte> x => new Value { data = await Upload(x.data.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Uint8, shape = x.shape },
|
|
107
|
+
Tensor<ushort> x => new Value { data = await Upload(x.data.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Uint16, shape = x.shape },
|
|
108
|
+
Tensor<uint> x => new Value { data = await Upload(x.data.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Uint32, shape = x.shape },
|
|
109
|
+
Tensor<ulong> x => new Value { data = await Upload(x.data.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Uint64, shape = x.shape },
|
|
110
|
+
Tensor<bool> x => new Value { data = await Upload(x.data.ToStream(), name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Bool, shape = x.shape },
|
|
111
|
+
string x => new Value { data = await Upload(x.ToStream(), name, mime: @"text/plain", maxDataUrlSize: maxDataUrlSize), type = Dtype.String },
|
|
112
|
+
IList x => new Value { data = await Upload(JsonConvert.SerializeObject(x).ToStream(), name, mime: @"application/json", maxDataUrlSize: maxDataUrlSize), type = Dtype.List },
|
|
113
|
+
IDictionary x => new Value { data = await Upload(JsonConvert.SerializeObject(x).ToStream(), name, mime: @"application/json", maxDataUrlSize: maxDataUrlSize), type = Dtype.Dict },
|
|
114
|
+
Image x => new Value { data = "", type = Dtype.Image },
|
|
115
|
+
Stream x => new Value { data = await Upload(x, name, maxDataUrlSize: maxDataUrlSize), type = Dtype.Binary },
|
|
116
|
+
Enum x => await ToValue(x.ToObject(), name, maxDataUrlSize: maxDataUrlSize),
|
|
117
|
+
_ => throw new InvalidOperationException($"Failed to serialize value '{value}' of type `{value.GetType()}` because it is not supported"),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
private async Task<object?> ToObject(Value value) { // INCOMPLETE // Image
|
|
121
|
+
if (value.type == Dtype.Null)
|
|
122
|
+
return null;
|
|
123
|
+
using var stream = await Download(value.data!);
|
|
124
|
+
return value.type switch {
|
|
125
|
+
Dtype.Float32 => stream.ToObject<float>(value.shape!),
|
|
126
|
+
Dtype.Float64 => stream.ToObject<double>(value.shape!),
|
|
127
|
+
Dtype.Int8 => stream.ToObject<sbyte>(value.shape!),
|
|
128
|
+
Dtype.Int16 => stream.ToObject<short>(value.shape!),
|
|
129
|
+
Dtype.Int32 => stream.ToObject<int>(value.shape!),
|
|
130
|
+
Dtype.Int64 => stream.ToObject<long>(value.shape!),
|
|
131
|
+
Dtype.Uint8 => stream.ToObject<byte>(value.shape!),
|
|
132
|
+
Dtype.Uint16 => stream.ToObject<ushort>(value.shape!),
|
|
133
|
+
Dtype.Uint32 => stream.ToObject<uint>(value.shape!),
|
|
134
|
+
Dtype.Uint64 => stream.ToObject<ulong>(value.shape!),
|
|
135
|
+
Dtype.Bool => stream.ToObject<bool>(value.shape!),
|
|
136
|
+
Dtype.String => new StreamReader(stream).ReadToEnd(),
|
|
137
|
+
Dtype.List => JsonConvert.DeserializeObject<JArray>(new StreamReader(stream).ReadToEnd()),
|
|
138
|
+
Dtype.Dict => JsonConvert.DeserializeObject<JObject>(new StreamReader(stream).ReadToEnd()),
|
|
139
|
+
Dtype.Image => default,
|
|
140
|
+
Dtype.Binary => stream.Clone(),
|
|
141
|
+
_ => throw new InvalidOperationException($"Failed to deserialize value with type {value.type} because it is not supported"),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private async Task<string> Upload(
|
|
146
|
+
Stream stream,
|
|
147
|
+
string name,
|
|
148
|
+
string? mime = @"application/octet-stream",
|
|
149
|
+
int maxDataUrlSize = 4 * 1024 * 1024
|
|
150
|
+
) {
|
|
151
|
+
if (stream.Length <= maxDataUrlSize) {
|
|
152
|
+
var data = Convert.ToBase64String(stream.ToArray<byte>());
|
|
153
|
+
var result = $"data:{mime};base64,{data}";
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
var value = await client.Request<CreateValueResponse>(
|
|
157
|
+
method: @"POST",
|
|
158
|
+
path: "/values",
|
|
159
|
+
payload: new () { [@"name"] = name }
|
|
160
|
+
);
|
|
161
|
+
await client.Upload(stream, value!.uploadUrl!, mime: mime);
|
|
162
|
+
return value.downloadUrl!;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private async Task<Stream> Download(string url) {
|
|
166
|
+
if (url.StartsWith(@"data:")) {
|
|
167
|
+
var dataIdx = url.LastIndexOf(",") + 1;
|
|
168
|
+
var b64Data = url.Substring(dataIdx);
|
|
169
|
+
var data = Convert.FromBase64String(b64Data);
|
|
170
|
+
return new MemoryStream(data, 0, data.Length, false, false);
|
|
171
|
+
}
|
|
172
|
+
return await client.Download(url);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
[Preserve, Serializable]
|
|
176
|
+
private class CreateValueResponse {
|
|
177
|
+
public string? uploadUrl;
|
|
178
|
+
public string? downloadUrl;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
[Preserve, Serializable]
|
|
182
|
+
private class RemotePrediction : Prediction {
|
|
183
|
+
public new Value[]? results;
|
|
184
|
+
}
|
|
185
|
+
#endregion
|
|
186
|
+
}
|
|
187
|
+
}
|