com.sky.ondeviceagent / Runtime /Inference /Common /ModelPathResolver.cs
Sky-Kim's picture
Initial commit
2e7837a
Raw
History Blame Contribute Delete
5.65 kB
using System;
using System.IO;
using UnityEngine;
namespace OnDeviceAgent.Inference
{
// Resolves a "Model/<X>/..." relative path to a concrete file path. In a player build it resolves under
// Application.streamingAssetsPath; in the Editor the weights live only in the com.sky.sentis.* packages
// (Models~), so the path is redirected there and nothing is duplicated into Assets/StreamingAssets. The
// build step copies the package payloads into StreamingAssets so the player ships them.
public static class ModelPathResolver
{
public static string GetModelFilePath(string relativePath)
{
var rel = (relativePath ?? string.Empty).Replace('\\', '/').TrimStart('/');
#if UNITY_EDITOR
var packagePath = TryResolveInPackage(rel);
if (packagePath != null)
return packagePath;
#endif
return Path.Combine(Application.streamingAssetsPath, rel.Replace('/', Path.DirectorySeparatorChar));
}
#if UNITY_EDITOR
// StreamingAssets subfolder -> owning package.
public static readonly (string streamingAssetsSubdir, string packageName)[] PackageMap =
{
("Model/E5", "com.sky.sentis.e5"),
("Model/OpenWakeWord", "com.sky.sentis.openwakeword"),
("Model/SileroVad", "com.sky.sentis.silero-vad"),
("Model/Supertonic", "com.sky.sentis.supertonic"),
("Model/Whisper", "com.sky.sentis.whisper"),
("Model/YOLO", "com.sky.sentis.yolox"),
};
// Physical root of a package. Embedded packages live at <project>/Packages/<name>, but git and
// registry dependencies resolve into Library/PackageCache/<name>@<hash>, so the location is
// queried from the Package Manager and only falls back to the project-relative path.
static string ResolvePackageRoot(string pkg)
{
var info = UnityEditor.PackageManager.PackageInfo.FindForAssetPath("Packages/" + pkg);
if (info != null && !string.IsNullOrEmpty(info.resolvedPath))
return info.resolvedPath;
return Path.GetFullPath(Path.Combine("Packages", pkg));
}
static string TryResolveInPackage(string rel)
{
foreach (var (sub, pkg) in PackageMap)
{
if (rel != sub && !rel.StartsWith(sub + "/", StringComparison.Ordinal))
continue;
var tail = rel.Substring(sub.Length).TrimStart('/');
var full = Path.Combine(ResolvePackageRoot(pkg), "Models~", tail.Replace('/', Path.DirectorySeparatorChar));
if (File.Exists(full) || Directory.Exists(full))
return full;
return null; // package is known but the file is missing; fall back to StreamingAssets
}
return null;
}
// Copies every package's Models~ payload into Assets/StreamingAssets/<subdir> so a player build
// ships the weights. Called by the build pre-process step.
public static void ProvisionToStreamingAssets()
{
foreach (var (sub, pkg) in PackageMap)
{
var src = Path.Combine(ResolvePackageRoot(pkg), "Models~");
if (!Directory.Exists(src))
continue;
var dst = Path.Combine(Application.streamingAssetsPath, sub.Replace('/', Path.DirectorySeparatorChar));
CopyDir(src, dst);
WriteFilesManifest(dst);
}
}
// Lists every copied file under dst (relative, forward slashes), so the Android runtime can stage the
// tree out of the APK without directory enumeration. Excludes .meta and the manifest itself.
const string FilesManifestFile = "files_manifest.txt";
static void WriteFilesManifest(string dst)
{
if (!Directory.Exists(dst))
return;
var entries = new System.Collections.Generic.List<string>();
foreach (var file in Directory.GetFiles(dst, "*", SearchOption.AllDirectories))
{
if (file.EndsWith(".meta", StringComparison.Ordinal))
continue;
var rel = file.Substring(dst.Length).TrimStart(Path.DirectorySeparatorChar, '/').Replace('\\', '/');
if (rel == FilesManifestFile)
continue;
entries.Add(rel);
}
entries.Sort(StringComparer.Ordinal);
File.WriteAllText(Path.Combine(dst, FilesManifestFile), string.Join("\n", entries) + "\n");
}
// Deletes Assets/StreamingAssets/Model so the weights never linger in the project tree.
public static void RemoveFromStreamingAssets()
{
var modelRoot = Path.Combine(Application.streamingAssetsPath, "Model");
if (Directory.Exists(modelRoot))
Directory.Delete(modelRoot, true);
var meta = modelRoot + ".meta";
if (File.Exists(meta))
File.Delete(meta);
}
static void CopyDir(string src, string dst)
{
Directory.CreateDirectory(dst);
foreach (var file in Directory.GetFiles(src))
{
if (file.EndsWith(".meta", StringComparison.Ordinal))
continue;
File.Copy(file, Path.Combine(dst, Path.GetFileName(file)), true);
}
foreach (var dir in Directory.GetDirectories(src))
CopyDir(dir, Path.Combine(dst, Path.GetFileName(dir)));
}
#endif
}
}