diff --git a/.github/doxygen/Doxyfile b/.github/doxygen/Doxyfile
index 39172320..80ef0fa9 100644
--- a/.github/doxygen/Doxyfile
+++ b/.github/doxygen/Doxyfile
@@ -48,7 +48,7 @@ PROJECT_NAME = "LLM for Unity"
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = v3.0.2
+PROJECT_NUMBER = v3.0.3
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0c82d67e..1ceed088 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+## v3.0.3
+#### 🚀 Features
+
+- Support Qwen 3.5 models (PR: #391)
+- Upgrade LlamaLib to v2.0.4 (llama.cpp b8209) (PR: #391)
+- add button to redownload LlamaLib (PR: #393)
+
+
## v3.0.2
#### 🚀 Features
diff --git a/CHANGELOG.release.md b/CHANGELOG.release.md
index 13c884ca..1f61b2f2 100644
--- a/CHANGELOG.release.md
+++ b/CHANGELOG.release.md
@@ -1,13 +1,6 @@
### 🚀 Features
-- Cache LlamaLib to prevent re-downloads (PR: #386)
-- Implement strategies for context overflow (chat truncation, chat summarization) (PR: #384)
-- Upgrade LlamaLib to v2.0.4 (PR: #384)
-- Re-introduce UI dropdown for level of debug messages (PR: #384)
-
-### 🐛 Fixes
-
-- Fix context overflow with caching and overflow strategies (PR: #384)
-- Ensure macOS build includes the required runtime library (PR: #382)
-- Fix inference for AMD GPUs using Vulkan (PR: #384)
+- Support Qwen 3.5 models (PR: #391)
+- Upgrade LlamaLib to v2.0.4 (llama.cpp b8209) (PR: #391)
+- add button to redownload LlamaLib (PR: #393)
diff --git a/Editor/LLMEditor.cs b/Editor/LLMEditor.cs
index adf34e87..e0204cf6 100644
--- a/Editor/LLMEditor.cs
+++ b/Editor/LLMEditor.cs
@@ -280,6 +280,24 @@ async Task createButtons()
}
}
+ public override async Task AddOtherToggles()
+ {
+ if (GUILayout.Button("Redownload libraries", GUILayout.Width(buttonWidth)))
+ {
+ bool confirmed = EditorUtility.DisplayDialog(
+ "Confirm Action",
+ "Are you sure you want to redownload the libraries?",
+ "Yes",
+ "Cancel"
+ );
+
+ if (confirmed)
+ {
+ await LLMUnitySetup.RedownloadLibrary();
+ }
+ }
+ }
+
async Task AddLoadButtons()
{
if (showCustomURL) await createCustomURLField();
diff --git a/Editor/PropertyEditor.cs b/Editor/PropertyEditor.cs
index 8bab355f..48ca83b4 100644
--- a/Editor/PropertyEditor.cs
+++ b/Editor/PropertyEditor.cs
@@ -3,6 +3,7 @@
using UnityEngine;
using System.Reflection;
using System.Collections.Generic;
+using System.Threading.Tasks;
namespace LLMUnity
{
@@ -96,11 +97,17 @@ public virtual void AddLogo()
}
}
+ public virtual async Task AddOtherToggles()
+ {
+ await Task.CompletedTask;
+ }
+
public virtual void AddOptionsToggles(SerializedObject llmScriptSO)
{
AddLogo();
AddAdvancedOptionsToggle(llmScriptSO);
AddDebugModeToggle();
+ _ = AddOtherToggles();
Space();
}
diff --git a/Runtime/LLMUnitySetup.cs b/Runtime/LLMUnitySetup.cs
index f12d5c54..2b40526c 100644
--- a/Runtime/LLMUnitySetup.cs
+++ b/Runtime/LLMUnitySetup.cs
@@ -98,9 +98,9 @@ public class LLMUnitySetup
{
// DON'T CHANGE! the version is autocompleted with a GitHub action
/// LLM for Unity version
- public static string Version = "v3.0.2";
+ public static string Version = "v3.0.3";
/// LlamaLib version
- public static string LlamaLibVersion = "v2.0.4";
+ public static string LlamaLibVersion = "v2.0.5";
/// LlamaLib release url
public static string LlamaLibReleaseURL = $"https://github.com/undreamai/LlamaLib/releases/download/{LlamaLibVersion}";
/// LlamaLib name
@@ -115,6 +115,8 @@ public class LLMUnitySetup
public static string modelDownloadPath = Path.Combine(LLMUnityStore, "models");
/// cache download path
public static string cacheDownloadPath = Path.Combine(LLMUnityStore, "cache");
+ public static string cacheZipPath = Path.Combine(cacheDownloadPath, Path.GetFileName(LlamaLibURL));
+ public static string cacheZipHashPath = cacheZipPath + ".sha256";
/// Path of file with build information for runtime
public static string LLMManagerPath = GetAssetPath("LLMManager.json");
@@ -131,8 +133,8 @@ public class LLMUnitySetup
}},
{"Medium models (up to 10B)", new(string, string, string)[]
{
+ ("Qwen 3.5 9B", "https://huggingface.co/unsloth/Qwen3.5-9B-GGUF/resolve/main/Qwen3.5-9B-Q4_K_M.gguf", null),
("Llama 3.1 8B", "https://huggingface.co/bartowski/Meta-Llama-3.1-8B-Instruct-GGUF/resolve/main/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf", "https://huggingface.co/meta-llama/Meta-Llama-3.1-8B/blob/main/LICENSE"),
- ("Qwen 3 8B", "https://huggingface.co/unsloth/Qwen3-8B-GGUF/resolve/main/Qwen3-8B-Q4_K_M.gguf", null),
("DeepSeek R1 Distill Llama 8B", "https://huggingface.co/lmstudio-community/DeepSeek-R1-Distill-Llama-8B-GGUF/resolve/main/DeepSeek-R1-Distill-Llama-8B-Q4_K_M.gguf", null),
("DeepSeek R1 Distill Qwen 7B", "https://huggingface.co/lmstudio-community/DeepSeek-R1-Distill-Qwen-7B-GGUF/resolve/main/DeepSeek-R1-Distill-Qwen-7B-Q4_K_M.gguf", null),
("Gemma 2 9B it", "https://huggingface.co/bartowski/gemma-2-9b-it-GGUF/resolve/main/gemma-2-9b-it-Q4_K_M.gguf", "https://ai.google.dev/gemma/terms"),
@@ -141,17 +143,17 @@ public class LLMUnitySetup
}},
{"Small models (up to 5B)", new(string, string, string)[]
{
+ ("Qwen 3.5 4B", "https://huggingface.co/unsloth/Qwen3.5-4B-GGUF/resolve/main/Qwen3.5-4B-Q4_K_M.gguf", null),
("Llama 3.2 3B", "https://huggingface.co/hugging-quants/Llama-3.2-3B-Instruct-Q4_K_M-GGUF/resolve/main/llama-3.2-3b-instruct-q4_k_m.gguf", "https://huggingface.co/meta-llama/Llama-3.2-1B/blob/main/LICENSE.txt"),
("Gemma 3 4B", "https://huggingface.co/lmstudio-community/gemma-3-4b-it-GGUF/resolve/main/gemma-3-4b-it-Q4_K_M.gguf", "https://ai.google.dev/gemma/terms"),
("Phi 4 4B", "https://huggingface.co/bartowski/microsoft_Phi-4-mini-instruct-GGUF/resolve/main/microsoft_Phi-4-mini-instruct-Q4_K_M.gguf", null),
- ("Qwen 3 4B", "https://huggingface.co/unsloth/Qwen3-4B-GGUF/resolve/main/Qwen3-4B-Q4_K_M.gguf", null),
}},
{"Tiny models (up to 2B)", new(string, string, string)[]
{
+ ("Qwen 3.5 2B", "https://huggingface.co/unsloth/Qwen3.5-2B-GGUF/resolve/main/Qwen3.5-2B-Q4_K_M.gguf", null),
+ ("Qwen 3.5 0.8B", "https://huggingface.co/unsloth/Qwen3.5-0.8B-GGUF/resolve/main/Qwen3.5-0.8B-Q4_K_M.gguf", null),
("Llama 3.2 1B", "https://huggingface.co/hugging-quants/Llama-3.2-1B-Instruct-Q4_K_M-GGUF/resolve/main/llama-3.2-1b-instruct-q4_k_m.gguf", "https://huggingface.co/meta-llama/Llama-3.2-1B/blob/main/LICENSE.txt"),
("Gemma 3 1B", "https://huggingface.co/lmstudio-community/gemma-3-1b-it-GGUF/resolve/main/gemma-3-1b-it-Q4_K_M.gguf", "https://ai.google.dev/gemma/terms"),
- ("Qwen 3 1.7B", "https://huggingface.co/unsloth/Qwen3-1.7B-GGUF/resolve/main/Qwen3-1.7B-Q4_K_M.gguf", null),
- ("Qwen 3 0.6B", "https://huggingface.co/unsloth/Qwen3-0.6B-GGUF/resolve/main/Qwen3-0.6B-Q4_K_M.gguf", null),
("DeepSeek R1 Distill Qwen 1.5B", "https://huggingface.co/lmstudio-community/DeepSeek-R1-Distill-Qwen-1.5B-GGUF/resolve/main/DeepSeek-R1-Distill-Qwen-1.5B-Q4_K_M.gguf", null),
}},
{"RAG models", new(string, string, string)[]
@@ -195,9 +197,11 @@ public static void LogWarning(string message)
public static void LogError(string message, bool throwException = false)
{
- if ((int)DebugMode > (int)DebugModeType.Error) return;
- Debug.LogError(message);
- foreach (Action errorCallback in errorCallbacks) errorCallback(message);
+ if ((int)DebugMode <= (int)DebugModeType.Error || throwException)
+ {
+ Debug.LogError(message);
+ foreach (Action errorCallback in errorCallbacks) errorCallback(message);
+ }
if (throwException) throw new LLMUnityException(message);
}
@@ -453,45 +457,45 @@ static void ExtractInsideDirectory(string zipPath, string extractPath, string pr
}
}
- static async Task DownloadAndExtractInsideDirectory(string url, string path, string setupDir)
+ static async Task DownloadAndExtractInsideDirectory()
{
- string urlName = Path.GetFileName(url);
- string zipPath = Path.Combine(cacheDownloadPath, urlName);
- string setupFile = Path.Combine(setupDir, urlName + ".complete");
+ string setupDir = Path.Combine(libraryPath, "setup");
+ Directory.CreateDirectory(setupDir);
+
+ string setupFile = Path.Combine(setupDir, Path.GetFileName(LlamaLibURL) + ".complete");
if (File.Exists(setupFile)) return;
Directory.CreateDirectory(cacheDownloadPath);
foreach (string existingZipPath in Directory.GetFiles(cacheDownloadPath, "*.zip"))
{
- if (existingZipPath != zipPath)
+ if (existingZipPath != cacheZipPath)
{
- Debug.Log(existingZipPath);
File.Delete(existingZipPath);
}
}
- string hashurl = url + ".sha256";
- string hashPath = zipPath + ".sha256";
- string hash = File.Exists(hashPath)? File.ReadAllText(hashPath).Trim() : "";
+ string hashurl = LlamaLibURL + ".sha256";
+ string cacheZipNewHashPath = cacheZipHashPath + ".new";
+ string hash = File.Exists(cacheZipHashPath)? File.ReadAllText(cacheZipHashPath).Trim() : "";
bool same_hash = false;
try
{
new ResumingWebClient().GetURLFileSize(hashurl); // avoid showing error if url doesn't exist
- await DownloadFile(hashurl, hashPath+".new", debug: false);
- same_hash = File.ReadAllText(hashPath+".new").Trim() == hash;
+ await DownloadFile(hashurl, cacheZipNewHashPath, debug: false);
+ same_hash = File.ReadAllText(cacheZipNewHashPath).Trim() == hash;
} catch {}
- if (!File.Exists(zipPath) || !same_hash) await DownloadFile(url, zipPath, true, null, SetLibraryProgress);
+ if (!File.Exists(cacheZipPath) || !same_hash) await DownloadFile(LlamaLibURL, cacheZipPath, true, null, SetLibraryProgress);
AssetDatabase.StartAssetEditing();
- ExtractInsideDirectory(zipPath, path, $"{libraryName}/runtimes/");
+ ExtractInsideDirectory(cacheZipPath, libraryPath, $"{libraryName}/runtimes/");
CreateEmptyFile(setupFile);
AssetDatabase.StopAssetEditing();
- if (File.Exists(hashPath+".new"))
+ if (File.Exists(cacheZipNewHashPath))
{
- if (File.Exists(hashPath)) File.Delete(hashPath);
- File.Move(hashPath+".new", hashPath);
+ if (File.Exists(cacheZipHashPath)) File.Delete(cacheZipHashPath);
+ File.Move(cacheZipNewHashPath, cacheZipHashPath);
}
}
@@ -526,26 +530,41 @@ static void DeleteEarlierVersions()
static async Task DownloadLibrary()
{
if (libraryProgress < 1) return;
- libraryProgress = 0;
-
try
{
DeleteEarlierVersions();
-
- string setupDir = Path.Combine(libraryPath, "setup");
- Directory.CreateDirectory(setupDir);
-
- // setup LlamaLib in StreamingAssets
- await DownloadAndExtractInsideDirectory(LlamaLibURL, libraryPath, setupDir);
}
catch (Exception e)
{
LogError(e.Message);
}
+ for (int i=1; i<=3; i++)
+ {
+ if (i > 1) Log("Downloading LlamaLib failed, try #" + i);
+ libraryProgress = 0;
+ try
+ {
+ await DownloadAndExtractInsideDirectory();
+ break;
+ }
+ catch (Exception e)
+ {
+ LogError(e.Message);
+ }
+ }
+
libraryProgress = 1;
}
+ public static async Task RedownloadLibrary()
+ {
+ if (File.Exists(cacheZipPath)) File.Delete(cacheZipPath);
+ if (File.Exists(cacheZipHashPath)) File.Delete(cacheZipHashPath);
+ if (Directory.Exists(libraryPath)) Directory.Delete(libraryPath, true);
+ await DownloadLibrary();
+ }
+
private static void SetLibraryProgress(float progress)
{
libraryProgress = Math.Min(0.99f, progress);
diff --git a/Tests/Editor/TestLLM.cs b/Tests/Editor/TestLLM.cs
index 937cfbd2..8189fd39 100644
--- a/Tests/Editor/TestLLM.cs
+++ b/Tests/Editor/TestLLM.cs
@@ -170,7 +170,7 @@ public virtual void SetParameters()
}
else
{
- reply1 = "Sure! Here's a fun fact: Ants are among the most common insects, often found in human homes, and they play an important role in soil fertility by breaking down organic matter.";
+ reply1 = "Sure! Here's a fun fact: Ants work together to build complex structures like nests, which is a fascinating example of teamwork.";
reply2 = "Of course! Here's a fun fact: Ants are among the most common insects, often found in human homes, and they play an important role in soil fertility by breaking down organic matter.";
}
tokens1 = 20;
@@ -336,7 +336,7 @@ public void TestChat(string reply, string replyGT)
var commonWords = words1.Intersect(words2).Count();
var totalWords = Math.Max(words1.Length, words2.Length);
- Assert.That((double)commonWords / totalWords >= 0.5);
+ Assert.That((double)commonWords / totalWords >= 0.25);
}
public void TestPostChat(int num)
@@ -566,7 +566,7 @@ public override void SetParameters()
else
{
reply1 = "Ants are known for their ability to build complex structures, such as hexagons, which is a key feature of their social behavior.";
- reply2 = "Of course! Ants are so sneaky and clever that they can even build intricate nests with just a few minutes of effort—no matter where you start!";
+ reply2 = "Of course! Ants are so smart—well, they’re not really!";
}
}
@@ -617,7 +617,7 @@ public override void SetParameters()
if (Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer)
{
reply1 = "Sure! Here's a fun fact: Ants are among the most common insects, often found in human homes or gardens.";
- reply2 = "Of course! Here’s a fun fact: Ants are so tiny that they can fit into the space between your fingers, but they’re actually super effective at building nests and keeping the soil alive.";
+ reply2 = "Of course! \"Ants are the most efficient insects on Earth—working together to build intricate nests and survive!\"";
}
else
{
diff --git a/VERSION b/VERSION
index 96506fd2..e314328a 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v3.0.2
+v3.0.3
diff --git a/package.json b/package.json
index 33dceeaf..609ffc18 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ai.undream.llm",
- "version": "3.0.2",
+ "version": "3.0.3",
"displayName": "LLM for Unity",
"description": "LLM for Unity allows to run and distribute Large Language Models (LLMs) in the Unity engine.",
"unity": "2022.3",