using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace SentisSkills.Inference
{
///
/// Where the model, tokenizer and developer block are read from. In the Editor, the package's Models~ folder in the
/// package cache. In a player, the copy the build puts in StreamingAssets/FunctionGemmaHome (see
/// FunctionGemmaBuildStaging). Android keeps StreamingAssets inside the APK, where files can't be opened, so
/// first copies them to persistentDataPath.
///
public static class FunctionGemmaFiles
{
public const string PackageName = "com.sky-kim.functiongemma-home";
public const string Model = "functiongemma_home_uint8.sentis";
public const string Tokenizer = "tokenizer.json";
public const string DevBlock = "fg_dev_block_home.txt";
public static readonly string[] All = { Model, Tokenizer, DevBlock };
/// StreamingAssets folder a player build copies Models~ into.
public const string StreamingFolder = "FunctionGemmaHome";
/// True where StreamingAssets is a URL inside the app package (Android) instead of a folder.
static bool StreamingAssetsInPackage => Application.streamingAssetsPath.Contains("://");
/// Folder holding .
public static string Root
{
get
{
#if UNITY_EDITOR
var info = UnityEditor.PackageManager.PackageInfo.FindForAssetPath($"Packages/{PackageName}");
return info != null ? Path.Combine(info.resolvedPath, "Models~") : null;
#else
return Path.Combine(StreamingAssetsInPackage ? Application.persistentDataPath : Application.streamingAssetsPath, StreamingFolder);
#endif
}
}
public static string PathOf(string file) => Root == null ? null : Path.Combine(Root, file);
///
/// Android: copies the files from the APK to persistentDataPath (once; later runs reuse the copy). Other
/// platforms read them in place.
///
public static IEnumerator CopyOutOfApk()
{
if (Application.isEditor || !StreamingAssetsInPackage) yield break;
DeleteLegacyCopies();
foreach (var file in All)
{
var dst = PathOf(file);
if (File.Exists(dst)) continue;
Directory.CreateDirectory(Path.GetDirectoryName(dst));
var part = dst + ".part";
using (var req = UnityWebRequest.Get($"{Application.streamingAssetsPath}/{StreamingFolder}/{file}"))
{
req.downloadHandler = new DownloadHandlerFile(part) { removeFileOnAbort = true };
Debug.Log($"[FunctionGemma] copying {file} to {dst}");
yield return req.SendWebRequest();
if (req.result != UnityWebRequest.Result.Success)
{
Debug.LogError($"[FunctionGemma] copy of {file} failed: {req.error}");
yield break;
}
}
File.Move(part, dst);
}
}
// Versions before 2.0 copied the files to persistentDataPath/Agent; drop them so they don't keep ~870 MB.
static void DeleteLegacyCopies()
{
var legacy = Path.Combine(Application.persistentDataPath, "Agent");
foreach (var file in new[] { Model, "functiongemma_home_f32.sentis", "functiongemma_home_uint8.sentis", Tokenizer, DevBlock })
{
var path = Path.Combine(legacy, file);
if (File.Exists(path)) File.Delete(path);
}
}
}
}