From 035fc31f8a7e6d01927677ef7d9dcf1fbc540637 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Fri, 28 Jun 2024 22:01:03 +0300 Subject: [PATCH 01/30] add android loader --- Runtime/LLMLib.cs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Runtime/LLMLib.cs b/Runtime/LLMLib.cs index 45fc4260..95cd5576 100644 --- a/Runtime/LLMLib.cs +++ b/Runtime/LLMLib.cs @@ -104,6 +104,8 @@ public static IntPtr LoadLibrary(string libraryName) handle = Linux.dlopen(libraryName); else if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer) handle = Mac.dlopen(libraryName); + else if (Application.platform == RuntimePlatform.Android) + handle = Android.dlopen(libraryName); else throw new PlatformNotSupportedException($"Current platform is unknown, unable to load library '{libraryName}'."); @@ -122,6 +124,8 @@ public static IntPtr GetSymbol(IntPtr library, string symbolName) handle = Linux.dlsym(library, symbolName); else if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer) handle = Mac.dlsym(library, symbolName); + else if (Application.platform == RuntimePlatform.Android) + handle = Android.dlsym(library, symbolName); else throw new PlatformNotSupportedException($"Current platform is unknown, unable to load symbol '{symbolName}' from library {library}."); @@ -139,6 +143,8 @@ public static void FreeLibrary(IntPtr library) Linux.dlclose(library); else if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer) Mac.dlclose(library); + else if (Application.platform == RuntimePlatform.Android) + Android.dlclose(library); else throw new PlatformNotSupportedException($"Current platform is unknown, unable to close library '{library}'."); } @@ -231,6 +237,22 @@ private static class Win32 [DllImport(SystemLibrary, SetLastError = true, CharSet = CharSet.Ansi)] public static extern void FreeLibrary(IntPtr hModule); } + + private static class Android + { + public static IntPtr dlopen(string path) => dlopen(path, 1); + // LoadLibrary for Android + [DllImport("__Internal")] + public static extern IntPtr dlopen(string filename, int flags); + + // GetSymbol for Android + [DllImport("__Internal")] + public static extern IntPtr dlsym(IntPtr handle, string symbol); + + // FreeLibrary for Android + [DllImport("__Internal")] + public static extern int dlclose(IntPtr handle); + } } public class LLMLib @@ -334,6 +356,10 @@ public static List PossibleArchitectures(bool gpu = false) architectures.Add("x64-no_acc"); } } + else if (Application.platform == RuntimePlatform.Android) + { + architectures.Add("android"); + } else { string error = "Unknown OS"; @@ -376,6 +402,10 @@ public static string GetArchitecturePath(string arch) { filename = $"macos-{arch}/libundreamai_macos-{arch}.dylib"; } + else if (Application.platform == RuntimePlatform.Android) + { + return "libundreamai_android_plugin.so"; + } else { string error = "Unknown OS"; From f9efdd81bc7bc4786ec686a7624bd915a98fb0ba Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Fri, 28 Jun 2024 22:03:04 +0300 Subject: [PATCH 02/30] add android postprocessor --- Editor/LLMBuildProcessor.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Editor/LLMBuildProcessor.cs b/Editor/LLMBuildProcessor.cs index d0362ead..a1ebecac 100644 --- a/Editor/LLMBuildProcessor.cs +++ b/Editor/LLMBuildProcessor.cs @@ -54,7 +54,7 @@ public void BuildCompleted() static List GetLibraryPlatformsToHide(BuildTarget platform) { - List platforms = new List(){ "windows", "macos", "linux" }; + List platforms = new List(){ "windows", "macos", "linux", "android" }; switch (platform) { case BuildTarget.StandaloneWindows: @@ -67,6 +67,9 @@ static List GetLibraryPlatformsToHide(BuildTarget platform) case BuildTarget.StandaloneOSX: platforms.Remove("macos"); break; + case BuildTarget.Android: + platforms.Remove("android"); + break; } return platforms; } From 61a00bb4dda3f36c6d6a978e32f4b034b524f4ad Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Fri, 12 Jul 2024 20:11:15 +0300 Subject: [PATCH 03/30] function to determine the number of big cores in Android --- Runtime/LLMUnitySetup.cs | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Runtime/LLMUnitySetup.cs b/Runtime/LLMUnitySetup.cs index 74022dbc..0a4c7dd4 100644 --- a/Runtime/LLMUnitySetup.cs +++ b/Runtime/LLMUnitySetup.cs @@ -8,6 +8,11 @@ using System; using System.IO.Compression; using System.Collections.Generic; +<<<<<<< HEAD +======= +using UnityEngine.Networking; +using System.Collections.Generic; +>>>>>>> d9fdc86 (function to determine the number of big cores in Android) /// @defgroup llm LLM /// @defgroup template Chat Templates @@ -290,5 +295,49 @@ public static void DownloadModel(LLM llm, int optionIndex) #endif /// \endcond + + /// + /// Calculates the number of big cores in Android based on https://docs.unity3d.com/2022.3/Documentation/Manual/android-thread-configuration.html + /// + /// + public static int AndroidGetNumBigCores() + { + List capacities = new List(); + int minCapacity = int.MaxValue; + try + { + string cpuPath = "/sys/devices/system/cpu/"; + int coreIndex; + if (Directory.Exists(cpuPath)) + { + foreach (string cpuDir in Directory.GetDirectories(cpuPath)) + { + string dirName = Path.GetFileName(cpuDir); + if (!dirName.StartsWith("cpu")) continue; + if (!int.TryParse(dirName.Substring(3), out coreIndex)) continue; + + string capacityPath = Path.Combine(cpuDir, "cpu_capacity"); + if (!File.Exists(capacityPath)) break; + + int capacity = int.Parse(File.ReadAllText(capacityPath).Trim()); + capacities.Add(capacity); + if (minCapacity > capacity) minCapacity = capacity; + } + } + } + catch (Exception e) + { + Debug.LogError(e.Message); + } + + int numBigCores = 0; + foreach (int capacity in capacities) + { + if (capacity >= 2 * minCapacity) numBigCores++; + } + + if (numBigCores == 0) numBigCores = SystemInfo.processorCount; + return numBigCores; + } } } From f1971e2e197a961b62d5b92e846db6ab37db272e Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Fri, 12 Jul 2024 20:11:57 +0300 Subject: [PATCH 04/30] use the number of big cores if on Android --- Runtime/LLM.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Runtime/LLM.cs b/Runtime/LLM.cs index 74e51e33..8dc8d475 100644 --- a/Runtime/LLM.cs +++ b/Runtime/LLM.cs @@ -196,10 +196,13 @@ protected virtual string GetLlamaccpArguments() } } + int numThreadsToUse = numThreads; + if (Application.platform == RuntimePlatform.Android && numThreads <= 0) numThreadsToUse = LLMUnitySetup.AndroidGetNumBigCores(); + int slots = GetNumClients(); string arguments = $"-m \"{modelPath}\" -c {contextSize} -b {batchSize} --log-disable -np {slots}"; if (remote) arguments += $" --port {port} --host 0.0.0.0"; - if (numThreads > 0) arguments += $" -t {numThreads}"; + if (numThreadsToUse > 0) arguments += $" -t {numThreadsToUse}"; if (loraPath != "") arguments += $" --lora \"{loraPath}\""; arguments += $" -ngl {numGPULayers}"; return arguments; From 1a84e1fdfd1e1da1b42101a446cf7af0b7e7520c Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Fri, 12 Jul 2024 20:39:25 +0300 Subject: [PATCH 05/30] cap big cores at processorCount --- Runtime/LLMUnitySetup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/LLMUnitySetup.cs b/Runtime/LLMUnitySetup.cs index 0a4c7dd4..2d4df4f8 100644 --- a/Runtime/LLMUnitySetup.cs +++ b/Runtime/LLMUnitySetup.cs @@ -336,7 +336,7 @@ public static int AndroidGetNumBigCores() if (capacity >= 2 * minCapacity) numBigCores++; } - if (numBigCores == 0) numBigCores = SystemInfo.processorCount; + if (numBigCores == 0 || numBigCores > SystemInfo.processorCount) numBigCores = SystemInfo.processorCount; return numBigCores; } } From 1170b0fc3ee3722b311769c9ff3ba6bb0e1a7f23 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Wed, 17 Jul 2024 13:20:39 +0300 Subject: [PATCH 06/30] detect big cores based on max frequency --- Runtime/LLMUnitySetup.cs | 115 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/Runtime/LLMUnitySetup.cs b/Runtime/LLMUnitySetup.cs index 2d4df4f8..73d32c1f 100644 --- a/Runtime/LLMUnitySetup.cs +++ b/Runtime/LLMUnitySetup.cs @@ -295,12 +295,125 @@ public static void DownloadModel(LLM llm, int optionIndex) #endif /// \endcond + public static int GetMaxFreqKHz(int cpuId) + { + string[] paths = new string[] + { + $"/sys/devices/system/cpu/cpufreq/stats/cpu{cpuId}/time_in_state", + $"/sys/devices/system/cpu/cpu{cpuId}/cpufreq/stats/time_in_state", + $"/sys/devices/system/cpu/cpu{cpuId}/cpufreq/cpuinfo_max_freq" + }; + + foreach (var path in paths) + { + if (!File.Exists(path)) continue; + + int maxFreqKHz = 0; + using (StreamReader sr = new StreamReader(path)) + { + string line; + while ((line = sr.ReadLine()) != null) + { + string[] parts = line.Split(' '); + if (parts.Length > 0 && int.TryParse(parts[0], out int freqKHz)) + { + if (freqKHz > maxFreqKHz) + { + maxFreqKHz = freqKHz; + } + } + } + } + if (maxFreqKHz != 0) return maxFreqKHz; + } + return -1; + } + + public static bool IsSmtCpu(int cpuId) + { + string[] paths = new string[] + { + $"/sys/devices/system/cpu/cpu{cpuId}/topology/core_cpus_list", + $"/sys/devices/system/cpu/cpu{cpuId}/topology/thread_siblings_list" + }; + + foreach (var path in paths) + { + if (!File.Exists(path)) continue; + using (StreamReader sr = new StreamReader(path)) + { + string line; + while ((line = sr.ReadLine()) != null) + { + if (line.Contains(",") || line.Contains("-")) + { + return true; + } + } + } + } + return false; + } /// - /// Calculates the number of big cores in Android based on https://docs.unity3d.com/2022.3/Documentation/Manual/android-thread-configuration.html + /// Calculates the number of big cores in Android similarly to ncnn (https://github.com/Tencent/ncnn) /// /// public static int AndroidGetNumBigCores() + { + int maxFreqKHzMin = int.MaxValue; + int maxFreqKHzMax = 0; + List cpuMaxFreqKHz = new List(); + List cpuIsSmtCpu = new List(); + + try + { + string cpuPath = "/sys/devices/system/cpu/"; + int coreIndex; + if (Directory.Exists(cpuPath)) + { + foreach (string cpuDir in Directory.GetDirectories(cpuPath)) + { + string dirName = Path.GetFileName(cpuDir); + if (!dirName.StartsWith("cpu")) continue; + if (!int.TryParse(dirName.Substring(3), out coreIndex)) continue; + + int maxFreqKHz = GetMaxFreqKHz(coreIndex); + cpuMaxFreqKHz.Add(maxFreqKHz); + if (maxFreqKHz > maxFreqKHzMax) maxFreqKHzMax = maxFreqKHz; + if (maxFreqKHz < maxFreqKHzMin) maxFreqKHzMin = maxFreqKHz; + cpuIsSmtCpu.Add(IsSmtCpu(coreIndex)); + } + } + } + catch (Exception e) + { + Debug.LogError(e.Message); + } + + int numBigCores = 0; + int numCores = SystemInfo.processorCount; + int maxFreqKHzMedium = (maxFreqKHzMin + maxFreqKHzMax) / 2; + if (maxFreqKHzMedium == maxFreqKHzMax) numBigCores = numCores; + else + { + for (int i = 0; i < cpuMaxFreqKHz.Count; i++) + { + if (cpuIsSmtCpu[i] || cpuMaxFreqKHz[i] >= maxFreqKHzMedium) numBigCores++; + } + } + + if (numBigCores == 0) numBigCores = SystemInfo.processorCount / 2; + else numBigCores = Math.Min(numBigCores, SystemInfo.processorCount); + + return numBigCores; + } + + /// + /// Calculates the number of big cores in Android similarly to Unity (https://docs.unity3d.com/2022.3/Documentation/Manual/android-thread-configuration.html) + /// + /// + public static int AndroidGetNumBigCoresCapacity() { List capacities = new List(); int minCapacity = int.MaxValue; From 983df380ce42275a589fda59209614b22b0f6e29 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Wed, 17 Jul 2024 13:31:09 +0300 Subject: [PATCH 07/30] demo android --- Samples~/SimpleInteraction/Scene.unity | 8 ++++---- Samples~/SimpleInteraction/SimpleInteraction.cs | 12 +++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Samples~/SimpleInteraction/Scene.unity b/Samples~/SimpleInteraction/Scene.unity index 9e41c779..abd3bacf 100644 --- a/Samples~/SimpleInteraction/Scene.unity +++ b/Samples~/SimpleInteraction/Scene.unity @@ -221,9 +221,9 @@ MonoBehaviour: m_UiScaleMode: 1 m_ReferencePixelsPerUnit: 100 m_ScaleFactor: 1 - m_ReferenceResolution: {x: 1280, y: 720} + m_ReferenceResolution: {x: 720, y: 720} m_ScreenMatchMode: 0 - m_MatchWidthOrHeight: 0 + m_MatchWidthOrHeight: 0.5 m_PhysicalUnit: 3 m_FallbackScreenDPI: 96 m_DefaultSpriteDPI: 96 @@ -1041,7 +1041,7 @@ MonoBehaviour: port: 13333 numThreads: -1 numGPULayers: 0 - debug: 0 + debug: 1 parallelPrompts: -1 asynchronousStartup: 1 dontDestroyOnLoad: 1 @@ -1053,7 +1053,7 @@ MonoBehaviour: modelProgress: 1 modelCopyProgress: 1 modelHide: 1 - chatTemplate: chatml + chatTemplate: alpaca --- !u!4 &1047848255 Transform: m_ObjectHideFlags: 0 diff --git a/Samples~/SimpleInteraction/SimpleInteraction.cs b/Samples~/SimpleInteraction/SimpleInteraction.cs index 9c088c3b..95d22133 100644 --- a/Samples~/SimpleInteraction/SimpleInteraction.cs +++ b/Samples~/SimpleInteraction/SimpleInteraction.cs @@ -9,11 +9,21 @@ public class SimpleInteraction : MonoBehaviour public LLMCharacter llmCharacter; public InputField playerText; public Text AIText; + int cores; void Start() { playerText.onSubmit.AddListener(onInputFieldSubmit); - playerText.Select(); + playerText.interactable = false; + cores = LLMUnitySetup.AndroidGetNumBigCores(); + AIText.text = $"Warming up the model...\nWill use {cores} cores"; + _ = llmCharacter.Warmup(WarmupDone); + } + + void WarmupDone() + { + AIText.text = $"Ready when you are ({cores} cores)!"; + AIReplyComplete(); } void onInputFieldSubmit(string message) From 0b2545890e518d55ef3f19041f5f40354eee16e0 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Wed, 17 Jul 2024 13:42:39 +0300 Subject: [PATCH 08/30] move arguments to Awake --- Runtime/LLM.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Runtime/LLM.cs b/Runtime/LLM.cs index 8dc8d475..24f376e9 100644 --- a/Runtime/LLM.cs +++ b/Runtime/LLM.cs @@ -215,8 +215,10 @@ protected virtual string GetLlamaccpArguments() public async void Awake() { if (!enabled) return; - if (asynchronousStartup) await Task.Run(() => StartLLMServer()); - else StartLLMServer(); + string arguments = GetLlamaccpArguments(); + if (arguments == null) return; + if (asynchronousStartup) await Task.Run(() => StartLLMServer(arguments)); + else StartLLMServer(arguments); if (dontDestroyOnLoad) DontDestroyOnLoad(transform.root.gameObject); if (basePrompt != "") await SetBasePrompt(basePrompt); } @@ -234,12 +236,10 @@ private void StopLogging() DestroyStreamWrapper(logStreamWrapper); } - private void StartLLMServer() + private void StartLLMServer(string arguments) { started = false; failed = false; - string arguments = GetLlamaccpArguments(); - if (arguments == null) return; bool useGPU = numGPULayers > 0; LLMUnitySetup.Log($"Server command: {arguments}"); From afa86b26fcd07cc2f92137835b72ba897690ab71 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Wed, 17 Jul 2024 13:50:22 +0300 Subject: [PATCH 09/30] demo android --- Samples~/SimpleInteraction/Scene.unity | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Samples~/SimpleInteraction/Scene.unity b/Samples~/SimpleInteraction/Scene.unity index abd3bacf..ab87b112 100644 --- a/Samples~/SimpleInteraction/Scene.unity +++ b/Samples~/SimpleInteraction/Scene.unity @@ -1045,15 +1045,16 @@ MonoBehaviour: parallelPrompts: -1 asynchronousStartup: 1 dontDestroyOnLoad: 1 - model: + model: smol_llama-220m-openhermes.q4_k_m.gguf lora: contextSize: 0 batchSize: 512 + basePrompt: SelectedModel: 0 modelProgress: 1 modelCopyProgress: 1 modelHide: 1 - chatTemplate: alpaca + chatTemplate: chatml --- !u!4 &1047848255 Transform: m_ObjectHideFlags: 0 From 4e6d39c8cbf3b3c3dbba957a1e42f3fc3679ea36 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Wed, 17 Jul 2024 23:52:26 +0300 Subject: [PATCH 10/30] android demo sample --- Samples~/AndroidDemo.meta | 8 + Samples~/AndroidDemo/AndroidDemo.cs | 106 + Samples~/AndroidDemo/AndroidDemo.cs.meta | 11 + Samples~/AndroidDemo/Scene.unity | 2261 ++++++++++++++++++++++ Samples~/AndroidDemo/Scene.unity.meta | 7 + 5 files changed, 2393 insertions(+) create mode 100644 Samples~/AndroidDemo.meta create mode 100644 Samples~/AndroidDemo/AndroidDemo.cs create mode 100644 Samples~/AndroidDemo/AndroidDemo.cs.meta create mode 100644 Samples~/AndroidDemo/Scene.unity create mode 100644 Samples~/AndroidDemo/Scene.unity.meta diff --git a/Samples~/AndroidDemo.meta b/Samples~/AndroidDemo.meta new file mode 100644 index 00000000..9b3afc06 --- /dev/null +++ b/Samples~/AndroidDemo.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4259a324b4c9af10a95ced582b171297 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/AndroidDemo/AndroidDemo.cs b/Samples~/AndroidDemo/AndroidDemo.cs new file mode 100644 index 00000000..7424a145 --- /dev/null +++ b/Samples~/AndroidDemo/AndroidDemo.cs @@ -0,0 +1,106 @@ +using UnityEngine; +using LLMUnity; +using UnityEngine.UI; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace LLMUnitySamples +{ + public class AndroidDemo : MonoBehaviour + { + public LLM llm; + public LLMCharacter llmCharacter; + + public GameObject ChatPanel; + public InputField playerText; + public Text AIText; + + public GameObject DownloadPanel; + public Scrollbar progressBar; + public Text progressText; + int cores; + + void Awake() + { + ChatPanel.SetActive(false); + DownloadPanel.SetActive(false); + } + + void Start() + { + playerText.onSubmit.AddListener(onInputFieldSubmit); + playerText.interactable = false; + StartCoroutine(Loading()); + } + + IEnumerator Loading() + { + DownloadPanel.SetActive(true); + AIText.text = "Downloading model..."; + Task downloadTask = llm.DownloadModel( + "https://huggingface.co/afrideva/smol_llama-220M-openhermes-GGUF/resolve/main/smol_llama-220m-openhermes.q4_k_m.gguf?download=true", + null, + SetProgress, + true + ); + while (!downloadTask.IsCompleted) yield return null; + DownloadPanel.SetActive(false); + + ChatPanel.SetActive(true); + cores = LLMUnitySetup.AndroidGetNumBigCores(); + AIText.text += $"\nWarming up the model...\nWill use {cores} cores"; + Task warmup = llmCharacter.Warmup(); + while (!warmup.IsCompleted) yield return null; + + AIText.text = $"Ready when you are ({cores} cores)!"; + AIReplyComplete(); + } + + void SetProgress(float progress) + { + progressText.text = ((int)(progress * 100)).ToString() + "%"; + progressBar.size = progress; + } + + void onInputFieldSubmit(string message) + { + playerText.interactable = false; + AIText.text = "..."; + _ = llmCharacter.Chat(message, SetAIText, AIReplyComplete); + } + + public void SetAIText(string text) + { + AIText.text = text; + } + + public void AIReplyComplete() + { + playerText.interactable = true; + playerText.Select(); + playerText.text = ""; + } + + public void CancelRequests() + { + llmCharacter.CancelRequests(); + AIReplyComplete(); + } + + public void ExitGame() + { + Debug.Log("Exit button clicked"); + Application.Quit(); + } + + bool onValidateWarning = true; + void OnValidate() + { + if (onValidateWarning && !llmCharacter.remote && llmCharacter.llm != null && llmCharacter.llm.model == "") + { + Debug.LogWarning($"Please select a model in the {llmCharacter.llm.gameObject.name} GameObject!"); + onValidateWarning = false; + } + } + } +} diff --git a/Samples~/AndroidDemo/AndroidDemo.cs.meta b/Samples~/AndroidDemo/AndroidDemo.cs.meta new file mode 100644 index 00000000..3409ae47 --- /dev/null +++ b/Samples~/AndroidDemo/AndroidDemo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 708542fec6999ea3ebd7c69404932bb3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/AndroidDemo/Scene.unity b/Samples~/AndroidDemo/Scene.unity new file mode 100644 index 00000000..9e404a5d --- /dev/null +++ b/Samples~/AndroidDemo/Scene.unity @@ -0,0 +1,2261 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657832, g: 0.49641222, b: 0.57481664, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &107963744 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 107963746} + - component: {fileID: 107963747} + m_Layer: 0 + m_Name: AndroidDemo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &107963746 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 107963744} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &107963747 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 107963744} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 708542fec6999ea3ebd7c69404932bb3, type: 3} + m_Name: + m_EditorClassIdentifier: + llm: {fileID: 1047848254} + llmCharacter: {fileID: 498662973} + ChatPanel: {fileID: 1084608230} + playerText: {fileID: 1966107897} + AIText: {fileID: 887085510} + DownloadPanel: {fileID: 332743750} + progressBar: {fileID: 332743752} + progressText: {fileID: 381203299} +--- !u!1 &158550913 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 158550917} + - component: {fileID: 158550916} + - component: {fileID: 158550915} + - component: {fileID: 158550914} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &158550914 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 158550913} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &158550915 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 158550913} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 720, y: 720} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0.5 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &158550916 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 158550913} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_VertexColorAlwaysGammaSpace: 0 + m_AdditionalShaderChannelsFlag: 0 + m_UpdateRectTransformForStandalone: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &158550917 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 158550913} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 268891590} + - {fileID: 332743751} + - {fileID: 1084608231} + - {fileID: 1308766010} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &268891589 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 268891590} + - component: {fileID: 268891592} + - component: {fileID: 268891591} + m_Layer: 5 + m_Name: Panel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &268891590 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 268891589} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1.01, y: 1.01, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 158550917} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &268891591 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 268891589} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &268891592 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 268891589} + m_CullTransparentMesh: 1 +--- !u!1 &332743750 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 332743751} + - component: {fileID: 332743754} + - component: {fileID: 332743753} + - component: {fileID: 332743752} + m_Layer: 5 + m_Name: DownloadPanel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &332743751 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 332743750} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1688602497} + - {fileID: 1705264488} + - {fileID: 381203298} + m_Father: {fileID: 158550917} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: -34.8} + m_SizeDelta: {x: 160, y: 18} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &332743752 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 332743750} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 0.31764707, g: 0.6431373, b: 0.31764707, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 659217392} + m_HandleRect: {fileID: 659217391} + m_Direction: 0 + m_Value: 0 + m_Size: 0 + m_NumberOfSteps: 0 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &332743753 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 332743750} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.5207577, g: 0.5207577, b: 0.5207577, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &332743754 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 332743750} + m_CullTransparentMesh: 1 +--- !u!1 &381203297 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 381203298} + - component: {fileID: 381203300} + - component: {fileID: 381203299} + m_Layer: 5 + m_Name: Progress + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &381203298 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 381203297} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 332743751} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 5, y: 0} + m_SizeDelta: {x: 79.6491, y: 19.2105} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &381203299 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 381203297} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 1 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 0% +--- !u!222 &381203300 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 381203297} + m_CullTransparentMesh: 1 +--- !u!1 &433287079 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 433287080} + - component: {fileID: 433287082} + - component: {fileID: 433287081} + m_Layer: 5 + m_Name: Text (Legacy) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &433287080 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 433287079} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 724531320} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &433287081 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 433287079} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 16 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 'Stop + +' +--- !u!222 &433287082 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 433287079} + m_CullTransparentMesh: 1 +--- !u!1 &498662970 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 498662972} + - component: {fileID: 498662973} + m_Layer: 0 + m_Name: LLMCharacter + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &498662972 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 498662970} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1024.2354, y: 495.014, z: -3.4752133} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &498662973 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 498662970} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3f6c87a428fd5d0be9bbc686bdc8c3c2, type: 3} + m_Name: + m_EditorClassIdentifier: + advancedOptions: 0 + remote: 0 + llm: {fileID: 1047848254} + host: localhost + port: 13333 + save: + saveCache: 0 + debugPrompt: 0 + stream: 1 + grammar: + cachePrompt: 1 + seed: 0 + numPredict: 256 + temperature: 0.2 + topK: 40 + topP: 0.9 + minP: 0.05 + repeatPenalty: 1.1 + presencePenalty: 0 + frequencyPenalty: 0 + tfsZ: 1 + typicalP: 1 + repeatLastN: 64 + penalizeNl: 1 + penaltyPrompt: + mirostat: 0 + mirostatTau: 5 + mirostatEta: 0.1 + nProbs: 0 + ignoreEos: 0 + nKeep: -1 + stop: [] + playerName: user + AIName: assistant + prompt: A chat between a curious human and an artificial intelligence assistant. + The assistant gives helpful, detailed, and polite answers to the human's questions. + setNKeepToPrompt: 1 + chat: [] + grammarString: +--- !u!1 &659217390 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 659217391} + - component: {fileID: 659217393} + - component: {fileID: 659217392} + m_Layer: 5 + m_Name: Handle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &659217391 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 659217390} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1705264488} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &659217392 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 659217390} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &659217393 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 659217390} + m_CullTransparentMesh: 1 +--- !u!1 &724531319 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 724531320} + - component: {fileID: 724531323} + - component: {fileID: 724531322} + - component: {fileID: 724531321} + m_Layer: 5 + m_Name: StopButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &724531320 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 724531319} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 433287080} + m_Father: {fileID: 1084608231} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 10.624025, y: -80.006996} + m_SizeDelta: {x: 107.0426, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &724531321 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 724531319} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 724531322} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 0} + m_TargetAssemblyTypeName: SimpleInteraction, Assembly-CSharp + m_MethodName: CancelRequests + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!114 &724531322 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 724531319} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.31764707, g: 0.6431373, b: 0.31764707, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &724531323 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 724531319} + m_CullTransparentMesh: 1 +--- !u!1 &726528676 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 726528679} + - component: {fileID: 726528678} + - component: {fileID: 726528677} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &726528677 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 726528676} + m_Enabled: 1 +--- !u!20 &726528678 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 726528676} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &726528679 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 726528676} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &856480601 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 856480602} + - component: {fileID: 856480604} + - component: {fileID: 856480603} + m_Layer: 5 + m_Name: Player title + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &856480602 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 856480601} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1084608231} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 10.624025, y: 62.99302} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &856480603 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 856480601} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 1 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Player +--- !u!222 &856480604 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 856480601} + m_CullTransparentMesh: 1 +--- !u!1 &887085508 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 887085509} + - component: {fileID: 887085511} + - component: {fileID: 887085510} + - component: {fileID: 887085512} + m_Layer: 5 + m_Name: AIText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &887085509 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 887085508} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2091685447} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: -20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &887085510 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 887085508} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 16 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: +--- !u!222 &887085511 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 887085508} + m_CullTransparentMesh: 1 +--- !u!210 &887085512 +SortingGroup: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 887085508} + m_Enabled: 1 + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 1 + m_SortAtRoot: 0 +--- !u!1 &909474451 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 909474453} + - component: {fileID: 909474452} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &909474452 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 909474451} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &909474453 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 909474451} + serializedVersion: 2 + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &1047848253 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1047848255} + - component: {fileID: 1047848254} + m_Layer: 0 + m_Name: LLM + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1047848254 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1047848253} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a50e3140c3ecaaf1c848dbf141cc2074, type: 3} + m_Name: + m_EditorClassIdentifier: + advancedOptions: 0 + remote: 0 + port: 13333 + numThreads: -1 + numGPULayers: 0 + debug: 1 + parallelPrompts: -1 + asynchronousStartup: 1 + dontDestroyOnLoad: 1 + model: smol_llama-220m-openhermes.q4_k_m.gguf + lora: + contextSize: 0 + batchSize: 512 + basePrompt: + SelectedModel: 0 + modelProgress: 1 + modelCopyProgress: 1 + modelHide: 1 + chatTemplate: chatml +--- !u!4 &1047848255 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1047848253} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1059033619 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1059033620} + - component: {fileID: 1059033622} + - component: {fileID: 1059033621} + m_Layer: 5 + m_Name: Placeholder + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1059033620 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1059033619} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1966107896} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -0.5} + m_SizeDelta: {x: -20, y: -13} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1059033621 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1059033619} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 16 + m_FontStyle: 2 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Enter text... +--- !u!222 &1059033622 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1059033619} + m_CullTransparentMesh: 1 +--- !u!1 &1084608230 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1084608231} + m_Layer: 5 + m_Name: ChatPanel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1084608231 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1084608230} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1966107896} + - {fileID: 2091685447} + - {fileID: 856480602} + - {fileID: 1342801405} + - {fileID: 724531320} + m_Father: {fileID: 158550917} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 87.00699} + m_SizeDelta: {x: 400, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &1171567417 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1171567418} + - component: {fileID: 1171567420} + - component: {fileID: 1171567419} + m_Layer: 5 + m_Name: Text (Legacy) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1171567418 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1171567417} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1308766010} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 2} + m_SizeDelta: {x: 25, y: 25} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1171567419 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1171567417} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: "\xD7" +--- !u!222 &1171567420 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1171567417} + m_CullTransparentMesh: 1 +--- !u!1 &1308766009 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1308766010} + - component: {fileID: 1308766013} + - component: {fileID: 1308766012} + - component: {fileID: 1308766011} + m_Layer: 5 + m_Name: ExitButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1308766010 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1308766009} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1171567418} + m_Father: {fileID: 158550917} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 25, y: 25} + m_Pivot: {x: 1, y: 1} +--- !u!114 &1308766011 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1308766009} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1308766012} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 0} + m_TargetAssemblyTypeName: SimpleInteraction, Assembly-CSharp + m_MethodName: ExitGame + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 1 +--- !u!114 &1308766012 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1308766009} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.24722636, g: 0.24722636, b: 0.24722636, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10913, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1308766013 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1308766009} + m_CullTransparentMesh: 1 +--- !u!1 &1342801404 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1342801405} + - component: {fileID: 1342801407} + - component: {fileID: 1342801406} + m_Layer: 5 + m_Name: AI title + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1342801405 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1342801404} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1084608231} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 5.6240253, y: -133.00699} + m_SizeDelta: {x: 160, y: 29.6652} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1342801406 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1342801404} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 1 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: AI +--- !u!222 &1342801407 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1342801404} + m_CullTransparentMesh: 1 +--- !u!1 &1609985808 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1609985809} + - component: {fileID: 1609985811} + - component: {fileID: 1609985810} + m_Layer: 5 + m_Name: Text (Legacy) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1609985809 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1609985808} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1966107896} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -0.5} + m_SizeDelta: {x: -20, y: -13} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1609985810 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1609985808} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 16 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 0 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: +--- !u!222 &1609985811 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1609985808} + m_CullTransparentMesh: 1 +--- !u!1 &1688602496 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1688602497} + - component: {fileID: 1688602499} + - component: {fileID: 1688602498} + m_Layer: 5 + m_Name: Title + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1688602497 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1688602496} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 332743751} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0.000025749, y: 15.999993} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1688602498 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1688602496} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 16 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 1 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Downloading model... +--- !u!222 &1688602499 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1688602496} + m_CullTransparentMesh: 1 +--- !u!1 &1705264487 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1705264488} + m_Layer: 5 + m_Name: Sliding Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1705264488 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1705264487} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 659217391} + m_Father: {fileID: 332743751} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -0.0000038146973} + m_SizeDelta: {x: 0, y: -20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &1966107895 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1966107896} + - component: {fileID: 1966107899} + - component: {fileID: 1966107898} + - component: {fileID: 1966107897} + m_Layer: 5 + m_Name: PlayerInput + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1966107896 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1966107895} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1059033620} + - {fileID: 1609985809} + m_Father: {fileID: 1084608231} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: -0.000022888184} + m_SizeDelta: {x: 400, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1966107897 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1966107895} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d199490a83bb2b844b9695cbf13b01ef, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1966107898} + m_TextComponent: {fileID: 1609985810} + m_Placeholder: {fileID: 1059033621} + m_ContentType: 0 + m_InputType: 0 + m_AsteriskChar: 42 + m_KeyboardType: 0 + m_LineType: 1 + m_HideMobileInput: 0 + m_CharacterValidation: 0 + m_CharacterLimit: 0 + m_OnSubmit: + m_PersistentCalls: + m_Calls: [] + m_OnDidEndEdit: + m_PersistentCalls: + m_Calls: [] + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] + m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_CustomCaretColor: 0 + m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412} + m_Text: + m_CaretBlinkRate: 0.85 + m_CaretWidth: 1 + m_ReadOnly: 0 + m_ShouldActivateOnSelect: 1 +--- !u!114 &1966107898 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1966107895} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.31764707, g: 0.6431373, b: 0.31764707, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10911, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1966107899 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1966107895} + m_CullTransparentMesh: 1 +--- !u!1 &2015159264 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2015159267} + - component: {fileID: 2015159266} + - component: {fileID: 2015159265} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &2015159265 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2015159264} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &2015159266 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2015159264} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &2015159267 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2015159264} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2091685446 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2091685447} + - component: {fileID: 2091685449} + - component: {fileID: 2091685448} + m_Layer: 5 + m_Name: AIImage + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2091685447 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2091685446} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 887085509} + m_Father: {fileID: 1084608231} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: -197.7609} + m_SizeDelta: {x: 400, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2091685448 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2091685446} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.53662825, g: 0.53662825, b: 0.9029823, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2091685449 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2091685446} + m_CullTransparentMesh: 1 +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 726528679} + - {fileID: 909474453} + - {fileID: 158550917} + - {fileID: 2015159267} + - {fileID: 1047848255} + - {fileID: 498662972} + - {fileID: 107963746} diff --git a/Samples~/AndroidDemo/Scene.unity.meta b/Samples~/AndroidDemo/Scene.unity.meta new file mode 100644 index 00000000..124260ce --- /dev/null +++ b/Samples~/AndroidDemo/Scene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 15abb96f71f7fc08db7606aa88332b00 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From aab7b11747aa0ec992d7b48feb242773d846dae0 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Thu, 18 Jul 2024 18:27:25 +0300 Subject: [PATCH 11/30] downloader with resume capabilities --- Runtime/ResumingWebClient.cs | 144 ++++++++++++++++++++++++++++++ Runtime/ResumingWebClient.cs.meta | 11 +++ 2 files changed, 155 insertions(+) create mode 100644 Runtime/ResumingWebClient.cs create mode 100644 Runtime/ResumingWebClient.cs.meta diff --git a/Runtime/ResumingWebClient.cs b/Runtime/ResumingWebClient.cs new file mode 100644 index 00000000..cd21f0af --- /dev/null +++ b/Runtime/ResumingWebClient.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace LLMUnity +{ + public class ResumingWebClient : WebClient + { + private const int timeoutMs = 30 * 1000; + private SynchronizationContext _context; + private const int DefaultDownloadBufferLength = 65536; + List requests = new List(); + + public ResumingWebClient() + { + _context = SynchronizationContext.Current ?? new SynchronizationContext(); + } + + public Task DownloadFileTaskAsyncResume(Uri address, string fileName, bool resume = false, Callback progressCallback = null) + { + var tcs = new TaskCompletionSource(address); + FileStream? fs = null; + long bytesToSkip = 0; + + try + { + FileMode filemode = FileMode.Create; + if (resume) + { + var fileInfo = new FileInfo(fileName); + if (fileInfo.Exists) bytesToSkip = fileInfo.Length; + } + + WebRequest request = GetWebRequest(address); + if (request is HttpWebRequest webRequest && bytesToSkip > 0) + { + filemode = FileMode.Append; + LLMUnitySetup.Log($"File exists at {fileName}, skipping {bytesToSkip} bytes"); + webRequest.AddRange(bytesToSkip); + webRequest.ReadWriteTimeout = timeoutMs; + } + + fs = new FileStream(fileName, filemode, FileAccess.Write); + DownloadBitsAsync(request, fs, bytesToSkip, progressCallback, tcs); + } + catch (Exception e) + { + fs?.Close(); + tcs.TrySetException(e); + } + + return tcs.Task; + } + + public void CancelDownloadAsync() + { + LLMUnitySetup.Log("Cancellation requested, aborting download."); + foreach (WebRequest request in requests) AbortRequest(request); + requests.Clear(); + } + + public void AbortRequest(WebRequest request) + { + try + { + request?.Abort(); + } + catch (Exception e) + { + LLMUnitySetup.LogError($"Error aborting request: {e.Message}"); + } + } + + private async void DownloadBitsAsync(WebRequest request, Stream writeStream, long bytesToSkip = 0, Callback progressCallback = null, TaskCompletionSource tcs = null) + { + try + { + requests.Add(request); + WebResponse response = await request.GetResponseAsync().ConfigureAwait(false); + + long contentLength = response.ContentLength; + byte[] copyBuffer = new byte[contentLength == -1 || contentLength > DefaultDownloadBufferLength ? DefaultDownloadBufferLength : contentLength]; + + long TotalBytesToReceive = Math.Max(contentLength, 0) + bytesToSkip; + long BytesReceived = bytesToSkip; + + using (writeStream) + using (Stream readStream = response.GetResponseStream()) + { + if (readStream != null) + { + while (true) + { + int bytesRead = await readStream.ReadAsync(new Memory(copyBuffer)).ConfigureAwait(false); + if (bytesRead == 0) + { + break; + } + + BytesReceived += bytesRead; + if (BytesReceived != TotalBytesToReceive) + { + PostProgressChanged(progressCallback, BytesReceived, TotalBytesToReceive); + } + + await writeStream.WriteAsync(new ReadOnlyMemory(copyBuffer, 0, bytesRead)).ConfigureAwait(false); + } + } + + if (TotalBytesToReceive < 0) + { + TotalBytesToReceive = BytesReceived; + } + PostProgressChanged(progressCallback, BytesReceived, TotalBytesToReceive); + } + tcs.TrySetResult(true); + } + catch (Exception e) + { + tcs.TrySetException(e); + LLMUnitySetup.LogError(e.Message); + AbortRequest(request); + tcs.TrySetResult(false); + } + finally + { + writeStream?.Close(); + requests.Remove(request); + } + } + + private void PostProgressChanged(Callback progressCallback, long BytesReceived, long TotalBytesToReceive) + { + if (progressCallback != null && BytesReceived > 0) + { + float progressPercentage = TotalBytesToReceive < 0 ? 0 : TotalBytesToReceive == 0 ? 1 : (float)BytesReceived / TotalBytesToReceive; + _context.Post(_ => progressCallback?.Invoke(progressPercentage), null); + } + } + } +} diff --git a/Runtime/ResumingWebClient.cs.meta b/Runtime/ResumingWebClient.cs.meta new file mode 100644 index 00000000..369fc67e --- /dev/null +++ b/Runtime/ResumingWebClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 00e47c7cca64b8c57ba4b7b6b2c2c5b8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From aed6af39089d6292483c695dbf1dc477051732ce Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Thu, 18 Jul 2024 18:28:26 +0300 Subject: [PATCH 12/30] use resume downloader in LLMUnitySetup --- Runtime/LLMUnitySetup.cs | 58 +++++++++++++++------------------------- 1 file changed, 21 insertions(+), 37 deletions(-) diff --git a/Runtime/LLMUnitySetup.cs b/Runtime/LLMUnitySetup.cs index 73d32c1f..b88ca024 100644 --- a/Runtime/LLMUnitySetup.cs +++ b/Runtime/LLMUnitySetup.cs @@ -4,15 +4,9 @@ using System.IO; using UnityEngine; using System.Threading.Tasks; -using System.Net; using System; using System.IO.Compression; using System.Collections.Generic; -<<<<<<< HEAD -======= -using UnityEngine.Networking; -using System.Collections.Generic; ->>>>>>> d9fdc86 (function to determine the number of big cores in Android) /// @defgroup llm LLM /// @defgroup template Chat Templates @@ -165,71 +159,61 @@ static async Task InitializeOnLoad() await DownloadLibrary(); LoadDebugMode(); } + #else [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] void InitializeOnLoad() { LoadDebugMode(); } + #endif -#if UNITY_EDITOR - [HideInInspector] public static float libraryProgress = 1; + static Dictionary downloadClients = new Dictionary(); - public class DownloadStatus + public static void CancelDownload(string savePath) { - Callback progresscallback; - - public DownloadStatus(Callback progresscallback = null) - { - this.progresscallback = progresscallback; - } - - public void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) - { - progresscallback?.Invoke(e.ProgressPercentage / 100.0f); - } + if (!downloadClients.ContainsKey(savePath)) return; + downloadClients[savePath].CancelDownloadAsync(); + downloadClients.Remove(savePath); } public static async Task DownloadFile( string fileUrl, string savePath, bool overwrite = false, - TaskCallback callback = null, Callback progresscallback = null, - bool async = true + TaskCallback callback = null, Callback progressCallback = null ) { - // download a file to the specified path if (File.Exists(savePath) && !overwrite) { Log($"File already exists at: {savePath}"); } else { - Log($"Downloading {fileUrl}..."); + Log($"Downloading {fileUrl} to {savePath}..."); string tmpPath = Path.Combine(Application.temporaryCachePath, Path.GetFileName(savePath)); - WebClient client = new WebClient(); - DownloadStatus downloadStatus = new DownloadStatus(progresscallback); - client.DownloadProgressChanged += downloadStatus.DownloadProgressChanged; - if (async) - { - await client.DownloadFileTaskAsync(fileUrl, tmpPath); - } - else - { - client.DownloadFile(fileUrl, tmpPath); - } - + ResumingWebClient client = new ResumingWebClient(); + downloadClients[savePath] = client; + await client.DownloadFileTaskAsyncResume(new Uri(fileUrl), tmpPath, !overwrite, progressCallback); + downloadClients.Remove(savePath); +#if UNITY_EDITOR AssetDatabase.StartAssetEditing(); +#endif Directory.CreateDirectory(Path.GetDirectoryName(savePath)); File.Move(tmpPath, savePath); +#if UNITY_EDITOR AssetDatabase.StopAssetEditing(); +#endif Log($"Download complete!"); } - progresscallback?.Invoke(1f); + progressCallback?.Invoke(1f); if (callback != null) await callback.Invoke(savePath); } +#if UNITY_EDITOR + [HideInInspector] public static float libraryProgress = 1; + public static async Task AddAsset(string assetPath, string basePath) { if (!File.Exists(assetPath)) From 665f903423f5ebb3b8bc9d93d5d7972a68e6f803 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Thu, 18 Jul 2024 18:36:01 +0300 Subject: [PATCH 13/30] adapt tests to download changes --- Tests/Runtime/TestLLM.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Runtime/TestLLM.cs b/Tests/Runtime/TestLLM.cs index 60947fcc..cbb05923 100644 --- a/Tests/Runtime/TestLLM.cs +++ b/Tests/Runtime/TestLLM.cs @@ -32,7 +32,7 @@ public async Task Init() string modelUrl = "https://huggingface.co/afrideva/smol_llama-220M-openhermes-GGUF/resolve/main/smol_llama-220m-openhermes.q4_k_m.gguf?download=true"; string modelPath = "LLMUnityTests/smol_llama-220m-openhermes.q4_k_m.gguf"; string fullModelPath = LLMUnitySetup.GetAssetPath(modelPath); - _ = LLMUnitySetup.DownloadFile(modelUrl, fullModelPath, false, null, null, false); + await LLMUnitySetup.DownloadFile(modelUrl, fullModelPath, false, null, null); await llm.SetModel(fullModelPath); llm.parallelPrompts = 1; llm.SetTemplate("alpaca"); From a003f3ec8e03aff1403c11cce3a42a1da7a74dc9 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Fri, 19 Jul 2024 11:26:03 +0300 Subject: [PATCH 14/30] use alpaca template, larger x, connect buttons --- Samples~/AndroidDemo/Scene.unity | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Samples~/AndroidDemo/Scene.unity b/Samples~/AndroidDemo/Scene.unity index 9e404a5d..da2763c6 100644 --- a/Samples~/AndroidDemo/Scene.unity +++ b/Samples~/AndroidDemo/Scene.unity @@ -884,8 +884,8 @@ MonoBehaviour: m_OnClick: m_PersistentCalls: m_Calls: - - m_Target: {fileID: 0} - m_TargetAssemblyTypeName: SimpleInteraction, Assembly-CSharp + - m_Target: {fileID: 107963747} + m_TargetAssemblyTypeName: LLMUnitySamples.AndroidDemo, Assembly-CSharp m_MethodName: CancelRequests m_Mode: 1 m_Arguments: @@ -895,7 +895,7 @@ MonoBehaviour: m_FloatArgument: 0 m_StringArgument: m_BoolArgument: 0 - m_CallState: 2 + m_CallState: 0 --- !u!114 &724531322 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1532,10 +1532,10 @@ MonoBehaviour: m_Calls: [] m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} - m_FontSize: 14 + m_FontSize: 20 m_FontStyle: 0 m_BestFit: 0 - m_MinSize: 10 + m_MinSize: 2 m_MaxSize: 40 m_Alignment: 4 m_AlignByGeometry: 0 @@ -1589,7 +1589,7 @@ RectTransform: m_AnchorMin: {x: 1, y: 1} m_AnchorMax: {x: 1, y: 1} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 25, y: 25} + m_SizeDelta: {x: 35, y: 35} m_Pivot: {x: 1, y: 1} --- !u!114 &1308766011 MonoBehaviour: @@ -1635,8 +1635,8 @@ MonoBehaviour: m_OnClick: m_PersistentCalls: m_Calls: - - m_Target: {fileID: 0} - m_TargetAssemblyTypeName: SimpleInteraction, Assembly-CSharp + - m_Target: {fileID: 107963747} + m_TargetAssemblyTypeName: LLMUnitySamples.AndroidDemo, Assembly-CSharp m_MethodName: ExitGame m_Mode: 1 m_Arguments: From 7d053297a220df22592477c5e0b701da00da1b20 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Fri, 19 Jul 2024 11:37:26 +0300 Subject: [PATCH 15/30] move model download to LLM --- Runtime/LLM.cs | 29 +++++++++++++++++++++++++++++ Runtime/LLMUnitySetup.cs | 12 ------------ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/Runtime/LLM.cs b/Runtime/LLM.cs index 24f376e9..e2e8088e 100644 --- a/Runtime/LLM.cs +++ b/Runtime/LLM.cs @@ -122,6 +122,35 @@ async Task CopyAsset(string path) return path; } + public async Task DownloadDefaultModel(int optionIndex) + { + // download default model and disable model editor properties until the model is set + SelectedModel = optionIndex; + string modelUrl = LLMUnitySetup.modelOptions[optionIndex].Item2; + if (modelUrl == null) return; + string modelName = Path.GetFileName(modelUrl).Split("?")[0]; + await DownloadModel(modelUrl, modelName); + } + + public async Task DownloadModel(string modelUrl, string modelName = null, Callback progressCallback = null, bool overwrite = false) + { + modelProgress = 0; + if (modelName == null) modelName = model; + string modelPath = LLMUnitySetup.GetAssetPath(modelName); + + Callback callback = (floatArg) => + { + progressCallback?.Invoke(floatArg); + SetModelProgress(floatArg); + }; + await LLMUnitySetup.DownloadFile(modelUrl, modelPath, overwrite, SetModel, callback); + } + + public async Task DownloadModel(string modelUrl, Callback progressCallback = null, bool overwrite = false) + { + await DownloadModel(modelUrl, null, progressCallback, overwrite); + } + /// /// Allows to set the model used by the LLM. /// The model provided is copied to the Assets/StreamingAssets folder that allows it to also work in the build. diff --git a/Runtime/LLMUnitySetup.cs b/Runtime/LLMUnitySetup.cs index b88ca024..5b59e1f2 100644 --- a/Runtime/LLMUnitySetup.cs +++ b/Runtime/LLMUnitySetup.cs @@ -265,18 +265,6 @@ private static void SetLibraryProgress(float progress) libraryProgress = progress; } - public static void DownloadModel(LLM llm, int optionIndex) - { - // download default model and disable model editor properties until the model is set - llm.SelectedModel = optionIndex; - string modelUrl = modelOptions[optionIndex].Item2; - if (modelUrl == null) return; - llm.modelProgress = 0; - string modelName = Path.GetFileName(modelUrl).Split("?")[0]; - string modelPath = GetAssetPath(modelName); - Task downloadTask = DownloadFile(modelUrl, modelPath, false, llm.SetModel, llm.SetModelProgress); - } - #endif /// \endcond public static int GetMaxFreqKHz(int cpuId) From e1bbb0474385934f3cb72637366ed40d22d76a5f Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Fri, 19 Jul 2024 11:37:53 +0300 Subject: [PATCH 16/30] use LLM download --- Editor/LLMEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Editor/LLMEditor.cs b/Editor/LLMEditor.cs index 39e30dc9..ace849ce 100644 --- a/Editor/LLMEditor.cs +++ b/Editor/LLMEditor.cs @@ -35,7 +35,7 @@ public void AddModelLoaders(SerializedObject llmScriptSO, LLM llmScript) int newIndex = EditorGUILayout.Popup("Model", llmScript.SelectedModel, options); if (newIndex != llmScript.SelectedModel) { - LLMUnitySetup.DownloadModel(llmScript, newIndex); + llmScript.DownloadDefaultModel(newIndex); } if (GUILayout.Button("Load model", GUILayout.Width(buttonWidth))) From 450aedb321ae058276be974b84f99a63fe1c3ab9 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Fri, 19 Jul 2024 11:40:36 +0300 Subject: [PATCH 17/30] set template, use short args for download --- Samples~/AndroidDemo/AndroidDemo.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Samples~/AndroidDemo/AndroidDemo.cs b/Samples~/AndroidDemo/AndroidDemo.cs index 7424a145..677c1db5 100644 --- a/Samples~/AndroidDemo/AndroidDemo.cs +++ b/Samples~/AndroidDemo/AndroidDemo.cs @@ -39,11 +39,10 @@ IEnumerator Loading() AIText.text = "Downloading model..."; Task downloadTask = llm.DownloadModel( "https://huggingface.co/afrideva/smol_llama-220M-openhermes-GGUF/resolve/main/smol_llama-220m-openhermes.q4_k_m.gguf?download=true", - null, - SetProgress, - true + SetProgress ); while (!downloadTask.IsCompleted) yield return null; + llm.SetTemplate("alpaca"); DownloadPanel.SetActive(false); ChatPanel.SetActive(true); From ee9d050bc51bd2d440e1d349c05c319c53b2ccc2 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Sat, 20 Jul 2024 15:01:07 +0300 Subject: [PATCH 18/30] check if all bytes are downloaded before starting --- Runtime/ResumingWebClient.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Runtime/ResumingWebClient.cs b/Runtime/ResumingWebClient.cs index cd21f0af..b6e8d4d5 100644 --- a/Runtime/ResumingWebClient.cs +++ b/Runtime/ResumingWebClient.cs @@ -19,6 +19,14 @@ public ResumingWebClient() _context = SynchronizationContext.Current ?? new SynchronizationContext(); } + private long GetRemoteFileSizeAsync(Uri address) + { + WebRequest request = GetWebRequest(address); + request.Method = "HEAD"; + WebResponse response = request.GetResponse(); + return response.ContentLength; + } + public Task DownloadFileTaskAsyncResume(Uri address, string fileName, bool resume = false, Callback progressCallback = null) { var tcs = new TaskCompletionSource(address); @@ -37,6 +45,14 @@ public Task DownloadFileTaskAsyncResume(Uri address, string fileName, bool resum WebRequest request = GetWebRequest(address); if (request is HttpWebRequest webRequest && bytesToSkip > 0) { + long remoteFileSize = GetRemoteFileSizeAsync(address); + if (bytesToSkip >= remoteFileSize) + { + LLMUnitySetup.Log($"File is already fully downloaded: {fileName}"); + tcs.TrySetResult(true); + return tcs.Task; + } + filemode = FileMode.Append; LLMUnitySetup.Log($"File exists at {fileName}, skipping {bytesToSkip} bytes"); webRequest.AddRange(bytesToSkip); From d5bd618bfe798206fae087fbac7e70aa398a143f Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Sat, 20 Jul 2024 15:04:50 +0300 Subject: [PATCH 19/30] download on start functionality --- Runtime/LLM.cs | 46 ++++++++++++++++++++++++++++++++++------ Runtime/LLMUnitySetup.cs | 4 +++- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/Runtime/LLM.cs b/Runtime/LLM.cs index e2e8088e..31ada6c9 100644 --- a/Runtime/LLM.cs +++ b/Runtime/LLM.cs @@ -71,6 +71,11 @@ public class LLM : MonoBehaviour /// the path of the model being used (relative to the Assets/StreamingAssets folder). /// Models with .gguf format are allowed. [Model] public string model = ""; + /// toggle to enable model download on build + [Model] public bool downloadOnBuild = false; + /// the URL of the model to use. + /// Models with .gguf format are allowed. + [ModelDownload] public string modelURL = ""; /// the path of the LORA model being used (relative to the Assets/StreamingAssets folder). /// Models with .bin format are allowed. [ModelAdvanced] public string lora = ""; @@ -81,7 +86,8 @@ public class LLM : MonoBehaviour [ModelAdvanced] public int batchSize = 512; /// a base prompt to use as a base for all LLMCharacter objects [TextArea(5, 10), ChatAdvanced] public string basePrompt = ""; - + /// Boolean set to true if the server has started and is ready to receive requests, false otherwise. + public bool modelDownloaded { get; protected set; } = false; /// Boolean set to true if the server has started and is ready to receive requests, false otherwise. public bool started { get; protected set; } = false; /// Boolean set to true if the server has failed to start. @@ -101,10 +107,12 @@ public class LLM : MonoBehaviour StreamWrapper logStreamWrapper = null; Thread llmThread = null; List streamWrappers = new List(); + List> progressCallbacks = new List>(); public void SetModelProgress(float progress) { modelProgress = progress; + foreach (Callback progressCallback in progressCallbacks) progressCallback?.Invoke(progress); } /// \endcond @@ -122,22 +130,32 @@ async Task CopyAsset(string path) return path; } + public void ResetSelectedModel() + { + SelectedModel = 0; + modelURL = ""; + model = ""; + } + public async Task DownloadDefaultModel(int optionIndex) { // download default model and disable model editor properties until the model is set + if (optionIndex == 0) + { + ResetSelectedModel(); + return; + } SelectedModel = optionIndex; string modelUrl = LLMUnitySetup.modelOptions[optionIndex].Item2; - if (modelUrl == null) return; + modelURL = modelUrl; string modelName = Path.GetFileName(modelUrl).Split("?")[0]; await DownloadModel(modelUrl, modelName); } - public async Task DownloadModel(string modelUrl, string modelName = null, Callback progressCallback = null, bool overwrite = false) + public async Task DownloadModel(string modelUrl, string modelName, Callback progressCallback = null, bool overwrite = false) { modelProgress = 0; - if (modelName == null) modelName = model; string modelPath = LLMUnitySetup.GetAssetPath(modelName); - Callback callback = (floatArg) => { progressCallback?.Invoke(floatArg); @@ -146,9 +164,21 @@ public async Task DownloadModel(string modelUrl, string modelName = null, Callba await LLMUnitySetup.DownloadFile(modelUrl, modelPath, overwrite, SetModel, callback); } - public async Task DownloadModel(string modelUrl, Callback progressCallback = null, bool overwrite = false) + public async Task DownloadModel() + { + await DownloadModel(modelURL, model); + } + + public async Task WaitUntilModelDownloaded(Callback progressCallback = null) + { + if (progressCallback != null) progressCallbacks.Add(progressCallback); + while (!modelDownloaded) await Task.Yield(); + if (progressCallback != null) progressCallbacks.Remove(progressCallback); + } + + public async Task WaitUntilReady() { - await DownloadModel(modelUrl, null, progressCallback, overwrite); + while (!started) await Task.Yield(); } /// @@ -244,6 +274,8 @@ protected virtual string GetLlamaccpArguments() public async void Awake() { if (!enabled) return; + if (downloadOnBuild) await DownloadModel(); + modelDownloaded = true; string arguments = GetLlamaccpArguments(); if (arguments == null) return; if (asynchronousStartup) await Task.Run(() => StartLLMServer(arguments)); diff --git a/Runtime/LLMUnitySetup.cs b/Runtime/LLMUnitySetup.cs index 5b59e1f2..405173c6 100644 --- a/Runtime/LLMUnitySetup.cs +++ b/Runtime/LLMUnitySetup.cs @@ -43,6 +43,7 @@ public class LocalRemoteAttribute : PropertyAttribute {} public class RemoteAttribute : PropertyAttribute {} public class LocalAttribute : PropertyAttribute {} public class ModelAttribute : PropertyAttribute {} + public class ModelDownloadAttribute : PropertyAttribute {} public class ModelAdvancedAttribute : PropertyAttribute {} public class ChatAttribute : PropertyAttribute {} public class ChatAdvancedAttribute : PropertyAttribute {} @@ -149,7 +150,8 @@ public static void SetDebugMode(DebugModeType newDebugMode) public static string GetAssetPath(string relPath = "") { // Path to store llm server binaries and models - return Path.Combine(Application.streamingAssetsPath, relPath).Replace('\\', '/'); + string assetsDir = Application.platform == RuntimePlatform.Android ? Application.persistentDataPath : Application.streamingAssetsPath; + return Path.Combine(assetsDir, relPath).Replace('\\', '/'); } #if UNITY_EDITOR From 44ba7f24a1606753641e7b5b6e0759445bcdad0e Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Sat, 20 Jul 2024 15:05:23 +0300 Subject: [PATCH 20/30] add model url if download on build --- Editor/LLMEditor.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Editor/LLMEditor.cs b/Editor/LLMEditor.cs index ace849ce..cb22d3ff 100644 --- a/Editor/LLMEditor.cs +++ b/Editor/LLMEditor.cs @@ -45,7 +45,7 @@ public void AddModelLoaders(SerializedObject llmScriptSO, LLM llmScript) string path = EditorUtility.OpenFilePanelWithFilters("Select a gguf model file", "", new string[] { "Model Files", "gguf" }); if (!string.IsNullOrEmpty(path)) { - llmScript.SelectedModel = 0; + llmScript.ResetSelectedModel(); llmScript.SetModel(path); } }; @@ -90,6 +90,10 @@ public void AddModelSettings(SerializedObject llmScriptSO) { attributeClasses.Add(typeof(ModelAdvancedAttribute)); } + if (llmScriptSO.FindProperty("downloadOnBuild").boolValue) + { + attributeClasses.Add(typeof(ModelDownloadAttribute)); + } ShowPropertiesOfClass("", llmScriptSO, attributeClasses, false); Space(); } From d1e3d3420b9d5cb80ac07b784e9ceea18682ee2d Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Sat, 20 Jul 2024 15:06:27 +0300 Subject: [PATCH 21/30] wait on download, server ready --- Samples~/AndroidDemo/AndroidDemo.cs | 34 +++++++++++------------------ Samples~/AndroidDemo/Scene.unity | 12 +++++----- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/Samples~/AndroidDemo/AndroidDemo.cs b/Samples~/AndroidDemo/AndroidDemo.cs index 677c1db5..bf239f49 100644 --- a/Samples~/AndroidDemo/AndroidDemo.cs +++ b/Samples~/AndroidDemo/AndroidDemo.cs @@ -20,37 +20,29 @@ public class AndroidDemo : MonoBehaviour public Text progressText; int cores; - void Awake() - { - ChatPanel.SetActive(false); - DownloadPanel.SetActive(false); - } - - void Start() + async void Start() { playerText.onSubmit.AddListener(onInputFieldSubmit); playerText.interactable = false; - StartCoroutine(Loading()); + await ShowDownloadScreen(); + await WarmUp(); } - IEnumerator Loading() + async Task ShowDownloadScreen() { + ChatPanel.SetActive(false); DownloadPanel.SetActive(true); - AIText.text = "Downloading model..."; - Task downloadTask = llm.DownloadModel( - "https://huggingface.co/afrideva/smol_llama-220M-openhermes-GGUF/resolve/main/smol_llama-220m-openhermes.q4_k_m.gguf?download=true", - SetProgress - ); - while (!downloadTask.IsCompleted) yield return null; - llm.SetTemplate("alpaca"); + // await llm.WaitUntilModelDownloaded(SetProgress); DownloadPanel.SetActive(false); - ChatPanel.SetActive(true); - cores = LLMUnitySetup.AndroidGetNumBigCores(); - AIText.text += $"\nWarming up the model...\nWill use {cores} cores"; - Task warmup = llmCharacter.Warmup(); - while (!warmup.IsCompleted) yield return null; + } + async Task WarmUp() + { + llm.SetTemplate("alpaca"); + cores = LLMUnitySetup.AndroidGetNumBigCores(); + AIText.text += $"Warming up the model...\nWill use {cores} cores"; + await llmCharacter.Warmup(); AIText.text = $"Ready when you are ({cores} cores)!"; AIReplyComplete(); } diff --git a/Samples~/AndroidDemo/Scene.unity b/Samples~/AndroidDemo/Scene.unity index da2763c6..e1efa5c6 100644 --- a/Samples~/AndroidDemo/Scene.unity +++ b/Samples~/AndroidDemo/Scene.unity @@ -1323,22 +1323,24 @@ MonoBehaviour: advancedOptions: 0 remote: 0 port: 13333 - numThreads: -1 + numThreads: 2 numGPULayers: 0 - debug: 1 + debug: 0 parallelPrompts: -1 asynchronousStartup: 1 dontDestroyOnLoad: 1 - model: smol_llama-220m-openhermes.q4_k_m.gguf + model: Phi-3-mini-4k-instruct-q4.gguf + downloadOnBuild: 0 + modelURL: https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-gguf/resolve/main/Phi-3-mini-4k-instruct-q4.gguf?download=true lora: contextSize: 0 batchSize: 512 basePrompt: - SelectedModel: 0 + SelectedModel: 3 modelProgress: 1 modelCopyProgress: 1 modelHide: 1 - chatTemplate: chatml + chatTemplate: phi-3 --- !u!4 &1047848255 Transform: m_ObjectHideFlags: 0 From 80b52217c2efb34b48900e10167cdff60c52090c Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Sat, 20 Jul 2024 15:20:07 +0300 Subject: [PATCH 22/30] show progress --- Samples~/AndroidDemo/AndroidDemo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Samples~/AndroidDemo/AndroidDemo.cs b/Samples~/AndroidDemo/AndroidDemo.cs index bf239f49..681f6dfb 100644 --- a/Samples~/AndroidDemo/AndroidDemo.cs +++ b/Samples~/AndroidDemo/AndroidDemo.cs @@ -32,7 +32,7 @@ async Task ShowDownloadScreen() { ChatPanel.SetActive(false); DownloadPanel.SetActive(true); - // await llm.WaitUntilModelDownloaded(SetProgress); + await llm.WaitUntilModelDownloaded(SetProgress); DownloadPanel.SetActive(false); ChatPanel.SetActive(true); } From 8ca4b796ab5b6bd02b4c5da9d85207b41b59f418 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Sun, 21 Jul 2024 19:46:12 +0300 Subject: [PATCH 23/30] download on build --- Samples~/AndroidDemo/Scene.unity | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Samples~/AndroidDemo/Scene.unity b/Samples~/AndroidDemo/Scene.unity index e1efa5c6..81f310b9 100644 --- a/Samples~/AndroidDemo/Scene.unity +++ b/Samples~/AndroidDemo/Scene.unity @@ -1330,7 +1330,7 @@ MonoBehaviour: asynchronousStartup: 1 dontDestroyOnLoad: 1 model: Phi-3-mini-4k-instruct-q4.gguf - downloadOnBuild: 0 + downloadOnBuild: 1 modelURL: https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-gguf/resolve/main/Phi-3-mini-4k-instruct-q4.gguf?download=true lora: contextSize: 0 From bc6e019275afb0543c4b0b101eac1385befb4cec Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Sun, 21 Jul 2024 19:46:36 +0300 Subject: [PATCH 24/30] set template only when started --- Runtime/LLM.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/LLM.cs b/Runtime/LLM.cs index 31ada6c9..0635d368 100644 --- a/Runtime/LLM.cs +++ b/Runtime/LLM.cs @@ -218,7 +218,7 @@ public async Task SetLora(string path) public void SetTemplate(string templateName) { chatTemplate = templateName; - llmlib?.LLM_SetTemplate(LLMObject, chatTemplate); + if (started) llmlib?.LLM_SetTemplate(LLMObject, chatTemplate); } /// From f1955fd934dcdf064053bf68d12bca36c5eaf116 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Mon, 22 Jul 2024 11:05:42 +0300 Subject: [PATCH 25/30] use symlinks instaed of copying --- Runtime/LLM.cs | 12 +++---- Runtime/LLMCharacter.cs | 4 +-- Runtime/LLMUnitySetup.cs | 71 ++++++++++++++++++++++++---------------- Tests/Runtime/TestLLM.cs | 2 +- 4 files changed, 52 insertions(+), 37 deletions(-) diff --git a/Runtime/LLM.cs b/Runtime/LLM.cs index 0635d368..e05c939f 100644 --- a/Runtime/LLM.cs +++ b/Runtime/LLM.cs @@ -117,13 +117,13 @@ public void SetModelProgress(float progress) /// \endcond - async Task CopyAsset(string path) + string CopyAsset(string path) { #if UNITY_EDITOR if (!EditorApplication.isPlaying) { modelCopyProgress = 0; - path = await LLMUnitySetup.AddAsset(path, LLMUnitySetup.GetAssetPath()); + path = LLMUnitySetup.AddAsset(path, LLMUnitySetup.GetAssetPath()); modelCopyProgress = 1; } #endif @@ -187,10 +187,10 @@ public async Task WaitUntilReady() /// Models supported are in .gguf format. /// /// path to model to use (.gguf format) - public async Task SetModel(string path) + public void SetModel(string path) { // set the model and enable the model editor properties - model = await CopyAsset(path); + model = CopyAsset(path); SetTemplate(ChatTemplate.FromGGUF(LLMUnitySetup.GetAssetPath(model))); #if UNITY_EDITOR if (!EditorApplication.isPlaying) EditorUtility.SetDirty(this); @@ -203,9 +203,9 @@ public async Task SetModel(string path) /// Models supported are in .bin format. /// /// path to LORA model to use (.bin format) - public async Task SetLora(string path) + public void SetLora(string path) { - lora = await CopyAsset(path); + lora = CopyAsset(path); #if UNITY_EDITOR if (!EditorApplication.isPlaying) EditorUtility.SetDirty(this); #endif diff --git a/Runtime/LLMCharacter.cs b/Runtime/LLMCharacter.cs index 8d36ead9..7fb45489 100644 --- a/Runtime/LLMCharacter.cs +++ b/Runtime/LLMCharacter.cs @@ -319,10 +319,10 @@ public async Task LoadTemplate() /// Set the grammar file of the LLMCharacter /// /// path to the grammar file - public async void SetGrammar(string path) + public void SetGrammar(string path) { #if UNITY_EDITOR - if (!EditorApplication.isPlaying) path = await LLMUnitySetup.AddAsset(path, LLMUnitySetup.GetAssetPath()); + if (!EditorApplication.isPlaying) path = LLMUnitySetup.AddAsset(path, LLMUnitySetup.GetAssetPath()); #endif grammar = path; InitGrammar(); diff --git a/Runtime/LLMUnitySetup.cs b/Runtime/LLMUnitySetup.cs index 405173c6..b83865b9 100644 --- a/Runtime/LLMUnitySetup.cs +++ b/Runtime/LLMUnitySetup.cs @@ -7,6 +7,7 @@ using System; using System.IO.Compression; using System.Collections.Generic; +using System.Runtime.InteropServices; /// @defgroup llm LLM /// @defgroup template Chat Templates @@ -182,7 +183,7 @@ public static void CancelDownload(string savePath) public static async Task DownloadFile( string fileUrl, string savePath, bool overwrite = false, - TaskCallback callback = null, Callback progressCallback = null + Callback callback = null, Callback progressCallback = null ) { if (File.Exists(savePath) && !overwrite) @@ -210,13 +211,32 @@ public static async Task DownloadFile( } progressCallback?.Invoke(1f); - if (callback != null) await callback.Invoke(savePath); + callback?.Invoke(savePath); } #if UNITY_EDITOR [HideInInspector] public static float libraryProgress = 1; - public static async Task AddAsset(string assetPath, string basePath) + private static async Task DownloadLibrary() + { + if (libraryProgress < 1) return; + libraryProgress = 0; + string libZip = Path.Combine(Application.temporaryCachePath, Path.GetFileName(LlamaLibURL)); + if (!Directory.Exists(libraryPath)) + { + await DownloadFile(LlamaLibURL, libZip, true, null, SetLibraryProgress); + ZipFile.ExtractToDirectory(libZip, libraryPath); + File.Delete(libZip); + } + libraryProgress = 1; + } + + private static void SetLibraryProgress(float progress) + { + libraryProgress = progress; + } + + public static string AddAsset(string assetPath, string basePath) { if (!File.Exists(assetPath)) { @@ -231,42 +251,37 @@ public static async Task AddAsset(string assetPath, string basePath) { // if the asset is not in the assets dir copy it over fullPath = Path.Combine(basePathSlash, Path.GetFileName(assetPath)); - Log($"copying {assetPath} to {fullPath}"); AssetDatabase.StartAssetEditing(); - await Task.Run(() => + foreach (string filename in new string[] {fullPath, fullPath + ".meta"}) { - foreach (string filename in new string[] {fullPath, fullPath + ".meta"}) - { - if (File.Exists(fullPath)) - File.Delete(fullPath); - } - File.Copy(assetPath, fullPath); - }); + if (File.Exists(filename)) File.Delete(filename); + } + CreateSymlink(assetPath, fullPath); AssetDatabase.StopAssetEditing(); - Log("copying complete!"); } return fullPath.Substring(basePathSlash.Length + 1); } - private static async Task DownloadLibrary() + public static void CreateSymlink(string sourcePath, string targetPath) { - if (libraryProgress < 1) return; - libraryProgress = 0; - string libZip = Path.Combine(Application.temporaryCachePath, Path.GetFileName(LlamaLibURL)); - if (!Directory.Exists(libraryPath)) - { - await DownloadFile(LlamaLibURL, libZip, true, null, SetLibraryProgress); - ZipFile.ExtractToDirectory(libZip, libraryPath); - File.Delete(libZip); - } - libraryProgress = 1; - } + bool isDirectory = Directory.Exists(sourcePath); + if (!isDirectory && !File.Exists(sourcePath)) throw new FileNotFoundException($"Source path does not exist: {sourcePath}"); - private static void SetLibraryProgress(float progress) - { - libraryProgress = progress; + bool success; +#if UNITY_STANDALONE_WIN + success = CreateSymbolicLink(targetPath, sourcePath, (int)isDirectory); +#else + success = symlink(sourcePath, targetPath) == 0; +#endif + if (!success) throw new IOException($"Failed to create symbolic link: {targetPath}"); } + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + private static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags); + + [DllImport("libc", SetLastError = true)] + private static extern int symlink(string oldpath, string newpath); + #endif /// \endcond public static int GetMaxFreqKHz(int cpuId) diff --git a/Tests/Runtime/TestLLM.cs b/Tests/Runtime/TestLLM.cs index cbb05923..e997a327 100644 --- a/Tests/Runtime/TestLLM.cs +++ b/Tests/Runtime/TestLLM.cs @@ -33,7 +33,7 @@ public async Task Init() string modelPath = "LLMUnityTests/smol_llama-220m-openhermes.q4_k_m.gguf"; string fullModelPath = LLMUnitySetup.GetAssetPath(modelPath); await LLMUnitySetup.DownloadFile(modelUrl, fullModelPath, false, null, null); - await llm.SetModel(fullModelPath); + llm.SetModel(fullModelPath); llm.parallelPrompts = 1; llm.SetTemplate("alpaca"); llm.asynchronousStartup = false; From afad390447df83329eb2f1f3dc4395e713e91bd7 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Mon, 22 Jul 2024 20:23:46 +0300 Subject: [PATCH 26/30] move models if using URL --- Editor/LLMBuildProcessor.cs | 100 ++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 44 deletions(-) diff --git a/Editor/LLMBuildProcessor.cs b/Editor/LLMBuildProcessor.cs index a1ebecac..2e17bf84 100644 --- a/Editor/LLMBuildProcessor.cs +++ b/Editor/LLMBuildProcessor.cs @@ -8,17 +8,18 @@ namespace LLMUnity { - public class LLMBuildProcessor : IPreprocessBuildWithReport, IPostprocessBuildWithReport + public class LLMBuildProcessor : MonoBehaviour, IPreprocessBuildWithReport, IPostprocessBuildWithReport { public int callbackOrder => 0; static string tempDir = Path.Combine(Application.temporaryCachePath, "LLMBuildProcessor", Path.GetFileName(LLMUnitySetup.libraryPath)); - static string foldersMovedCache = Path.Combine(tempDir, "moved.json"); + static List movedPairs = new List(); + static string movedCache = Path.Combine(tempDir, "moved.json"); [InitializeOnLoadMethod] private static void InitializeOnLoad() { if (!Directory.Exists(tempDir)) Directory.CreateDirectory(tempDir); - else ResetLibraryPlatforms(); + else ResetMoves(); } // CALLED BEFORE THE BUILD @@ -26,8 +27,9 @@ public void OnPreprocessBuild(BuildReport report) { // Start listening for errors when build starts Application.logMessageReceived += OnBuildError; - List platforms = GetLibraryPlatformsToHide(report.summary.platform); - HideLibraryPlatforms(platforms); + HideLibraryPlatforms(report.summary.platform); + HideModels(); + if (movedPairs.Count > 0) AssetDatabase.Refresh(); } // CALLED DURING BUILD TO CHECK FOR ERRORS @@ -49,13 +51,40 @@ public void OnPostprocessBuild(BuildReport report) public void BuildCompleted() { Application.logMessageReceived -= OnBuildError; - ResetLibraryPlatforms(); + ResetMoves(); } - static List GetLibraryPlatformsToHide(BuildTarget platform) + static bool MovePath(string source, string target) + { + bool moved = false; + if (File.Exists(source)) + { + File.Move(source, target); + moved = true; + } + else if (Directory.Exists(source)) + { + Directory.Move(source, target); + moved = true; + } + if (moved) + { + movedPairs.Add(new MovedPair {source = source, target = target}); + File.WriteAllText(movedCache, JsonUtility.ToJson(new FoldersMovedWrapper { movedPairs = movedPairs })); + } + return moved; + } + + static void MoveAssetAndMeta(string source, string target) + { + MovePath(source + ".meta", target + ".meta"); + MovePath(source, target); + } + + static void HideLibraryPlatforms(BuildTarget buildPlatform) { List platforms = new List(){ "windows", "macos", "linux", "android" }; - switch (platform) + switch (buildPlatform) { case BuildTarget.StandaloneWindows: case BuildTarget.StandaloneWindows64: @@ -71,61 +100,44 @@ static List GetLibraryPlatformsToHide(BuildTarget platform) platforms.Remove("android"); break; } - return platforms; - } - static bool MovePath(string source, string target, List foldersMoved = null) - { - bool moved = false; - if (File.Exists(source)) - { - File.Move(source, target); - moved = true; - } - else if (Directory.Exists(source)) - { - Directory.Move(source, target); - moved = true; - } - if (moved && foldersMoved != null) foldersMoved.Add(new FoldersMovedPair {source = source, target = target}); - return moved; - } - - static void HideLibraryPlatforms(List platforms) - { - List foldersMoved = new List(); foreach (string dirname in Directory.GetDirectories(LLMUnitySetup.libraryPath)) { foreach (string platform in platforms) { if (Path.GetFileName(dirname).StartsWith(platform)) { - string movePath = Path.Combine(tempDir, Path.GetFileName(dirname)); - MovePath(dirname + ".meta", movePath + ".meta", foldersMoved); - MovePath(dirname, movePath, foldersMoved); - File.WriteAllText(foldersMovedCache, JsonUtility.ToJson(new FoldersMovedWrapper { foldersMoved = foldersMoved })); + MoveAssetAndMeta(dirname, Path.Combine(tempDir, Path.GetFileName(dirname))); } } } - if (foldersMoved.Count > 0) AssetDatabase.Refresh(); } - static void ResetLibraryPlatforms() + static void HideModels() + { + foreach (LLM llm in FindObjectsOfType()) + { + if (!llm.downloadOnBuild) continue; + if (llm.modelURL != "") MoveAssetAndMeta(LLMUnitySetup.GetAssetPath(llm.model), Path.Combine(tempDir, Path.GetFileName(llm.model))); + if (llm.loraURL != "") MoveAssetAndMeta(LLMUnitySetup.GetAssetPath(llm.lora), Path.Combine(tempDir, Path.GetFileName(llm.lora))); + } + } + + static void ResetMoves() { - if (!File.Exists(foldersMovedCache)) return; - List foldersMoved = JsonUtility.FromJson(File.ReadAllText(foldersMovedCache)).foldersMoved; - if (foldersMoved == null) return; + if (!File.Exists(movedCache)) return; + List movedPairs = JsonUtility.FromJson(File.ReadAllText(movedCache)).movedPairs; + if (movedPairs == null) return; bool refresh = false; - foreach (var pair in foldersMoved) refresh |= MovePath(pair.target, pair.source); + foreach (var pair in movedPairs) refresh |= MovePath(pair.target, pair.source); if (refresh) AssetDatabase.Refresh(); - - File.Delete(foldersMovedCache); + File.Delete(movedCache); } } [Serializable] - public struct FoldersMovedPair + public struct MovedPair { public string source; public string target; @@ -134,6 +146,6 @@ public struct FoldersMovedPair [Serializable] public class FoldersMovedWrapper { - public List foldersMoved; + public List movedPairs; } } From 32ab6efc3d9dfba99e595a2a0046d40cc87710a4 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Mon, 22 Jul 2024 20:25:13 +0300 Subject: [PATCH 27/30] add function to extract files from APK --- Runtime/LLMUnitySetup.cs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Runtime/LLMUnitySetup.cs b/Runtime/LLMUnitySetup.cs index b83865b9..3dd76da9 100644 --- a/Runtime/LLMUnitySetup.cs +++ b/Runtime/LLMUnitySetup.cs @@ -8,6 +8,7 @@ using System.IO.Compression; using System.Collections.Generic; using System.Runtime.InteropServices; +using UnityEngine.Networking; /// @defgroup llm LLM /// @defgroup template Chat Templates @@ -214,6 +215,43 @@ public static async Task DownloadFile( callback?.Invoke(savePath); } + public static async Task AndroidExtractFile(string assetName, bool overwrite = false, int chunkSize = 1024*1024) + { + string source = "jar:file://" + Application.dataPath + "!/assets/" + assetName; + string target = GetAssetPath(assetName); + if (!overwrite && File.Exists(target)) + { + Debug.Log($"File {target} already exists"); + return; + } + + Debug.Log($"Extracting {source} to {target}"); + + // UnityWebRequest to read the file from StreamingAssets + UnityWebRequest www = UnityWebRequest.Get(source); + // Send the request and await its completion + var operation = www.SendWebRequest(); + + while (!operation.isDone) await Task.Delay(1); + if (www.result != UnityWebRequest.Result.Success) + { + Debug.LogError("Failed to load file from StreamingAssets: " + www.error); + } + else + { + byte[] buffer = new byte[chunkSize]; + using (Stream responseStream = new MemoryStream(www.downloadHandler.data)) + using (FileStream fileStream = new FileStream(target, FileMode.Create, FileAccess.Write)) + { + int bytesRead; + while ((bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length)) > 0) + { + await fileStream.WriteAsync(buffer, 0, bytesRead); + } + } + } + } + #if UNITY_EDITOR [HideInInspector] public static float libraryProgress = 1; From 81382577ad5131c0a26a448619a4c0ffb759b812 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Mon, 22 Jul 2024 20:26:06 +0300 Subject: [PATCH 28/30] add lora and model download attributes --- Editor/LLMEditor.cs | 9 ++++----- Editor/PropertyEditor.cs | 17 +++++++++++++++-- Runtime/LLMUnitySetup.cs | 3 ++- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Editor/LLMEditor.cs b/Editor/LLMEditor.cs index cb22d3ff..cb778f9c 100644 --- a/Editor/LLMEditor.cs +++ b/Editor/LLMEditor.cs @@ -86,15 +86,14 @@ public void AddModelAddonLoaders(SerializedObject llmScriptSO, LLM llmScript, bo public void AddModelSettings(SerializedObject llmScriptSO) { List attributeClasses = new List { typeof(ModelAttribute) }; + List excludeAttributeClasses = new List { typeof(ModelDownloadAttribute), typeof(ModelDownloadAdvancedAttribute) }; + if (llmScriptSO.FindProperty("downloadOnBuild").boolValue) excludeAttributeClasses.Remove(typeof(ModelDownloadAttribute)); if (llmScriptSO.FindProperty("advancedOptions").boolValue) { attributeClasses.Add(typeof(ModelAdvancedAttribute)); + if (llmScriptSO.FindProperty("downloadOnBuild").boolValue) excludeAttributeClasses.Remove(typeof(ModelDownloadAdvancedAttribute)); } - if (llmScriptSO.FindProperty("downloadOnBuild").boolValue) - { - attributeClasses.Add(typeof(ModelDownloadAttribute)); - } - ShowPropertiesOfClass("", llmScriptSO, attributeClasses, false); + ShowPropertiesOfClass("", llmScriptSO, attributeClasses, false, excludeAttributeClasses); Space(); } diff --git a/Editor/PropertyEditor.cs b/Editor/PropertyEditor.cs index 7229804a..e5175a87 100644 --- a/Editor/PropertyEditor.cs +++ b/Editor/PropertyEditor.cs @@ -94,10 +94,23 @@ public List GetPropertiesOfClass(SerializedObject so, List attributeClasses, bool addSpace = true) + public void ShowPropertiesOfClass(string title, SerializedObject so, List attributeClasses, bool addSpace = true, List excludeAttributeClasses = null) { // display a property if it belongs to a certain class and/or has a specific attribute class List properties = GetPropertiesOfClass(so, attributeClasses); + if (excludeAttributeClasses != null) + { + List excludeProperties = GetPropertiesOfClass(so, excludeAttributeClasses); + List removeProperties = new List(); + foreach (SerializedProperty excprop in excludeProperties) + { + foreach (SerializedProperty prop in properties) + { + if (prop.displayName == excprop.displayName) removeProperties.Add(prop); + } + } + foreach (SerializedProperty prop in removeProperties) properties.Remove(prop); + } if (properties.Count == 0) return; if (title != "") EditorGUILayout.LabelField(title, EditorStyles.boldLabel); foreach (SerializedProperty prop in properties) @@ -141,7 +154,7 @@ public Attribute GetPropertyAttribute(SerializedProperty prop, Type attributeCla { foreach (Attribute attr in fieldInfo.GetCustomAttributes(attributeClass, true)) { - if (attr.GetType() == attributeClass) + if (attributeClass.IsAssignableFrom(attr.GetType())) return attr; } } diff --git a/Runtime/LLMUnitySetup.cs b/Runtime/LLMUnitySetup.cs index 3dd76da9..bc9d9fb1 100644 --- a/Runtime/LLMUnitySetup.cs +++ b/Runtime/LLMUnitySetup.cs @@ -45,7 +45,8 @@ public class LocalRemoteAttribute : PropertyAttribute {} public class RemoteAttribute : PropertyAttribute {} public class LocalAttribute : PropertyAttribute {} public class ModelAttribute : PropertyAttribute {} - public class ModelDownloadAttribute : PropertyAttribute {} + public class ModelDownloadAttribute : ModelAttribute {} + public class ModelDownloadAdvancedAttribute : ModelAdvancedAttribute {} public class ModelAdvancedAttribute : PropertyAttribute {} public class ChatAttribute : PropertyAttribute {} public class ChatAdvancedAttribute : PropertyAttribute {} From 82be503f3528bb117cc27b2a5f53ac06cdda0592 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Mon, 22 Jul 2024 20:26:44 +0300 Subject: [PATCH 29/30] download or extract model and lora --- Runtime/LLM.cs | 63 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/Runtime/LLM.cs b/Runtime/LLM.cs index e05c939f..43ac2799 100644 --- a/Runtime/LLM.cs +++ b/Runtime/LLM.cs @@ -68,17 +68,20 @@ public class LLM : MonoBehaviour [LLMAdvanced] public bool asynchronousStartup = true; /// select to not destroy the LLM GameObject when loading a new Scene. [LLMAdvanced] public bool dontDestroyOnLoad = true; + /// toggle to enable model download on build + [Model] public bool downloadOnBuild = false; /// the path of the model being used (relative to the Assets/StreamingAssets folder). /// Models with .gguf format are allowed. [Model] public string model = ""; - /// toggle to enable model download on build - [Model] public bool downloadOnBuild = false; /// the URL of the model to use. /// Models with .gguf format are allowed. [ModelDownload] public string modelURL = ""; /// the path of the LORA model being used (relative to the Assets/StreamingAssets folder). /// Models with .bin format are allowed. [ModelAdvanced] public string lora = ""; + /// the URL of the LORA to use. + /// Models with .bin format are allowed. + [ModelDownloadAdvanced] public string loraURL = ""; /// Size of the prompt context (0 = context size of the model). /// This is the number of tokens the model can take as input when generating responses. [ModelAdvanced] public int contextSize = 0; @@ -87,7 +90,7 @@ public class LLM : MonoBehaviour /// a base prompt to use as a base for all LLMCharacter objects [TextArea(5, 10), ChatAdvanced] public string basePrompt = ""; /// Boolean set to true if the server has started and is ready to receive requests, false otherwise. - public bool modelDownloaded { get; protected set; } = false; + public bool modelsDownloaded { get; protected set; } = false; /// Boolean set to true if the server has started and is ready to receive requests, false otherwise. public bool started { get; protected set; } = false; /// Boolean set to true if the server has failed to start. @@ -96,6 +99,7 @@ public class LLM : MonoBehaviour /// \cond HIDE public int SelectedModel = 0; [HideInInspector] public float modelProgress = 1; + [HideInInspector] public float loraProgress = 1; [HideInInspector] public float modelCopyProgress = 1; [HideInInspector] public bool modelHide = true; @@ -107,12 +111,19 @@ public class LLM : MonoBehaviour StreamWrapper logStreamWrapper = null; Thread llmThread = null; List streamWrappers = new List(); - List> progressCallbacks = new List>(); + List> modelProgressCallbacks = new List>(); + List> loraProgressCallbacks = new List>(); public void SetModelProgress(float progress) { modelProgress = progress; - foreach (Callback progressCallback in progressCallbacks) progressCallback?.Invoke(progress); + foreach (Callback modelProgressCallback in modelProgressCallbacks) modelProgressCallback?.Invoke(progress); + } + + public void SetLoraProgress(float progress) + { + loraProgress = progress; + foreach (Callback loraProgressCallback in loraProgressCallbacks) loraProgressCallback?.Invoke(progress); } /// \endcond @@ -152,28 +163,39 @@ public async Task DownloadDefaultModel(int optionIndex) await DownloadModel(modelUrl, modelName); } - public async Task DownloadModel(string modelUrl, string modelName, Callback progressCallback = null, bool overwrite = false) + public async Task DownloadModel(string modelUrl, string modelName, bool overwrite = false) { modelProgress = 0; string modelPath = LLMUnitySetup.GetAssetPath(modelName); - Callback callback = (floatArg) => - { - progressCallback?.Invoke(floatArg); - SetModelProgress(floatArg); - }; - await LLMUnitySetup.DownloadFile(modelUrl, modelPath, overwrite, SetModel, callback); + await LLMUnitySetup.DownloadFile(modelUrl, modelPath, overwrite, SetModel, SetModelProgress); + } + + public async Task DownloadLora(string loraUrl, string loraName, bool overwrite = false) + { + loraProgress = 0; + string loraPath = LLMUnitySetup.GetAssetPath(loraName); + await LLMUnitySetup.DownloadFile(loraUrl, loraPath, overwrite, SetLora, SetLoraProgress); + } + + public async Task DownloadModels() + { + if (modelURL != "") await DownloadModel(modelURL, model); + if (loraURL != "") await DownloadLora(loraURL, lora); } - public async Task DownloadModel() + public async Task AndroidExtractModels() { - await DownloadModel(modelURL, model); + if (!downloadOnBuild || modelURL == "") await LLMUnitySetup.AndroidExtractFile(model); + if (!downloadOnBuild || loraURL == "") await LLMUnitySetup.AndroidExtractFile(lora); } - public async Task WaitUntilModelDownloaded(Callback progressCallback = null) + public async Task WaitUntilModelsDownloaded(Callback modelProgressCallback = null, Callback loraProgressCallback = null) { - if (progressCallback != null) progressCallbacks.Add(progressCallback); - while (!modelDownloaded) await Task.Yield(); - if (progressCallback != null) progressCallbacks.Remove(progressCallback); + if (modelProgressCallback != null) modelProgressCallbacks.Add(modelProgressCallback); + if (loraProgressCallback != null) loraProgressCallbacks.Add(loraProgressCallback); + while (!modelsDownloaded) await Task.Yield(); + if (modelProgressCallback != null) modelProgressCallbacks.Remove(modelProgressCallback); + if (loraProgressCallback != null) loraProgressCallbacks.Remove(loraProgressCallback); } public async Task WaitUntilReady() @@ -274,8 +296,9 @@ protected virtual string GetLlamaccpArguments() public async void Awake() { if (!enabled) return; - if (downloadOnBuild) await DownloadModel(); - modelDownloaded = true; + if (downloadOnBuild) await DownloadModels(); + modelsDownloaded = true; + if (Application.platform == RuntimePlatform.Android) await AndroidExtractModels(); string arguments = GetLlamaccpArguments(); if (arguments == null) return; if (asynchronousStartup) await Task.Run(() => StartLLMServer(arguments)); From 5833951a24b577e6e376af10c5910b7b41937bf1 Mon Sep 17 00:00:00 2001 From: Antonis Makropoulos Date: Mon, 22 Jul 2024 20:52:13 +0300 Subject: [PATCH 30/30] do not change template when downloading models at start --- Runtime/LLM.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Runtime/LLM.cs b/Runtime/LLM.cs index 43ac2799..76e31ab4 100644 --- a/Runtime/LLM.cs +++ b/Runtime/LLM.cs @@ -163,11 +163,11 @@ public async Task DownloadDefaultModel(int optionIndex) await DownloadModel(modelUrl, modelName); } - public async Task DownloadModel(string modelUrl, string modelName, bool overwrite = false) + public async Task DownloadModel(string modelUrl, string modelName, bool overwrite = false, bool setTemplate = true) { modelProgress = 0; string modelPath = LLMUnitySetup.GetAssetPath(modelName); - await LLMUnitySetup.DownloadFile(modelUrl, modelPath, overwrite, SetModel, SetModelProgress); + await LLMUnitySetup.DownloadFile(modelUrl, modelPath, overwrite, (string path) => SetModel(path, setTemplate), SetModelProgress); } public async Task DownloadLora(string loraUrl, string loraName, bool overwrite = false) @@ -177,10 +177,10 @@ public async Task DownloadLora(string loraUrl, string loraName, bool overwrite = await LLMUnitySetup.DownloadFile(loraUrl, loraPath, overwrite, SetLora, SetLoraProgress); } - public async Task DownloadModels() + public async Task DownloadModels(bool overwrite = false) { - if (modelURL != "") await DownloadModel(modelURL, model); - if (loraURL != "") await DownloadLora(loraURL, lora); + if (modelURL != "") await DownloadModel(modelURL, model, overwrite, false); + if (loraURL != "") await DownloadLora(loraURL, lora, overwrite); } public async Task AndroidExtractModels() @@ -209,11 +209,11 @@ public async Task WaitUntilReady() /// Models supported are in .gguf format. /// /// path to model to use (.gguf format) - public void SetModel(string path) + public void SetModel(string path, bool setTemplate = true) { // set the model and enable the model editor properties model = CopyAsset(path); - SetTemplate(ChatTemplate.FromGGUF(LLMUnitySetup.GetAssetPath(model))); + if (setTemplate) SetTemplate(ChatTemplate.FromGGUF(LLMUnitySetup.GetAssetPath(model))); #if UNITY_EDITOR if (!EditorApplication.isPlaying) EditorUtility.SetDirty(this); #endif